| /* |
| * Xing VBR tagging for LAME. |
| * |
| * Copyright (c) 1999 A.L. Faber |
| * Copyright (c) 2001 Jonathan Dee |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the |
| * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
| * Boston, MA 02111-1307, USA. |
| */ |
| |
| /* $Id: VbrTag.c,v 1.94.2.2 2010/02/20 21:01:49 robert Exp $ */ |
| |
| #ifdef HAVE_CONFIG_H |
| # include <config.h> |
| #endif |
| |
| #include "lame.h" |
| #include "machine.h" |
| #include "encoder.h" |
| #include "util.h" |
| #include "bitstream.h" |
| #include "VbrTag.h" |
| #include "lame_global_flags.h" |
| |
| #ifdef __sun__ |
| /* woraround for SunOS 4.x, it has SEEK_* defined here */ |
| #include <unistd.h> |
| #endif |
| |
| |
| #ifdef _DEBUG |
| /* #define DEBUG_VBRTAG */ |
| #endif |
| |
| /* |
| * 4 bytes for Header Tag |
| * 4 bytes for Header Flags |
| * 100 bytes for entry (NUMTOCENTRIES) |
| * 4 bytes for FRAME SIZE |
| * 4 bytes for STREAM_SIZE |
| * 4 bytes for VBR SCALE. a VBR quality indicator: 0=best 100=worst |
| * 20 bytes for LAME tag. for example, "LAME3.12 (beta 6)" |
| * ___________ |
| * 140 bytes |
| */ |
| #define VBRHEADERSIZE (NUMTOCENTRIES+4+4+4+4+4) |
| |
| #define LAMEHEADERSIZE (VBRHEADERSIZE + 9 + 1 + 1 + 8 + 1 + 1 + 3 + 1 + 1 + 2 + 4 + 2 + 2) |
| |
| /* the size of the Xing header (MPEG1 and MPEG2) in kbps */ |
| #define XING_BITRATE1 128 |
| #define XING_BITRATE2 64 |
| #define XING_BITRATE25 32 |
| |
| |
| |
| static const char VBRTag0[] = { "Xing" }; |
| static const char VBRTag1[] = { "Info" }; |
| |
| |
| |
| |
| /* Lookup table for fast CRC computation |
| * See 'CRC_update_lookup' |
| * Uses the polynomial x^16+x^15+x^2+1 */ |
| |
| static const unsigned int crc16_lookup[256] = { |
| 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, |
| 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, |
| 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, |
| 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, |
| 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, |
| 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, |
| 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, |
| 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, |
| 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, |
| 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, |
| 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, |
| 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, |
| 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, |
| 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, |
| 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, |
| 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, |
| 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, |
| 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, |
| 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, |
| 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, |
| 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, |
| 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, |
| 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, |
| 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, |
| 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, |
| 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, |
| 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, |
| 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, |
| 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, |
| 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, |
| 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, |
| 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 |
| }; |
| |
| |
| |
| |
| |
| /*********************************************************************** |
| * Robert Hegemann 2001-01-17 |
| ***********************************************************************/ |
| |
| static void |
| addVbr(VBR_seek_info_t * v, int bitrate) |
| { |
| int i; |
| |
| v->nVbrNumFrames++; |
| v->sum += bitrate; |
| v->seen++; |
| |
| if (v->seen < v->want) { |
| return; |
| } |
| |
| if (v->pos < v->size) { |
| v->bag[v->pos] = v->sum; |
| v->pos++; |
| v->seen = 0; |
| } |
| if (v->pos == v->size) { |
| for (i = 1; i < v->size; i += 2) { |
| v->bag[i / 2] = v->bag[i]; |
| } |
| v->want *= 2; |
| v->pos /= 2; |
| } |
| } |
| |
| static void |
| Xing_seek_table(VBR_seek_info_t * v, unsigned char *t) |
| { |
| int i, indx; |
| int seek_point; |
| |
| if (v->pos <= 0) |
| return; |
| |
| for (i = 1; i < NUMTOCENTRIES; ++i) { |
| float j = i / (float) NUMTOCENTRIES, act, sum; |
| indx = (int) (floor(j * v->pos)); |
| if (indx > v->pos - 1) |
| indx = v->pos - 1; |
| act = v->bag[indx]; |
| sum = v->sum; |
| seek_point = (int) (256. * act / sum); |
| if (seek_point > 255) |
| seek_point = 255; |
| t[i] = seek_point; |
| } |
| } |
| |
| #ifdef DEBUG_VBR_SEEKING_TABLE |
| static void |
| print_seeking(unsigned char *t) |
| { |
| int i; |
| |
| printf("seeking table "); |
| for (i = 0; i < NUMTOCENTRIES; ++i) { |
| printf(" %d ", t[i]); |
| } |
| printf("\n"); |
| } |
| #endif |
| |
| |
| /**************************************************************************** |
| * AddVbrFrame: Add VBR entry, used to fill the VBR the TOC entries |
| * Paramters: |
| * nStreamPos: how many bytes did we write to the bitstream so far |
| * (in Bytes NOT Bits) |
| **************************************************************************** |
| */ |
| void |
| AddVbrFrame(lame_global_flags * gfp) |
| { |
| lame_internal_flags *gfc = gfp->internal_flags; |
| |
| int kbps = bitrate_table[gfp->version][gfc->bitrate_index]; |
| assert(gfc->VBR_seek_table.bag); |
| addVbr(&gfc->VBR_seek_table, kbps); |
| } |
| |
| |
| /*-------------------------------------------------------------*/ |
| static int |
| ExtractI4(unsigned char *buf) |
| { |
| int x; |
| /* big endian extract */ |
| x = buf[0]; |
| x <<= 8; |
| x |= buf[1]; |
| x <<= 8; |
| x |= buf[2]; |
| x <<= 8; |
| x |= buf[3]; |
| return x; |
| } |
| |
| static void |
| CreateI4(unsigned char *buf, int nValue) |
| { |
| /* big endian create */ |
| buf[0] = (nValue >> 24) & 0xff; |
| buf[1] = (nValue >> 16) & 0xff; |
| buf[2] = (nValue >> 8) & 0xff; |
| buf[3] = (nValue) & 0xff; |
| } |
| |
| |
| |
| static void |
| CreateI2(unsigned char *buf, int nValue) |
| { |
| /* big endian create */ |
| buf[0] = (nValue >> 8) & 0xff; |
| buf[1] = (nValue) & 0xff; |
| } |
| |
| /* check for magic strings*/ |
| static int |
| IsVbrTag(const unsigned char *buf) |
| { |
| int isTag0, isTag1; |
| |
| isTag0 = ((buf[0] == VBRTag0[0]) && (buf[1] == VBRTag0[1]) && (buf[2] == VBRTag0[2]) |
| && (buf[3] == VBRTag0[3])); |
| isTag1 = ((buf[0] == VBRTag1[0]) && (buf[1] == VBRTag1[1]) && (buf[2] == VBRTag1[2]) |
| && (buf[3] == VBRTag1[3])); |
| |
| return (isTag0 || isTag1); |
| } |
| |
| #define SHIFT_IN_BITS_VALUE(x,n,v) ( x = (x << (n)) | ( (v) & ~(-1 << (n)) ) ) |
| |
| static void |
| setLameTagFrameHeader(lame_global_flags const *gfp, unsigned char *buffer) |
| { |
| lame_internal_flags *gfc = gfp->internal_flags; |
| char abyte, bbyte; |
| |
| SHIFT_IN_BITS_VALUE(buffer[0], 8u, 0xffu); |
| |
| SHIFT_IN_BITS_VALUE(buffer[1], 3u, 7); |
| SHIFT_IN_BITS_VALUE(buffer[1], 1u, (gfp->out_samplerate < 16000) ? 0 : 1); |
| SHIFT_IN_BITS_VALUE(buffer[1], 1u, gfp->version); |
| SHIFT_IN_BITS_VALUE(buffer[1], 2u, 4 - 3); |
| SHIFT_IN_BITS_VALUE(buffer[1], 1u, (!gfp->error_protection) ? 1 : 0); |
| |
| SHIFT_IN_BITS_VALUE(buffer[2], 4u, gfc->bitrate_index); |
| SHIFT_IN_BITS_VALUE(buffer[2], 2u, gfc->samplerate_index); |
| SHIFT_IN_BITS_VALUE(buffer[2], 1u, 0); |
| SHIFT_IN_BITS_VALUE(buffer[2], 1u, gfp->extension); |
| |
| SHIFT_IN_BITS_VALUE(buffer[3], 2u, gfp->mode); |
| SHIFT_IN_BITS_VALUE(buffer[3], 2u, gfc->mode_ext); |
| SHIFT_IN_BITS_VALUE(buffer[3], 1u, gfp->copyright); |
| SHIFT_IN_BITS_VALUE(buffer[3], 1u, gfp->original); |
| SHIFT_IN_BITS_VALUE(buffer[3], 2u, gfp->emphasis); |
| |
| /* the default VBR header. 48 kbps layer III, no padding, no crc */ |
| /* but sampling freq, mode andy copyright/copy protection taken */ |
| /* from first valid frame */ |
| buffer[0] = (uint8_t) 0xff; |
| abyte = (buffer[1] & (unsigned char) 0xf1); |
| { |
| int bitrate; |
| if (1 == gfp->version) { |
| bitrate = XING_BITRATE1; |
| } |
| else { |
| if (gfp->out_samplerate < 16000) |
| bitrate = XING_BITRATE25; |
| else |
| bitrate = XING_BITRATE2; |
| } |
| |
| if (gfp->VBR == vbr_off) |
| bitrate = gfp->brate; |
| |
| if (gfp->free_format) |
| bbyte = 0x00; |
| else |
| bbyte = 16 * BitrateIndex(bitrate, gfp->version, gfp->out_samplerate); |
| } |
| |
| /* Use as much of the info from the real frames in the |
| * Xing header: samplerate, channels, crc, etc... |
| */ |
| if (gfp->version == 1) { |
| /* MPEG1 */ |
| buffer[1] = abyte | (char) 0x0a; /* was 0x0b; */ |
| abyte = buffer[2] & (char) 0x0d; /* AF keep also private bit */ |
| buffer[2] = (char) bbyte | abyte; /* 64kbs MPEG1 frame */ |
| } |
| else { |
| /* MPEG2 */ |
| buffer[1] = abyte | (char) 0x02; /* was 0x03; */ |
| abyte = buffer[2] & (char) 0x0d; /* AF keep also private bit */ |
| buffer[2] = (char) bbyte | abyte; /* 64kbs MPEG2 frame */ |
| } |
| } |
| |
| /*-------------------------------------------------------------*/ |
| /* Same as GetVbrTag below, but only checks for the Xing tag. |
| requires buf to contain only 40 bytes */ |
| /*-------------------------------------------------------------*/ |
| int |
| CheckVbrTag(unsigned char *buf) |
| { |
| int h_id, h_mode; |
| |
| /* get selected MPEG header data */ |
| h_id = (buf[1] >> 3) & 1; |
| h_mode = (buf[3] >> 6) & 3; |
| |
| /* determine offset of header */ |
| if (h_id) { |
| /* mpeg1 */ |
| if (h_mode != 3) |
| buf += (32 + 4); |
| else |
| buf += (17 + 4); |
| } |
| else { |
| /* mpeg2 */ |
| if (h_mode != 3) |
| buf += (17 + 4); |
| else |
| buf += (9 + 4); |
| } |
| |
| return IsVbrTag(buf); |
| } |
| |
| int |
| GetVbrTag(VBRTAGDATA * pTagData, unsigned char *buf) |
| { |
| int i, head_flags; |
| int h_bitrate, h_id, h_mode, h_sr_index; |
| int enc_delay, enc_padding; |
| |
| /* get Vbr header data */ |
| pTagData->flags = 0; |
| |
| /* get selected MPEG header data */ |
| h_id = (buf[1] >> 3) & 1; |
| h_sr_index = (buf[2] >> 2) & 3; |
| h_mode = (buf[3] >> 6) & 3; |
| h_bitrate = ((buf[2] >> 4) & 0xf); |
| h_bitrate = bitrate_table[h_id][h_bitrate]; |
| |
| /* check for FFE syncword */ |
| if ((buf[1] >> 4) == 0xE) |
| pTagData->samprate = samplerate_table[2][h_sr_index]; |
| else |
| pTagData->samprate = samplerate_table[h_id][h_sr_index]; |
| /* if( h_id == 0 ) */ |
| /* pTagData->samprate >>= 1; */ |
| |
| |
| |
| /* determine offset of header */ |
| if (h_id) { |
| /* mpeg1 */ |
| if (h_mode != 3) |
| buf += (32 + 4); |
| else |
| buf += (17 + 4); |
| } |
| else { |
| /* mpeg2 */ |
| if (h_mode != 3) |
| buf += (17 + 4); |
| else |
| buf += (9 + 4); |
| } |
| |
| if (!IsVbrTag(buf)) |
| return 0; |
| |
| buf += 4; |
| |
| pTagData->h_id = h_id; |
| |
| head_flags = pTagData->flags = ExtractI4(buf); |
| buf += 4; /* get flags */ |
| |
| if (head_flags & FRAMES_FLAG) { |
| pTagData->frames = ExtractI4(buf); |
| buf += 4; |
| } |
| |
| if (head_flags & BYTES_FLAG) { |
| pTagData->bytes = ExtractI4(buf); |
| buf += 4; |
| } |
| |
| if (head_flags & TOC_FLAG) { |
| if (pTagData->toc != NULL) { |
| for (i = 0; i < NUMTOCENTRIES; i++) |
| pTagData->toc[i] = buf[i]; |
| } |
| buf += NUMTOCENTRIES; |
| } |
| |
| pTagData->vbr_scale = -1; |
| |
| if (head_flags & VBR_SCALE_FLAG) { |
| pTagData->vbr_scale = ExtractI4(buf); |
| buf += 4; |
| } |
| |
| pTagData->headersize = ((h_id + 1) * 72000 * h_bitrate) / pTagData->samprate; |
| |
| buf += 21; |
| enc_delay = buf[0] << 4; |
| enc_delay += buf[1] >> 4; |
| enc_padding = (buf[1] & 0x0F) << 8; |
| enc_padding += buf[2]; |
| /* check for reasonable values (this may be an old Xing header, */ |
| /* not a INFO tag) */ |
| if (enc_delay < 0 || enc_delay > 3000) |
| enc_delay = -1; |
| if (enc_padding < 0 || enc_padding > 3000) |
| enc_padding = -1; |
| |
| pTagData->enc_delay = enc_delay; |
| pTagData->enc_padding = enc_padding; |
| |
| #ifdef DEBUG_VBRTAG |
| fprintf(stderr, "\n\n********************* VBR TAG INFO *****************\n"); |
| fprintf(stderr, "tag :%s\n", VBRTag); |
| fprintf(stderr, "head_flags :%d\n", head_flags); |
| fprintf(stderr, "bytes :%d\n", pTagData->bytes); |
| fprintf(stderr, "frames :%d\n", pTagData->frames); |
| fprintf(stderr, "VBR Scale :%d\n", pTagData->vbr_scale); |
| fprintf(stderr, "enc_delay = %i \n", enc_delay); |
| fprintf(stderr, "enc_padding= %i \n", enc_padding); |
| fprintf(stderr, "toc:\n"); |
| if (pTagData->toc != NULL) { |
| for (i = 0; i < NUMTOCENTRIES; i++) { |
| if ((i % 10) == 0) |
| fprintf(stderr, "\n"); |
| fprintf(stderr, " %3d", (int) (pTagData->toc[i])); |
| } |
| } |
| fprintf(stderr, "\n***************** END OF VBR TAG INFO ***************\n"); |
| #endif |
| return 1; /* success */ |
| } |
| |
| |
| /**************************************************************************** |
| * InitVbrTag: Initializes the header, and write empty frame to stream |
| * Paramters: |
| * fpStream: pointer to output file stream |
| * nMode : Channel Mode: 0=STEREO 1=JS 2=DS 3=MONO |
| **************************************************************************** |
| */ |
| int |
| InitVbrTag(lame_global_flags * gfp) |
| { |
| int kbps_header; |
| lame_internal_flags *gfc = gfp->internal_flags; |
| #define MAXFRAMESIZE 2880 /* or 0xB40, the max freeformat 640 32kHz framesize */ |
| |
| /* |
| * Xing VBR pretends to be a 48kbs layer III frame. (at 44.1kHz). |
| * (at 48kHz they use 56kbs since 48kbs frame not big enough for |
| * table of contents) |
| * let's always embed Xing header inside a 64kbs layer III frame. |
| * this gives us enough room for a LAME version string too. |
| * size determined by sampling frequency (MPEG1) |
| * 32kHz: 216 bytes@48kbs 288bytes@ 64kbs |
| * 44.1kHz: 156 bytes 208bytes@64kbs (+1 if padding = 1) |
| * 48kHz: 144 bytes 192 |
| * |
| * MPEG 2 values are the same since the framesize and samplerate |
| * are each reduced by a factor of 2. |
| */ |
| |
| |
| if (1 == gfp->version) { |
| kbps_header = XING_BITRATE1; |
| } |
| else { |
| if (gfp->out_samplerate < 16000) |
| kbps_header = XING_BITRATE25; |
| else |
| kbps_header = XING_BITRATE2; |
| } |
| |
| if (gfp->VBR == vbr_off) |
| kbps_header = gfp->brate; |
| |
| /** make sure LAME Header fits into Frame |
| */ |
| { |
| int total_frame_size = ((gfp->version + 1) * 72000 * kbps_header) / gfp->out_samplerate; |
| int header_size = (gfc->sideinfo_len + LAMEHEADERSIZE); |
| gfc->VBR_seek_table.TotalFrameSize = total_frame_size; |
| if (total_frame_size < header_size || total_frame_size > MAXFRAMESIZE) { |
| /* disable tag, it wont fit */ |
| gfp->bWriteVbrTag = 0; |
| return 0; |
| } |
| } |
| |
| gfc->VBR_seek_table.nVbrNumFrames = 0; |
| gfc->VBR_seek_table.nBytesWritten = 0; |
| gfc->VBR_seek_table.sum = 0; |
| |
| gfc->VBR_seek_table.seen = 0; |
| gfc->VBR_seek_table.want = 1; |
| gfc->VBR_seek_table.pos = 0; |
| |
| if (gfc->VBR_seek_table.bag == NULL) { |
| gfc->VBR_seek_table.bag = malloc(400 * sizeof(int)); |
| if (gfc->VBR_seek_table.bag != NULL) { |
| gfc->VBR_seek_table.size = 400; |
| } |
| else { |
| gfc->VBR_seek_table.size = 0; |
| ERRORF(gfc, "Error: can't allocate VbrFrames buffer\n"); |
| gfp->bWriteVbrTag = 0; |
| return -1; |
| } |
| } |
| |
| /* write dummy VBR tag of all 0's into bitstream */ |
| { |
| uint8_t buffer[MAXFRAMESIZE]; |
| size_t i, n; |
| |
| memset(buffer, 0, sizeof(buffer)); |
| setLameTagFrameHeader(gfp, buffer); |
| n = gfc->VBR_seek_table.TotalFrameSize; |
| for (i = 0; i < n; ++i) { |
| add_dummy_byte(gfp, buffer[i], 1); |
| } |
| } |
| /* Success */ |
| return 0; |
| } |
| |
| |
| |
| /* fast CRC-16 computation - uses table crc16_lookup 8*/ |
| static int |
| CRC_update_lookup(int value, int crc) |
| { |
| int tmp; |
| tmp = crc ^ value; |
| crc = (crc >> 8) ^ crc16_lookup[tmp & 0xff]; |
| return crc; |
| } |
| |
| void |
| UpdateMusicCRC(uint16_t * crc, unsigned char *buffer, int size) |
| { |
| int i; |
| for (i = 0; i < size; ++i) |
| *crc = CRC_update_lookup(buffer[i], *crc); |
| } |
| |
| |
| |
| |
| |
| /**************************************************************************** |
| * Jonathan Dee 2001/08/31 |
| * |
| * PutLameVBR: Write LAME info: mini version + info on various switches used |
| * Paramters: |
| * pbtStreamBuffer : pointer to output buffer |
| * id3v2size : size of id3v2 tag in bytes |
| * crc : computation of crc-16 of Lame Tag so far (starting at frame sync) |
| * |
| **************************************************************************** |
| */ |
| static int |
| PutLameVBR(lame_global_flags const *gfp, size_t nMusicLength, uint8_t * pbtStreamBuffer, uint16_t crc) |
| { |
| lame_internal_flags *gfc = gfp->internal_flags; |
| |
| int nBytesWritten = 0; |
| int i; |
| |
| int enc_delay = lame_get_encoder_delay(gfp); /* encoder delay */ |
| int enc_padding = lame_get_encoder_padding(gfp); /* encoder padding */ |
| |
| /*recall: gfp->VBR_q is for example set by the switch -V */ |
| /* gfp->quality by -q, -h, -f, etc */ |
| |
| int nQuality = (100 - 10 * gfp->VBR_q - gfp->quality); |
| |
| |
| const char *szVersion = get_lame_very_short_version(); |
| uint8_t nVBR; |
| uint8_t nRevision = 0x00; |
| uint8_t nRevMethod; |
| uint8_t vbr_type_translator[] = { 1, 5, 3, 2, 4, 0, 3 }; /*numbering different in vbr_mode vs. Lame tag */ |
| |
| uint8_t nLowpass = |
| (((gfp->lowpassfreq / 100.0) + .5) > 255 ? 255 : (gfp->lowpassfreq / 100.0) + .5); |
| |
| uint32_t nPeakSignalAmplitude = 0; |
| |
| uint16_t nRadioReplayGain = 0; |
| uint16_t nAudiophileReplayGain = 0; |
| |
| uint8_t nNoiseShaping = gfp->internal_flags->noise_shaping; |
| uint8_t nStereoMode = 0; |
| int bNonOptimal = 0; |
| uint8_t nSourceFreq = 0; |
| uint8_t nMisc = 0; |
| uint16_t nMusicCRC = 0; |
| |
| /*psy model type: Gpsycho or NsPsytune */ |
| unsigned char bExpNPsyTune = gfp->exp_nspsytune & 1; |
| unsigned char bSafeJoint = (gfp->exp_nspsytune & 2) != 0; |
| |
| unsigned char bNoGapMore = 0; |
| unsigned char bNoGapPrevious = 0; |
| |
| int nNoGapCount = gfp->internal_flags->nogap_total; |
| int nNoGapCurr = gfp->internal_flags->nogap_current; |
| |
| |
| uint8_t nAthType = gfp->ATHtype; /*4 bits. */ |
| |
| uint8_t nFlags = 0; |
| |
| /* if ABR, {store bitrate <=255} else { store "-b"} */ |
| int nABRBitrate; |
| switch (gfp->VBR) { |
| case vbr_abr:{ |
| nABRBitrate = gfp->VBR_mean_bitrate_kbps; |
| break; |
| } |
| case vbr_off:{ |
| nABRBitrate = gfp->brate; |
| break; |
| } |
| default:{ /*vbr modes */ |
| nABRBitrate = gfp->VBR_min_bitrate_kbps; |
| } |
| } |
| |
| |
| /*revision and vbr method */ |
| if (gfp->VBR < sizeof(vbr_type_translator)) |
| nVBR = vbr_type_translator[gfp->VBR]; |
| else |
| nVBR = 0x00; /*unknown. */ |
| |
| nRevMethod = 0x10 * nRevision + nVBR; |
| |
| |
| /* ReplayGain */ |
| if (gfc->findReplayGain) { |
| if (gfc->RadioGain > 0x1FE) |
| gfc->RadioGain = 0x1FE; |
| if (gfc->RadioGain < -0x1FE) |
| gfc->RadioGain = -0x1FE; |
| |
| nRadioReplayGain = 0x2000; /* set name code */ |
| nRadioReplayGain |= 0xC00; /* set originator code to `determined automatically' */ |
| |
| if (gfc->RadioGain >= 0) |
| nRadioReplayGain |= gfc->RadioGain; /* set gain adjustment */ |
| else { |
| nRadioReplayGain |= 0x200; /* set the sign bit */ |
| nRadioReplayGain |= -gfc->RadioGain; /* set gain adjustment */ |
| } |
| } |
| |
| /* peak sample */ |
| if (gfc->findPeakSample) |
| nPeakSignalAmplitude = abs((int) ((((FLOAT) gfc->PeakSample) / 32767.0) * pow(2, 23) + .5)); |
| |
| /*nogap */ |
| if (nNoGapCount != -1) { |
| if (nNoGapCurr > 0) |
| bNoGapPrevious = 1; |
| |
| if (nNoGapCurr < nNoGapCount - 1) |
| bNoGapMore = 1; |
| } |
| |
| /*flags */ |
| |
| nFlags = nAthType + (bExpNPsyTune << 4) |
| + (bSafeJoint << 5) |
| + (bNoGapMore << 6) |
| + (bNoGapPrevious << 7); |
| |
| |
| if (nQuality < 0) |
| nQuality = 0; |
| |
| /*stereo mode field... a bit ugly. */ |
| |
| switch (gfp->mode) { |
| case MONO: |
| nStereoMode = 0; |
| break; |
| case STEREO: |
| nStereoMode = 1; |
| break; |
| case DUAL_CHANNEL: |
| nStereoMode = 2; |
| break; |
| case JOINT_STEREO: |
| if (gfp->force_ms) |
| nStereoMode = 4; |
| else |
| nStereoMode = 3; |
| break; |
| case NOT_SET: |
| /* FALLTHROUGH */ |
| default: |
| nStereoMode = 7; |
| break; |
| } |
| |
| /*Intensity stereo : nStereoMode = 6. IS is not implemented */ |
| |
| if (gfp->in_samplerate <= 32000) |
| nSourceFreq = 0x00; |
| else if (gfp->in_samplerate == 48000) |
| nSourceFreq = 0x02; |
| else if (gfp->in_samplerate > 48000) |
| nSourceFreq = 0x03; |
| else |
| nSourceFreq = 0x01; /*default is 44100Hz. */ |
| |
| |
| /*Check if the user overrided the default LAME behaviour with some nasty options */ |
| |
| if (gfp->short_blocks == short_block_forced || gfp->short_blocks == short_block_dispensed || ((gfp->lowpassfreq == -1) && (gfp->highpassfreq == -1)) || /* "-k" */ |
| (gfp->scale_left < gfp->scale_right) || |
| (gfp->scale_left > gfp->scale_right) || |
| (gfp->disable_reservoir && gfp->brate < 320) || |
| gfp->noATH || gfp->ATHonly || (nAthType == 0) || gfp->in_samplerate <= 32000) |
| bNonOptimal = 1; |
| |
| nMisc = nNoiseShaping + (nStereoMode << 2) |
| + (bNonOptimal << 5) |
| + (nSourceFreq << 6); |
| |
| |
| nMusicCRC = gfc->nMusicCRC; |
| |
| |
| /*Write all this information into the stream */ |
| CreateI4(&pbtStreamBuffer[nBytesWritten], nQuality); |
| nBytesWritten += 4; |
| |
| strncpy((char *) &pbtStreamBuffer[nBytesWritten], szVersion, 9); |
| nBytesWritten += 9; |
| |
| pbtStreamBuffer[nBytesWritten] = nRevMethod; |
| nBytesWritten++; |
| |
| pbtStreamBuffer[nBytesWritten] = nLowpass; |
| nBytesWritten++; |
| |
| CreateI4(&pbtStreamBuffer[nBytesWritten], nPeakSignalAmplitude); |
| nBytesWritten += 4; |
| |
| CreateI2(&pbtStreamBuffer[nBytesWritten], nRadioReplayGain); |
| nBytesWritten += 2; |
| |
| CreateI2(&pbtStreamBuffer[nBytesWritten], nAudiophileReplayGain); |
| nBytesWritten += 2; |
| |
| pbtStreamBuffer[nBytesWritten] = nFlags; |
| nBytesWritten++; |
| |
| if (nABRBitrate >= 255) |
| pbtStreamBuffer[nBytesWritten] = 0xFF; |
| else |
| pbtStreamBuffer[nBytesWritten] = nABRBitrate; |
| nBytesWritten++; |
| |
| pbtStreamBuffer[nBytesWritten] = enc_delay >> 4; /* works for win32, does it for unix? */ |
| pbtStreamBuffer[nBytesWritten + 1] = (enc_delay << 4) + (enc_padding >> 8); |
| pbtStreamBuffer[nBytesWritten + 2] = enc_padding; |
| |
| nBytesWritten += 3; |
| |
| pbtStreamBuffer[nBytesWritten] = nMisc; |
| nBytesWritten++; |
| |
| |
| pbtStreamBuffer[nBytesWritten++] = 0; /*unused in rev0 */ |
| |
| CreateI2(&pbtStreamBuffer[nBytesWritten], gfp->preset); |
| nBytesWritten += 2; |
| |
| CreateI4(&pbtStreamBuffer[nBytesWritten], (int) nMusicLength); |
| nBytesWritten += 4; |
| |
| CreateI2(&pbtStreamBuffer[nBytesWritten], nMusicCRC); |
| nBytesWritten += 2; |
| |
| /*Calculate tag CRC.... must be done here, since it includes |
| *previous information*/ |
| |
| for (i = 0; i < nBytesWritten; i++) |
| crc = CRC_update_lookup(pbtStreamBuffer[i], crc); |
| |
| CreateI2(&pbtStreamBuffer[nBytesWritten], crc); |
| nBytesWritten += 2; |
| |
| return nBytesWritten; |
| } |
| |
| static long |
| skipId3v2(FILE * fpStream) |
| { |
| size_t nbytes; |
| long id3v2TagSize; |
| unsigned char id3v2Header[10]; |
| |
| /* seek to the beginning of the stream */ |
| if (fseek(fpStream, 0, SEEK_SET) != 0) { |
| return -2; /* not seekable, abort */ |
| } |
| /* read 10 bytes in case there's an ID3 version 2 header here */ |
| nbytes = fread(id3v2Header, 1, sizeof(id3v2Header), fpStream); |
| if (nbytes != sizeof(id3v2Header)) { |
| return -3; /* not readable, maybe opened Write-Only */ |
| } |
| /* does the stream begin with the ID3 version 2 file identifier? */ |
| if (!strncmp((char *) id3v2Header, "ID3", 3)) { |
| /* the tag size (minus the 10-byte header) is encoded into four |
| * bytes where the most significant bit is clear in each byte */ |
| id3v2TagSize = (((id3v2Header[6] & 0x7f) << 21) |
| | ((id3v2Header[7] & 0x7f) << 14) |
| | ((id3v2Header[8] & 0x7f) << 7) |
| | (id3v2Header[9] & 0x7f)) |
| + sizeof id3v2Header; |
| } |
| else { |
| /* no ID3 version 2 tag in this stream */ |
| id3v2TagSize = 0; |
| } |
| return id3v2TagSize; |
| } |
| |
| |
| |
| size_t |
| lame_get_lametag_frame(lame_global_flags const *gfp, unsigned char *buffer, size_t size) |
| { |
| lame_internal_flags *gfc; |
| int stream_size; |
| int nStreamIndex; |
| uint8_t btToc[NUMTOCENTRIES]; |
| |
| if (gfp == 0) { |
| return 0; |
| } |
| if (gfp->bWriteVbrTag == 0) { |
| return 0; |
| } |
| gfc = gfp->internal_flags; |
| if (gfc == 0) { |
| return 0; |
| } |
| if (gfc->Class_ID != LAME_ID) { |
| return 0; |
| } |
| if (gfc->VBR_seek_table.pos <= 0) { |
| return 0; |
| } |
| if (size < gfc->VBR_seek_table.TotalFrameSize) { |
| return gfc->VBR_seek_table.TotalFrameSize; |
| } |
| if (buffer == 0) { |
| return 0; |
| } |
| |
| memset(buffer, 0, gfc->VBR_seek_table.TotalFrameSize); |
| |
| /* 4 bytes frame header */ |
| |
| setLameTagFrameHeader(gfp, buffer); |
| |
| /* Clear all TOC entries */ |
| memset(btToc, 0, sizeof(btToc)); |
| |
| if (gfp->free_format) { |
| int i; |
| for (i = 1; i < NUMTOCENTRIES; ++i) |
| btToc[i] = 255 * i / 100; |
| } |
| else { |
| Xing_seek_table(&gfc->VBR_seek_table, btToc); |
| } |
| #ifdef DEBUG_VBR_SEEKING_TABLE |
| print_seeking(btToc); |
| #endif |
| |
| /* Start writing the tag after the zero frame */ |
| nStreamIndex = gfc->sideinfo_len; |
| /* note! Xing header specifies that Xing data goes in the |
| * ancillary data with NO ERROR PROTECTION. If error protecton |
| * in enabled, the Xing data still starts at the same offset, |
| * and now it is in sideinfo data block, and thus will not |
| * decode correctly by non-Xing tag aware players */ |
| if (gfp->error_protection) |
| nStreamIndex -= 2; |
| |
| /* Put Vbr tag */ |
| if (gfp->VBR == vbr_off) { |
| buffer[nStreamIndex++] = VBRTag1[0]; |
| buffer[nStreamIndex++] = VBRTag1[1]; |
| buffer[nStreamIndex++] = VBRTag1[2]; |
| buffer[nStreamIndex++] = VBRTag1[3]; |
| |
| } |
| else { |
| buffer[nStreamIndex++] = VBRTag0[0]; |
| buffer[nStreamIndex++] = VBRTag0[1]; |
| buffer[nStreamIndex++] = VBRTag0[2]; |
| buffer[nStreamIndex++] = VBRTag0[3]; |
| } |
| |
| /* Put header flags */ |
| CreateI4(&buffer[nStreamIndex], FRAMES_FLAG + BYTES_FLAG + TOC_FLAG + VBR_SCALE_FLAG); |
| nStreamIndex += 4; |
| |
| /* Put Total Number of frames */ |
| CreateI4(&buffer[nStreamIndex], gfc->VBR_seek_table.nVbrNumFrames); |
| nStreamIndex += 4; |
| |
| /* Put total audio stream size, including Xing/LAME Header */ |
| stream_size = gfc->VBR_seek_table.nBytesWritten + gfc->VBR_seek_table.TotalFrameSize; |
| CreateI4(&buffer[nStreamIndex], stream_size); |
| nStreamIndex += 4; |
| |
| /* Put TOC */ |
| memcpy(&buffer[nStreamIndex], btToc, sizeof(btToc)); |
| nStreamIndex += sizeof(btToc); |
| |
| |
| if (gfp->error_protection) { |
| /* (jo) error_protection: add crc16 information to header */ |
| CRC_writeheader(gfc, (char *) buffer); |
| } |
| { |
| /*work out CRC so far: initially crc = 0 */ |
| uint16_t crc = 0x00; |
| int i; |
| for (i = 0; i < nStreamIndex; i++) |
| crc = CRC_update_lookup(buffer[i], crc); |
| /*Put LAME VBR info */ |
| nStreamIndex += PutLameVBR(gfp, stream_size, buffer + nStreamIndex, crc); |
| } |
| |
| #ifdef DEBUG_VBRTAG |
| { |
| VBRTAGDATA TestHeader; |
| GetVbrTag(&TestHeader, buffer); |
| } |
| #endif |
| |
| return gfc->VBR_seek_table.TotalFrameSize; |
| } |
| |
| /*********************************************************************** |
| * |
| * PutVbrTag: Write final VBR tag to the file |
| * Paramters: |
| * lpszFileName: filename of MP3 bit stream |
| * nVbrScale : encoder quality indicator (0..100) |
| **************************************************************************** |
| */ |
| |
| int |
| PutVbrTag(lame_global_flags const *gfp, FILE * fpStream) |
| { |
| lame_internal_flags *gfc = gfp->internal_flags; |
| |
| long lFileSize; |
| long id3v2TagSize; |
| size_t nbytes; |
| uint8_t buffer[MAXFRAMESIZE]; |
| |
| if (gfc->VBR_seek_table.pos <= 0) |
| return -1; |
| |
| /* Seek to end of file */ |
| fseek(fpStream, 0, SEEK_END); |
| |
| /* Get file size */ |
| lFileSize = ftell(fpStream); |
| |
| /* Abort if file has zero length. Yes, it can happen :) */ |
| if (lFileSize == 0) |
| return -1; |
| |
| /* |
| * The VBR tag may NOT be located at the beginning of the stream. |
| * If an ID3 version 2 tag was added, then it must be skipped to write |
| * the VBR tag data. |
| */ |
| |
| id3v2TagSize = skipId3v2(fpStream); |
| |
| if (id3v2TagSize < 0) { |
| return id3v2TagSize; |
| } |
| |
| /*Seek to the beginning of the stream */ |
| fseek(fpStream, id3v2TagSize, SEEK_SET); |
| |
| nbytes = lame_get_lametag_frame(gfp, buffer, sizeof(buffer)); |
| if (nbytes > sizeof(buffer)) { |
| return -1; |
| } |
| |
| if (nbytes < 1) { |
| return 0; |
| } |
| |
| /* Put it all to disk again */ |
| if (fwrite(buffer, nbytes, 1, fpStream) != 1) { |
| return -1; |
| } |
| |
| return 0; /* success */ |
| } |