一.音频压缩得以实现的理论前提
1.声音信号中本身存在冗余,信号幅度分布是非均匀的,小幅度的样值比大幅度样值出现的概率高,样本之间存在相关性,这就导致了时间上的冗余,编码压缩可以实现减少冗余。
2.根据人耳的听觉特性,那些人耳所感知不到的音频可以直接舍去不需要再进行编码。
二.人类听觉系统感知的特点
1.人耳听觉系统
人类听觉系统大致等效于一个在0Hz~20KHz频率范围内由25个重叠的带通滤波器组成的滤波器组。人耳不能区分同一频带内同时发生的不同声音,500Hz以下每个临界频带的带宽大约是100Hz,从500Hz起, 临界频带带宽线性增加。
2.频域掩蔽效应
听觉系统中存在一个听觉阈值电平,低于这个电平的声音信号就听不到。听觉阈值的大小随声音频率的改变而改变。一个人是否听到声音取决于声音的频率,以及声音的幅度是否高于这种频率下的听觉阈值。听觉掩蔽特性:即听觉阈值电平是自适应的,会随听到的不同频率声音而发生变化。一个高强度纯音会使得该频率附近的最小可听阈曲线提升,掩蔽掉在其下的(原本能听到的)声音。这个纯音称为掩蔽声,掩蔽声存在时,音调音刚刚能被听到时的阈值称为掩蔽阈值。在编码时,可以去除掩蔽阈值以下的信号分量,并忽略可能会被掩蔽的量化噪声。
3.临界频带
临界频带是指当某个纯音被以它为中心频率、且具有一定带宽的连续噪声所掩蔽时,如果该纯音刚好被听到时的功率等于这一频带内的噪声功率,这个带宽为临界频带宽度。
掩蔽效应在一定频率范围内不随带宽增大而改变,直至超过某个频率值。通常认为从20Hz到16kHz有25个临界频带,单位为bark,1 Bark = 一个临界频带的宽度。
三.MPEG-1音频编码原理
1.感知音频编码器的框图:
从图中可以看出,该编码器有两条线:一条是上方PCM码流经过滤波器组形成块,再通过线性量化器形成颗粒成为帧比特流,另一条是下方所展示的流程。
2.各部分的说明:
(1)多相滤波器组
将PCM样本变换到32个子带的频域信号,使得信号具有较高的时间分辨率,确保在短暂冲击信号的情况下,编码的声音信号具有足够高的质量。
(2)心理声学模型
用来计算信号中不可感知的部分,通过子带分析滤波器组使信号具有高的时间分辨率,确保在短暂冲击信号情况下,编码
的声音信号具有足够高的质量,又可以使信号通过FFT运算具有高的频率分辨率,因为掩蔽阈值是从功率谱密度推出来的。
在低频子带中,为了保护音调和共振峰的结构,就要求用较小的量化阶、较多的量化级数,即分配较多的位数来表示样本值。而话音中的摩擦音和类似噪声的声音,通常出现在高频子带中,对它分配较少的位数。
因为乐音和噪音信号的掩蔽能力不同,因此在临界频带的谱值中,将两种信号分离:可将局部峰值视为乐音,然后将本临界频带内的剩余频谱组成一个代表噪声频率(无调成份)。
全局掩蔽阈值的计算:
(3)线性量化器
比特分配:
对每个子带计算:掩噪比MNR (dB) = 信噪比SNR - 信掩比SMR,然后找出其中具有最低MNR的子带,并给该子带多分配一些比特,然后重新计算MNR,继续分配,重复该步骤,直至没有比特可以分配。这样可以使得在满足比特率和掩蔽要求的前提下,使MNR最小。
计算比例因子:
对各个子带每36个样点(Layer I为12个样点)进行一次比例因子的计算,先确定12个连续样值中的最大值,查Layer II、Layer I比例因子表中比这它大的最小值作为量化比例因子,每12个样值计算出一个比例因子,Layer II中将每个子带分为3组,每组各有12个取样值,因此36个样值具有3个比例因子,比例因子可以使得比较准确地计算出子带的声压级,一般比例因子从低频子带到高频子带连续下降。
颗粒形成:
对量化级别在3、5、9级时,采用“颗粒” 优化:
四.MPEG-1编码器调试
1.命令行设置
输入文件名 输出文件名
本实验所用的测试文件均为.wav文件,采样率为44100hz,立体声。
2.参考了老师推荐的博客,在print_config()
函数中增加一些输入、输出的主要参数:
fprintf (stderr, "--------------------------------------------\n");
fprintf(stderr, "========== 基本信息 ==========\n");
fprintf(stderr, "输入文件:%s\n", inPath);
fprintf(stderr, "输出文件:%s\n", outPath);
fprintf(stderr, "采样频率:%.1f kHz\n", s_freq[header->version][header->sampling_frequency]);
fprintf(stderr, "输出文件码率:%d kbps\n", bitrate[header->version][header->bitrate_index]);
输出比例因子和比特分配表,在main()
中添加:
if (frameNum == 2) {
fprintf(stderr, "声道数:%d\n", nch);
fprintf(stderr, "目前观测第 %d 帧\n", frameNum);
fprintf(stderr, "本帧比特预算:%d bits\n", adb);
fprintf(stderr, "\n");
fprintf(stderr, "---------比例因子---------\n");
for (ch = 0; ch < nch; ch++) // 每个声道单独输出
{
fprintf(stderr, "--- 声道%2d ----\n", ch + 1);
for (sb = 0; sb < frame.sblimit; sb++) // 每个子带
{
fprintf(stderr, "子带[%2d]:\t", sb + 1);
for (int gr = 0; gr < 3; gr++) {
fprintf(stderr, "%2d\t", scalar[ch][gr][sb]);
}
fprintf(stderr, "\n");
}
}
fprintf(tracefile, "\n");
/* 比特分配表 */
fprintf(stderr, "========== 比特分配表 ==========\n"); //输出比特分配结果
for (ch = 0; ch < nch; ch++) {
fprintf(stderr, "------ 声道%2d ------\n", ch + 1); //按声道分配
for (sb = 0; sb < frame.sblimit; sb++) {
fprintf(stderr, "子带[%2d]:\t%2d\n", sb + 1, bit_alloc[ch][sb]);
}
fprintf(stderr, "\n");
}
}
五.测试结果
1.乐音music2.wav:
========== 基本信息 ==========
输入文件:music2.wav
输出文件:music2.mp2
采样频率:44.1 kHz
输出文件码率:192 kbps
encode_init: using tablenum 1 with sblimit 30
声道数:2
目前观测第 2 帧
本帧比特预算:5016 bits
========== 比特分配表 ==========
------ 声道 1 ------
子带[ 1]: 4
子带[ 2]: 4
子带[ 3]: 4
子带[ 4]: 5
子带[ 5]: 4
子带[ 6]: 4
子带[ 7]: 4
子带[ 8]: 5
子带[ 9]: 5
子带[10]: 5
子带[11]: 4
子带[12]: 5
子带[13]: 4
子带[14]: 5
子带[15]: 4
子带[16]: 3
子带[17]: 4
子带[18]: 4
子带[19]: 4
子带[20]: 2
子带[21]: 3
子带[22]: 2
子带[23]: 3
子带[24]: 2
子带[25]: 1
子带[26]: 0
子带[27]: 1
子带[28]: 1
子带[29]: 1
子带[30]: 0
------ 声道 2 ------
子带[ 1]: 4
子带[ 2]: 4
子带[ 3]: 4
子带[ 4]: 5
子带[ 5]: 4
子带[ 6]: 4
子带[ 7]: 4
子带[ 8]: 5
子带[ 9]: 5
子带[10]: 5
子带[11]: 4
子带[12]: 5
子带[13]: 4
子带[14]: 5
子带[15]: 4
子带[16]: 3
子带[17]: 4
子带[18]: 4
子带[19]: 4
子带[20]: 2
子带[21]: 3
子带[22]: 2
子带[23]: 3
子带[24]: 2
子带[25]: 1
子带[26]: 0
子带[27]: 1
子带[28]: 1
子带[29]: 1
子带[30]: 0
Hit end of audio data
Avg slots/frame = 626.937; b/smp = 4.35; bitrate = 192.000 kbps
2.自己制作的噪声noise2.wav:
========== 基本信息 ==========
输入文件:noise2.wav
输出文件:noise2.mp2
采样频率:44.1 kHz
输出文件码率:192 kbps
encode_init: using tablenum 1 with sblimit 30
声道数:2
目前观测第 2 帧
本帧比特预算:5016 bits
========== 比特分配表 ==========
------ 声道 1 ------
子带[ 1]: 3
子带[ 2]: 4
子带[ 3]: 3
子带[ 4]: 6
子带[ 5]: 5
子带[ 6]: 4
子带[ 7]: 5
子带[ 8]: 4
子带[ 9]: 5
子带[10]: 5
子带[11]: 4
子带[12]: 5
子带[13]: 4
子带[14]: 4
子带[15]: 5
子带[16]: 5
子带[17]: 3
子带[18]: 4
子带[19]: 4
子带[20]: 5
子带[21]: 6
子带[22]: 4
子带[23]: 3
子带[24]: 1
子带[25]: 0
子带[26]: 0
子带[27]: 0
子带[28]: 0
子带[29]: 0
子带[30]: 0
------ 声道 2 ------
子带[ 1]: 4
子带[ 2]: 4
子带[ 3]: 3
子带[ 4]: 6
子带[ 5]: 5
子带[ 6]: 4
子带[ 7]: 5
子带[ 8]: 4
子带[ 9]: 5
子带[10]: 5
子带[11]: 4
子带[12]: 5
子带[13]: 4
子带[14]: 4
子带[15]: 5
子带[16]: 5
子带[17]: 3
子带[18]: 4
子带[19]: 4
子带[20]: 5
子带[21]: 6
子带[22]: 4
子带[23]: 3
子带[24]: 1
子带[25]: 0
子带[26]: 0
子带[27]: 0
子带[28]: 0
子带[29]: 0
子带[30]: 0
Hit end of audio data
Avg slots/frame = 626.936; b/smp = 4.35; bitrate = 191.999 kbps
3.利用流媒体的ffmpeg指令混叠两段音频得到mix2.wav:
========== 基本信息 ==========
输入文件:mix2.wav
输出文件:mix2.mp2
采样频率:44.1 kHz
输出文件码率:192 kbps
encode_init: using tablenum 1 with sblimit 30
声道数:2
目前观测第 2 帧
本帧比特预算:5016 bits
========== 比特分配表 ==========
------ 声道 1 ------
子带[ 1]: 4
子带[ 2]: 5
子带[ 3]: 3
子带[ 4]: 5
子带[ 5]: 5
子带[ 6]: 4
子带[ 7]: 5
子带[ 8]: 5
子带[ 9]: 5
子带[10]: 5
子带[11]: 4
子带[12]: 5
子带[13]: 4
子带[14]: 5
子带[15]: 4
子带[16]: 3
子带[17]: 4
子带[18]: 4
子带[19]: 4
子带[20]: 2
子带[21]: 3
子带[22]: 2
子带[23]: 4
子带[24]: 1
子带[25]: 1
子带[26]: 0
子带[27]: 0
子带[28]: 1
子带[29]: 1
子带[30]: 0
------ 声道 2 ------
子带[ 1]: 4
子带[ 2]: 4
子带[ 3]: 4
子带[ 4]: 5
子带[ 5]: 5
子带[ 6]: 4
子带[ 7]: 5
子带[ 8]: 5
子带[ 9]: 5
子带[10]: 5
子带[11]: 4
子带[12]: 5
子带[13]: 4
子带[14]: 5
子带[15]: 4
子带[16]: 3
子带[17]: 4
子带[18]: 4
子带[19]: 4
子带[20]: 2
子带[21]: 3
子带[22]: 2
子带[23]: 4
子带[24]: 1
子带[25]: 1
子带[26]: 0
子带[27]: 0
子带[28]: 1
子带[29]: 1
子带[30]: 0
Hit end of audio data
Avg slots/frame = 626.937; b/smp = 4.35; bitrate = 192.000 kbps