解码一帧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方法内实现
//
//<<<<INVERSE QUANTIZE SAMPLES=============================================
//9.
//>>>>SYNTHESIZE VIA POLYPHASE MDCT========================================
//
// 在decodeFrame方法内用objFilter.synthesisSubBand()调用class Synthesis
// 的synthesisSubBand方法实现多相合成滤波
//
//<<<<SYNTHESIZE VIA POLYPHASE MDCT========================================
//10.
//>>>>OUTPUT PCM SAMPLES===================================================
//
// 见Audio.java
//
//<<<<OUTPUT PCM SAMPLES===================================================
private final static float[] floatSamples = new float[32];
/*
* decodeFrame -- 解码1帧Layer3
*/
public void decodeFrame() throws Exception {
getSideInfo();
int nSlots = objHeader.getMainDataSlots();
int buflen = objInBitStream.getBuffBytes();
int data_begin = objSI.main_data_begin;
// 若出错(buflen<data_begin):
// 不解码当前这一帧,将下一帧(或几帧)主数据(main_data)填入位流缓冲区.
//
while (buflen < data_begin) {
objInBitStream.append(nSlots);
//System.out.println("Skip Bytes: " + objHeader.getFrameSize());
objHeader.syncFrame();
nSlots = objHeader.getMainDataSlots();
getSideInfo();
buflen = objInBitStream.getBuffBytes();
data_begin = objSI.main_data_begin;
}
//丢弃帧的填充位
int discard = buflen - objInBitStream.getBytePos() - data_begin;
objInBitStream.skipBytes(discard);
objInBitStream.append(nSlots);
int gr, ch, sb, ss;
for (gr = 0; gr < intMaxGr; gr++) {
for (ch = 0; ch < intChannels; ch++) {
if (objHeader.getVersion() == Header.MPEG1)
getScaleFactors_1(ch, gr);
else
getScaleFactors_2(ch, gr);
huffmanDecoder(ch, gr);
requantizer(ch, gr,xr[ch]);
}
if(boolIntensityStereo)
i_stereo(gr);
if(objHeader.isMSStereo())
ms_stereo();
for (ch = intFirstChannel; ch <= intLastChannel; ch++) {
antialias(ch, gr);
hybrid(ch, gr);
//>>>>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];
//<<<<INVERSE QUANTIZE SAMPLES
for (ss = 0; ss < 18; ss++) {
for (sb = 0; sb < 32; sb++)
floatSamples[sb] = xr[ch][sb][ss];
objFilter.synthesisSubBand(floatSamples, ch);
}
}
}
}
在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 多相合成滤波过程示意图如下:
上图清晰地示意出一个声道的多相合成滤波的过程(图中bit应为float,DCT表示矩阵运算),共5个步骤。
1.移位 (Shift)确保每一次将数据写入FIFO队列内的正确位置,首先计算出本次写入到FIFO队列的首址。
2.矩阵运算 (Matrixing) 将32个输入数据变换为64个输出数据。为了提高程序运行的效率,将64个输出数据直接写进FIFO队列,这64个数据在FIFO中是邻接的,本次写入的首址由第1步计算得到。矩阵运算的快速算法请参考《 MP3解码之DCT(32→64)快速算法的展开 》,该贴详细讲解了各点DCT快速算法代码编写和展开式。矩阵运算的快速算法DCT(32->64)推导过程如下:
3.构建U向量 计算u_vector可以用下述代码实现:
// Build the U vector
for (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,这一步应该这么算:
// Dewindowing
for (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 samples
for (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源码较长,不贴这了。需要的话到 http://jmp123.sf.net/ 下载。
相关推荐
Java编写的解码器是一种基于Java编程语言实现的软件组件,专门用于解析和播放MP3音频文件。在本文中,我们将深入探讨Java MP3解码器的原理、实现细节以及其在面试和项目中的应用。 首先,理解MP3格式是至关重要的。...
标签"java mp3 无JMF 解码器"进一步强调了这个解码器的关键特性:它是用Java语言编写的,专注于MP3格式,且不依赖JMF。这使得它具有跨平台性,可以在任何支持Java的系统上运行。 压缩包内的文件"jmp123_400_utf8_...
《使用Java编写MP3播放器》是一份关于利用Java编程语言实现MP3音频解码的文档。MP3作为广泛使用的音频压缩格式,其解码技术是数字音频处理领域的重要组成部分。该文档旨在介绍如何利用Java编写一个MP3解码器,并提供...
总结来说,Java MP3音频文件解码器是一个独立的、用纯Java编写的工具,用于将MP3文件转换为可处理的原始音频数据。它的设计和实现涉及了音频编码原理、数据处理优化以及Java编程技术,对于理解和处理音频数据的...
MP3解码的过程涉及多个模块协同工作,主要包括同步及差错检查、主控模块、尺度因子解码、哈夫曼解码、逆量化、立体声解码、混淆缩减、IMDCT、频率反转以及合成多相滤波等关键步骤。 1. **同步及差错检查**:该模块...
《微分器设计与应用——信号滤波与求导》是一部深入探讨微分器在信号处理中的关键作用的著作,其各章节通过仿真的方式详细阐述了相关知识点。微分器在电子工程、通信系统和控制理论等领域具有广泛的应用,主要功能是...
基于通用可编程GPU的视频编解码器——架构、算法与实现
论文可能会介绍如何使用Java来实现解码算法,包括使用Java的IO流处理位流,以及如何利用Java的多线程和内存管理优化性能。 5. **源码分析**:源码是理解理论知识的最佳实践。通过阅读和分析代码,开发者可以了解到...
读取MP3文件需要使用Java的I/O流,如FileInputStream和BufferedInputStream,来高效地读取和处理文件内容。 8. **用户界面设计**: 使用JavaFX或Swing库可以创建包含播放/暂停按钮、音量控制器、播放列表等元素的...
《H.264视频编解码器中去方块滤波的实现》这篇论文主要探讨了在H.264视频编码标准中,如何有效实施去方块滤波技术,以消除由于块编码带来的方块效应,同时保持图像的主要结构特征。在H.264标准中,去方块滤波作为...
在描述中提到的VC下的C工程源码,意味着这个编解码器是用C语言编写,并且可以在Visual C++环境下编译和运行。这对于软件开发者来说非常有用,因为C语言的源代码通常更容易理解和修改,便于进行二次开发和定制。 ...
视频编解码中去块滤波模块的原理及设计网上很难下载到这篇文章,愿与大家分享!
总的来说,"jmf_mp3解码器"结合JLayer1.0.1的使用,为Java开发者提供了一个跨平台的解决方案,使得在Java应用程序中播放MP3文件变得简单而有效。这种技术的应用场景广泛,可以用于开发音乐播放器、多媒体教学软件、...
在本文中,我们将深入探讨如何使用Java编程语言来实现一个MP3播放器。MP3播放器是音频处理软件,能够读取、解码并播放MP3格式的音频文件。Java以其跨平台的特性,成为开发此类应用的理想选择。让我们一起探讨实现这...
Java编写的MP3播放器是一种基于Java编程语言开发的音频播放软件,主要用于播放MP3格式的音频文件。这种播放器的实现主要依赖于Java的多媒体处理库,如Java Media Framework (JMF) 或者 jogl 等第三方库。下面我们将...
解码器首先解析MP3文件的帧结构,然后使用熵编码技术如 Huffman 编码来解压音频数据,最后通过滤波等处理还原音频波形。 **千千静听的音频播放技术** 千千静听作为一个流行的音频播放器,其核心竞争力在于其对各种...
例如,以下是一个简单的示例代码片段,演示了如何使用Java Sound API和MP3 SPI播放MP3文件: ```java import javax.sound.sampled.*; public class MP3Player { public static void main(String[] args) { try {...
- 熵解码:根据视频编码标准,解码器使用熵解码器(如H.264的 CABAC 或 AVC 解码)恢复隐藏的信息。 - 反量化:将熵解码后的量化系数恢复为浮点数,用于重构图像。 - 逆变换:执行逆离散余弦变换(IDCT)或其他逆...