blob: 3aaf8fd6dfa4c1069db05095db9fd607fc9a32ab [file] [log] [blame]
/*
* 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 */
}