mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-01 08:09:41 +02:00
1083 lines
31 KiB
C
1083 lines
31 KiB
C
/*
|
|
* 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.106 2017/08/06 18:15:47 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"
|
|
#include "tables.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
|
|
|
|
extern const char* get_lame_tag_encoder_short_version(void);
|
|
|
|
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 const* 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_internal_flags * gfc)
|
|
{
|
|
int kbps = bitrate_table[gfc->cfg.version][gfc->ov_enc.bitrate_index];
|
|
assert(gfc->VBR_seek_table.bag);
|
|
addVbr(&gfc->VBR_seek_table, kbps);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------*/
|
|
static int
|
|
ExtractI4(const 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, uint32_t 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) & ~(-1u << (n)) ) )
|
|
|
|
static void
|
|
setLameTagFrameHeader(lame_internal_flags const *gfc, unsigned char *buffer)
|
|
{
|
|
SessionConfig_t const *const cfg = &gfc->cfg;
|
|
EncResult_t const *const eov = &gfc->ov_enc;
|
|
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, (cfg->samplerate_out < 16000) ? 0 : 1);
|
|
SHIFT_IN_BITS_VALUE(buffer[1], 1u, cfg->version);
|
|
SHIFT_IN_BITS_VALUE(buffer[1], 2u, 4 - 3);
|
|
SHIFT_IN_BITS_VALUE(buffer[1], 1u, (!cfg->error_protection) ? 1 : 0);
|
|
|
|
SHIFT_IN_BITS_VALUE(buffer[2], 4u, eov->bitrate_index);
|
|
SHIFT_IN_BITS_VALUE(buffer[2], 2u, cfg->samplerate_index);
|
|
SHIFT_IN_BITS_VALUE(buffer[2], 1u, 0);
|
|
SHIFT_IN_BITS_VALUE(buffer[2], 1u, cfg->extension);
|
|
|
|
SHIFT_IN_BITS_VALUE(buffer[3], 2u, cfg->mode);
|
|
SHIFT_IN_BITS_VALUE(buffer[3], 2u, eov->mode_ext);
|
|
SHIFT_IN_BITS_VALUE(buffer[3], 1u, cfg->copyright);
|
|
SHIFT_IN_BITS_VALUE(buffer[3], 1u, cfg->original);
|
|
SHIFT_IN_BITS_VALUE(buffer[3], 2u, cfg->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 == cfg->version) {
|
|
bitrate = XING_BITRATE1;
|
|
}
|
|
else {
|
|
if (cfg->samplerate_out < 16000)
|
|
bitrate = XING_BITRATE25;
|
|
else
|
|
bitrate = XING_BITRATE2;
|
|
}
|
|
|
|
if (cfg->vbr == vbr_off)
|
|
bitrate = cfg->avg_bitrate;
|
|
|
|
if (cfg->free_format)
|
|
bbyte = 0x00;
|
|
else
|
|
bbyte = 16 * BitrateIndex(bitrate, cfg->version, cfg->samplerate_out);
|
|
}
|
|
|
|
/* Use as much of the info from the real frames in the
|
|
* Xing header: samplerate, channels, crc, etc...
|
|
*/
|
|
if (cfg->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 */
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static int CheckVbrTag(unsigned char *buf);
|
|
|
|
/*-------------------------------------------------------------*/
|
|
/* 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);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
GetVbrTag(VBRTAGDATA * pTagData, const unsigned char *buf)
|
|
{
|
|
int i, head_flags;
|
|
int h_bitrate, h_id, h_mode, h_sr_index, h_layer;
|
|
int enc_delay, enc_padding;
|
|
|
|
/* get Vbr header data */
|
|
pTagData->flags = 0;
|
|
|
|
/* get selected MPEG header data */
|
|
h_layer = (buf[1] >> 1) & 3;
|
|
if ( h_layer != 0x01 ) {
|
|
/* the following code assumes Layer-3, so give up here */
|
|
return 0;
|
|
}
|
|
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 ((void *)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)
|
|
{
|
|
lame_internal_flags *gfc = gfp->internal_flags;
|
|
SessionConfig_t const *const cfg = &gfc->cfg;
|
|
int kbps_header;
|
|
|
|
#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 == cfg->version) {
|
|
kbps_header = XING_BITRATE1;
|
|
}
|
|
else {
|
|
if (cfg->samplerate_out < 16000)
|
|
kbps_header = XING_BITRATE25;
|
|
else
|
|
kbps_header = XING_BITRATE2;
|
|
}
|
|
|
|
if (cfg->vbr == vbr_off)
|
|
kbps_header = cfg->avg_bitrate;
|
|
|
|
/** make sure LAME Header fits into Frame
|
|
*/
|
|
{
|
|
int total_frame_size = ((cfg->version + 1) * 72000 * kbps_header) / cfg->samplerate_out;
|
|
int header_size = (cfg->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 */
|
|
gfc->cfg.write_lame_tag = 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 = lame_calloc(int, 400);
|
|
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");
|
|
gfc->cfg.write_lame_tag = 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(gfc, buffer);
|
|
n = gfc->VBR_seek_table.TotalFrameSize;
|
|
for (i = 0; i < n; ++i) {
|
|
add_dummy_byte(gfc, buffer[i], 1);
|
|
}
|
|
}
|
|
/* Success */
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* fast CRC-16 computation - uses table crc16_lookup 8*/
|
|
static uint16_t
|
|
CRC_update_lookup(uint16_t value, uint16_t crc)
|
|
{
|
|
uint16_t tmp;
|
|
tmp = crc ^ value;
|
|
crc = (crc >> 8) ^ crc16_lookup[tmp & 0xff];
|
|
return crc;
|
|
}
|
|
|
|
void
|
|
UpdateMusicCRC(uint16_t * crc, unsigned char const *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 const *gfc = gfp->internal_flags;
|
|
SessionConfig_t const *const cfg = &gfc->cfg;
|
|
|
|
int nBytesWritten = 0;
|
|
int i;
|
|
|
|
int enc_delay = gfc->ov_enc.encoder_delay; /* encoder delay */
|
|
int enc_padding = gfc->ov_enc.encoder_padding; /* encoder padding */
|
|
|
|
/*recall: cfg->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);
|
|
|
|
|
|
/*
|
|
NOTE:
|
|
Even though the specification for the LAME VBR tag
|
|
did explicitly mention other encoders than LAME,
|
|
many SW/HW decoder seem to be able to make use of
|
|
this tag only, if the encoder version starts with LAME.
|
|
To be compatible with such decoders, ANY encoder will
|
|
be forced to write a fake LAME version string!
|
|
As a result, the encoder version info becomes worthless.
|
|
*/
|
|
const char *szVersion = get_lame_tag_encoder_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 =
|
|
(((cfg->lowpassfreq / 100.0) + .5) > 255 ? 255 : (cfg->lowpassfreq / 100.0) + .5);
|
|
|
|
uint32_t nPeakSignalAmplitude = 0;
|
|
|
|
uint16_t nRadioReplayGain = 0;
|
|
uint16_t nAudiophileReplayGain = 0;
|
|
|
|
uint8_t nNoiseShaping = cfg->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 = 1; /* only NsPsytune */
|
|
unsigned char bSafeJoint = (cfg->use_safe_joint_stereo) != 0;
|
|
|
|
unsigned char bNoGapMore = 0;
|
|
unsigned char bNoGapPrevious = 0;
|
|
|
|
int nNoGapCount = gfp->nogap_total;
|
|
int nNoGapCurr = gfp->nogap_current;
|
|
|
|
|
|
uint8_t nAthType = cfg->ATHtype; /*4 bits. */
|
|
|
|
uint8_t nFlags = 0;
|
|
|
|
/* if ABR, {store bitrate <=255} else { store "-b"} */
|
|
int nABRBitrate;
|
|
switch (cfg->vbr) {
|
|
case vbr_abr:{
|
|
nABRBitrate = cfg->vbr_avg_bitrate_kbps;
|
|
break;
|
|
}
|
|
case vbr_off:{
|
|
nABRBitrate = cfg->avg_bitrate;
|
|
break;
|
|
}
|
|
default:{ /*vbr modes */
|
|
nABRBitrate = bitrate_table[cfg->version][cfg->vbr_min_bitrate_index];;
|
|
}
|
|
}
|
|
|
|
|
|
/*revision and vbr method */
|
|
if (cfg->vbr < sizeof(vbr_type_translator))
|
|
nVBR = vbr_type_translator[cfg->vbr];
|
|
else
|
|
nVBR = 0x00; /*unknown. */
|
|
|
|
nRevMethod = 0x10 * nRevision + nVBR;
|
|
|
|
|
|
/* ReplayGain */
|
|
if (cfg->findReplayGain) {
|
|
int RadioGain = gfc->ov_rpg.RadioGain;
|
|
if (RadioGain > 0x1FE)
|
|
RadioGain = 0x1FE;
|
|
if (RadioGain < -0x1FE)
|
|
RadioGain = -0x1FE;
|
|
|
|
nRadioReplayGain = 0x2000; /* set name code */
|
|
nRadioReplayGain |= 0xC00; /* set originator code to `determined automatically' */
|
|
|
|
if (RadioGain >= 0)
|
|
nRadioReplayGain |= RadioGain; /* set gain adjustment */
|
|
else {
|
|
nRadioReplayGain |= 0x200; /* set the sign bit */
|
|
nRadioReplayGain |= -RadioGain; /* set gain adjustment */
|
|
}
|
|
}
|
|
|
|
/* peak sample */
|
|
if (cfg->findPeakSample)
|
|
nPeakSignalAmplitude =
|
|
abs((int) ((((FLOAT) gfc->ov_rpg.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 (cfg->mode) {
|
|
case MONO:
|
|
nStereoMode = 0;
|
|
break;
|
|
case STEREO:
|
|
nStereoMode = 1;
|
|
break;
|
|
case DUAL_CHANNEL:
|
|
nStereoMode = 2;
|
|
break;
|
|
case JOINT_STEREO:
|
|
if (cfg->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 (cfg->samplerate_in <= 32000)
|
|
nSourceFreq = 0x00;
|
|
else if (cfg->samplerate_in == 48000)
|
|
nSourceFreq = 0x02;
|
|
else if (cfg->samplerate_in > 48000)
|
|
nSourceFreq = 0x03;
|
|
else
|
|
nSourceFreq = 0x01; /*default is 44100Hz. */
|
|
|
|
|
|
/*Check if the user overrided the default LAME behaviour with some nasty options */
|
|
|
|
if (cfg->short_blocks == short_block_forced || cfg->short_blocks == short_block_dispensed || ((cfg->lowpassfreq == -1) && (cfg->highpassfreq == -1)) || /* "-k" */
|
|
(cfg->disable_reservoir && cfg->avg_bitrate < 320) ||
|
|
cfg->noATH || cfg->ATHonly || (nAthType == 0) || cfg->samplerate_in <= 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], cfg->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;
|
|
SessionConfig_t const *cfg;
|
|
unsigned long stream_size;
|
|
unsigned int nStreamIndex;
|
|
uint8_t btToc[NUMTOCENTRIES];
|
|
|
|
if (gfp == 0) {
|
|
return 0;
|
|
}
|
|
gfc = gfp->internal_flags;
|
|
if (gfc == 0) {
|
|
return 0;
|
|
}
|
|
if (!is_lame_internal_flags_valid(gfc)) {
|
|
return 0;
|
|
}
|
|
cfg = &gfc->cfg;
|
|
if (cfg->write_lame_tag == 0) {
|
|
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(gfc, buffer);
|
|
|
|
/* Clear all TOC entries */
|
|
memset(btToc, 0, sizeof(btToc));
|
|
|
|
if (cfg->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 = cfg->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 (cfg->error_protection)
|
|
nStreamIndex -= 2;
|
|
|
|
/* Put Vbr tag */
|
|
if (cfg->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 (cfg->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;
|
|
unsigned 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 */
|
|
}
|