文档章节

(十一)用JAVA编写MP3解码器——立体声处理

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

解码一帧Layer3第5步:立体声处理 -- class Layer3的ms_stereo和i_stereo方法

 

      MP3采用的立体声编码方式有中侧立体和强度立体声两种。

      1.中侧立体声(Middle/Side stereo)简称MS_stereo,在这种模式中,用中/侧声道规格化值Mi/Si取代左/右声道的值Li/Ri,重建左/右声道的值Li/Ri用下述公式:

      变换前Mi值在左声道,Si值在右声道。应用上述公式把Mi/Si频谱值变换为Li/Ri并放到左/右声道,就完成了中侧立体声解码。

 

      2.强度立体声(intensity stereo)  在MP3编码一帧结束后,有损压缩把频谱中表现音乐细节的高频成份压缩掉了。为了保留频谱中的高频部分,把这些高频数据编码附加到频带的后面部分。以长块为例,用rzero_bandL保存非零哈夫曼值的频带数,则从rzero_bandL至21的频带为强度立体声的编码数据。

      压制MP3时采用强度立体声编码,能提升音乐的细节,使压缩后的MP3听起来层次上更丰富。但是,很遗憾地告诉你,强度立体声是专利算法,受法律保护滴,很多MP3压缩器都没的这个功能,所以用强度立体声编码的MP3并不多见。

      对采用强度立体声编码的MP3,是否其进行强度声解码,不是训练有素的耳朵,听不出差别,反正我在调试程序时硬就没听出关闭强度立体声解码前后的区别。ISO/IEC 11172-3不对混合块中的长块作强度立体声处理,但很多MP3解码程序都作了处理,源码中把处理混合块中的长块的强度立体声的代码注释掉了,如果你愿意的话,可以取消掉注释把它加进来。是否对混合块中的长块作强度立体声处理,我也听不出什么差别,音乐细胞不发达呀,尽管我很喜欢听。记得去年的某个时候,我把以前买的CD拿出来听,好久没听过了,特亲切。放CD没的MP3方便,于是上网一顿海搜,找到MP3压缩器的No.1--LAME,用VBR模式的128Kpbs--320Kpbs,540M的CD压缩至不到60M,听起来和CD没的什么差别,觉得MP3编码真是个好,于是又上网海搜,想了解下MP3的技术细节,但是很遗憾,其中泛泛而谈的多,深入介绍其细节的少。好奇心的驱使下,想搞明白MP3的那些事,很长一段时间,业余时间就耗这上面了。也是有感于很多涉及MP3解码的关键技术的论文,都不是免费的,我就想把我弄清的部分公开出来,算是一个科谱宣传吧(这话说的:)可能你比我还懂的多)。

 

      联合立体声,请复习《(二)用JAVA编写MP3解码器——帧头信息解码》,对联合立体声(jiont stereo)作了简介。

 

【提示】以下代码是Layer3.java的一部分,应遵守《(一)用JAVA编写MP3解码器——前言》中的许可协议。

 

      今天就说到这,下回再说,上源码。class Layer3内的立体声解码方法的源码如下:

//5.
	//>>>>STEREO===============================================================
	/*
	 * 在requantizer方法内已经作了除以根2处理, ms_stereo()内不再除以根2.
	 */
	private void ms_stereo() {
		int sb, ss;
		float tmp0, tmp1;
		int rzero_xr = (rzero_index[0] > rzero_index[1]) ? rzero_index[0] : rzero_index[1];
		int rzero_sb = (rzero_xr + 17) / 18;
		for (sb = 0; sb < rzero_sb; sb++)
			for (ss = 0; ss < 18; ss++) {
				tmp0 = xr[0][sb][ss];
				tmp1 = xr[1][sb][ss];
				xr[0][sb][ss] = tmp0 + tmp1;
				xr[1][sb][ss] = tmp0 - tmp1;
			}
		rzero_index[0] = rzero_index[1] = rzero_xr;
	}

	private static float[][] lsf_is_coef;
	private static float[] is_coef;
	/*
	 *  解码一个频带强度立体声,MPEG 1.0
	 */
	private void is_lines_1(int is_pos, int idx0, int max_width,int idx_step) {
		float xr0;
		int sb32 = idx0 / 18;
		int ss18 = idx0 % 18;
		for (int w = max_width; w > 0; w--) {
			xr0 = xr[0][sb32][ss18];
			xr[0][sb32][ss18] = xr0 * is_coef[is_pos];
			xr[1][sb32][ss18] = xr0 * is_coef[6 - is_pos];
			ss18 += idx_step;
			if (ss18 >= 18) {
				ss18 -= 18;
				sb32++;
			}
		}
	}
	/*
	 * 解码一个频带强度立体声,MPEG 2.0/2.5
	 */
	private void is_lines_2(int tab2, int is_pos, int idx0, int max_width,int idx_step) {
		float xr0;
		int sb32 = idx0 / 18;
		int ss18 = idx0 % 18;
		for (int w = max_width; w > 0; w--) {
			xr0 = xr[0][sb32][ss18];
			if (is_pos == 0)
				xr[1][sb32][ss18] = xr0;
			else {
				if ((is_pos & 1) == 0)
					xr[1][sb32][ss18] = xr0 * lsf_is_coef[tab2][(is_pos - 1) >> 1];
				else {
					xr[0][sb32][ss18] = xr0 * lsf_is_coef[tab2][(is_pos - 1) >> 1];
					xr[1][sb32][ss18] = xr0;
				}
			}
			ss18 += idx_step;
			if (ss18 >= 18) {
				ss18 -= 18;
				sb32++;
			}
		}
	}

	/*
	 * 强度立体声(intensity stereo)解码
	 * 公式:
	 * lsf_is_coef -- coefficients for LSF intensity stereo,ISO 13818-3,sesion 2.4.3.2
	 * lsf_is_coef[0][i] = (1 / sqrt(sqrt(2)))^(i + 1)
	 * lsf_is_coef[1][i] = (1 /      sqrt(2)) ^(i + 1)
	 * i=0..14
	 *
	 * is_coef -- coefficients for intensity stereo,iso11172-3,sesion 2.4.3.4.9.3
	 * is_coef[i] = tan(i * (PI / 12))
	 * is_coef[i] = is_coef[i] / (1 + is_coef[i])
	 * i=0..6
	 */
	private void i_stereo(final int gr) {
		if(objSI.ch[0].gr[gr].mixed_block_flag != objSI.ch[1].gr[gr].mixed_block_flag
				|| objSI.ch[0].gr[gr].block_type != objSI.ch[1].gr[gr].block_type)
			return;
		GRInfo gr_info = objSI.ch[1].gr[gr];	//信息保存在右声道.
		int is_p, idx, sfb;

		if(objHeader.getVersion() == Header.MPEG1) {	//MPEG 1.0
			if(gr_info.block_type == 2) {
				//MPEG 1.0, short block/mixed block
				int w3;
				//int do_long = 0;
				//if(gr_info.mixed_block_flag == 1)
				//	do_long = 1;
				for (w3 = 0; w3 < 3; w3++) {
					sfb = rzero_bandS[w3]; //混合块sfb最小为3
					//if (sfb > 3)
					//	do_long = 0;
					for (; sfb < 12; sfb++) {
						idx = 3*intSfbIdxShort[sfb] + w3;
						is_p = scfS[1][w3][sfb];
						if(is_p >= 7)
							continue;
						is_lines_1(is_p,idx,intWidthShort[sfb],3);
					}
				}
				/*if(do_long == 1) {
					for (sfb = rzero_bandL; sfb < 8; sfb++) {
						is_p = scfL[1][sfb];
						if(is_p < 7)
							is_lines_1(is_p,sfbIndexOfEndL[sfb],intWidthLong[sfb],1);
					}
				}*/
			} else {
				//MPEG 1.0, long block
				for (sfb = rzero_bandL; sfb <= 21; sfb++) {
					is_p = scfL[1][sfb];
					if(is_p < 7)
						is_lines_1(is_p,intSfbIdxLong[sfb],intWidthLong[sfb],1);
				}
			}
		} else {	//MPEG 2.0/2.5
			final int tab2 = gr_info.scalefac_compress & 0x1;
			if(gr_info.block_type == 2) {
				//MPEG 2.0/2.5, short block/mixed block
				int w3;
				//int do_long = 0;
				for (w3 = 0; w3 < 3; w3++) {
					sfb = rzero_bandS[w3]; //混合块sfb最小为3
					//if (sfb > 3)
					//	do_long = 0;
					for (; sfb < 12; sfb++) {
						idx = 3*intSfbIdxShort[sfb] + w3;
						is_p = scfS[1][w3][sfb];
						is_lines_2(tab2, scfS[1][w3][sfb], idx, intWidthShort[sfb],3);
					}
				}
				//if(do_long == 1)
				//	for (sfb = rzero_bandL; sfb < 8; sfb++)
				//		is_lines_2(tab2, scfL[1][sfb], sfbIndexOfEndL[sfb], intWidthLong[sfb],1);
			} else {
				//MPEG 2.0/2.5, long block
				for (sfb = rzero_bandL; sfb <= 21; sfb++)
					is_lines_2(tab2, scfL[1][sfb], intSfbIdxLong[sfb], intWidthLong[sfb],1);
			}
		}
	}
	//<<<<STEREO===============================================================

 

上一篇:(十)用JAVA编写MP3解码器——逆量化和重排序

下一篇:(十二)用JAVA编写MP3解码器——消混叠处理

 

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

 

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

暗之幻影
粉丝 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
《Java程序员由笨鸟到菜鸟》电子版书正式发布,欢迎大家下载

在众多朋友的支持和鼓励下,《Java程序员由菜鸟到笨鸟》电子版终于和大家见面了。本电子书涵盖了从java基础到javaweb开放框架的大部分内容。在编写的过程中,难免会出现一些错误,希望大家能...

长平狐
2012/11/12
265
0
《Java程序员由笨鸟到菜鸟》电子版书正式发布,欢迎大家下载

在众多朋友的支持和鼓励下,《Java程序员由菜鸟到笨鸟》电子版终于和大家见面了。本电子书涵盖了从java基础到javaweb开放框架的大部分内容。在编写的过程中,难免会出现一些错误,希望大家能...

长平狐
2012/11/12
151
0
OSChina 开源周刊第三十三期 —— Node.js 和 io.js 准备合作!

每周技术抢先看,总有你想要的! 开源资讯 Node.js 和 io.js 准备合作!合久必分,分久必合? Nervana 开源深度学习软件,性能超 Facebook、Nvidia产品 B 站建开源工作组 多 APP 使用其开源项...

OSC编辑部
2015/05/09
236
0

没有更多内容

加载失败,请刷新页面

加载更多

iOS Xcode升级包地址(感谢大神)

下载地址:DeviceSupport

_____1____
17分钟前
6
0
Qt编写自定义控件71-圆弧进度条

一、前言 现在web形式的图表框架非常流行,国产代表就是echart,本人用过几次,三个字屌爆了来形容,非常强大,而且易用性也非常棒,还是开源免费的,使用起来不要太爽,内置的各种图表和仪表...

飞扬青云
17分钟前
4
0
润乾报表与 ActiveReport JS 功能对比

简介 润乾报表是用于报表制作的大型企业级报表软件,核心特点在于开创性地提出了非线性报表数学模型,采用了革命性的多源关联分片、不规则分组、自由格间运算、行列对称等技术,使得复杂报表...

泡泡糖儿
29分钟前
5
0
【1015】LNMP架构二

【1015】LNMP架构二 三、PHP安装 PHP安装和LAMP安装PHP方法有差别,需要开启php-fpm服务 1、下载PHP7至/usr/local/src/ 切换目录:cd /usr/local/src 2、解压缩 tar -jxvf php-7.3.0.tar.gz...

飞翔的竹蜻蜓
今天
5
0
浅谈Visitor访问者模式

一、前言 什么叫访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+对其进行处理就叫作访问,那么...

青衣霓裳
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部