文档章节

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

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

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

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

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

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

暗之幻影
2015/01/04
50
0
JavaIO/输出输入入门

IO称之为输入输出,Java的IO是通过java.io包下的类和接口来支持,包含两大类:输入,输出. 在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Write...

Zhao-Qian
2013/01/04
467
0
为什么用 Java—— 一个 Python 程序员告诉你

来源:董老师在硅谷 这篇文章专门给程序员写的,普通读者慎入。原作者:Kevin Sookocheff 译者:Celia Zhen,原文点击文末链接。 每当我告诉别人我一直在用Java工作时,大家的反应都是: “纳...

oschina
2016/03/19
60.3K
84
JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千!

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m366917/article/details/52724939 JAVA基础再回首(三十)——JAVA基础再回首完美结束,感概万千! 经过了几...

Aduroidpc
2016/10/02
0
0
(深度)Java多线程系列(6):volatile 关键字的使用

原文地址:https://segmentfault.com/a/1190000008637202 作为 Java 语言的一个关键字,被看作是轻量级的 (锁)。虽然 只具有的部分功能,但是一般使用 会比使用 更有效率。在编写多线程程序...

芝麻粒儿
03/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

哪些情况下适合使用云服务器?

我们一直在说云服务器价格适中,具备弹性扩展机制,适合部署中小规模的网站或应用。那么云服务器到底适用于哪些情况呢?如果您需要经常原始计算能力,那么使用独立服务器就能满足需求,因为他...

云漫网络Ruan
50分钟前
3
0
Kafka 2.3 Producer (0.9以后版本适用)

kafka0.9版本以后用java重新编写了producer,废除了原来scala编写的版本。 这里直接使用最新2.3版本,0.9以后的版本都适用。 注意引用的包为:org.apache.kafka.clients.producer import ja...

实时计算
59分钟前
3
0
Java 中的 String 有没有长度限制

转载: https://juejin.im/post/5d53653f5188257315539f9a String是Java中很重要的一个数据类型,除了基本数据类型以外,String是被使用的最广泛的了,但是,关于String,其实还是有很多东西...

低至一折起
今天
14
0
OpenStack 简介和几种安装方式总结

OpenStack :是一个由NASA和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenSta...

小海bug
昨天
11
0
DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部