Logo Search packages:      
Sourcecode: lastfm version File versions  Download package

VbrTag.c

/*
 *    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.77 2004/03/23 01:57:16 olcios 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 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;
    }
}

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;
}


/*-------------------------------------------------------------*/
/* 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;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 (gfp->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)((((FLOAT8)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->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++]=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, (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 */
}



Generated by  Doxygen 1.6.0   Back to index