MPEG音频编码实验
实验概述
通过了解Layer编码器的原理,熟悉和理解编码过程,深入理解理解心理声学模型,并要求能够完善并运行实验代码,输入三段不同特性的音频文件,输出其音频的采样率和目标码率以及输出某个数据帧的分配的比特数,比例因子和比特分配结果。
实验内容
1.理解程序设计的整体框架
重点理解
- 多相滤波器组
将PCM样本变换到32个子带的频域信号- 心理声学模型
计算信号中不可听觉感知的部分,计算噪声遮蔽效应- 比特分配器
根据心理声学模型的计算结果,为每个子带信号分配比特数- 装帧
产生MPEG-I兼容的比特流
2 . 多相滤波器组
在MPEG-1音频编码中使用多相滤波器的原因
- 将音频信号变换成多个子代,使信号具有高的时间分辨率,确保在短暂冲击信号情况下,编码的声音信号具有足够高的质量,又可以使信号通过FFT运算具有高的频率分辨率,因为掩蔽阈值是从功率谱密度推出来的。在低频子带中,为了保护音调和共振峰的结构,就要求用较小的量化阶、较多的量化级数,即分配较多的位数来表示样本值。而话音中的摩擦音和类似噪声的声音,通常出现在高频子带中,对它分配较少的位数。
示意图
3 .心理声学模型
MPEG-I 标准定义了两个模型:
- 心理声学模型1
特点:计算复杂度低,但对假设用户听不到的部分压缩太严重
步骤:
1.将样本变换到频域
32个等分的子带信号并不能精确地反映人耳的听觉特性。引入FFT补偿频率分辨率不足的问题。
2、确定声压级别
3、考虑安静时阈值
也即绝对阈值。在标准中有根据输入PCM信号的采
样率编制的“频率、临界频带率和绝对阈值”表。此表为多位科学家经多次心理声学实验所得
4、将音频信号分解成“乐音(tones)” 和“非乐音/噪声”
部分:因为两种信号的掩蔽能力不同
5、音调和非音调掩蔽成分的消除
利用标准中给出的绝对阈值消除被掩蔽成分;考虑在每个临界频带内,小于0.5Bark的距离中只保留最高功率的成分
6、单个掩蔽阈值的计算
音调成分和非音调成分单个掩蔽阈值根据标
准中给出的算法求得
7、全局掩蔽阈值的计算
8、每个子带的掩蔽阈值
选择出本子带中最小的阈值作为子带阈值
对高频不正确——高频区的临界频带很宽,可能跨越多个子带,从而导致模型1将临界带宽内所有的非音调部分集中为一个代表频率,当一个子带在很宽的频带内却远离代表频率时,无法得到准确的非音调掩蔽值。但计算量低
9、计算每个子带信号掩蔽比(signal-to-mask ratio, SMR)
SMR = 信号能量 / 掩蔽阈值
并将SMR传递给编码单元
- 心理声学模型2
特点:提供了适合Layer III编码的更多特征, 实际实现的模型复杂度取决所需要的压缩因子
概述:
与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比特)一起传送,如果缩放因子和下一个只有很小的差别,就只传送大的一个,这种情况对于稳态信号经常出现如果要给瞬态信号编码,则要在瞬态的前、后沿传送两个或所有三个比例因子
4 . 理解码率分配的实现思路
5 .装帧
数字帧的内容:
- 帧头:Header
- 无码检测CRC
- 声音数据:比特分配表,比例因子选择信息,比例因子,子带样值
- 辅助数据
输出音频的采样率和目标码率
实验步骤
1.了解整个编码框架
2.根据所给代码添加程序,使得满足要求:输出音频的采样率和目标码率以及输出某帧所分配的比特数,该帧的比例因子, 该帧的比特分配结果
3.选择三个不同特性的音频文件:噪声(持续噪声、突发噪声)、音乐、混合作为输入
4.得到输出的mp2文件,很带有输出信息的txt文件
5.分析结果,得出结论
实验过程
调试:
在m2aenc.c中进行更改
在int main (int argc, char **argv)
加入下面代码
int gr;
FILE *out_txt=NULL;
unsigned char *outTXT=NULL;
out_txt=fopen("output.txt","w");
在下面这段代码中
#ifdef NEWENCODE
sf_transmission_pattern (scalar, scfsi, &frame);
main_bit_allocation_new (smr, scfsi, bit_alloc, &adb, &frame, &glopts);
//main_bit_allocation (smr, scfsi, bit_alloc, &adb, &frame, &glopts);
if (error_protection)
CRC_calc (&frame, bit_alloc, scfsi, &crc);
write_header (&frame, &bs);
//encode_info (&frame, &bs);
if (error_protection)
putbits (&bs, crc, 16);
write_bit_alloc (bit_alloc, &frame, &bs);
//encode_bit_alloc (bit_alloc, &frame, &bs);
write_scalefactors(bit_alloc, scfsi, scalar, &frame, &bs);
//encode_scale (bit_alloc, scfsi, scalar, &frame, &bs);
subband_quantization_new (scalar, *sb_sample, j_scale, *j_sample, bit_alloc,
*subband, &frame);
//subband_quantization (scalar, *sb_sample, j_scale, *j_sample, bit_alloc,
// *subband, &frame);
write_samples_new(*subband, bit_alloc, &frame, &bs);
//sample_encoding (*subband, bit_alloc, &frame, &bs);
#else
transmission_pattern (scalar, scfsi, &frame);
main_bit_allocation (smr, scfsi, bit_alloc, &adb, &frame, &glopts);
if (error_protection)
CRC_calc (&frame, bit_alloc, scfsi, &crc);
encode_info (&frame, &bs);
if (error_protection)
encode_CRC (crc, &bs);
encode_bit_alloc (bit_alloc, &frame, &bs);
encode_scale (bit_alloc, scfsi, scalar, &frame, &bs);
subband_quantization (scalar, *sb_sample, j_scale, *j_sample, bit_alloc,
*subband, &frame);
sample_encoding (*subband, bit_alloc, &frame, &bs);
#endif
/* If not all the bits were used, write out a stack of zeros */
for (i = 0; i < adb; i++)
put1bit (&bs, 0);
if (header.dab_extension) {
/* Reserve some bytes for X-PAD in DAB mode */
putbits (&bs, 0, header.dab_length * 8);
for (i = header.dab_extension - 1; i >= 0; i--) {
CRC_calcDAB (&frame, bit_alloc, scfsi, scalar, &crc, i);
/* this crc is for the previous frame in DAB mode */
if (bs.buf_byte_idx + lg_frame < bs.buf_size)
bs.buf[bs.buf_byte_idx + lg_frame] = crc;
/* reserved 2 bytes for F-PAD in DAB mode */
putbits (&bs, crc, 8);
}
putbits (&bs, 0, 16);
}
frameBits = sstell (&bs) - sentBits;
if (frameBits % 8) { /* a program failure */
fprintf (stderr, "Sent %ld bits = %ld slots plus %ld\n", frameBits,
frameBits / 8, frameBits % 8);
fprintf (stderr, "If you are reading this, the program is broken\n");
fprintf (stderr, "email [mfc at NOTplanckenerg.com] without the NOT\n");
fprintf (stderr, "with the command line arguments and other info\n");
exit (0);
}
sentBits += frameBits;
}
close_bit_stream_w (&bs);
if ((glopts.verbosity > 1) && (glopts.vbr == TRUE)) {
int i;
#ifdef NEWENCODE
extern int vbrstats_new[15];
#else
extern int vbrstats[15];
#endif
fprintf (stdout, "VBR stats:\n");
for (i = 1; i < 15; i++)
fprintf (stdout, "%4i ", bitrate[header.version][i]);
fprintf (stdout, "\n");
for (i = 1; i < 15; i++)
#ifdef NEWENCODE
fprintf (stdout,"%4i ",vbrstats_new[i]);
#else
fprintf (stdout, "%4i ", vbrstats[i]);
#endif
fprintf (stdout, "\n");
}
fprintf (stderr,"Avg slots/frame = %.3f; b/smp = %.2f; bitrate = %.3f kbps\n",
(FLOAT) sentBits / (frameNum * 8),
(FLOAT) sentBits / (frameNum * 1152),
(FLOAT) sentBits / (frameNum * 1152) *
s_freq[header.version][header.sampling_frequency]);
if (fclose (musicin) != 0)
{
fprintf (stderr, "Could not close \"%s\".\n", original_file_name);
exit (2);
}
fprintf (stderr, "\nDone\n");
time(&end_time);
total_time = end_time - start_time;
printf("total time is %d\n", total_time);
free(outTXT);
fclose(out_txt);
system("pause");
exit (0);
}
加入以下代码
fprintf(out_txt, "该音频声道数:%d\n", nch);
fprintf(out_txt, "观测第 %d 帧\n", frameNum);
fprintf(out_txt, "本帧所分配比特:%d bits\n", adb);
fprintf(out_txt, "该帧比例因子和比特分配结果如下:\n");
for (ch = 0; ch < nch; ch++)
{
fprintf(out_txt, "--- 声道%2d ----\n", ch + 1);
for (sb = 0; sb < frame.sblimit; sb++)
{
fprintf(out_txt, "子带[%2d]比例因子:\t", sb + 1);
for (gr = 0; gr < 3; gr++)
{
fprintf(out_txt, "%2d\t", scalar[ch][gr][sb]);
}
fprintf(out_txt, "\n");
fprintf(out_txt, "子带[%2d]比特分配表:\t%2d\n", sb + 1, bit_alloc[ch][sb]);
fprintf(out_txt, "\n");
}
}
输出的几个文件
实验结果
噪声
噪音+音乐
音乐
实验总结
本次实验最关键的点在于理解心理声学模型和整个编码过程中每个步骤的关键所在,根据实验结果,我们可以看到子带和比例因子的数值,对比三段音频的输出。可以看到由于特性不同,会根据具体情况来分配比特数。