X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Flame%2FVbrTag.c;fp=lib%2Flame%2FVbrTag.c;h=c5ca217c14d62daade7adff9e917eea1ce7210cc;hp=0000000000000000000000000000000000000000;hb=698acf324aaa52147b1486646f6549ffd95804da;hpb=f8d07c79494e8536e682da73cee2057740a0e4db diff --git a/lib/lame/VbrTag.c b/lib/lame/VbrTag.c new file mode 100644 index 0000000..c5ca217 --- /dev/null +++ b/lib/lame/VbrTag.c @@ -0,0 +1,944 @@ +/* + * Xing VBR tagging for LAME. + * + * Copyright (c) 1999 A.L. Faber + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 Library 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.1 2002/04/28 17:30:17 kramm Exp $ */ + +#include "config_static.h" + +#include "machine.h" +#if defined(__riscos__) && defined(FPA10) +#include "ymath.h" +#else +#include +#endif + + +#include "bitstream.h" +#include "lame.h" +#include "VbrTag.h" +#include "version.h" + +#include +#include +#include + +#ifdef WITH_DMALLOC +#include +#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 + + + +const static char VBRTag[]={"Xing"}; +const static char VBRTag2[]={"Info"}; + + + + +/* Lookup table for fast CRC computation + * See 'CRC_update_lookup' + * Uses the polynomial x^16+x^15+x^2+1 */ + +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->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, index; + int seek_point; + + if (v->pos <= 0) + return; + + for (i = 1; i < NUMTOCENTRIES; ++i) { + float j = i/(float)NUMTOCENTRIES, act, sum; + index = (int)(floor(j * v->pos)); + if (index > v->pos-1) + index = v->pos-1; + act = v->bag[index]; + sum = v->sum; + seek_point = (int)(256. * act / sum); + if (seek_point > 255) + seek_point = 255; + t[i] = seek_point; + } +} + +#if 0 +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]; + + if (gfc->VBR_seek_table.bag == NULL) { + gfc->VBR_seek_table.sum = 0; + gfc->VBR_seek_table.seen = 0; + gfc->VBR_seek_table.want = 1; + gfc->VBR_seek_table.pos = 0; + 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"); + return; + } + } + addVbr(&gfc->VBR_seek_table, kbps); + gfp->nVbrNumFrames++; +} + + +/*-------------------------------------------------------------*/ +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; +} + + +/*-------------------------------------------------------------*/ +/* 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, h_sr_index; + + /* get selected MPEG header data */ + h_id = (buf[1] >> 3) & 1; + h_sr_index = (buf[2] >> 2) & 3; + 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); + } + + if( buf[0] != VBRTag[0] && buf[0] != VBRTag2[0] ) return 0; /* fail */ + if( buf[1] != VBRTag[1] && buf[1] != VBRTag2[1]) return 0; /* header not found*/ + if( buf[2] != VBRTag[2] && buf[2] != VBRTag2[2]) return 0; + if( buf[3] != VBRTag[3] && buf[3] != VBRTag2[3]) return 0; + return 1; +} + +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( buf[0] != VBRTag[0] && buf[0] != VBRTag2[0] ) return 0; /* fail */ + if( buf[1] != VBRTag[1] && buf[1] != VBRTag2[1]) return 0; /* header not found*/ + if( buf[2] != VBRTag[2] && buf[2] != VBRTag2[2]) return 0; + if( buf[3] != VBRTag[3] && buf[3] != VBRTag2[3]) 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;itoc[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;itoc[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 nMode,SampIndex; + lame_internal_flags *gfc = gfp->internal_flags; +#define MAXFRAMESIZE 2880 // or 0xB40, the max freeformat 640 32kHz framesize + // uint8_t pbtStreamBuffer[MAXFRAMESIZE]; + nMode = gfp->mode; + SampIndex = gfc->samplerate_index; + + + /* Clear Frame position array variables */ + //gfp->pVbrFrames=NULL; + gfp->nVbrNumFrames=0; + gfp->nVbrFrameBufferSize=0; + + + /* Clear stream buffer */ + // memset(pbtStreamBuffer,0x00,sizeof(pbtStreamBuffer)); + + + + /* + // 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. + */ + { + int i,bitrate,tot; + 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; + + gfp->TotalFrameSize= + ((gfp->version+1)*72000*bitrate) / gfp->out_samplerate; + + tot = (gfc->sideinfo_len+LAMEHEADERSIZE); + + if (gfp->TotalFrameSize < tot || + gfp->TotalFrameSize > MAXFRAMESIZE ) { + // disable tag, it wont fit + gfp->bWriteVbrTag = 0; + return 0; + } + + for (i=0; iTotalFrameSize; ++i) + add_dummy_byte(gfp,0); + } + + /* Success */ + return 0; +} + + + +/* fast CRC-16 computation - uses table crc16_lookup 8*/ +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; ibWriteVbrTag) + return; + + if (nStart) + MSGF( gfp->internal_flags, "Writing LAME Tag..."); + else + MSGF( gfp->internal_flags, "done\n"); + +} + + +/**************************************************************************** + * 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) + * + **************************************************************************** +*/ +int PutLameVBR(lame_global_flags *gfp, FILE *fpStream, uint8_t *pbtStreamBuffer, uint32_t id3v2size, uint16_t crc) +{ + lame_internal_flags *gfc = gfp->internal_flags; +// FLOAT fVersion = LAME_MAJOR_VERSION + 0.01 * LAME_MINOR_VERSION; + + int nBytesWritten = 0; + int nFilesize = 0; //size of fpStream. Will be equal to size after process finishes. + 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 ); + + ieee754_float32_t fPeakSignalAmplitude = 0; //TODO... + uint16_t nRadioReplayGain = 0; //TODO... + uint16_t nAudioPhileReplayGain = 0; //TODO... + + + + + uint8_t nNoiseShaping = gfp->internal_flags->noise_shaping; + uint8_t nStereoMode = 0; + int bNonOptimal = 0; + uint8_t nSourceFreq = 0; + uint8_t nMisc = 0; + uint32_t nMusicLength = 0; + int bId3v1Present = ((gfp->internal_flags->tag_spec.flags & CHANGED_FLAG) + && !(gfp->internal_flags->tag_spec.flags & V2_ONLY_FLAG)); + 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 = (gfp->VBR==vbr_abr)?gfp->VBR_mean_bitrate_kbps:gfp->brate; + + //revision and vbr method + if (gfp->VBR>=0 && gfp->VBR < sizeof(vbr_type_translator)) + nVBR = vbr_type_translator[gfp->VBR]; + else + nVBR = 0x00; //unknown. + + nRevMethod = 0x10 * nRevision + nVBR; + + //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; + } + + if (gfp->mode_automs) + nStereoMode = 5; + + /*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->disable_reservoir || + gfp->noATH || + gfp->ATHonly || + (nAthType == 0) || + gfp->in_samplerate <= 32000) + bNonOptimal = 1; + + nMisc = nNoiseShaping + + (nStereoMode << 2) + + (bNonOptimal << 5) + + (nSourceFreq << 6); + + + + //get filesize + fseek(fpStream, 0, SEEK_END); + nFilesize = ftell(fpStream); + + + nMusicLength = nFilesize - id3v2size; //omit current frame + if (bId3v1Present) + nMusicLength-=128; //id3v1 present. + nMusicCRC = gfc->nMusicCRC; + + + /*Write all this information into the stream*/ + CreateI4(&pbtStreamBuffer[nBytesWritten], nQuality); + nBytesWritten+=4; + + strncpy(&pbtStreamBuffer[nBytesWritten], szVersion, 9); + nBytesWritten+=9; + + pbtStreamBuffer[nBytesWritten] = nRevMethod ; + nBytesWritten++; + + pbtStreamBuffer[nBytesWritten] = nLowpass; + nBytesWritten++; + + memmove(&pbtStreamBuffer[nBytesWritten], &fPeakSignalAmplitude, 4); + 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++; + + + memset(pbtStreamBuffer+nBytesWritten,0, 3); //unused in rev0 + nBytesWritten+=3; + + CreateI4(&pbtStreamBuffer[nBytesWritten], nMusicLength); + nBytesWritten+=4; + + CreateI2(&pbtStreamBuffer[nBytesWritten], nMusicCRC); + nBytesWritten+=2; + + /*Calculate tag CRC.... must be done here, since it includes + *previous information*/ + + for (i = 0;iinternal_flags; + + long lFileSize; + int nStreamIndex; + char abyte,bbyte; + uint8_t btToc[NUMTOCENTRIES]; + uint8_t pbtStreamBuffer[MAXFRAMESIZE]; + + int i; + uint16_t crc = 0x00; + + unsigned char id3v2Header[10]; + size_t id3v2TagSize; + + if (gfc->VBR_seek_table.pos <= 0) + return -1; + + + /* Clear stream buffer */ + memset(pbtStreamBuffer,0x00,sizeof(pbtStreamBuffer)); + + /* 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. + */ + + /* seek to the beginning of the stream */ + fseek(fpStream,0,SEEK_SET); + /* read 10 bytes in case there's an ID3 version 2 header here */ + fread(id3v2Header,1,sizeof id3v2Header,fpStream); + /* 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; + } + + /* Seek to first real frame */ + fseek(fpStream,id3v2TagSize+gfp->TotalFrameSize,SEEK_SET); + + /* Read the header (first valid frame) */ + fread(pbtStreamBuffer,4,1,fpStream); + + /* 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 */ + pbtStreamBuffer[0]=(uint8_t) 0xff; + abyte = (pbtStreamBuffer[1] & (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; + + 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 */ + pbtStreamBuffer[1]=abyte | (char) 0x0a; /* was 0x0b; */ + abyte = pbtStreamBuffer[2] & (char) 0x0d; /* AF keep also private bit */ + pbtStreamBuffer[2]=(char) bbyte | abyte; /* 64kbs MPEG1 frame */ + }else{ + /* MPEG2 */ + pbtStreamBuffer[1]=abyte | (char) 0x02; /* was 0x03; */ + abyte = pbtStreamBuffer[2] & (char) 0x0d; /* AF keep also private bit */ + pbtStreamBuffer[2]=(char) bbyte | abyte; /* 64kbs MPEG2 frame */ + } + + /* Clear all TOC entries */ + memset(btToc,0,sizeof(btToc)); + + Xing_seek_table (&gfc->VBR_seek_table, btToc); + /* print_seeking (btToc); */ + + /* 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) + { + pbtStreamBuffer[nStreamIndex++]=VBRTag2[0]; + pbtStreamBuffer[nStreamIndex++]=VBRTag2[1]; + pbtStreamBuffer[nStreamIndex++]=VBRTag2[2]; + pbtStreamBuffer[nStreamIndex++]=VBRTag2[3]; + + } + else + { + pbtStreamBuffer[nStreamIndex++]=VBRTag[0]; + pbtStreamBuffer[nStreamIndex++]=VBRTag[1]; + pbtStreamBuffer[nStreamIndex++]=VBRTag[2]; + pbtStreamBuffer[nStreamIndex++]=VBRTag[3]; + } + + /* Put header flags */ + CreateI4(&pbtStreamBuffer[nStreamIndex],FRAMES_FLAG+BYTES_FLAG+TOC_FLAG+VBR_SCALE_FLAG); + nStreamIndex+=4; + + /* Put Total Number of frames */ + CreateI4(&pbtStreamBuffer[nStreamIndex],gfp->nVbrNumFrames); + nStreamIndex+=4; + + /* Put Total file size */ + CreateI4(&pbtStreamBuffer[nStreamIndex],(int)lFileSize); + nStreamIndex+=4; + + /* Put TOC */ + memcpy(&pbtStreamBuffer[nStreamIndex],btToc,sizeof(btToc)); + nStreamIndex+=sizeof(btToc); + + + if (gfp->error_protection) { + /* (jo) error_protection: add crc16 information to header */ + CRC_writeheader(gfc, pbtStreamBuffer); + } + + + + //work out CRC so far: initially crc = 0 + for (i = 0;i< nStreamIndex ;i++) + crc = CRC_update_lookup(pbtStreamBuffer[i], crc); + + /*Put LAME VBR info*/ + nStreamIndex+=PutLameVBR(gfp, fpStream, pbtStreamBuffer + nStreamIndex, id3v2TagSize,crc); + +#ifdef DEBUG_VBRTAG + { + VBRTAGDATA TestHeader; + GetVbrTag(&TestHeader,pbtStreamBuffer); + } +#endif + + /*Seek to the beginning of the stream */ + fseek(fpStream,id3v2TagSize,SEEK_SET); + + /* Put it all to disk again */ + if (fwrite(pbtStreamBuffer,(unsigned int)gfp->TotalFrameSize,1,fpStream)!=1) + { + return -1; + } + /* Save to delete the frame buffer */ + //free(gfp->pVbrFrames); see HACKING for instructions on how + //gfp->pVbrFrames=NULL; memory in 'gfp' is allocated/free'd + + return 0; /* success */ +} +