博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(十四)用JAVA编写MP3解码器——多相合成滤波
阅读量:7071 次
发布时间:2019-06-28

本文共 3643 字,大约阅读时间需要 12 分钟。

hot3.png

解码一帧Layer3第8步:多相频率倒置 (Inverse Quantize Samples)

      也可以称为频率倒相(Frequency Inversion),在数据进入多相滤波器前进行频率倒相,把奇数序号子带的奇数号样本乘上-1,这样做的目的是校正多相滤波器组对频率的倒相。为了充分利用decodeFrame方法内的循环,把这部分简短的代码放进decodeFrame内。

 

解码一帧Layer3第9步:多相合成滤波 (Poly Phase Synthesis Filterbank) 

      多相合成滤波是调用频度最高的一个模块,采用标准立体声编码的MP3一帧要调用18*2*2=72次。多相合成滤波是解码的关键模块,算法及实现代码都可能再优化,加之Layer1和Layer2也要调用,所以将多相合成滤波单独封装在Synthesis类。

      解码一帧Layer3的各步快讲解完了,把解码一帧写进class Layer3内的decodeFrame方法。这部分源码如下:

//8.	//>>>>INVERSE QUANTIZE SAMPLES=============================================	//	// 在class Layer3的decodeFrame方法内实现	//	//<<<
>>>SYNTHESIZE VIA POLYPHASE MDCT======================================== // // 在decodeFrame方法内用objFilter.synthesisSubBand()调用class Synthesis // 的synthesisSubBand方法实现多相合成滤波 // //<<<
>>>OUTPUT PCM SAMPLES=================================================== // // 见Audio.java // //<<<>>>INVERSE QUANTIZE SAMPLES int rzero_sb = (17 + rzero_index[ch]) / 18; for (sb = 1; sb < rzero_sb; sb += 2) for (ss = 1; ss < 18; ss += 2) xr[ch][sb][ss] = -xr[ch][sb][ss]; //<<<

 

      在decodeFrame方法内调用解码一帧MP3的10个步骤的方法,其中还要考虑容错处理。我们知道一帧的字节数是可以计算出来的,依据什么去计算呢?无论是Layer1、Layer2还是Layer3,帧的长度用槽(slot)描述,Layer2和Layer3一槽是一字节,Layer1一槽是4字节。根据MPEG Audio层的压缩方式,就可以计算出一帧的长度。再看上面代码中“丢弃帧的填充位”就容易理解了:一帧的长度事先可以计算出来,如果MP3编码器压缩后的一帧小于计算出的帧长,就要凑足帧长,加之现在有的MP3编码器(MP3 pro)可以在这个位置写入自己辅助信息来提升MP3的高频谱增强对音乐的细节表现,不处理辅助位(况且绝大多数MP3不是用MP3 pro压缩的)不影响解码结果,所以这里对填充数据直接作舍弃处理。class Layer3申明了Synthesis类对象objFilter,调用objFilter.synthesisSubBand(floatSamples, ch)完成多相合成滤,请注意这一句是放在3重循环体内的。

 

封装多相合成滤波类class Synthesis   多相合成滤波过程示意图如下:

 

Poly phase synthesis filterband

 

      上图清晰地示意出一个声道的多相合成滤波的过程(图中bit应为float,DCT表示矩阵运算),共5个步骤。

 

      1.移位 (Shift)确保每一次将数据写入FIFO队列内的正确位置,首先计算出本次写入到FIFO队列的首址。

 

      2.矩阵运算 (Matrixing) 将32个输入数据变换为64个输出数据。为了提高程序运行的效率,将64个输出数据直接写进FIFO队列,这64个数据在FIFO中是邻接的,本次写入的首址由第1步计算得到。矩阵运算的快速算法请参考《 》,该贴详细讲解了各点DCT快速算法代码编写和展开式。矩阵运算的快速算法DCT(32->64)推导过程如下:

矩阵运算的快速算法DCT(32->64)

 

 

conclusion

 

 

     3.构建U向量   计算u_vector可以用下述代码实现:

// Build the U vectorfor (i = 0; i < 512; i += 64) {	k = i << 1;	for (j = 0; j < 32; j++) {		u_vector[i + j]      = curfifo[(off + k + j) & 0x3FF];		u_vector[i + j + 32] = curfifo[(off + k + j + 96) & 0x3FF];	}}

     构建U向量就是把FIFO队列中的数据抽取一部分出来写入u_vector。这里的off由第1步计算得到,从这段代码可以看出一个off值对应u_vector中的16个下标值。u_vector用于第4步的加窗运算,同样是出于运行效率的考虑,u_vector可以省掉,第4步时根据“u_vector中的16个下标值规律” 直接到FIFO队列中相应的位置去取数。找出u_vector下标值的规律之后,再将窗口系数按这个规律打乱顺序重新排列。是怎样的规律自己琢磨下上面的代码就看出来了,我相信你的观察能力哈。省掉u_vector带来的好处一是可以减少运算,二是可以减小存储开销,有好处滴~

 

      4.加窗运算 (Dewindowing)  这是滤波的最后一步。如果没有省掉u_vector,这一步应该这么算:

// Dewindowingfor (i = 0; i < 512; i++)	u_vector[i] *= dewin[i];

       其中的dewin[i]是窗口系数D[i]*32768,窗口系数D[]的512个常量由解码规范的文档中给出,如果要对解码器加入多段频率均衡,就在这一步进行。通过加窗后得到的u_vector用于计算PCM样本。

 

      5.计算32个PCM样本   如果没有省掉u_vector,这一步应该这么算:

// Calculate and output 32 samplesfor (i = 0; i < 32; i++) {	sum = 0.0f;	for (j = 0; j < 512; j += 32)		sum += u_vector[j + i];	PCMi = sum > 32767 ? 32767 : (sum < -32768 ? -32768 : (int)sum);	pcmbuf[idx]     = (byte)(PCMi >>> 1);   	pcmbuf[idx + 1] = (byte)(PCMi >>> 9);       	idx += idx_step;}

      计算得到的PCM样本暂存到pcmbuf[],解码完一帧将PCM数据送入音频输出模块播放,解码一帧的任务就结束了。采用16位PCM输出的话,一个PCM样本值占2字节,输出的是立体声的话要求左右声道的PCM样本值交替排列在pcmbuf[]内,上述代码中idx完成“交替”作用。

      一个粒度组内的一个声道的PCM样本数为18*32=576个,立体声编码的MP3一帧的PCM样本数为2*2*576=2304个,字节数为2*2304=4608字节。计算32个PCM样本以极高的频度被调用(解码一帧被调用72次),JAVA没有宏定义,出于效率考虑,没有编写计算PCM样本的方法供调用,而是在需要计算的每一处单独放入这部分进代码。取消计算32个PCM的调用、取消掉u_vector、矩阵运算采用了展开式,这3方面的原因导致class Synthesis的代码看起来很长,乍一看也复杂得让人理不清头绪。不过,换来的运行效率提大幅度升,通过对比测试,这3项优化措施使解码速度提升30%以上 ,所以这里对代码做这样的优化很成功。前面讲到的哈夫曼解码、逆量化(用查表法)和重排序、IMDCT等模块,编写代码时也都充分考虑到了提高运行效率,所以这个用JAVA写的MP3解码器速度是很快的。我说她解码快,是同MADLIB和MPG123(名气比较大的开源的用C写的MP3解码器)实测对比的结论,都不用音频输出模块,赛对同一MP3的解码时间,我的解码器一胜一负,不错的成绩。自己赞一下~

      多相合成滤波Synthesis.java源码较长,不贴这了。需要的话到 下载。

 

上一篇:

下一篇:

转载于:https://my.oschina.net/darkness/blog/363522

你可能感兴趣的文章
程序员娶妻子的经典准则
查看>>
Synology NAS 存储系统多路径连接Vmware ESXi 6.5
查看>>
K8s 原理架构介绍(一)
查看>>
微软正式发布OneDrive 提供100GB免费空间
查看>>
移动端web无刷新上传图片【兼容安卓IOS】
查看>>
MySQL-MongoDB开源监控利器PMM增加slack报警功能
查看>>
PowerShell 查找删除Windows补丁文件
查看>>
Powershell 创建文件,安全组和DFS链接
查看>>
在Linux系统中安装Subversion版本控制
查看>>
云计算的一匹黑马——SAP
查看>>
利用SCVMM 2012 R2来管理Azure虚拟机
查看>>
siri为什么比谷歌搜索“笨”?
查看>>
数据为本,洞悉安全
查看>>
学习iBatis时的一个苦逼经历
查看>>
Python 面试中 8 个必考问题
查看>>
浅谈IT技术选型和未来技术发展趋势
查看>>
表值变量 在批量操作中的用法
查看>>
makefile 教程
查看>>
Bridge mode
查看>>
python - PyDev统一编码
查看>>