目录
一、内容概述
感知音频编码原理流程图
简述:
输入声音信号经过一个多相滤波器组
,变换到多个子带
,同时经过心理声学模型
计算以频率为自变量的噪声掩蔽阈值。量化和编码部分用信掩比SMR
决定分配给子带信号的量化位数,使量化噪声<掩蔽阈值(噪掩比越小越好,这样人耳就因为频率掩蔽特性
听不见噪声了,从而达到降噪的效果)。最后通过数据帧
包装将量化的子带样本和其它数据按照规定的帧格式组装成比特数据流。
中心思想:
分析信号,人耳不能感知的部分不编码。
1. 子带分解&多相滤波器组
1.1. 为什么要将音频信号分解为子带进行处理?
从信号角度,合适的信号分解能在不损失比特率的情况下,降低量化可能造成的误差,利用不同子序列的不同特征,采用不同的编码机制,编码更加灵活,编码效率更高;
从人耳听觉角度,对于人耳无法分辨或者不需要分辨的部分信号,可以采用不同的编码机制,屏蔽掉或者直接去除这部分信号,在提高传输效率的同时提高音频主观质量。
1.2. 怎样分解为子带?
信号分解可以利用数字滤波器实现,采用分析-综合滤波器组。
在m2aenc.c main函数
中体现为
{
int gr, bl, ch;
/* New polyphase filter 多相滤波器组
Combines windowing and filtering. Ricardo Feb'03 */
for( gr = 0; gr < 3; gr++ ) // 36样值分3组
for ( bl = 0; bl < 12; bl++ ) // 每组做12次子带分解
for ( ch = 0; ch < nch; ch++ ) // 分两个声道
WindowFilterSubband( &buffer[ch][gr * 12 * 32 + 32 * bl], ch, &(*sb_sample)[ch][gr][bl][0] );
}
1.3. 存在的问题还有?
等带宽的滤波器组与人类听觉系统的临界频带不对应,滤波器组与其逆过程并不是完全无失真的,滤波后的相邻子带存在频率混叠现象等。
2. 心理声学模型
2.1. 听觉阈值
听觉系统存在一个听觉阈值电平,低于这个电平的声音信号人耳听不到。听觉阈值的大小随声音频率的改变而改变。一个人是否听到声音取决于声音的频率,以及声音的幅度是否高于这种频率下的听觉阈值。
2.2. 频率掩蔽效应
2.3. 临界频带
临界频带是指当某个纯银被以它为中心频率、且具有一定带宽的连续噪声所掩蔽时,如果该纯音刚好被听到时的功率等于这一频带内的噪声功率,它的带宽称为临界频带宽度。
2.4. 模块实现
心理声学模型的最大作用就是去除冗余信息,在流程图中可以看到,其输入为经过FFT变换后高的频率分辨率的信号和比例因子,输出的是掩蔽阈值电平(掩蔽值的计算是通过大量实验得到的经验计算函数),由比例因子和掩蔽电平确定最终输入量化比特数分配模块的比例因子,对不同子带的动态比特分配值,进而确定对不同子带的量化比特数。
3. 量化和编码
3.1. 比例因子取值和编码
Layer I:12个子带,各个子带中每12个样点进行一次比例因子计算,先定出12个样点中绝对值的最大值,查比例因子表中比这个最大值大的最小值作为比例因子,用6bit表示。
Layer 2:为了降低比例因子传输码率,采用了利用人耳掩蔽特性的编码策略。
3.2. 比特分配及编码
对每个子带计算掩噪比MNR (dB) = 信噪比SNR - 信掩比SMR,然后找出其中具有最低MNR的子带,并给该子带多分配一些比特(对最小MNR的子带分配比特,使获益最大的子带的量化级别增加一级),然后重新计算MNR,继续分配,循环此过程,直至没有比特可以分配。这样可以使得在满足比特率和掩蔽要求的前提下,使MNR最小。
4. 数据帧包装
编码时是以数据帧为单位进行的,每个数据帧包括帧头、CRC、比特分配、比例因子、样值、辅助数据等。
二、实验思路
这里我录了10s的噪声文件noise.wav(吃海底捞爆米花),一首《回家》纯音乐music.wav,10s我最爱的米津玄师
今年的新歌《Pale Blue》(超级好听啊啊啊!)手机录的外放文件music_noise.wav作为实验材料。
-
命令行参数
-
出现问题的解决方法
调试报错:错误 D8016 “/ZI”和“/Gy-”命令行选项不兼容
-
添加TRACE注释
m2aenc.h
添加
#define TRACE 1 // 开辟trace文件输出某一数据帧信息
#define TRACE_FILE "info_frame.txt"
FILE* infoFp;
m2aenc.c
添加
输出基本信息(采样率、目标码率)
void print_config (frame_info * frame, int *psy, char *inPath,char *outPath)
{
...
fprintf (stderr, "--------------------------------------------\n");
#if TRACE
fprintf(infoFp, "==================基本信息====================");
fprintf(infoFp, "输入文件 : %s\n", inPath);
fprintf(infoFp, "输出文件 : %s\n", inPath);
fprintf(infoFp, "采样频率 :%.1f kHz\n",
s_freq[header->version][header->sampling_frequency]);
fprintf(infoFp,"输出文件码率 :% d kbps ", bitrate[header->version][header->bitrate_index]);
#endif // TRACE
}
输出某一帧的比特预算、比例因子、实际比特分配数(main函数
)
// 输出比例因子和比特分配表
#if TRACE
if (frameNum==4) //输出第四帧的比例因子和比特分配表
{
fprintf(infoFp, "当前帧为第 %d 帧\n", frameNum);
fprintf(infoFp, "比特预算值 : %d bits\n", adb);
fprintf(infoFp,"==================比例因子====================\n");
for (ch = 0; ch < nch; ch++)
{
fprintf(infoFp, "++++++++++声道 : %2d++++++++++\n", ch + 1); // 输出第ch+1个声道
for (sb = 0; sb < frame.sblimit; sb++)
{
fprintf(infoFp, "子带[%2d]:\t", sb + 1);
for (int gr = 0; gr < 3; gr++)
{
fprintf(infoFp, "%2d\t", scalar[ch][gr][sb]);
}
fprintf(infoFp, "\n");
}
}
fprintf(infoFp, "==================比特分配表===================\n");
for (ch = 0; ch < nch; ch++) {
fprintf(infoFp, "++++++++++声道 : %2d++++++++++\n", ch + 1); //按声道分配
for (sb = 0; sb < frame.sblimit; sb++) {
fprintf(infoFp, "子带[%2d]:\t%2d\n", sb + 1, bit_alloc[ch][sb]);
}
}
}
#endif // TRACE
三、输出结果
输出信息文档
输出mp2文件
噪声文件信息
纯乐音输出信息
混合乐音输出信息
四、总结反思
因为乐音和混合声用的不是同一首歌,同时前者为电脑下载原音频,后者手机录制,长度也不同,可以看出两者采样率不同,比特分配预算不同。比较噪声和混合声,可以看出,实际分配的比特数后者少于前者,主要是因为人发声的语音范围有限,高频分量较少,可以不编码处理。