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以上才能流畅播放。
mpeg 1.0
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 |
mpeg 2.0
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 <http://www.gnu.org/licenses/>.
- *
- * If you would like to negotiate alternate licensing terms, you may do
- * so by contacting the author: <http://jmp123.sourceforge.net/>.
- */
-
- 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);
- }
- }
【下载地址】http://jmp123.sourceforge.net/