文档章节

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

暗之幻影
 暗之幻影
发布于 2015/01/04 15:29
字数 3627
阅读 20
收藏 0

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/

本文转载自:http://lfp001.iteye.com/blog/739606

暗之幻影
粉丝 20
博文 377
码字总数 71245
作品 0
南京
高级程序员
私信 提问
(五)用JAVA编写MP3解码器——解析文件信息

前文提到解析MP3标签,程序源码中也已经出现了调用解析MP3标签、打印MP3文件信息的功能,这儿先说说MP3文件信息的解析。 解析MP3的文件信息对MP3解码器来说只是一个附加功能,如果不加入这部...

暗之幻影
2015/01/04
61
0
疯狂Spring Cloud连载(11)——Feign的编码器与解码器

本文节选自《疯狂Spring Cloud微服务架构实战》 京东购买地址:https://item.jd.com/12256011.html 当当网购买地址:http://product.dangdang.com/25201393.html Spring Cloud教学视频:htt...

杨大仙的程序空间
2017/10/23
3.1K
5
直播,音视频编码器和解码器(EasyDarwin)-Android

使用摄像头采集视频数据,并通过MediaCodec进行H264编码,之后打包成RTSP格式并上传的。 TextuewView也提供了一个setTransform方法,该方法接收一个matrix参数,使用该参数对当前的渲染内容进...

shareus
2018/05/18
0
0
Java8 BASE64编解码

Java8 BASE64编解码 Base64是一种用64个字符来表示任意二进制数据的方法。 Base64是一种最常见的二进制编码方法。 Java一直缺少BASE64编码 API,以至于通常在项目开发中会选用第三方的API...

秋风醉了
2015/04/02
1K
0
如何用JMF编写MP3音乐播放器1——(图文)如何使用JMF及其环境变量的配置

Java Media Framework JMF,全名为Java Media Framework,它可以在java applet和application中使用音频,视频或者其他基于时间的多媒体。 1.JMF对音频的支持 但是本人经过测试和编写,发现JMF并...

cui314461408
2013/08/09
5.7K
0

没有更多内容

加载失败,请刷新页面

加载更多

Electron React Node

NPM配置手册https://cloud.tencent.com/developer/section/1490263 URL编码参考手册:https://www.w3cschool.cn/htmltags/html-urlencode.html 在配置React环境时,提示 npm ERR! code ECON......

DB_Terrill
9分钟前
0
0
Knative 实战:基于阿里云 Kafka 实现消息推送

在 Knative 中已经提供了对 Kafka 事件源的支持,那么如何在阿里云上基于 Kafka 实现消息推送,本文给大家解锁这一新的姿势。 背景 消息队列 for Apache Kafka 是阿里云提供的分布式、高吞吐...

阿里云官方博客
9分钟前
0
0
自动评论csdn博客文章实现

正文 我们来用java代码爬取csdn博客网站,然后自动评论,这一波操作可以说是相当风骚了,话不多说,咱上代码。 第一步是登录代码,这个网上一大把,代码中用到了jsoup依赖包,用于解析html获...

码农实战
11分钟前
0
0
java速查手册Java Syntax Cheatsheet for Algorithms by TCXX

Numbers static MAX_VALUE xxxValue() public int compareTo(XXX) public boolean equals(Object o) static Integer valueOf(int i) static Integer valueOf(String s) static Integer value......

momo1987
19分钟前
1
0
vue Property or method "***" is not defined on the instance but referenced during render.报错

报错:title不是个方法 解决: 把:去掉不报错了

栾小糖
23分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部