一、实验原理
MPEG-1 Audio LayerII编码器原理
框架图:
多相滤波器组:
- 将 PCM样本变换到32个子带的频域信号 ;
如果输入的采样频率为48kHz,那么子带的频率宽度为48/(2*32)=0.75Hz
缺点: - 等带宽的滤波器组与人类听觉系统的临界频带不对应
在低频区域,单个子带会覆盖多个临界频带。在这种情况下量化比特数不能兼每个临界频带。 - 滤波器组与其逆过程不是无失真的
但滤波器组引入的误差很小,且听不到 - 子带间频率有混叠
滤波后的相邻子带有频率混叠现象,一个子带中的信号可以影响相邻子带的输出
心理声学模型:
计算信号中不可听觉感知的部分(计算噪声遮蔽效应)
MPEG-1定义了两种模型。
心理声学模型1:
计算复杂度低,但对假设用户听不到的部分压缩太严重
心理声学模型2:
提供了适合Layer III编码的更多特征
计算:
1.将样本变换到频域
32个等分的子带信号并不能精确地反映人耳的听觉特性。 引入FFT补偿频率分辨率不足的问题。
- 采用Hann加权和DFT
- Hann加权减少频域中的边界效应
- 此变换不同于多相滤波器组,因为模型需要更精细的频率分辨率,而且计算掩蔽阈值也需要每个频率 的幅值
- 模型1:采用512 (Layer I) 或1024 (Layers II and
III)样本窗口- Layer I:每帧384个样本点,512个样本点足够覆盖
- Layer II 和LayerIII:每帧1152个样本点,每帧两次 计算,模型1选择两个信号掩蔽比(SMR)中较小的 一 个
2.确定声压级别
- 子带n中的声压级别:
其中X(k)是在子带n中的频谱线的声压级别,scfmax(n)在是一帧中子带n的三个缩放因子中最大的一个。
3.考虑安静时阈值
- 也即绝对阈值。在标准中有根据输入PCM信号的采样率编制的“频率、临界频带率和绝对阈值”表。
4.将音频信号分解成“乐音”和“非乐音/噪声”部分(因为两种信号的掩蔽能力不同)
- 模型1:根据音频频谱的局部功率最大值确定乐音成分
- 局部峰值为乐音,然后将本临界频带内的剩余频谱合在一起,组成一个代表噪声频率(无调成份)
5.音调和非音调掩蔽成分的消除
- 利用标准中给出的绝对阈值消除被掩蔽成分; 考虑在每个临界频带内,小于0.5Bark的距离 中只保留最高功率的成分
6.单个掩蔽阈值的计算
- 音调成分和非音调成分单个掩蔽阈值根据标 准中给出的算法求得。
7.全局掩蔽阈值的计算
- 某一频率点i的总掩蔽阈值可通过该点的绝对掩蔽阈值与单独掩蔽阈值相加来获得:
其中LTq(i)是频率点i的绝对掩蔽阈值;LTtm(z(j),z(i))是第j个音调掩蔽成分对频率点i的掩蔽阈值,对频率点i有掩蔽效应的音调掩蔽成分共m个;LTnm(z(j),z(i))是第j个非音调掩蔽成分对频率点i的掩蔽阈值,对频率点i有掩蔽效应的非音调掩蔽成分共n个。 - 还要考虑别的临界频带的影响。一个掩蔽信号会对其 它频带上的信号产生掩蔽效应。这种掩蔽效应称为掩 蔽扩散。
8.每个子带的掩蔽阈值
- 选择出本子带中最小的阈值作为子带阈值
- 对高频不正确——高频区的临界频带很宽, 可能跨越多个子带,从而导致模型1将临界带宽内所有的非音调部分集中为一个代表频率,当一个子带在很宽的频带内却远离代表频率时,无法得到准确的非音调掩蔽值。但计算量低。
- 选择出本子带中最小的阈值作为子带阈值
9.计算每个子带信号掩蔽比,并将SMR传递给编码单元
- SMR = 信号能量 / 掩蔽阈值
比特分配器:
根据心理声学模型的计算结果,为每个子带信号分配比特数
Layer I 编码:码率分配
- 在调整到固定的码率之前
- 先确定可用于样值编码的有效比特数
- 这个数值取决于比例因子、比例因子选择信 息、比特分配信息以及辅助数据所需比特数
- 比特分配过程:
- 对每个子带计算掩蔽-噪声比MNR,是信噪比SNR-信掩比SMR,即:MNR=SNR-SMR
- NMR=SMR-SNR。
- 算法:使整帧和每个子带的总噪声—掩蔽比最小
- 计算噪声-掩蔽比:
NMR = SMR – SNR (dB)
其中SNR 由MPEG-I标准给定 (为量化水平的函数),NMR:表示波形误差与感知测量之间的误差 - 循环,直到没有比特可用:
- NMR = SMR– SNR (dB)
- 对最高NMR的子带分配比特,使获益最大的子带的 量化级别增加一级
- 重新计算分配了更多比特子带的NMR
- 计算噪声-掩蔽比:
装帧:
产生MPEG-1兼容的比特流。
- Header:帧头格式
- Frequency:取样频率
- Pad bit:填充指示位
- Priv. bit:应用特定
- Mode:通道模式
- Mode extesion:通道模式扩展,当通道模式为联合 立体声时有效
- 利用立体声双声道的相关性 编码
- M, S:middle-side
- Frequency:取样频率
Layer II编码
- 与Layer I类似,但对Layer I有增强
- 装帧、缩放因子表示、量化
- 缩放因子(比例因子)一般从低频子带到高频子带出现连续下降
- 帧结构:
- 3 组/帧 x 12个样本/子带 x 32个子带/帧 = 1152个样本/帧
- 每个样本的overhead更少
- 缩放因子:每个子带的3个组尽可能共用缩放因子
- Layer I: 1个/12个样本
- Layer II: 1个/(24/36)个样本
- 1/2/3个缩放因子和缩放因子选择信息(scale factor selection information, SCFSI) (每子带2比特)一起传送
- 如果缩放因子和下一个只有很小的差别,就只传送大的一个, 这种情况对于稳态信号经常出现
- 如果要给瞬态信号编码,则要在瞬态的前、后沿传送两个或 所有三个比例因子
- 1/2/3个缩放因子和缩放因子选择信息(scale factor selection information, SCFSI) (每子带2比特)一起传送
- 量化:
- Layer I:每个子带从相同的量化集合中选择
- 每个子带取共14个量化器中的一个
- Layer II:
- 根据采样和码率量化,不同子带可以从不同的量 化器集合中选择
- 对量化级别在3、5、9级时,采用“颗粒” 优化
- Layer I:每个子带从相同的量化集合中选择
- 装帧:
二、实验内容
输出音频的采样率和目标码率
在m2aecn.c文件中的print_config()函数中有采样率和目标码率的输出
fprintf (stderr, "Input File : '%s' %.1f kHz\n",
(strcmp (inPath, "-") ? inPath : "stdin"),
s_freq[header->version][header->sampling_frequency]);//输出文件路径和音频采样率
fprintf (stderr, "Output File: '%s'\n",
(strcmp (outPath, "-") ? outPath : "stdout"));//输出文件路径
fprintf (stderr, "%d kbps ", bitrate[header->version][header->bitrate_index]);//音频采样率
根据提示在命令行窗口输入文件名字,得到输出结果:
可知,音频采样率为44.1kHz,目标码率为192kbps。
输出到txt文件中:
fprintf(output, "音频采样率:%.1fkhz \r\n", s_freq[header.version][header.sampling_frequency]);
fprintf(output, "目标码率:%dkbps \r\n", bitrate[header.version][header.bitrate_index]);
输出某个数据帧的信息
在循环前打开输出信息的文件
FILE* output = fopen("output.txt", "wb");
while (get_audio (musicin, buffer, num_samples, nch, &header) > 0)
- 该帧所分配的比特数
if(frameNum==2)
{
fprintf(output, "选择的数据帧:%d \r\n", frameNum);
fprintf(output, "该帧所分配的比特数:%d\r\n", adb);
}
- 该帧的比例因子
比例因子存储在三维数组scalar中,第一维是声道,第二维表示第三帧中的第几帧,第三维表示子带序号。
//输出比例因子
if (frameNum == 2)
{
fprintf(output, "\n比例因子:\n");
for (int k = 0; k < nch; k++)
{
fprintf(output, "声道%d \r\n", k + 1);
for (int j = 0; j < frame.sblimit; j++)//子带总数
{
fprintf(output, "子带%d: ", j + 1);
for (int m = 0; m < 3; m++)//每个子带有3个比例因子
{
fprintf(output, "%d\t", scalar[k][m][j]);
}
fprintf(output, "\r\n");
}
}
}
- 该帧的比特分配结果
比特分配的结果存放在二维数组bit_alloc中,其第一维表示声道数,第二维表示子带序号。
//比特分配结果
if (frameNum == 2)
{
fprintf(output, "\n比特分配:\n");
for (int k = 0; k < nch; k++)
{
fprintf(output, "声道%d \r\n", k + 1);
for (int j = 0; j < frame.sblimit; j++)
{
fprintf(output, "子带%d:%d\r\n", j, bit_alloc[k][j]);
}
}
}
输出的txt文件: