| /********** |
| 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.1 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-2015 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; |
| } |