(二)用JAVA编写MP3解码器——帧头信息解码

1.解析帧头   帧头共4字节,从高位到低位这32比特的含义如下:

比特数名称内容
11sync0x7FF
2version1=mpeg1.0, 0=mpeg2.0
2lay4-lay = layerI, II or III
1error protection0=yes, 1=no
4bitrate_index见下文
2sampling_freq见下文
1padding填充位
1extension见下文
2mode见下文
2mode_ext联合立体声(joint stereo)模式
1copyright0=no 1=yes
1original0=no 1=yes
2emphasis预加重

  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\值1234567891011121314
layer1326496128160192224256288320352384416448
layer2324856648096112128160192224256320384
layer332404856648096112128160192224256320

 

mpeg 2.0
Layer\值1234567891011121314
layer1324856648096112128144160176192224256
layer28162432404856648096112128144160
layer38162432404856648096112128144160


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 其值表示采用哪种联合立体声方式

extensionintensity_stereoms_stereo
00offoff
01onof
10ofon
11onon


  帧头信息解码除解码上述信息外,还要进行帧同步、计算帧长、计算帧边信息长度等供后续解码。

  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/

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值