论坛首页 Java企业应用论坛

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

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

  文件以字节为单位读取,MP3解码器输入的数据是位流,即每次需要读取几比特,这就需要将字节流转换为比特流。解码器将文件按每次读取几比特将一个文件处理完,所以读取位流的方法以很高的频度被调用。也就是说,MP3文件是通过本类的方法每次将几比特送入解码器,从机而把一个文件解码完的。解码器的功能就是将送入的位流解码成PCM数据,然后由音频处理模块将PCM数据送入音频硬件播放。


  设置缓冲区(位流蓄水池)bitReservoir,解码器的其它模块在需要的时候通过调用append(int len)方法 从文件读取len字节 放进bitReservoir,仅当文件读完时append方法返回值为-1。暂存缓冲区中已经读取的比特、字节位置、剩下的字节数等。MP3解码过程中每次最少读入1位,最多读入17位,为了提高读取位流的效率,本类中共提供了3个读取位流的方法:get1Bit()、getBits9(int n)、getBits17(int n),读取的比特数n越小,方法体内执行的操作也就越少,根据每次要读取的比特数分别调用相应的方法。当然,如果要从位流缓冲区读取2位,调用getBits17(2)也是可以返回正确的值的,但getBits17(int n)方法体内要执行的操作要多一些,导致程序效率要低一些。


  JAVA没有象C语言的unsigned int那样的无符号整数类型,所以本类的方法中多处出现类似于iret &= 0xffff这样的语句来取32位int类型整数中的低位,确保每次从位流缓冲区读入的一字节是一个0~255的正整数,因为位流缓冲区bitReservoir是byte数组,byte类型的取值范围是-128~+127。例如int iret=bitReservoir[bytePos],假如bitReservoir[bytePos]值为10进制-9,用2进制表示的这个byte类型的值为11110111,赋给int类型的iret将byte转换为int类型,32位长iret的10进制值仍为-9,但用2进制表示为11111111111111111111111111110111,高24位全为1;若将该语句写成int iret=bitReservoir[bytePos] & 0xFF,则iret的值用2进制表示为00000000000000000000000011110111,这才是正确的结果。还需要特别指出的是本类中的get1Byte()方法不是用来读取8比特用的,除非缓冲区bitReservoir已经是字节对齐的,即intBitPos=0才行,也就是说读8bit不等同于读1byte。get1Byte()方法的使用见哈夫曼解码模块,相对于解码帧边信息、解码增益因子而言,哈夫曼解码需要读取的比特数多,故采用了更为高效的字节对齐的、用一个32位的int变量来“2级缓冲”的方法。


   续的解码模块共实例化了2个BitStream对象,一个是供解码帧边信息用,每次调用append(int len)方法向bitReservoir添加不超过32字节;另一个供解码主信息(主信息又可细分为增益因子、用哈夫曼编码的主数据两部分),每次向bitReservoir添加的字节数不超过1732。

 

  BitStream的成员变量iraInput为IRandomAcces类型,既可用于读取本地磁盘文件,也可用于读取网络文件;申明为static类型,使2个BitStream实例由同一文件向缓冲区bitReservoir填充数据。


位流读取比较简单,效率是关键。BitStream.java源码:

/*
* BitStream.java -- 读取位流
* 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 BitStream {
	private static final int RESELEN = 4096;
	private static IRandomAccess iraInput;
	private byte[] bitReservoir;
	private int bitPos;
	private int bytePos;
	private int endPos;		//bitReservoir已填入的字节数

	public BitStream(IRandomAccess iraIn) {
		iraInput = iraIn;
		bitReservoir = new byte[RESELEN + 4];	// 长度不小于512+1732(最大帧长)
	}

	public BitStream(int len) {
		bitReservoir = new byte[len];			// 任意长度,用于帧边信息解码
	}

	/**
	 * 向bitReservoir添加len字节,RESELEN后尾部要空出4字节用于32bit的"2级缓冲".
	 */
	public int append(int len) {
		if (len + endPos > RESELEN) {
			//将缓冲区bytePos及之后的字节移动到缓冲区首
			System.arraycopy(bitReservoir, bytePos, bitReservoir, 0, endPos - bytePos);
			endPos -= bytePos;
			bitPos = bytePos = 0;
		}
		try {
			if(iraInput.read(bitReservoir, endPos, len) < len)
				return -1;
		} catch (Exception e) {
			return -1;
		}

		endPos += len;
		return len;
	}

	public void resetIndex() {
		bytePos = bitPos = endPos = 0;
	}

	/**
	 * 从缓冲区bitReservoir读取1 bit
	 */
	public int get1Bit() {
		int bit = bitReservoir[bytePos] << bitPos;
		bit >>= 7;
		bit &= 0x1;
		bitPos++;
		bytePos += bitPos >> 3;
		bitPos &= 0x7;
		return bit;
	}

	/*
	 * 2 <= n <= 17
	 */
	public int getBits17(int n) {
		int iret = bitReservoir[bytePos];
		iret <<= 8;
		iret |= bitReservoir[bytePos + 1] & 0xff;
		iret <<= 8;
		iret |= bitReservoir[bytePos + 2] & 0xff;
		iret <<= bitPos;
		iret &= 0xffffff;  // 高8位置0;
 		iret >>= 24 - n;
		bitPos += n;
		bytePos += bitPos >> 3;
		bitPos &= 0x7;
		return iret;
	}

	/**
	 * 2<= n <= 9
	 */
	public int getBits9(int n) {
		int iret = bitReservoir[bytePos];
		iret <<= 8;
		iret |= bitReservoir[bytePos + 1] & 0xff;
		iret <<= bitPos;
		iret &= 0xffff;  // 高16位置0;
		iret >>= 16 - n;
		bitPos += n;
		bytePos += bitPos >> 3;
		bitPos &= 0x7;
		return iret;
	}

	public int getBytePos() {
		return bytePos;
	}

	public int getBuffBytes() {
		return endPos;
	}

	public int getBitPos() {
		return bitPos;
	}

	/**
	 * 返回值是0-255的无符号整数
	 */
	public int get1Byte() {
		return bitReservoir[bytePos++] & 0xff;
	}

	public void backBits(int n) {
		bitPos -= n;
		bytePos += bitPos >> 3;
		bitPos &= 0x7;
	}

	public void skipBytes(int nbytes) {
		bytePos += nbytes;
		bitPos = 0;
	}
}
 

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

下一篇:(四)用JAVA编写MP3解码器——读取文件

 

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

论坛首页 Java企业应用版

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