论坛首页 Java企业应用论坛

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

浏览 14856 次
精华帖 (0) :: 良好帖 (12) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-08-17   最后修改:2010-09-18

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);
	}
}
 

 

上一篇:(一)用JAVA编写MP3解码器——前言

下一篇:(三)用JAVA编写MP3解码器——读取位流

 

【下载地址】http://jmp123.sourceforge.net/

   发表时间:2010-08-25  
看不懂呢,大师,这么多的左移,右移等各种运算,看得人眼都花了,能不能讲以下解码和编码的原理啊!
0 请登录后投票
   发表时间:2010-08-25  
比如需要左移和右移的原因,最好能全点哦!如果您有时间。我想您的文章没有被评为精华,应该是注释太少吧!实现的原理也大都没讲啊!好期待您的补充!
0 请登录后投票
   发表时间:2010-08-26   最后修改:2010-08-26
xiexifeng113 写道
看不懂呢,大师,这么多的左移,右移等各种运算,看得人眼都花了,能不能讲以下解码和编码的原理啊!

表示帧头信息的几个变量的含义、占几比特都作了注解。
移位操作就是从帧头的4字节中取相关的那几比特出来。第一个表格解释了帧头4字节中哪几位表示什么含义。
原理后文陆续作简介。
0 请登录后投票
   发表时间:2010-08-27  
xiexifeng113 写道
比如需要左移和右移的原因,最好能全点哦!如果您有时间。我想您的文章没有被评为精华,应该是注释太少吧!实现的原理也大都没讲啊!好期待您的补充!


解码类的代码, 基本不看编码/解码规范  注释啊, 原理啊基本是没有大的作用的。 看明白规范, 基本代码也就出来了。
0 请登录后投票
   发表时间:2010-09-13  
楼主,我用你的代码测试读取一首位速为320kbps的歌曲的位速,读取出来的数据是128kbps哦
0 请登录后投票
   发表时间:2010-09-16  
MO_oC 写道
楼主,我用你的代码测试读取一首位速为320kbps的歌曲的位速,读取出来的数据是128kbps哦

通过查表得到位率。
你用其它的播放器播放试试看,是否真的是320Kbps。
0 请登录后投票
   发表时间:2010-09-16  

这是文件属性的

这是酷狗的
 

 

这是你的程序的
 

 

 

这是那首歌曲的下载地址:http://upwap.ru/1035149
 

  • 大小: 6.4 KB
  • 大小: 11.4 KB
  • 大小: 11.1 KB
0 请登录后投票
   发表时间:2010-09-16  
楼主,我看到有些程序是先解析VBR信息,如果存在VBR信息就这样
return (int)((medFrameSize * (double)getFrequency()) / (1000.0 * ((getLayerIndex()==3) ? 12.0 : 144.0)));

不存在的话就查表
0 请登录后投票
   发表时间:2010-09-16  
MO_oC 写道
楼主,我看到有些程序是先解析VBR信息,如果存在VBR信息就这样
return (int)((medFrameSize * (double)getFrequency()) / (1000.0 * ((getLayerIndex()==3) ? 12.0 : 144.0)));

不存在的话就查表

这种方法计算当前帧的位率是正确的,但VBR和ABR每一帧的位率不一定相同,如果是用LAME压缩的MP3的话,通过读LAME tag可以知道其最小的位率是多大,最高位率无法知道,除非去扫描每一帧得到最大位率。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics