/*
* 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 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.80.2.2 2005/11/27 10:20:35 bouvigne Exp $ */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "machine.h"
#include "bitstream.h"
#include "lame.h"
#include "VbrTag.h"
#include "version.h"
#include <assert.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif
#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
const static char VBRTag0[]={"Xing"};
const static char VBRTag1[]={"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;
}
}
void print_seeking(unsigned char *t)
{
int i;
printf("seeking table ");
for (i = 0; i < NUMTOCENTRIES; ++i) {
printf(" %d ", t[i]);
}
printf("\n");
}
/****************************************************************************
* 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);
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;
}
/* check for magic strings*/
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);
}
/*-------------------------------------------------------------*/
/* 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);
}
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 nMode,SampIndex;
int i,kbps_header,tot;
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;
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.
*/
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;
gfp->TotalFrameSize=
((gfp->version+1)*72000*kbps_header) / 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;
}
/* write dummy VBR tag of all 0's into bitstream */
for (i=0; i<gfp->TotalFrameSize; ++i)
add_dummy_byte(gfp,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");
return -1;
}
}
/* 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; 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)
*
****************************************************************************
*/
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 );
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;
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;
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>=0 && 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->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);
/*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++;
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], 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;
}
/***********************************************************************
*
* 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 *gfp,FILE *fpStream,int nVbrScale)
{
lame_internal_flags * gfc = gfp->internal_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;
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 */
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));
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);
}
/*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++]=VBRTag1[0];
pbtStreamBuffer[nStreamIndex++]=VBRTag1[1];
pbtStreamBuffer[nStreamIndex++]=VBRTag1[2];
pbtStreamBuffer[nStreamIndex++]=VBRTag1[3];
}
else
{
pbtStreamBuffer[nStreamIndex++]=VBRTag0[0];
pbtStreamBuffer[nStreamIndex++]=VBRTag0[1];
pbtStreamBuffer[nStreamIndex++]=VBRTag0[2];
pbtStreamBuffer[nStreamIndex++]=VBRTag0[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, (char*)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;
}
return 0; /* success */
}