文件以字节为单位读取,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;
}
}