MPEG原理分析及MPEG编码器的调试
一、实验要求
- 理解程序设计的整体框架
- 理解感知音频编码的设计思想
■ 两条线
■ 时、频分析的矛盾! - 理解心理声学模型的实现过程
■ 临界频带的概念
■ 掩蔽值计算的思路 - 理解码率分配的实现思路
- 输出音频的采样率和目标码率
- 选择三个不同特性的音频文件
■ 噪声(持续噪声、突发噪声)
■ 音乐
■ 混合 - 某个数据帧,输出
■ 该帧所分配的比特数
■ 该帧的比例因子
■ 该帧的比特分配结果
二、实验原理
1、感知音频编码的设计思想
分析信号,去掉不能被感知的部分。
2、听觉阈值曲线
听觉系统中存在一个听觉阈值电平,低于这个电平的声音信号就听不到。
- 听觉阈值的大小随声音频率的改变而改变;
- 一个人是否听到声音取决于声音的频率,以及声音的幅度是否高于这种频率下的听觉阈值;
- 蓝线为听觉阈值,声强在蓝线以下的部分无法被听到;
- 两个声音响度级相同,但强度不一定相同,还与频率有关;
- 声压级越高,等响度曲线趋于平坦;
- 人耳对3~4KHz的声音感觉最灵敏;
- 声压级(SPL):
S P L = 20 l g P r m s P r e f SPL=20lg\frac{P_{rms}}{P_{ref}} SPL=20lgPrefPrms
P r e s = 2 × 1 0 − 5 P_{res}=2×10^{-5} Pres=2×10−5,零声级的参考声压值,为具有正常听力的年轻人对1kHz的声音刚好能察觉的声压值。
3、频域掩蔽域随声压级变化曲线
听觉阈值电平是自适应的,会随听到的不同频率声音而发生变化。音调音的掩蔽阈的宽度随频率而变化,掩蔽曲线不对称,高频段一侧的曲线斜率缓些,低频音容易对高频音产生掩蔽。
4、临界频带
- 临界频带是指当某个纯音被以它为中心频率、且具有一定带宽的连续噪声所掩蔽时,如果该纯音刚好被听到时的功率等于这一频带内的噪声功率,这个带宽为临界频带宽度。
- 研究窄带噪声对纯音掩蔽量的规律时被发现的。使噪声的中心频率等于信号频率,只改变噪声的带宽同时保持噪声的功率谱密度不变,测试纯音听阈随掩蔽噪声带宽变化的特性。纯音的听阈随掩蔽噪声带宽的增大而增大,在带宽增大到某一特定值之后听阈保持恒定不变。
- 通常认为从20Hz到16kHz有25个临界频带,单位为bark,1Bark=一个临界频带的宽度。
5、掩蔽值计算的思路
音乐与语音信号大都由一系列复杂的频谱分量构成,相应的这些多个掩蔽分量也会相互影响并最终获得一个整体的掩蔽阈值。对于多个掩蔽分量的综合掩蔽效果,目前有多种模型给出了描述方案 。
Lutfi 对多个掩蔽音同时存在时的综合掩蔽效果进行了研究:每个掩蔽音的掩蔽效果先独立变换然后再线性相加。
将输入信号变换到频域,再将结果分解成一些尽量与临界频带尽可能相似的子带,然后对每个子带进行量化,量化方式应当使得量化噪声听不见。
三、实验代码
1、程序设计整体框架
MPEG-1音频压缩编码:
输入声音信号经过一个多相滤波器组,变换到多个子带。同时经过“心理声学模型”计算以频率为自变量的噪声掩蔽阈值。量化和编码部分用信掩比 SMR 决定分配给子带信号的量化位数,使量化噪声<掩蔽域值。最后通过数据帧包装将量化的子带样本和其它数据按照规定的帧格式组装成比特数据流。
(1)多相滤波器组,用来分割子带
划分子带的方法有两种:线性划分和非线性划分。
- 线性划分可能一个子带覆盖好几个临界频带
- 以Layer 1为例,先分成32个相等的子带。
(2)量化和编码
①比例因子的取值和编码
对各个子带每12个样点进行一次比例因子计算。先定出12个样点中绝对值的最大值。查比例因子表中比这个最大值大的最小值作为比例因子。用6比特表示。
- 第2层的一帧对应36个子带样值,是第1层的三倍,原则上要传三个比例因子。为了降低比例因子的传输码率,采用了利用人耳时域掩蔽特性的编码策略。
- 每帧中每个子带的三个比例因子被一起考虑,划分成特定的几种模式。根据这些模式,1个、2个或3个比例因子和比例因子选择信息(每子带2比特)一起被传送。如果一个比例因子和下一个只有很小的差别,就只传送大的一个,这种情况对于稳态信号经常出现。
- 使用这一算法后,和第1层相比,第2层传输的比例因子平均减少了2个,即传输码率由22.5Kb/s降低到了7.5Kb/s 。
②比特分配及编码
- 在调整到固定的码率之前
■ 先确定可用于样值编码的有效比特数。
■ 这个数值取决于比例因子、比例因子选择信息、比特分配信息以及辅助数据所需比特数。 - 比特分配的过程
■ 对每个子带计算掩蔽,噪声比MNR=信噪比SNR-信掩比SMR,即MNR=SNR-SMR。
■ 使整个一帧和每个子带的总噪声掩蔽比最小。这是一个循环过程,每一次循环使获益最大的子带的量化级别增加一级,当然所用比特数不能超过一帧所能提供的最大数目。
■ 第1层一帧用4比特给每个子带的比特分配信息编码;而第2层只在低频段用4比特,高频段则用2比特。
③子带样值的量化和编码
- 输入以12个样本为一组,每组样本经过时间 频率变换之后进行一次比特分配并记录一个比例因子 (scale factor)
- 比特分配信息告诉解码器每个样本由几位表示,比例因子用6比特表示,解码器使用这个6比特的比例因子乘逆量化器的每个输出样本值,以恢复被量化的子带值。比例因子的作用是充分利用量化器的量化范围,通过比特分配和比例因子相配合,可以表示动态范围超过120dB的样本。
- 第2层中,量化级别的数目随子带的不同而不同,但量化等级仍然覆盖了3~65535的范围,同时子带不被分配给比特的概率增加了,没有分配给比特的子带就不被量化。低频段的量化等级有15级,中频段7级,高频段只有3级。
(3)数据帧包装
码流结构的设计
- 有利于使用低复杂性和少延时的解码器,编码后的声音信号包含多个短且恒定间隔的切入点。
- 编码数据允许在编码码流中插入整数倍的切入点,以利记录、播放和编辑短的声音序列,并能精确指定编辑点。
- 为了能简单地在解码器中实现上述功能,切入点的帧必须包括码流解码所需的完整信息
- 这些特性在数字声音广播领域也很重要,因为由于经济原因须要有低复杂性的解码器,而且码流中要有许多切入点,以易于对传输误码造成的连续样值错误进行块隐藏。
- 层 I 每帧含384个样本数据。每帧由32个子带分别输出的12个样本值组成
■ 以48kHz采样,一帧长为32×12×20.83µs=8ms。 - 层 II 每帧包含1152个样本。低、中、高频段对比特分配不同,分别用4、3、2比特。比特流中增加了一个比特因子选择信息域,解码器根据这个域的信息可知道是否需要以及如何共享比例因子。
- 帧头(Header):每帧开始的头32个比特,包含有同步和状态比特流信息,在所有层都相同 同步码字为12bit全1码(1111,1111,1111 )。
- 误码检测 CRC :使用一种 16bit 奇偶校验字,可供在比特流中作检测误码用。在所有层都相同
- 声音数据:由比特分配表、比例因子选择信息、比例因子和子带样值组成,其中子带样值是声音数据的最大部分。每层声音数据不同。
- 辅助数据:用作辅助数据比特流。
2、代码调试
针对实验要求,在代码中进行了部分添改。
(1)文件头定义宏
#define FRAME_TRACE 1
(2)print_config()
函数
添加代码,打印输入文件、输出文件、输出音频的采样率和目标码率。
#if FRAME_TRACE
FILE* need;
need = fopen("need.txt", "a");
fprintf(need, "========== 基本信息 ==========\n");
fprintf(need, "输入文件:%s\n", inPath);
fprintf(need, "输出文件:%s\n", outPath);
fprintf(need, "采样频率:%.1f kHz\n", s_freq[header->version][header->sampling_frequency]);
fprintf(need, "输出文件码率:%d kbps\n", bitrate[header->version][header->bitrate_index]);
fclose(need);
#endif // FRAME_TRACE
(3)main()
函数
添加代码,输出某个数据帧以下内容:
- 该帧所分配的比特数
- 该帧的比例因子
- 该帧的比特分配结果
#if FRAME_TRACE
FILE* need;
need = fopen("need.txt", "a");
if (frameNum == 2) {
fprintf(need, "声道数:%d\n", nch);
fprintf(need, "目前观测第 %d 帧\n", frameNum);
fprintf(need, "本帧比特预算:%d bits\n", adb);
fprintf(need, "\n");
/* 比例因子 */
fprintf(need, "========== 比例因子 ==========\n");
for (ch = 0; ch < nch; ch++) // 每个声道单独输出
{
fprintf(need, "------ 声道%2d ------\n", ch + 1);
for (sb = 0; sb < frame.sblimit; sb++) // 每个子带
{
fprintf(need, "子带[%2d]:\t", sb + 1);
for (int gr = 0; gr < 3; gr++) {
fprintf(need, "%2d\t", scalar[ch][gr][sb]);
}
fprintf(need, "\n");
}
}
fprintf(need, "\n");
/* 比特分配表 */
fprintf(need, "========== 比特分配表 ==========\n"); //输出比特分配结果
for (ch = 0; ch < nch; ch++) {
fprintf(need, "------ 声道%2d ------\n", ch + 1); //按声道分配
for (sb = 0; sb < frame.sblimit; sb++) {
fprintf(need, "子带[%2d]:\t%2d\n", sb + 1, bit_alloc[ch][sb]);
}
fprintf(need, "\n");
}
}
fclose(need);
#endif // FRAME_TRACE
四、实验结果
1、乐音zytest.mp3
调试属性:
生成文件:
其中need.txt
为了便于区分,生成后改名为zytestneed.txt
zytestneed.txt
文件:
========== 基本信息 ==========
输入文件:zytest.mp3
输出文件:zytest.mp2
采样频率:44.1 kHz
输出文件码率:192 kbps
声道数:2
目前观测第 2 帧
本帧比特预算:5016 bits
========== 比例因子 ==========
------ 声道 1 ------
子带[ 1]: 11 11 9
子带[ 2]: 10 10 9
子带[ 3]: 8 9 8
子带[ 4]: 11 9 9
子带[ 5]: 9 9 8
子带[ 6]: 8 7 9
子带[ 7]: 8 9 9
子带[ 8]: 9 11 11
子带[ 9]: 9 11 8
子带[10]: 9 9 9
子带[11]: 9 11 9
子带[12]: 10 10 11
子带[13]: 9 8 9
子带[14]: 9 9 9
子带[15]: 10 10 9
子带[16]: 11 9 14
子带[17]: 8 10 10
子带[18]: 10 11 10
子带[19]: 7 11 10
子带[20]: 9 10 11
子带[21]: 9 10 8
子带[22]: 8 10 10
子带[23]: 9 8 8
子带[24]: 10 11 11
子带[25]: 9 11 9
子带[26]: 9 8 9
子带[27]: 9 10 9
子带[28]: 10 10 9
子带[29]: 8 12 11
子带[30]: 11 10 9
------ 声道 2 ------
子带[ 1]: 10 10 9
子带[ 2]: 10 8 10
子带[ 3]: 10 12 9
子带[ 4]: 10 10 9
子带[ 5]: 10 8 10
子带[ 6]: 9 8 8
子带[ 7]: 9 11 10
子带[ 8]: 10 9 9
子带[ 9]: 10 11 10
子带[10]: 11 11 10
子带[11]: 9 9 9
子带[12]: 13 11 10
子带[13]: 12 10 10
子带[14]: 9 9 8
子带[15]: 10 9 9
子带[16]: 10 7 9
子带[17]: 12 8 11
子带[18]: 10 7 8
子带[19]: 10 11 11
子带[20]: 9 9 11
子带[21]: 10 10 10
子带[22]: 12 9 9
子带[23]: 11 11 9
子带[24]: 11 10 9
子带[25]: 8 11 10
子带[26]: 8 11 10
子带[27]: 9 11 10
子带[28]: 8 9 12
子带[29]: 11 9 10
子带[30]: 9 11 10
========== 比特分配表 ==========
------ 声道 1 ------
子带[ 1]: 3
子带[ 2]: 3
子带[ 3]: 3
子带[ 4]: 5
子带[ 5]: 3
子带[ 6]: 4
子带[ 7]: 3
子带[ 8]: 4
子带[ 9]: 3
子带[10]: 4
子带[11]: 2
子带[12]: 3
子带[13]: 3
子带[14]: 4
子带[15]: 3
子带[16]: 1
子带[17]: 2
子带[18]: 3
子带[19]: 2
子带[20]: 1
子带[21]: 1
子带[22]: 1
子带[23]: 2
子带[24]: 1
子带[25]: 1
子带[26]: 1
子带[27]: 0
子带[28]: 0
子带[29]: 1
子带[30]: 0
------ 声道 2 ------
子带[ 1]: 4
子带[ 2]: 3
子带[ 3]: 3
子带[ 4]: 5
子带[ 5]: 4
子带[ 6]: 5
子带[ 7]: 3
子带[ 8]: 3
子带[ 9]: 4
子带[10]: 3
子带[11]: 2
子带[12]: 4
子带[13]: 3
子带[14]: 4
子带[15]: 3
子带[16]: 1
子带[17]: 2
子带[18]: 3
子带[19]: 2
子带[20]: 1
子带[21]: 1
子带[22]: 1
子带[23]: 2
子带[24]: 1
子带[25]: 1
子带[26]: 1
子带[27]: 0
子带[28]: 0
子带[29]: 1
子带[30]: 0
2、噪音zynoise.wav
调试属性:
生成文件:
其中need.txt
为了便于区分,生成后改名为zynoiseneed.txt
zynoiseneed.txt
文件:
========== 基本信息 ==========
输入文件:zynoise.wav
输出文件:zynoise.mp2
采样频率:44.1 kHz
输出文件码率:192 kbps
声道数:2
目前观测第 2 帧
本帧比特预算:5016 bits
========== 比例因子 ==========
------ 声道 1 ------
子带[ 1]: 30 29 27
子带[ 2]: 33 37 30
子带[ 3]: 34 35 31
子带[ 4]: 33 36 32
子带[ 5]: 33 36 31
子带[ 6]: 33 37 35
子带[ 7]: 33 39 36
子带[ 8]: 34 34 36
子带[ 9]: 35 36 35
子带[10]: 34 36 35
子带[11]: 35 34 34
子带[12]: 37 35 34
子带[13]: 35 36 35
子带[14]: 38 36 38
子带[15]: 40 39 40
子带[16]: 43 42 40
子带[17]: 42 41 40
子带[18]: 44 42 44
子带[19]: 43 43 43
子带[20]: 41 42 43
子带[21]: 44 42 42
子带[22]: 42 43 43
子带[23]: 43 43 43
子带[24]: 44 44 42
子带[25]: 44 44 42
子带[26]: 44 42 46
子带[27]: 42 43 43
子带[28]: 44 43 43
子带[29]: 45 43 45
子带[30]: 44 43 42
------ 声道 2 ------
子带[ 1]: 30 30 28
子带[ 2]: 34 35 31
子带[ 3]: 34 37 34
子带[ 4]: 34 36 31
子带[ 5]: 34 35 34
子带[ 6]: 33 34 34
子带[ 7]: 34 38 35
子带[ 8]: 34 34 34
子带[ 9]: 36 37 35
子带[10]: 34 36 35
子带[11]: 36 36 37
子带[12]: 37 35 34
子带[13]: 35 35 35
子带[14]: 37 38 38
子带[15]: 38 40 39
子带[16]: 39 41 41
子带[17]: 40 43 41
子带[18]: 43 42 43
子带[19]: 43 42 42
子带[20]: 46 43 43
子带[21]: 45 43 46
子带[22]: 44 41 43
子带[23]: 42 44 42
子带[24]: 44 42 42
子带[25]: 43 43 42
子带[26]: 44 43 43
子带[27]: 42 44 43
子带[28]: 44 40 41
子带[29]: 42 42 43
子带[30]: 44 42 41
========== 比特分配表 ==========
------ 声道 1 ------
子带[ 1]: 7
子带[ 2]: 4
子带[ 3]: 3
子带[ 4]: 4
子带[ 5]: 4
子带[ 6]: 5
子带[ 7]: 3
子带[ 8]: 3
子带[ 9]: 4
子带[10]: 3
子带[11]: 4
子带[12]: 4
子带[13]: 3
子带[14]: 3
子带[15]: 2
子带[16]: 2
子带[17]: 1
子带[18]: 0
子带[19]: 1
子带[20]: 0
子带[21]: 0
子带[22]: 0
子带[23]: 0
子带[24]: 0
子带[25]: 0
子带[26]: 0
子带[27]: 0
子带[28]: 0
子带[29]: 0
子带[30]: 0
------ 声道 2 ------
子带[ 1]: 6
子带[ 2]: 4
子带[ 3]: 3
子带[ 4]: 5
子带[ 5]: 4
子带[ 6]: 4
子带[ 7]: 4
子带[ 8]: 4
子带[ 9]: 4
子带[10]: 3
子带[11]: 3
子带[12]: 3
子带[13]: 3
子带[14]: 3
子带[15]: 3
子带[16]: 1
子带[17]: 2
子带[18]: 1
子带[19]: 0
子带[20]: 0
子带[21]: 0
子带[22]: 0
子带[23]: 0
子带[24]: 0
子带[25]: 0
子带[26]: 0
子带[27]: 0
子带[28]: 0
子带[29]: 0
子带[30]: 0
3、混合音zymatch.mp3
调试属性:
生成文件:
其中need.txt
为了便于区分,生成后改名为zymatchneed.txt
zymatchneed.txt
文件:
========== 基本信息 ==========
输入文件:zymatch.mp3
输出文件:zymatch.mp2
采样频率:44.1 kHz
输出文件码率:192 kbps
声道数:2
目前观测第 2 帧
本帧比特预算:5016 bits
========== 比例因子 ==========
------ 声道 1 ------
子带[ 1]: 4 4 4
子带[ 2]: 13 11 15
子带[ 3]: 12 11 15
子带[ 4]: 11 11 14
子带[ 5]: 12 9 12
子带[ 6]: 13 14 11
子带[ 7]: 14 12 14
子带[ 8]: 13 12 14
子带[ 9]: 13 11 12
子带[10]: 10 11 12
子带[11]: 14 13 13
子带[12]: 14 13 12
子带[13]: 9 11 15
子带[14]: 14 11 14
子带[15]: 14 11 13
子带[16]: 14 14 14
子带[17]: 10 15 14
子带[18]: 13 12 14
子带[19]: 14 12 13
子带[20]: 12 12 13
子带[21]: 13 13 15
子带[22]: 11 11 13
子带[23]: 11 14 13
子带[24]: 13 12 14
子带[25]: 14 11 15
子带[26]: 10 11 12
子带[27]: 12 11 13
子带[28]: 12 11 15
子带[29]: 14 12 14
子带[30]: 13 9 14
------ 声道 2 ------
子带[ 1]: 63 6 4
子带[ 2]: 63 10 13
子带[ 3]: 63 14 14
子带[ 4]: 63 15 12
子带[ 5]: 63 18 13
子带[ 6]: 63 16 14
子带[ 7]: 63 13 14
子带[ 8]: 63 12 12
子带[ 9]: 63 11 12
子带[10]: 63 9 13
子带[11]: 63 11 11
子带[12]: 63 18 11
子带[13]: 63 13 13
子带[14]: 63 18 16
子带[15]: 63 14 13
子带[16]: 63 14 14
子带[17]: 63 21 14
子带[18]: 63 17 15
子带[19]: 63 16 18
子带[20]: 63 19 15
子带[21]: 63 18 13
子带[22]: 63 16 14
子带[23]: 63 12 13
子带[24]: 63 16 14
子带[25]: 63 15 14
子带[26]: 63 13 13
子带[27]: 63 14 14
子带[28]: 63 15 12
子带[29]: 63 16 13
子带[30]: 63 14 16
========== 比特分配表 ==========
------ 声道 1 ------
子带[ 1]: 5
子带[ 2]: 4
子带[ 3]: 4
子带[ 4]: 4
子带[ 5]: 5
子带[ 6]: 4
子带[ 7]: 4
子带[ 8]: 5
子带[ 9]: 4
子带[10]: 4
子带[11]: 4
子带[12]: 5
子带[13]: 4
子带[14]: 5
子带[15]: 2
子带[16]: 2
子带[17]: 4
子带[18]: 3
子带[19]: 3
子带[20]: 2
子带[21]: 1
子带[22]: 2
子带[23]: 3
子带[24]: 2
子带[25]: 1
子带[26]: 1
子带[27]: 2
子带[28]: 1
子带[29]: 2
子带[30]: 0
------ 声道 2 ------
子带[ 1]: 5
子带[ 2]: 5
子带[ 3]: 4
子带[ 4]: 6
子带[ 5]: 5
子带[ 6]: 4
子带[ 7]: 4
子带[ 8]: 5
子带[ 9]: 4
子带[10]: 4
子带[11]: 4
子带[12]: 5
子带[13]: 4
子带[14]: 5
子带[15]: 2
子带[16]: 2
子带[17]: 4
子带[18]: 3
子带[19]: 3
子带[20]: 2
子带[21]: 1
子带[22]: 2
子带[23]: 3
子带[24]: 2
子带[25]: 1
子带[26]: 1
子带[27]: 2
子带[28]: 1
子带[29]: 2
子带[30]: 0