blob: 3481b7c04377c3a4faab9c695e0ddda1532a48c2 [file] [log] [blame]
/**********
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 3 of the License, or (at your
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
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 Lesser 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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
**********/
// "liveMedia"
// Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
// MP3 internal implementation details
// Implementation
#include "MP3InternalsHuffman.hh"
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
// This is crufty old code that needs to be cleaned up #####
static unsigned const live_tabsel[2][3][16] = {
{ {32,32,64,96,128,160,192,224,256,288,320,352,384,416,448,448},
{32,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,384},
{32,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,320} },
{ {32,32,48,56,64,80,96,112,128,144,160,176,192,224,256,256},
{8,8,16,24,32,40,48,56,64,80,96,112,128,144,160,160},
{8,8,16,24,32,40,48,56,64,80,96,112,128,144,160,160} }
};
/* Note: live_tabsel[*][*][0 or 15] shouldn't occur; use dummy values there */
static long const live_freqs[]
= { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000, 0 };
struct bandInfoStruct {
int longIdx[23];
int longDiff[22];
int shortIdx[14];
int shortDiff[13];
};
static struct bandInfoStruct const bandInfo[7] = {
/* MPEG 1.0 */
{ {0,4,8,12,16,20,24,30,36,44,52,62,74, 90,110,134,162,196,238,288,342,418,576},
{4,4,4,4,4,4,6,6,8, 8,10,12,16,20,24,28,34,42,50,54, 76,158},
{0,4*3,8*3,12*3,16*3,22*3,30*3,40*3,52*3,66*3, 84*3,106*3,136*3,192*3},
{4,4,4,4,6,8,10,12,14,18,22,30,56} } ,
{ {0,4,8,12,16,20,24,30,36,42,50,60,72, 88,106,128,156,190,230,276,330,384,576},
{4,4,4,4,4,4,6,6,6, 8,10,12,16,18,22,28,34,40,46,54, 54,192},
{0,4*3,8*3,12*3,16*3,22*3,28*3,38*3,50*3,64*3, 80*3,100*3,126*3,192*3},
{4,4,4,4,6,6,10,12,14,16,20,26,66} } ,
{ {0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576} ,
{4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102, 26} ,
{0,4*3,8*3,12*3,16*3,22*3,30*3,42*3,58*3,78*3,104*3,138*3,180*3,192*3} ,
{4,4,4,4,6,8,12,16,20,26,34,42,12} } ,
/* MPEG 2.0 */
{ {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
{6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 } ,
{0,4*3,8*3,12*3,18*3,24*3,32*3,42*3,56*3,74*3,100*3,132*3,174*3,192*3} ,
{4,4,4,6,6,8,10,14,18,26,32,42,18 } } ,
{ {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,330,394,464,540,576},
{6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,52,64,70,76,36 } ,
{0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,136*3,180*3,192*3} ,
{4,4,4,6,8,10,12,14,18,24,32,44,12 } } ,
{ {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
{6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 },
{0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,134*3,174*3,192*3},
{4,4,4,6,8,10,12,14,18,24,30,40,18 } } ,
/* MPEG 2.5, wrong! table (it's just a copy of MPEG 2.0/44.1kHz) */
{ {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576},
{6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 } ,
{0,4*3,8*3,12*3,18*3,24*3,32*3,42*3,56*3,74*3,100*3,132*3,174*3,192*3} ,
{4,4,4,6,6,8,10,14,18,26,32,42,18 } } ,
};
unsigned int n_slen2[512]; /* MPEG 2.0 slen for 'normal' mode */
unsigned int i_slen2[256]; /* MPEG 2.0 slen for intensity stereo */
#define MPG_MD_MONO 3
////////// MP3FrameParams //////////
MP3FrameParams::MP3FrameParams()
: bv(frameBytes, 0, sizeof frameBytes) /* by default */ {
oldHdr = firstHdr = 0;
static Boolean doneInit = False;
if (doneInit) return;
int i,j,k,l;
for (i=0;i<5;i++) {
for (j=0;j<6;j++) {
for (k=0;k<6;k++) {
int n = k + j * 6 + i * 36;
i_slen2[n] = i|(j<<3)|(k<<6)|(3<<12);
}
}
}
for (i=0;i<4;i++) {
for (j=0;j<4;j++) {
for (k=0;k<4;k++) {
int n = k + j * 4 + i * 16;
i_slen2[n+180] = i|(j<<3)|(k<<6)|(4<<12);
}
}
}
for (i=0;i<4;i++) {
for (j=0;j<3;j++) {
int n = j + i * 3;
i_slen2[n+244] = i|(j<<3) | (5<<12);
n_slen2[n+500] = i|(j<<3) | (2<<12) | (1<<15);
}
}
for (i=0;i<5;i++) {
for (j=0;j<5;j++) {
for (k=0;k<4;k++) {
for (l=0;l<4;l++) {
int n = l + k * 4 + j * 16 + i * 80;
n_slen2[n] = i|(j<<3)|(k<<6)|(l<<9)|(0<<12);
}
}
}
}
for (i=0;i<5;i++) {
for (j=0;j<5;j++) {
for (k=0;k<4;k++) {
int n = k + j * 4 + i * 20;
n_slen2[n+400] = i|(j<<3)|(k<<6)|(1<<12);
}
}
}
doneInit = True;
}
MP3FrameParams::~MP3FrameParams() {
}
void MP3FrameParams::setParamsFromHeader() {
if (hdr & (1<<20)) {
isMPEG2 = (hdr & (1<<19)) ? 0x0 : 0x1;
isMPEG2_5 = 0;
}
else {
isMPEG2 = 1;
isMPEG2_5 = 1;
}
layer = 4-((hdr>>17)&3);
if (layer == 4) layer = 3; // layer==4 is not allowed
bitrateIndex = ((hdr>>12)&0xf);
if (isMPEG2_5) {
samplingFreqIndex = ((hdr>>10)&0x3) + 6;
} else {
samplingFreqIndex = ((hdr>>10)&0x3) + (isMPEG2*3);
}
hasCRC = (hdr & 0x10000) == 0;
padding = ((hdr>>9)&0x1);
extension = ((hdr>>8)&0x1);
mode = ((hdr>>6)&0x3);
mode_ext = ((hdr>>4)&0x3);
copyright = ((hdr>>3)&0x1);
original = ((hdr>>2)&0x1);
emphasis = hdr & 0x3;
stereo = (mode == MPG_MD_MONO) ? 1 : 2;
if (((hdr>>10)&0x3) == 0x3) {
#ifdef DEBUG_ERRORS
fprintf(stderr,"Stream error - hdr: 0x%08x\n", hdr);
#endif
}
bitrate = live_tabsel[isMPEG2][layer-1][bitrateIndex];
samplingFreq = live_freqs[samplingFreqIndex];
isStereo = (stereo > 1);
isFreeFormat = (bitrateIndex == 0);
frameSize
= ComputeFrameSize(bitrate, samplingFreq, padding, isMPEG2, layer);
sideInfoSize = computeSideInfoSize();
}
unsigned MP3FrameParams::computeSideInfoSize() {
unsigned size;
if (isMPEG2) {
size = isStereo ? 17 : 9;
} else {
size = isStereo ? 32 : 17;
}
if (hasCRC) {
size += 2;
}
return size;
}
unsigned ComputeFrameSize(unsigned bitrate, unsigned samplingFreq,
Boolean usePadding, Boolean isMPEG2,
unsigned char layer) {
if (samplingFreq == 0) return 0;
unsigned const bitrateMultiplier = (layer == 1) ? 12000*4 : 144000;
unsigned framesize;
framesize = bitrate*bitrateMultiplier;
framesize /= samplingFreq<<(isMPEG2 ? 1 : 0);
framesize = framesize + usePadding - 4;
return framesize;
}
#define TRUNC_FAIRLY
static unsigned updateSideInfoSizes(MP3SideInfo& sideInfo, Boolean isMPEG2,
unsigned char const* mainDataPtr,
unsigned allowedNumBits,
unsigned& part23Length0a,
unsigned& part23Length0aTruncation,
unsigned& part23Length0b,
unsigned& part23Length0bTruncation,
unsigned& part23Length1a,
unsigned& part23Length1aTruncation,
unsigned& part23Length1b,
unsigned& part23Length1bTruncation) {
unsigned p23L0, p23L1 = 0, p23L0Trunc = 0, p23L1Trunc = 0;
p23L0 = sideInfo.ch[0].gr[0].part2_3_length;
p23L1 = isMPEG2 ? 0 : sideInfo.ch[0].gr[1].part2_3_length;
#ifdef TRUNC_ONLY0
if (p23L0 < allowedNumBits)
allowedNumBits = p23L0;
#endif
#ifdef TRUNC_ONLY1
if (p23L1 < allowedNumBits)
allowedNumBits = p23L1;
#endif
if (p23L0 + p23L1 > allowedNumBits) {
/* We need to shorten one or both fields */
unsigned truncation = p23L0 + p23L1 - allowedNumBits;
#ifdef TRUNC_FAIRLY
p23L0Trunc = (truncation*p23L0)/(p23L0 + p23L1);
p23L1Trunc = truncation - p23L0Trunc;
#endif
#if defined(TRUNC_FAVOR0) || defined(TRUNC_ONLY0)
p23L1Trunc = (truncation>p23L1) ? p23L1 : truncation;
p23L0Trunc = truncation - p23L1Trunc;
#endif
#if defined(TRUNC_FAVOR1) || defined(TRUNC_ONLY1)
p23L0Trunc = (truncation>p23L0) ? p23L0 : truncation;
p23L1Trunc = truncation - p23L0Trunc;
#endif
}
/* ASSERT: (p23L0Trunc <= p23L0) && (p23l1Trunc <= p23L1) */
p23L0 -= p23L0Trunc; p23L1 -= p23L1Trunc;
#ifdef DEBUG
fprintf(stderr, "updateSideInfoSizes (allowed: %d): %d->%d, %d->%d\n", allowedNumBits, p23L0+p23L0Trunc, p23L0, p23L1+p23L1Trunc, p23L1);
#endif
// The truncations computed above are still estimates. We need to
// adjust them so that the new fields will continue to end on
// Huffman-encoded sample boundaries:
updateSideInfoForHuffman(sideInfo, isMPEG2, mainDataPtr,
p23L0, p23L1,
part23Length0a, part23Length0aTruncation,
part23Length0b, part23Length0bTruncation,
part23Length1a, part23Length1aTruncation,
part23Length1b, part23Length1bTruncation);
p23L0 = part23Length0a + part23Length0b;
p23L1 = part23Length1a + part23Length1b;
sideInfo.ch[0].gr[0].part2_3_length = p23L0;
sideInfo.ch[0].gr[1].part2_3_length = p23L1;
part23Length0bTruncation
+= sideInfo.ch[1].gr[0].part2_3_length; /* allow for stereo */
sideInfo.ch[1].gr[0].part2_3_length = 0; /* output mono */
sideInfo.ch[1].gr[1].part2_3_length = 0; /* output mono */
return p23L0 + p23L1;
}
Boolean GetADUInfoFromMP3Frame(unsigned char const* framePtr,
unsigned totFrameSize,
unsigned& hdr, unsigned& frameSize,
MP3SideInfo& sideInfo, unsigned& sideInfoSize,
unsigned& backpointer, unsigned& aduSize) {
if (totFrameSize < 4) return False; // there's not enough data
MP3FrameParams fr;
fr.hdr = ((unsigned)framePtr[0] << 24) | ((unsigned)framePtr[1] << 16)
| ((unsigned)framePtr[2] << 8) | (unsigned)framePtr[3];
fr.setParamsFromHeader();
fr.setBytePointer(framePtr + 4, totFrameSize - 4); // skip hdr
frameSize = 4 + fr.frameSize;
if (fr.layer != 3) {
// Special case for non-layer III frames
backpointer = 0;
sideInfoSize = 0;
aduSize = fr.frameSize;
return True;
}
sideInfoSize = fr.sideInfoSize;
if (totFrameSize < 4 + sideInfoSize) return False; // not enough data
fr.getSideInfo(sideInfo);
hdr = fr.hdr;
backpointer = sideInfo.main_data_begin;
unsigned numBits = sideInfo.ch[0].gr[0].part2_3_length;
numBits += sideInfo.ch[0].gr[1].part2_3_length;
numBits += sideInfo.ch[1].gr[0].part2_3_length;
numBits += sideInfo.ch[1].gr[1].part2_3_length;
aduSize = (numBits+7)/8;
#ifdef DEBUG
fprintf(stderr, "mp3GetADUInfoFromFrame: hdr: %08x, frameSize: %d, part2_3_lengths: %d,%d,%d,%d, aduSize: %d, backpointer: %d\n", hdr, frameSize, sideInfo.ch[0].gr[0].part2_3_length, sideInfo.ch[0].gr[1].part2_3_length, sideInfo.ch[1].gr[0].part2_3_length, sideInfo.ch[1].gr[1].part2_3_length, aduSize, backpointer);
#endif
return True;
}
static void getSideInfo1(MP3FrameParams& fr, MP3SideInfo& si,
int stereo, int ms_stereo, long sfreq,
int /*single*/) {
int ch, gr;
#if 0
int powdiff = (single == 3) ? 4 : 0;
#endif
/* initialize all four "part2_3_length" fields to zero: */
si.ch[0].gr[0].part2_3_length = 0; si.ch[1].gr[0].part2_3_length = 0;
si.ch[0].gr[1].part2_3_length = 0; si.ch[1].gr[1].part2_3_length = 0;
si.main_data_begin = fr.getBits(9);
if (stereo == 1)
si.private_bits = fr.getBits(5);
else
si.private_bits = fr.getBits(3);
for (ch=0; ch<stereo; ch++) {
si.ch[ch].gr[0].scfsi = -1;
si.ch[ch].gr[1].scfsi = fr.getBits(4);
}
for (gr=0; gr<2; gr++) {
for (ch=0; ch<stereo; ch++) {
MP3SideInfo::gr_info_s_t& gr_info = si.ch[ch].gr[gr];
gr_info.part2_3_length = fr.getBits(12);
gr_info.big_values = fr.getBits(9);
gr_info.global_gain = fr.getBits(8);
#if 0
gr_info.pow2gain = gainpow2+256 - gr_info.global_gain + powdiff;
if (ms_stereo) gr_info.pow2gain += 2;
#endif
gr_info.scalefac_compress = fr.getBits(4);
/* window-switching flag == 1 for block_Type != 0 .. and block-type == 0 -> win-sw-flag = 0 */
gr_info.window_switching_flag = fr.get1Bit();
if (gr_info.window_switching_flag) {
int i;
gr_info.block_type = fr.getBits(2);
gr_info.mixed_block_flag = fr.get1Bit();
gr_info.table_select[0] = fr.getBits(5);
gr_info.table_select[1] = fr.getBits(5);
/*
* table_select[2] not needed, because there is no region2,
* but to satisfy some verifications tools we set it either.
*/
gr_info.table_select[2] = 0;
for (i=0;i<3;i++) {
gr_info.subblock_gain[i] = fr.getBits(3);
gr_info.full_gain[i]
= gr_info.pow2gain + ((gr_info.subblock_gain[i])<<3);
}
#ifdef DEBUG_ERRORS
if (gr_info.block_type == 0) {
fprintf(stderr,"Blocktype == 0 and window-switching == 1 not allowed.\n");
}
#endif
/* region_count/start parameters are implicit in this case. */
gr_info.region1start = 36>>1;
gr_info.region2start = 576>>1;
}
else
{
int i,r0c,r1c;
for (i=0; i<3; i++) {
gr_info.table_select[i] = fr.getBits(5);
}
r0c = gr_info.region0_count = fr.getBits(4);
r1c = gr_info.region1_count = fr.getBits(3);
gr_info.region1start = bandInfo[sfreq].longIdx[r0c+1] >> 1 ;
gr_info.region2start = bandInfo[sfreq].longIdx[r0c+1+r1c+1] >> 1;
gr_info.block_type = 0;
gr_info.mixed_block_flag = 0;
}
gr_info.preflag = fr.get1Bit();
gr_info.scalefac_scale = fr.get1Bit();
gr_info.count1table_select = fr.get1Bit();
}
}
}
static void getSideInfo2(MP3FrameParams& fr, MP3SideInfo& si,
int stereo, int ms_stereo, long sfreq,
int /*single*/) {
int ch;
#if 0
int powdiff = (single == 3) ? 4 : 0;
#endif
/* initialize all four "part2_3_length" fields to zero: */
si.ch[0].gr[0].part2_3_length = 0; si.ch[1].gr[0].part2_3_length = 0;
si.ch[0].gr[1].part2_3_length = 0; si.ch[1].gr[1].part2_3_length = 0;
si.main_data_begin = fr.getBits(8);
if (stereo == 1)
si.private_bits = fr.get1Bit();
else
si.private_bits = fr.getBits(2);
for (ch=0; ch<stereo; ch++) {
MP3SideInfo::gr_info_s_t& gr_info = si.ch[ch].gr[0];
gr_info.part2_3_length = fr.getBits(12);
si.ch[ch].gr[1].part2_3_length = 0; /* to ensure granule 1 unused */
gr_info.big_values = fr.getBits(9);
gr_info.global_gain = fr.getBits(8);
#if 0
gr_info.pow2gain = gainpow2+256 - gr_info.global_gain + powdiff;
if (ms_stereo) gr_info.pow2gain += 2;
#endif
gr_info.scalefac_compress = fr.getBits(9);
/* window-switching flag == 1 for block_Type != 0 .. and block-type == 0 -> win-sw-flag = 0 */
gr_info.window_switching_flag = fr.get1Bit();
if (gr_info.window_switching_flag) {
int i;
gr_info.block_type = fr.getBits(2);
gr_info.mixed_block_flag = fr.get1Bit();
gr_info.table_select[0] = fr.getBits(5);
gr_info.table_select[1] = fr.getBits(5);
/*
* table_select[2] not needed, because there is no region2,
* but to satisfy some verifications tools we set it either.
*/
gr_info.table_select[2] = 0;
for (i=0;i<3;i++) {
gr_info.subblock_gain[i] = fr.getBits(3);
gr_info.full_gain[i]
= gr_info.pow2gain + ((gr_info.subblock_gain[i])<<3);
}
#ifdef DEBUG_ERRORS
if (gr_info.block_type == 0) {
fprintf(stderr,"Blocktype == 0 and window-switching == 1 not allowed.\n");
}
#endif
/* region_count/start parameters are implicit in this case. */
/* check this again! */
if (gr_info.block_type == 2)
gr_info.region1start = 36>>1;
else {
gr_info.region1start = 54>>1;
}
gr_info.region2start = 576>>1;
}
else
{
int i,r0c,r1c;
for (i=0; i<3; i++) {
gr_info.table_select[i] = fr.getBits(5);
}
r0c = gr_info.region0_count = fr.getBits(4);
r1c = gr_info.region1_count = fr.getBits(3);
gr_info.region1start = bandInfo[sfreq].longIdx[r0c+1] >> 1 ;
gr_info.region2start = bandInfo[sfreq].longIdx[r0c+1+r1c+1] >> 1;
gr_info.block_type = 0;
gr_info.mixed_block_flag = 0;
}
gr_info.scalefac_scale = fr.get1Bit();
gr_info.count1table_select = fr.get1Bit();
}
}
#define MPG_MD_JOINT_STEREO 1
void MP3FrameParams::getSideInfo(MP3SideInfo& si) {
// First skip over the CRC if present:
if (hasCRC) getBits(16);
int single = -1;
int ms_stereo;
int sfreq = samplingFreqIndex;
if (stereo == 1) {
single = 0;
}
ms_stereo = (mode == MPG_MD_JOINT_STEREO) && (mode_ext & 0x2);
if (isMPEG2) {
getSideInfo2(*this, si, stereo, ms_stereo, sfreq, single);
} else {
getSideInfo1(*this, si, stereo, ms_stereo, sfreq, single);
}
}
static void putSideInfo1(BitVector& bv,
MP3SideInfo const& si, Boolean isStereo) {
int ch, gr, i;
int stereo = isStereo ? 2 : 1;
bv.putBits(si.main_data_begin,9);
if (stereo == 1)
bv.putBits(si.private_bits, 5);
else
bv.putBits(si.private_bits, 3);
for (ch=0; ch<stereo; ch++) {
bv.putBits(si.ch[ch].gr[1].scfsi, 4);
}
for (gr=0; gr<2; gr++) {
for (ch=0; ch<stereo; ch++) {
MP3SideInfo::gr_info_s_t const& gr_info = si.ch[ch].gr[gr];
bv.putBits(gr_info.part2_3_length, 12);
bv.putBits(gr_info.big_values, 9);
bv.putBits(gr_info.global_gain, 8);
bv.putBits(gr_info.scalefac_compress, 4);
bv.put1Bit(gr_info.window_switching_flag);
if (gr_info.window_switching_flag) {
bv.putBits(gr_info.block_type, 2);
bv.put1Bit(gr_info.mixed_block_flag);
for (i=0; i<2; i++)
bv.putBits(gr_info.table_select[i], 5);
for (i=0; i<3; i++)
bv.putBits(gr_info.subblock_gain[i], 3);
}
else {
for (i=0; i<3; i++)
bv.putBits(gr_info.table_select[i], 5);
bv.putBits(gr_info.region0_count, 4);
bv.putBits(gr_info.region1_count, 3);
}
bv.put1Bit(gr_info.preflag);
bv.put1Bit(gr_info.scalefac_scale);
bv.put1Bit(gr_info.count1table_select);
}
}
}
static void putSideInfo2(BitVector& bv,
MP3SideInfo const& si, Boolean isStereo) {
int ch, i;
int stereo = isStereo ? 2 : 1;
bv.putBits(si.main_data_begin,8);
if (stereo == 1)
bv.put1Bit(si.private_bits);
else
bv.putBits(si.private_bits, 2);
for (ch=0; ch<stereo; ch++) {
MP3SideInfo::gr_info_s_t const& gr_info = si.ch[ch].gr[0];
bv.putBits(gr_info.part2_3_length, 12);
bv.putBits(gr_info.big_values, 9);
bv.putBits(gr_info.global_gain, 8);
bv.putBits(gr_info.scalefac_compress, 9);
bv.put1Bit(gr_info.window_switching_flag);
if (gr_info.window_switching_flag) {
bv.putBits(gr_info.block_type, 2);
bv.put1Bit(gr_info.mixed_block_flag);
for (i=0; i<2; i++)
bv.putBits(gr_info.table_select[i], 5);
for (i=0; i<3; i++)
bv.putBits(gr_info.subblock_gain[i], 3);
}
else {
for (i=0; i<3; i++)
bv.putBits(gr_info.table_select[i], 5);
bv.putBits(gr_info.region0_count, 4);
bv.putBits(gr_info.region1_count, 3);
}
bv.put1Bit(gr_info.scalefac_scale);
bv.put1Bit(gr_info.count1table_select);
}
}
static void PutMP3SideInfoIntoFrame(MP3SideInfo const& si,
MP3FrameParams const& fr,
unsigned char* framePtr) {
if (fr.hasCRC) framePtr += 2; // skip CRC
BitVector bv(framePtr, 0, 8*fr.sideInfoSize);
if (fr.isMPEG2) {
putSideInfo2(bv, si, fr.isStereo);
} else {
putSideInfo1(bv, si, fr.isStereo);
}
}
Boolean ZeroOutMP3SideInfo(unsigned char* framePtr, unsigned totFrameSize,
unsigned newBackpointer) {
if (totFrameSize < 4) return False; // there's not enough data
MP3FrameParams fr;
fr.hdr = ((unsigned)framePtr[0] << 24) | ((unsigned)framePtr[1] << 16)
| ((unsigned)framePtr[2] << 8) | (unsigned)framePtr[3];
fr.setParamsFromHeader();
fr.setBytePointer(framePtr + 4, totFrameSize - 4); // skip hdr
if (totFrameSize < 4 + fr.sideInfoSize) return False; // not enough data
MP3SideInfo si;
fr.getSideInfo(si);
si.main_data_begin = newBackpointer; /* backpointer */
/* set all four "part2_3_length" and "big_values" fields to zero: */
si.ch[0].gr[0].part2_3_length = si.ch[0].gr[0].big_values = 0;
si.ch[1].gr[0].part2_3_length = si.ch[1].gr[0].big_values = 0;
si.ch[0].gr[1].part2_3_length = si.ch[0].gr[1].big_values = 0;
si.ch[1].gr[1].part2_3_length = si.ch[1].gr[1].big_values = 0;
PutMP3SideInfoIntoFrame(si, fr, framePtr + 4);
return True;
}
static unsigned MP3BitrateToBitrateIndex(unsigned bitrate /* in kbps */,
Boolean isMPEG2) {
for (unsigned i = 1; i < 15; ++i) {
if (live_tabsel[isMPEG2][2][i] >= bitrate)
return i;
}
// "bitrate" was larger than any possible, so return the largest possible:
return 14;
}
static void outputHeader(unsigned char* toPtr, unsigned hdr) {
toPtr[0] = (unsigned char)(hdr>>24);
toPtr[1] = (unsigned char)(hdr>>16);
toPtr[2] = (unsigned char)(hdr>>8);
toPtr[3] = (unsigned char)(hdr);
}
static void assignADUBackpointer(MP3FrameParams const& fr,
unsigned aduSize,
MP3SideInfo& sideInfo,
unsigned& availableBytesForBackpointer) {
// Give the ADU as large a backpointer as possible:
unsigned maxBackpointerSize = fr.isMPEG2 ? 255 : 511;
unsigned backpointerSize = availableBytesForBackpointer;
if (backpointerSize > maxBackpointerSize) {
backpointerSize = maxBackpointerSize;
}
// Store the new backpointer now:
sideInfo.main_data_begin = backpointerSize;
// Figure out how many bytes are available for the *next* ADU's backpointer:
availableBytesForBackpointer
= backpointerSize + fr.frameSize - fr.sideInfoSize ;
if (availableBytesForBackpointer < aduSize) {
availableBytesForBackpointer = 0;
} else {
availableBytesForBackpointer -= aduSize;
}
}
unsigned TranscodeMP3ADU(unsigned char const* fromPtr, unsigned fromSize,
unsigned toBitrate,
unsigned char* toPtr, unsigned toMaxSize,
unsigned& availableBytesForBackpointer) {
// Begin by parsing the input ADU's parameters:
unsigned hdr, inFrameSize, inSideInfoSize, backpointer, inAduSize;
MP3SideInfo sideInfo;
if (!GetADUInfoFromMP3Frame(fromPtr, fromSize,
hdr, inFrameSize, sideInfo, inSideInfoSize,
backpointer, inAduSize)) {
return 0;
}
fromPtr += (4+inSideInfoSize); // skip to 'main data'
// Alter the 4-byte MPEG header to reflect the output ADU:
// (different bitrate; mono; no CRC)
Boolean isMPEG2 = ((hdr&0x00080000) == 0);
unsigned toBitrateIndex = MP3BitrateToBitrateIndex(toBitrate, isMPEG2);
hdr &=~ 0xF000; hdr |= (toBitrateIndex<<12); // set bitrate index
hdr |= 0x10200; // turn on !error-prot and padding bits
hdr &=~ 0xC0; hdr |= 0xC0; // set mode to 3 (mono)
// Set up the rest of the parameters of the new ADU:
MP3FrameParams outFr;
outFr.hdr = hdr;
outFr.setParamsFromHeader();
// Figure out how big to make the output ADU:
unsigned inAveAduSize = inFrameSize - inSideInfoSize;
unsigned outAveAduSize = outFr.frameSize - outFr.sideInfoSize;
unsigned desiredOutAduSize /*=inAduSize*outAveAduSize/inAveAduSize*/
= (2*inAduSize*outAveAduSize + inAveAduSize)/(2*inAveAduSize);
// this rounds to the nearest integer
if (toMaxSize < (4 + outFr.sideInfoSize)) return 0;
unsigned maxOutAduSize = toMaxSize - (4 + outFr.sideInfoSize);
if (desiredOutAduSize > maxOutAduSize) {
desiredOutAduSize = maxOutAduSize;
}
// Figure out the new sizes of the various 'part23 lengths',
// and how much they are truncated:
unsigned part23Length0a, part23Length0aTruncation;
unsigned part23Length0b, part23Length0bTruncation;
unsigned part23Length1a, part23Length1aTruncation;
unsigned part23Length1b, part23Length1bTruncation;
unsigned numAduBits
= updateSideInfoSizes(sideInfo, outFr.isMPEG2,
fromPtr, 8*desiredOutAduSize,
part23Length0a, part23Length0aTruncation,
part23Length0b, part23Length0bTruncation,
part23Length1a, part23Length1aTruncation,
part23Length1b, part23Length1bTruncation);
#ifdef DEBUG
fprintf(stderr, "shrinkage %d->%d [(%d,%d),(%d,%d)] (trunc: [(%d,%d),(%d,%d)]) {%d}\n", inAduSize, (numAduBits+7)/8,
part23Length0a, part23Length0b, part23Length1a, part23Length1b,
part23Length0aTruncation, part23Length0bTruncation,
part23Length1aTruncation, part23Length1bTruncation,
maxOutAduSize);
#endif
unsigned actualOutAduSize = (numAduBits+7)/8;
// Give the new ADU an appropriate 'backpointer':
assignADUBackpointer(outFr, actualOutAduSize, sideInfo, availableBytesForBackpointer);
///// Now output the new ADU:
// 4-byte header
outputHeader(toPtr, hdr); toPtr += 4;
// side info
PutMP3SideInfoIntoFrame(sideInfo, outFr, toPtr); toPtr += outFr.sideInfoSize;
// 'main data', using the new lengths
unsigned toBitOffset = 0;
unsigned fromBitOffset = 0;
/* rebuild portion 0a: */
memmove(toPtr, fromPtr, (part23Length0a+7)/8);
toBitOffset += part23Length0a;
fromBitOffset += part23Length0a + part23Length0aTruncation;
/* rebuild portion 0b: */
shiftBits(toPtr, toBitOffset, fromPtr, fromBitOffset, part23Length0b);
toBitOffset += part23Length0b;
fromBitOffset += part23Length0b + part23Length0bTruncation;
/* rebuild portion 1a: */
shiftBits(toPtr, toBitOffset, fromPtr, fromBitOffset, part23Length1a);
toBitOffset += part23Length1a;
fromBitOffset += part23Length1a + part23Length1aTruncation;
/* rebuild portion 1b: */
shiftBits(toPtr, toBitOffset, fromPtr, fromBitOffset, part23Length1b);
toBitOffset += part23Length1b;
/* zero out any remaining bits (probably unnecessary, but...) */
unsigned char const zero = '\0';
shiftBits(toPtr, toBitOffset, &zero, 0,
actualOutAduSize*8 - numAduBits);
return 4 + outFr.sideInfoSize + actualOutAduSize;
}