【数据压缩11】Lab6 MPEG音频编码实验
一、实验目的
- 理解程序设计的整体框架
- 理解感知音频编码的设计思想
- 两条线
- 时-频分析的矛盾!
- 理解心理声学模型的实现过程
- 临界频带的概念
- 掩蔽值计算的思路
- 理解码率分配的实现思路
二、实验原理
1. 感知音频编码设计思想
MPEG-1 Audio LayerII编码器原理如下
- 输入PCM码流,采样率为32,44.1或48kHz;
- 输出为32kbps到384kbps。
(1)基本思想
基于人的生理和心理感知特性(心理声学模型),分析信号,对不被感知的部分不编码。声音压缩算法可以确立这种特性的模型来取消更多的冗余数据。
(2)上下两条线
- 上支线:子带分解考虑时域。子带分析滤波器组使信号具有高的时间分辨率,确保在短暂冲击信号情况下,编码的声音信号具有足够高的质量。
- 下支线:FFT和心理声学模型考虑频域。信号通过FFT运算具有高的频率分辨率,因为掩蔽阈值是从功率谱密度推出来的。
- 上支线为下支线的心理声学模型提供比例因子;下支线为上支线的量化器提供比特分配。
(3)时频矛盾
由于傅里叶变换的缺陷,变换结果只表示了信号中存在哪些频率,并不能表示某个频率出现的时间,如果时域上不同时刻出现尖峰,在频域上看两者是一样的。
(4)模块组成
- 多相滤波器组(Polyphase Filter Bank)
将 PCM样本变换到32个子带的频域信号。
如果输入的采样频率为48kHz,那么子带的频率宽度为48/(2*32)=0.75Hz
- 等带宽的滤波器组与人类听觉系统的临界频带不对应 ;
- 滤波器组与其逆过程不是无失真的;
- 子带间频率有混叠 ,混叠:一个单频正弦信号输入可能在两个子带中产生非零信号。
- 心理声学模型(Psychoacoustic Model)
计算信号中不可听觉感知的部分 (计算噪声遮蔽效应)
- 比特分配器(Bit Allocator)
根据心理声学模型的计算结果,为每个子带信号分配比特数。
- 装帧(Frame Creation)
2. 心理声学模型实现过程
(1)将样本变换到频域
32个等分的子带信号并不能精确地反映人耳的听觉特性。 引入FFT补偿频率分辨率不足的问题。
(2)确定声压级别
(3)考虑安静时阈值
即绝对阈值。在标准中有根据输入PCM信号的采样率编制的“频率、临界频带率和绝对阈值”表。此表为多位科学家经多次心理声学实验所得。
(4)音频信号分解
将音频信号分解成“乐音(tones)” 和“非乐音/噪声”部分:因为两种信号的掩蔽能力不同。
临界频带(Critical Band):
-
指当某个纯音被以它为中心频率、且具有一定带宽的连续噪声所掩蔽时,如果该纯音刚好被听到时的功率等于这一频带内的噪声功率,这个带宽为临界频带宽度。通常认为从20Hz到16kHz有25个临界频带,单位为
bark
。1 Bark = 一个临界频带的宽度
-
研究窄带噪声对纯音掩蔽量的规律时被发现的
- 使噪声的中心频率等于信号频率,只改变噪声的带宽同时保持噪声的功率谱密度不变,测试纯音听阈随掩蔽噪声带宽变化的特性。
- 纯音的听阈随掩蔽噪声带宽的增大而增大,在带宽增大到某一特定值之后听阈保持恒定不变。
(5)音调和非音调掩蔽成分的消除
利用标准中给出的绝对阈值消除被掩蔽成分;考虑在每个临界频带内,小于0.5Bark的距离中只保留最高功率的成分。
(6)单个掩蔽阈值的计算
音调成分和非音调成分单个掩蔽阈值根据标准中给出的算法求得。
隐蔽值计算:
Lutfi 对多个掩蔽音同时存在时的综合掩蔽效果进行了研究:每个掩蔽音的掩蔽效果先独立变换然后再线性相加。
(7)全局掩蔽阈值的计算
(8)每个子带的掩蔽阈值
选择出本子带中最小的阈值作为子带阈值
(9)计算每个子带信号掩蔽比(signal-to-mask ratio, SMR)
SMR = 信号能量 / 掩蔽阈值(SMR传递给编码单元)
3. 码率分配
算法:使整帧和每个子带的总噪声—掩蔽比MNR最小
(1)在调整到固定的码率之前
先确定可用于样值编码的有效比特数。这个数值取决于比例因子、比例因子选择信息、比特分配信息以及辅助数据所需比特数。
(2)比特分配的过程
- 对每个子带计算
噪声-掩蔽比MNR
,是信掩比SMR之差
与信噪比SNR
,其中SNR 由MPEG-I标准给定 (为量化水平的函数) ,噪声掩蔽比NMR
表示波形误差与感知测量之间的误差;
N M R = S M R – S N R ( d B ) NMR = SMR– SNR (dB) NMR=SMR–SNR(dB)
- 对最高NMR的子带分配比特,使获益最大的子带的量化级别增加一级,每提升1比特,信噪比就可以提升6dB;
- 重新计算分配了更多比特子带的
NMR
。
三、实验内容
- 输出音频的采样率和目标码率
- 选择三个不同特性的音频文件
- 噪声(持续噪声、突发噪声)
- 音乐
- 混合
- 某个数据帧,输出
- 该帧所分配的比特数
- 该帧的比例因子
- 该帧的比特分配结果
四、实验结果
1. 输出某一数据帧的各种信息
采样率: 44.1 kHz
目标码率:bitrate = 192.000 kbps
(1)m2eanc.h
头文件中打开FRAME_TRACE
# define FRAME_TRACE 1
m2eanc.c
的print_config函数中加入FRAME_TRACE
输出音频的采样率和目标码率
#if FRAME_TRACE
FILE* output;
output = fopen("output.txt", "a");
fprintf(output, "========== 基本信息 ==========\n");
fprintf(output, "输入文件:%s\n", inPath);
fprintf(output, "输出文件:%s\n", outPath);
fprintf(output, "采样频率:%.1f kHz\n", s_freq[header->version][header->sampling_frequency]);
fprintf(output, "目标码率:%d kbps\n", bitrate[header->version][header->bitrate_index]);//输出文件码率
fclose(output);
#endif // FRAME_TRACE
(2)m2eanc.c
的main函数中加入FRAME_TRACE
输出某个数据帧所分配的比特数、比例因子和比特分配结果
#if FRAME_TRACE
FILE* output;
output = fopen("output.txt", "a");
if (frameNum == 2) {
fprintf(output, "声道数:%d\n", nch);
fprintf(output, "目前观测第 %d 帧\n", frameNum);
fprintf(output, "本帧比特预算:%d bits\n", adb);
fprintf(output, "\n");
fprintf(output, "========== 比例因子 ==========\n");
for (ch = 0; ch < nch; ch++) // 每个声道单独输出
{
fprintf(output, "------ 声道%2d ------\n", ch + 1);
for (sb = 0; sb < frame.sblimit; sb++) // 每个子带
{
fprintf(output, "子带[%2d]:\t", sb + 1);
for (int gr = 0; gr < 3; gr++) {
fprintf(output, "%2d\t", scalar[ch][gr][sb]);
}
fprintf(output, "\n");
}
}
fprintf(output, "\n");
fprintf(output, "========== 比特分配表 ==========\n"); //输出比特分配结果
for (ch = 0; ch < nch; ch++) {
fprintf(output, "------ 声道%2d ------\n", ch + 1); //按声道分配
for (sb = 0; sb < frame.sblimit; sb++) {
fprintf(output, "子带[%2d]:\t%2d\n", sb + 1, bit_alloc[ch][sb]);
}
fprintf(output, "\n");
}
}
fclose(output);
#endif // FRAME_TRACE
2. 三个不同特性的音频文件
(1)音乐
test.wav
========== 基本信息 ==========
输入文件:test.wav
输出文件:test.mp2
采样频率:44.1 kHz
目标码率:192 kbps
声道数:1
目前观测第 2 帧
本帧比特预算:5016 bits
========== 比例因子 ==========
------ 声道 1 ------
子带[ 1]: 9 11 10
子带[ 2]: 14 14 14
子带[ 3]: 19 17 19
子带[ 4]: 25 23 24
子带[ 5]: 29 28 28
子带[ 6]: 23 25 23
子带[ 7]: 22 22 24
子带[ 8]: 22 22 21
子带[ 9]: 29 27 27
子带[10]: 30 30 32
子带[11]: 30 29 28
子带[12]: 27 28 28
子带[13]: 25 24 28
子带[14]: 26 27 23
子带[15]: 23 23 21
子带[16]: 26 23 25
子带[17]: 28 31 30
子带[18]: 30 33 31
子带[19]: 30 30 29
子带[20]: 28 29 29
子带[21]: 29 31 28
子带[22]: 32 30 30
子带[23]: 40 39 41
子带[24]: 54 50 50
子带[25]: 53 52 55
子带[26]: 55 53 54
子带[27]: 51 53 55
子带[28]: 53 53 52
子带[29]: 53 53 54
子带[30]: 53 52 52
========== 比特分配表 ==========
------ 声道 1 ------
子带[ 1]: 8
子带[ 2]: 8
子带[ 3]: 6
子带[ 4]: 8
子带[ 5]: 7
子带[ 6]: 8
子带[ 7]: 8
子带[ 8]: 6
子带[ 9]: 5
子带[10]: 6
子带[11]: 6
子带[12]: 7
子带[13]: 6
子带[14]: 6
子带[15]: 6
子带[16]: 5
子带[17]: 5
子带[18]: 5
子带[19]: 4
子带[20]: 6
子带[21]: 3
子带[22]: 3
子带[23]: 0
子带[24]: 0
子带[25]: 0
子带[26]: 0
子带[27]: 0
子带[28]: 0
子带[29]: 0
子带[30]: 0
(2)噪声
生成高斯白噪声noise_yyx.wav
Number = 100000;
noise_guass= 1*randn(1,Number);
audiowrite('noise_yyx.wav',noise_guass,44100);
========== 基本信息 ==========
输入文件:noise_yyx.wav
输出文件:noise_yyx.mp2
采样频率:44.1 kHz
目标码率:192 kbps
声道数:1
目前观测第 2 帧
本帧比特预算:5016 bits
========== 比例因子 ==========
------ 声道 1 ------
子带[ 1]: 8 8 9
子带[ 2]: 10 9 9
子带[ 3]: 9 7 10
子带[ 4]: 9 7 8
子带[ 5]: 10 10 8
子带[ 6]: 8 9 8
子带[ 7]: 9 8 10
子带[ 8]: 10 8 9
子带[ 9]: 9 9 10
子带[10]: 8 10 8
子带[11]: 7 7 8
子带[12]: 8 10 7
子带[13]: 8 10 8
子带[14]: 9 9 10
子带[15]: 9 7 9
子带[16]: 8 9 10
子带[17]: 11 9 10
子带[18]: 8 8 8
子带[19]: 8 9 9
子带[20]: 9 9 9
子带[21]: 9 10 8
子带[22]: 10 7 7
子带[23]: 9 9 9
子带[24]: 9 9 9
子带[25]: 9 9 11
子带[26]: 8 8 10
子带[27]: 9 10 9
子带[28]: 8 9 8
子带[29]: 10 10 8
子带[30]: 10 9 7
========== 比特分配表 ==========
------ 声道 1 ------
子带[ 1]: 5
子带[ 2]: 5
子带[ 3]: 4
子带[ 4]: 5
子带[ 5]: 5
子带[ 6]: 5
子带[ 7]: 5
子带[ 8]: 4
子带[ 9]: 4
子带[10]: 5
子带[11]: 4
子带[12]: 5
子带[13]: 3
子带[14]: 3
子带[15]: 4
子带[16]: 3
子带[17]: 4
子带[18]: 4
子带[19]: 3
子带[20]: 3
子带[21]: 2
子带[22]: 3
子带[23]: 3
子带[24]: 3
子带[25]: 2
子带[26]: 1
子带[27]: 2
子带[28]: 3
子带[29]: 2
子带[30]: 2
(3)音乐+噪声
[y,~]=audioread('test.wav');
[noise_guass,fs]=audioread('test.wav');
mix=y + noise_guass;
audiowrite('mix_yyx.wav',mix,fs);
========== 基本信息 ==========
输入文件:mix_yyx.wav
输出文件:mix_yyx.mp2
采样频率:44.1 kHz
目标码率:192 kbps
声道数:1
目前观测第 2 帧
本帧比特预算:5016 bits
========== 比例因子 ==========
------ 声道 1 ------
子带[ 1]: 6 8 7
子带[ 2]: 11 11 11
子带[ 3]: 16 14 16
子带[ 4]: 22 20 21
子带[ 5]: 26 25 25
子带[ 6]: 20 22 20
子带[ 7]: 19 19 21
子带[ 8]: 19 19 18
子带[ 9]: 26 24 24
子带[10]: 27 27 29
子带[11]: 27 26 25
子带[12]: 24 25 25
子带[13]: 22 21 25
子带[14]: 23 24 20
子带[15]: 20 20 18
子带[16]: 23 20 22
子带[17]: 25 28 27
子带[18]: 27 30 28
子带[19]: 27 27 26
子带[20]: 25 26 26
子带[21]: 26 28 25
子带[22]: 29 27 27
子带[23]: 37 36 38
子带[24]: 51 47 47
子带[25]: 50 49 52
子带[26]: 52 50 51
子带[27]: 48 50 52
子带[28]: 50 50 49
子带[29]: 50 50 51
子带[30]: 50 49 49
========== 比特分配表 ==========
------ 声道 1 ------
子带[ 1]: 8
子带[ 2]: 7
子带[ 3]: 6
子带[ 4]: 7
子带[ 5]: 6
子带[ 6]: 7
子带[ 7]: 7
子带[ 8]: 6
子带[ 9]: 4
子带[10]: 5
子带[11]: 5
子带[12]: 7
子带[13]: 7
子带[14]: 6
子带[15]: 6
子带[16]: 4
子带[17]: 4
子带[18]: 4
子带[19]: 3
子带[20]: 5
子带[21]: 3
子带[22]: 3
子带[23]: 0
子带[24]: 0
子带[25]: 0
子带[26]: 0
子带[27]: 0
子带[28]: 0
子带[29]: 0
子带[30]: 0
4. 结论
- 噪声:各子带都分配到的比特数相对比较均匀,这说明了这种信源分布比较均匀,对于每种频率上的音频都超过掩蔽曲线不少;
- 音乐:比特数集中在前面几个子带,后面的子带分配到的比特数较少,这主要是因为人发声范围有限,高频分量较少。基于感知编码,声音信号在时域上非均匀分布,小幅度的样值比大幅度的样值出现的概率高;在频域上功率谱密度非均匀,低频能量高、高频能量低。
DEBUG