1.解析帧头 帧头共4字节,从高位到低位这32比特的含义如下:
比特数 | 名称 | 内容 |
11 | sync | 0x7FF |
2 | version | 1=mpeg1.0, 0=mpeg2.0 |
2 | lay | 4-lay = layerI, II or III |
1 | error protection | 0=yes, 1=no |
4 | bitrate_index | 见下文 |
2 | sampling_freq | 见下文 |
1 | padding | 填充位 |
1 | extension | 见下文 |
2 | mode | 见下文 |
2 | mode_ext | 联合立体声(joint stereo)模式 |
1 | copyright | 0=no 1=yes |
1 | original | 0=no 1=yes |
2 | emphasis | 预加重 |
Header.parseHeader(int)方法中的这几行依次解码上面的各个变量:
intVersionID = (h >> 19) & 3;
intLayer = 4 - (h >> 17) & 3;
intProtectionBit = (h >> 16) & 0x1;
intBitrateIndex = (h >> 12) & 0xF;
intSamplingFrequency = (h >> 10) & 3;
intPaddingBit = (h >> 9) & 0x1;
intMode = (h >> 6) & 3;
intModeExtension = (h >> 4) & 3;
各变量的含义如下:
version MPEG的版本,本程序支持MPEG 1.0/2.0/2.5,从MPEG 2.0开始支持32Kbps以下的低位率。
lay MPEG Audio的压缩分为I、II、III共3层,Layer III的解码过程最为复杂。
error protection 设置为0表示有32位的循环冗余校检(CRC)。
bitrate_index 主数据的位率(单位KBits/s),例如对192Kbps的MP3,解码时每秒读取192*1024/8=24576字节的码流,如果你是从网络在线播放要确保每秒下载192/8=24KBytes以上才能流畅播放。
Layer\值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
layer1 | 32 | 64 | 96 | 128 | 160 | 192 | 224 | 256 | 288 | 320 | 352 | 384 | 416 | 448 |
layer2 | 32 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 160 | 192 | 224 | 256 | 320 | 384 |
layer3 | 32 | 40 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 160 | 192 | 224 | 256 | 320 |
Layer\值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
layer1 | 32 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 144 | 160 | 176 | 192 | 224 | 256 |
layer2 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 144 | 160 |
layer3 | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 80 | 96 | 112 | 128 | 144 | 160 |
sampling_freq PCM样本的采样率,用它来初始化音频硬件以播放MP3。
mpeg1.0时其值0,1,2分别对应的采样是44100Hz,48000Hz,32000Hz
mpeg2.0时其值0,1,2分别对应的采样是22050Hz,24000Hz,16000Hz
mpeg2.5时其值0,1,2分别对应的采样是11025Hz,12000Hz,8000Hz
padding 设置为1表示有1字节的填充位,相应帧的长度增加1字节。
mode 声道模式,其值表示的含义:
0 立体声(stereo)
1 联合立体声(joint stereo)
2 双声道(dual channel)
3 单声道(single channel)
联合立体声(joint stereo) 采用联合立体声编码方式的两个声道具有关联性。例如MS_stereo将两个声道相加、相差后处理,相减后去掉了左右声道相同的成份,后续的压缩可得到更高的压缩率。
extension 其值表示采用哪种联合立体声方式
extension | intensity_stereo | ms_stereo |
00 | off | off |
01 | on | of |
10 | of | on |
11 | on | on |
帧头信息解码除解码上述信息外,还要进行帧同步、计算帧长、计算帧边信息长度等供后续解码。
2. 帧同步 (1)帧头的4字节中高11位全部设置为1(11111111 111xxxxx xxxxxxxx xxxxxxxx),用它作为查找帧的重要依据。(2)考虑到MP3文件可能有的数据帧有损坏,帧同步时还要用version、lay、bitrate_index、sampling_freq的值是否合法去检验;(3)每一帧的 version、lay、sampling_freq保持不变,把已经解码的帧的这些变量保存起来,用以与下一帧这些变量的值比较; (4)根据当前帧的帧长,移到下一帧去解析下一帧的帧头来确定当前的4字节是否是有效的帧头。如源代码Header.syncFrame()方法中的这些行进行帧同步:
iraInput.read(b4, 0, 4);
h = makeInt32(b4, 0);
while(!bfind) {
// 1.查找帧同步字
while((h & intStandardMask) != intStandardMask
|| ((h >> 19) & 3) == 1 // version ID: 01 - reserved
|| ((h >> 17) & 3) == 0 // Layer index: 00 - reserved
|| ((h >> 12) & 0xf) == 0xf // Bitrate Index: 1111 - reserved
|| ((h >> 12) & 0xf) == 0 // Bitrate Index: 0000 - free
|| ((h >> 10) & 3) == 3) // Sampling Rate Index: 11 - reserved
{
//...
}
//...
// 2.与下一帧的同步头比较
cur_mask = 0xffe00000; //syncword
cur_mask |= h & 0x180000; //intVersionID
cur_mask |= h & 0x60000; //intLayer
cur_mask |= h & 0x60000; //intSamplingFrequency
if(iraInput.dump(intFrameSize-4, b4, 0, 4) < 4)
return false;
i = makeInt32(b4, 0);
bfind = (i & cur_mask) == cur_mask && ((i >> 19) & 3) != 1
&& ((i >> 17) & 3) != 0 && ((i >> 12) & 15) != 15
&& ((i >> 12) & 0xf) != 0 && ((i >> 10) & 3) != 3;
//...
3.计算帧长 一帧的长度应该用槽(slot)来描述,MPEG 1.0/2.0/2.5 对声音的3种压缩方式Layer1、Layer2和Layer3,每种压缩方式一帧的槽数是固定的,Layer1 一槽就是4个字节, Layer2和Layer3一槽就是一个字节,据此可以计算出帧的字节数;
4.计算帧边信息长度 根据MP3帧头解码出的表示立体声编码模式(mode)、MPEG的版本(version)、压缩层(lay)套公式计算。
5.解析VBR信息 见Header.parseVBR()方法,其中各个变量在其官方文档中有详细说明。如果你想了解细节,请查阅其官方文档。
Header.javar完整的源码如下:
/*
* Header.java -- MPEG 1.0/2.0/2.5 Audio Layer I/II/III 帧同步和帧头信息解码
* Copyright (C) 2010
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* If you would like to negotiate alternate licensing terms, you may do
* so by contacting the author: .
*/
package jmp123.decoder;
import jmp123.instream.IRandomAccess;
public final class Header {
public static final int MPEG1 = 3;
public static final int MPEG2 = 2;
public static final int MPEG25 = 0;
public static final int MAX_FRAMESIZE = 1732; //MPEG 1.0/2.0/2.5, Lay 1/2/3
/*
* intBitrateTable[intLSF][intLayer-1][intBitrateIndex]
*/
private static final int[][][] intBitrateTable = {
{
//MPEG 1
//Layer I
{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448},
//Layer II
{0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384},
//Layer III
{0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320}
},
{
//MPEG 2.0/2.5
//Layer I
{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256},
//Layer II
{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160},
//Layer III
{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160}
}
};
/*
* intSamplingRateTable[intVersionID][intSamplingFrequency]
*/
private static final int[][] intSamplingRateTable = {
{11025 , 12000 , 8000,0}, //MPEG Version 2.5
{0,0,0,0,}, //reserved
{22050, 24000, 16000 ,0}, //MPEG Version 2 (ISO/IEC 13818-3)
{44100, 48000, 32000,0} //MPEG Version 1 (ISO/IEC 11172-3)
};
/*
* intVersionID: 2 bits
* "00" MPEG Version 2.5 (unofficial extension of MPEG 2);
* "01" reserved;
* "10" MPEG Version 2 (ISO/IEC 13818-3);
* "11" MPEG Version 1 (ISO/IEC 11172-3).
*/
private static int intVersionID;
/*
* intLayer: 2 bits
* "11" Layer I
* "10" Layer II
* "01" Layer III
* "00" reserved
* 已换算intLayer=4-intLayer: 1-Layer I; 2-Layer II; 3-Layer III; 4-reserved
*/
private static int intLayer;
/*
* intProtectionBit: 1 bit
* "1" no CRC;
* "0" protected by 16 bit CRC following header.
*/
private static int intProtectionBit;
/*
* intBitrateIndex: 4 bits
*/
private static int intBitrateIndex;
/*
* intSamplingFrequency: 2 bits
* '00' 44.1kHz
* '01' 48kHz
* '10' 32kHz
* '11' reserved
*/
private static int intSamplingFrequency;
private static int intPaddingBit;
/*
* intMode: 2 bits
* '00' Stereo;
* '01' Joint Stereo (Stereo);
* '10' Dual channel (Two mono channels);
* '11' Single channel (Mono).
*/
private static int intMode;
/*
* intModeExtension: 2 bits
* intensity_stereo boolMS_Stereo
* '00' off off
* '01' on off
* '10' off on
* '11' on on
*/
private static int intModeExtension;
private static int intFrameSize;
private static int intMainDataBytes; //main_data length
private static int intSideInfoSize; //side_information length
private static int intLSF;
private static int intStandardMask = 0xffe00000;
private static boolean boolMS_Stereo, boolIntensityStereo;
private static IRandomAccess iraInput;
public Header(IRandomAccess in_rai) {
iraInput = in_rai;
}
private void parseHeader(int h) {
intVersionID = (h >> 19) & 3;
intLayer = 4 - (h >> 17) & 3;
intProtectionBit = (h >> 16) & 0x1;
intBitrateIndex = (h >> 12) & 0xF;
intSamplingFrequency = (h >> 10) & 3;
intPaddingBit = (h >> 9) & 0x1;
intMode = (h >> 6) & 3;
intModeExtension = (h >> 4) & 3;
boolMS_Stereo = intMode == 1 && (intModeExtension & 2) != 0;
boolIntensityStereo = intMode == 1 && (intModeExtension & 0x1) != 0;
intLSF = (intVersionID == MPEG1) ? 0 : 1;
switch (intLayer) {
case 1:
intFrameSize = intBitrateTable[intLSF][0][intBitrateIndex] * 12000;
intFrameSize /= intSamplingRateTable[intVersionID][intSamplingFrequency];
intFrameSize = ((intFrameSize+intPaddingBit)<<2);
break;
case 2:
intFrameSize = intBitrateTable[intLSF][1][intBitrateIndex] * 144000;
intFrameSize /= intSamplingRateTable[intVersionID][intSamplingFrequency];
intFrameSize += intPaddingBit;
break;
case 3:
intFrameSize = intBitrateTable[intLSF][2][intBitrateIndex] * 144000;
intFrameSize /= intSamplingRateTable[intVersionID][intSamplingFrequency]<<(intLSF);
intFrameSize += intPaddingBit;
//计算帧边信息长度
if(intVersionID == MPEG1)
intSideInfoSize = (intMode == 3) ? 17 : 32;
else
intSideInfoSize = (intMode == 3) ? 9 : 17;
break;
}
//计算主数据长度
intMainDataBytes = intFrameSize - 4 - intSideInfoSize;
if(intProtectionBit == 0)
intMainDataBytes -= 2;
}
private static void headerCRC() throws Exception {
if(iraInput.read() == -1 || iraInput.read() == -1)
throw new Exception("crc() 文件读完");
}
private static int makeInt32(byte[] b, int off) {
int h = b[off] & 0xff;
h <<= 8;
h |= b[off + 1] & 0xff;
h <<= 8;
h |= b[off + 2] & 0xff;
h <<= 8;
h |= b[off + 3] & 0xff;
return h;
}
private static int intFrameCounter; //当前帧序号
private static boolean boolSync; //true:帧头的特征未改变
private static final byte[] b4 = new byte[4];
/*
* 帧同步: 查找到帧同步字后与下一帧的intVersionID等比较,确定是否找到有效的同步字.
*/
public boolean syncFrame() throws Exception{
int h, idx = 0, i, cur_mask = 0;
iraInput.read(b4, 0, 4);
h = (b4[0]<<24) | ((b4[1] & 0xff)<<16) | ((b4[2] & 0xff)<<8) | (b4[3] & 0xff);
while(true) {
// 1.查找帧同步字
while((h & intStandardMask) != intStandardMask
|| ((h >> 19) & 3) == 1 // version ID: 01 - reserved
|| ((h >> 17) & 3) == 0 // Layer index: 00 - reserved
|| ((h >> 12) & 0xf) == 0xf // Bitrate Index: 1111 - reserved
|| ((h >> 12) & 0xf) == 0 // Bitrate Index: 0000 - free
|| ((h >> 10) & 3) == 3) // Sampling Rate Index: 11 - reserved
{
if((i = iraInput.read()) == -1)
return false;
idx++;
h = (h << 8) | i;
}
if (idx > 0)
boolSync = false;
// 2. 解析帧头
parseHeader(h);
//若intVersionID等帧的特征未改变(boolSync=true),不用与下一帧的同步头比较.
if(boolSync)
break;
if(idx >= 0xffff) {
System.out.println("\n搜索 64K 未发现MP3帧后放弃。");
return false;
}
// 3.与下一帧的同步头比较
cur_mask = 0xffe00000; //syncword
cur_mask |= h & 0x180000; //intVersionID
cur_mask |= h & 0x60000; //intLayer
cur_mask |= h & 0x60000; //intSamplingFrequency
//cur_mask |= h & 0xC0; //intMode
//intMode,intModeExtension 不是始终不变.
if(iraInput.dump(intFrameSize-4, b4, 0, 4) < 4)
return false;
i = makeInt32(b4, 0);
if( (i & cur_mask) == cur_mask && ((i >> 19) & 3) != 1
&& ((i >> 17) & 3) != 0 && ((i >> 12) & 15) != 15
&& ((i >> 12) & 0xf) != 0 && ((i >> 10) & 3) != 3 )
break;
idx++;
h = (h << 8) | iraInput.read();
}
//if(idx > 0)
// System.out.println("frs="+intFrameCounter+",skip bytes:" + idx);
if(boolSync == false) {
boolSync = true;
if(intStandardMask == 0xffe00000) { //是第一帧...
longFrameOffset = iraInput.getFilePointer();
longAllFrameSize = iraInput.length() - longFrameOffset;
longFrames = longAllFrameSize / intFrameSize;
parseVBR(); //若有VBR tag以上3个变量将被改写
intStandardMask = cur_mask;
floatFrameDuration = 1152f / (getFrequency() << intLSF);
}
}
if (intProtectionBit == 0)
headerCRC();
intFrameCounter++;
return true;
}
public boolean isMSStereo() {
return boolMS_Stereo;
}
public boolean isIStereo() {
return boolIntensityStereo;
}
public int getBitrate() {
return intBitrateTable[intLSF][intLayer-1][intBitrateIndex];
}
public int getBitrateIndex() {
return intBitrateIndex;
}
public int getChannels() {
return intMode == 3 ? 1 : 2;
}
public int getMode() {
return intMode;
}
public int getModeExtension() {
return intModeExtension;
}
public int getVersion() {
return intVersionID;
}
public int getLayer() {
return intLayer;
}
public int getSampleFrequency() {
return intSamplingFrequency;
}
public int getFrequency() {
return intSamplingRateTable[intVersionID][intSamplingFrequency];
}
public int getMainDataBytes() {
return intMainDataBytes;
}
public int getSideInfoSize() {
return intSideInfoSize;
}
public int getFrameSize() {
return intFrameSize;
}
public int getFrameCounter() {
return intFrameCounter;
}
// MP3 文件帧数等信息
private static long longAllFrameSize; //帧长度总和(文件长度减去ID3 tag, APE tag 等长度)
private static long longFrameOffset; //第一帧的偏移量
private static long longFrames; //帧数
private static float floatFrameDuration;//一帧时长(秒)
private static String strDuration;
public long getTrackFrames() {
return longFrames;
}
/*
* 返回MP3文件时长(秒)
*/
public float getDuration() {
return floatFrameDuration * longFrames;
}
/*
* 解码存储在第一帧的VBR信息.若第一帧存储的是VBR信息,帧边信息被填充为零,不解
* 码VBR tag而把这一帧作为音频帧不影响正常解码.
*/
private boolean boolVBRtag;
private byte[] byteVBRToc;
private int intTocNumber, intTocPer, intTocFactor;
private String strBitRate;
private boolean parseVBR() throws Exception {
int iTagSize = intFrameSize - intSideInfoSize;
if (iTagSize < 124)
return false;
byte[] b = new byte[iTagSize];
iraInput.dump(0, b, 0, intSideInfoSize);
for (int i = 2; i < intSideInfoSize; i++) //前2字节可能是CRC_word
if (b[i] != 0) {
b = null;
return false;
}
iraInput.dump(intSideInfoSize, b, 0, iTagSize);
//-------------------------------VBR tag------------------------------
int iOff = 0;
if ((b[0] == 'X' && b[1] == 'i' && b[2] == 'n' && b[3] == 'g') ||
(b[0] == 'I' && b[1] == 'n' && b[2] == 'f' && b[3] == 'o')) {
//--------Xing/Info header--------
boolVBRtag = true;
longAllFrameSize -= intFrameSize;
longFrameOffset += intFrameSize;
int xing_flags = makeInt32(b, 4);
iOff = 8;
if ((xing_flags & 1) == 1) { // track frames
longFrames = makeInt32(b, iOff);
iOff += 4;
System.out.println("track frames: " + longFrames +
" [" + new String(b,0,4) + "]");
}
if ((xing_flags & 0x2) != 0) { // track bytes
longAllFrameSize = makeInt32(b, iOff);
iOff += 4;
System.out.println(" track bytes: " + longAllFrameSize);
}
if ((xing_flags & 0x4) != 0) { // TOC: 100 bytes.
byteVBRToc = new byte[100];
System.arraycopy(b, iOff, byteVBRToc, 0, 100);
iOff += 100;
//System.out.println(" TOC: true");
}
if ((xing_flags & 0x8) != 0) { // VBR quality
int xing_quality = makeInt32(b, iOff);
iOff += 4;
System.out.println(" quality: " + xing_quality);
}
intTocNumber = 100; //TOC共100个表项
intTocPer = 1; //每个表项1字节
intTocFactor = 1;
} else if(b[0] == 'V' && b[1] == 'B' && b[2] == 'R' && b[3] == 'I') {
//--------VBRI header--------
//version ID: 2 bytes
//Delay: 2 bytes
int vbri_quality = (b[8] & 0xff) | (b[9] & 0xff);
System.out.println(" quality: " + vbri_quality +
" [" + new String(b,0,4) + "]");
longAllFrameSize = makeInt32(b, 10);
System.out.println(" track bytes: " + longAllFrameSize);
longFrames = makeInt32(b, 14);
System.out.println("track frames: " + longFrames);
intTocNumber = (b[18] & 0xff) | (b[19] & 0xff);
intTocFactor = (b[20] & 0xff) | (b[21] & 0xff);
intTocPer = (b[22] & 0xff) | (b[23] & 0xff);
//int toc_frames = (b[24] & 0xff) | (b[25] & 0xff); //每个TOC表项的帧数
int toc_size = intTocNumber * intTocPer;
iOff = 26 + toc_size;
System.out.println(" TOC: " + intTocNumber + " * " +
intTocPer + " = " + toc_size + "factor=" + intTocFactor);
if (intFrameSize - intSideInfoSize < iOff)
return false;
byteVBRToc = new byte[toc_size];
System.arraycopy(b, 26, byteVBRToc, 0, toc_size);
} else {
b = null;
return false;
}
//-------------------------------LAME tag------------------------------
//9+1+1+8+1+1+3+1+1+2+4+2+2=36 bytes
if(iTagSize - iOff < 36 || b[iOff] == 0) {
strBitRate = "VBR";
b = null;
return true;
}
//Encoder Version: 9 bytes
String strEncoder = new String(b, iOff, 9);
iOff += 9;
System.out.println(" encoder: " + strEncoder);
//'Info Tag' revision + VBR method: 1 byte
//boolean isCBR=false, isABR=false, isVBR=false;
int revi = (b[iOff] & 0xff) >> 4; //0:rev0; 1:rev1; 15:reserved
int lame_vbr = b[iOff++] & 0xf; //0:unknown
//Lowpass filter value(低通滤波上限值): 1 byte
int lowpass = b[iOff++] & 0xff;
System.out.println(" lowpass: " + (lowpass * 100) + "Hz" +" [revi "+revi+"]");
//Replay Gain(回放增益):8 bytes
float peak = Float.intBitsToFloat(makeInt32(b, iOff)); //Peak signal amplitude
iOff += 4;
int radio = ((b[iOff] & 0xff) << 8) | (b[iOff+1] & 0xff); //Radio Replay Gain
/*
* radio:
* bits 0h-2h: NAME of Gain adjustment:
* 000 = not set
* 001 = radio
* 010 = audiophile
* bits 3h-5h: ORIGINATOR of Gain adjustment:
* 000 = not set
* 001 = set by artist
* 010 = set by user
* 011 = set by my model
* 100 = set by simple RMS average
* bit 6h: Sign bit
* bits 7h-Fh: ABSOLUTE GAIN ADJUSTMENT.
* storing 10x the adjustment (to give the extra decimal place).
*/
iOff += 2;
int phile = ((b[iOff] & 0xff) << 8) | (b[iOff+1] & 0xff); //Audiophile Replay Gain
/*
* phile各位含义同上(radio)
*/
iOff += 2;
//Encoding flags + ATH Type: 1 byte
/*int enc_flag = (b[iOff] & 0xff) >> 4;
int ath_type = b[iOff] & 0xf;
//000?0000: LAME uses "--nspsytune" ?
boolean nsp = ((enc_flag & 0x1) == 0) ? false : true;
//00?00000: LAME uses "--nssafejoint" ?
boolean nsj = ((enc_flag & 0x2) == 0) ? false : true;
//0?000000: This track is --nogap continued in a next track ?
//is true for all but the last track in a --nogap album
boolean nogap_next = ((enc_flag & 0x4) == 0) ? false : true;
//?0000000: This track is the --nogap continuation of an earlier one ?
//is true for all but the first track in a --nogap album
boolean nogap_cont = ((enc_flag & 0x8) == 0) ? false : true;*/
iOff++;
// ABR/CBR位率或VBR的最小位率(0xFF表示位率为255Kbps以上): 1 byte
int lame_bitrate = b[iOff++] & 0xff;
switch (lame_vbr) {
case 1:
case 8: // CBR
strBitRate = String.format("CBR %1$dK", getBitrate());
break;
case 2:
case 9: // ABR
if(lame_bitrate < 0xff)
strBitRate = String.format("ABR %1$dK", lame_bitrate);
else
strBitRate = String.format("ABR %1$dK以上", lame_bitrate);
break;
default: // 0: unknown is VBR ?
if(lame_bitrate == 0) //unknown
strBitRate = "VBR";
else
strBitRate = String.format("VBR %1$dK以上", lame_bitrate);
}
//Encoder delays: 3 bytes
iOff += 3;
//Misc: 1 byte
iOff++;
//MP3 Gain: 1 byte.
//任何MP3能无损放大2^(mp3_gain/4).以1.5dB为步进值改变'Replay Gain'的3个域:
// 'Peak signal amplitude', 'Radio Replay Gain', 'Audiophile Replay Gain'
//mp3_gain = -127..+127, 对应的:
// 分贝值-190.5dB..+190.5dB; mp3_gain增加1, 增加1.5dB
// 放大倍数0.000000000276883..3611622602.83833951
int mp3_gain = b[iOff++]; //其缺省值为0
if(mp3_gain != 0)
System.out.println(" MP3 Gain: " + mp3_gain);
//Preset and surround info: 2 bytes
int preset_surround = ((b[iOff] & 0xff) << 8) | (b[iOff+1] & 0xff);
int surround_info = (preset_surround >> 11) & 0x7;
switch(surround_info) {
case 0: //no surround info
break;
case 1: //DPL encoding
System.out.println(" surround: DPL");
break;
case 2: //DPL2 encoding
System.out.println(" surround: DPL2");
break;
case 3: //Ambisonic encoding
System.out.println(" surround: Ambisonic");
break;
case 7: // reserved
System.out.println(" surround: invalid data");
break;
}
preset_surround &= 0x7ff; //11 bits: 2047 presets
if(preset_surround != 0) //0: unknown / no preset used
System.out.println(" surround: preset " + preset_surround);
iOff += 2;
//MusicLength: 4 bytes
//MP3文件原始的(即除去ID3 tag,APE tag等)'LAME Tag frame'和'音乐数据'的总字节数
int music_len = makeInt32(b, iOff);
iOff += 4;
if(music_len != 0)
longAllFrameSize = music_len;
//MusicCRC: 2 bytes
iOff += 2;
//CRC-16 of Info Tag: 2 bytes
b = null;
return true;
}
// -------------------------------------------------------------------
// 以下是辅助功能,删除掉源码及相关调用不影响正常播放
// -------------------------------------------------------------------
// 打印信息
public void printHeaderInfo() {
String[] sver = {"MPEG 2.5", "reserved", "MPEG 2.0", "MPEG 1.0"};
String[] mode_str = {", Stereo",", Joint Stereo",", Dual channel",", Single channel(Mono)"};
String[] exmode_str = {"","(I/S)","(M/S)","(I/S & M/S)"};
if(strDuration == null) {
float duration = getDuration();
int m = (int)(duration / 60);
strDuration = String.format("%1$02d:%2$02d", m, (int)(duration - m * 60 + 0.5));
progress = new StringBuffer(">----------------------------------------");
}
if(!boolVBRtag)
strBitRate = String.format("%1$dK", intBitrateTable[intLSF][intLayer-1][intBitrateIndex]);
System.out.println("\r" + sver[intVersionID] + ", Layer " + intLayer +
", " + getFrequency()+"Hz, " +
strBitRate +
mode_str[intMode] +
exmode_str[intModeExtension] + ", " +
strDuration);
}
private static StringBuffer progress;
private static int progress_index = 1;
public void printState() {
float t = intFrameCounter * floatFrameDuration;
int m = (int)(t / 60);
float s = t - 60 * m;
float percent;
if(boolVBRtag)
percent = (float)intFrameCounter / longFrames * 100;
else
percent = (float)iraInput.getFilePointer() / iraInput.length() * 100;
int i = ((int)(percent + 0.5) << 2) / 10;
if(i == progress_index) {
progress.replace(i-1, i+1, "=>");
progress_index++;
}
System.out.printf("\r%1$02d:%2$04.1f [%3$-41s] %4$.1f%%", m, s, progress, percent);
}
}
上一篇:(一)用JAVA编写MP3解码器——前言
下一篇:(三)用JAVA编写MP3解码器——读取位流
【下载地址】http://jmp123.sourceforge.net/