MPEG,即Moving Picture Experts Group(动态图像专家组)为ISO与IEC于1988年成立的专门针对运动图像和语音压缩制定国际标准的组织。
MPEG标准主要有以下五个,MPEG-1、MPEG-2、MPEG-4、MPEG-7及MPEG-21等。该专家组建于1988年,专门负责为CD建立视频和音频标准,而成员都是为视频、音频及系统领域的技术专家。及后,他们成功将声音和影像的记录脱离了传统的模拟方式,建立了ISO/IEC11172压缩编码标准,并制定出MPEG-格式,令视听传播方面进入了数码化时代。因此,大家现时泛指的MPEG-X版本,就是由ISO所制定而发布的视频、音频、数据的压缩标准。
MPEG标准的视频压缩编码技术主要利用了具有运动补偿的帧间压缩编码技术以减小时间冗余度,利用DCT技术以减小图像的空间冗余度,利用熵编码则在信息表示方面减小了统计冗余度。这几种技术的综合运用,大大增强了压缩性能。
而其中MUSICAM,即MPEG-1 Audio Layer Ⅱ(MP2)协议为音频类型的协议,其凭借相对较低的算法复杂度,以及在当时较为突出的压缩率和双声道的特点,广泛用于数字音频广播(DAB)和VCD等。同时由于其适当的复杂程度和优秀的声音质量,在数字演播室、DAB、DVB等数字节目的制作、交换、存储、传送中得到广泛应用。
本实验则对MPEG-1 Audio Layer Ⅲ(MP3)协议对应的文件格式.mp3文件进行解析,并凭借其结构与mp2文件类似的特点,通过代码分析实现对MPEG音频编码的实践与深入理解,并对编码过程中的相关量进行分析。
MPEG音频编码过程可分为时域部分(蓝)和频域部分(黄),其中时域部分使用子带滤波器进行子带分解形成处理单元,而频域部分则通过心理声学模型控制编码过程中量化的方式,从而去除音频文件中的感知冗余等多种冗余信息。
同时,MPEG音频编码使用感知音频编码方式,针对人耳听觉实际感受进行感知冗余的去除。根据人耳的听觉特性,人在分辨声音时存在生理接收和心理感受方面的感知极限,也因此音频信号中部分是无法被听到的。所以在音频编码压缩的过程中,针对不能被听到的部分可以不传送,而能被听到但不敏感的部分可以以较低质量传送,从而把有限的比特分配给较为敏感的信号,实现数据的压缩。
在如图所示的较为基本的子带编码(左半部分)系统中,以 综合滤波器组、比特分配机制 和 编码机制三大要素作为重要组成部分,对信号以不同频带进行分解后分别处理,就可以达到对于信号中不同的成分针对性地进行处理,在一定程度上解决因对不同的成分“一视同仁”而形成的比特浪费。
编码过程中子带分析滤波器组的分解,可使信号具有较高的时间分辨率,以确保在短暂冲击信号情况下,编码的声音信号具有足够高的质量。
在编码的过程中所需要决定的比特分配方式,则由频域部分进行分析。
心理声学模型所遵循原理即为人耳的听觉特性。对于特定的音频,人耳的感知存在一个听觉阈值电平,低于此电平的声音信号就听不到。故一个人是否听到声音取决于声音的频率,以及声音的幅度是否高于这种频率下的听觉阈值。同时,听觉阈值电平的大小也会随声音频率的改变而改变。
同时,对于频率较为接近的音频分量,其中一个较强的频率成分会导致周围频率成分的听觉阈值升高。此效应则为听觉掩蔽。
而当某个纯音被以它为中心频率、且具有一定带宽的连续噪声所掩蔽时,如果该纯音刚好被听到时的功率等于这一频带内的噪声功率,则这个带宽被称为临界频带宽度。换句话讲,纯音的听阈随着掩蔽噪声的带宽增大而增高,但当噪声的带宽达到临界频带后,纯音的听阈就不会再改变。凭借此规律,可以把听觉系统等效为多个固定临界频带所组合成的滤波器组。当聆听到多个频率特性的声音时,只要信噪比达到当前所在滤波器中的听阈,即可听到该频率成分。
综上所述,在压缩编码过程中,心理声学模型要凭借音频内成分和其频率所对应的听觉阈值的关系进行分析。
在经过心理声学模型之前,其输入的信号(一个处理单元)需要进行FFT,以保证其在分析时可有较高的频率分辨率,提高心理声学模型优化的精度。同时,心理声学模型也需要获取进行分组的处理单元的比例(缩放)因子,以结合信号本身与听阈的关系进行分析。
较为简单的心理声学模型Ⅰ运算过程包括:
FFT变换
在子带滤波器组中,32的子带样本数远不能满足人耳听觉的精度要求,故在心理声学模型处理前需要进行FFT变换。
在MPEG-1 Audio Layer Ⅰ编码中,处理单元(帧)为384个样本点,故使用512点FFT;在Layer Ⅱ和Layer Ⅲ中,对于1152样本点的处理单元,分为两次计算,使用1024点FFT变换。
确定各子带声压级别
由于在后续需要对各子带音频的强度与其对应的听觉阈值作比较,故需要对声压级别进行运算。其运算公式如下:
其中 X(k) 为子带n中频谱的生涯级别,即从FFT结果中取得;而 scfmax(n) 则为一帧中子带n的三个缩放因子中最大的一个,即从输入的比例因子中取得。
考虑绝对阈值
根据“频率、临界频带率和绝对阈值”表,获取对应频率下的听觉阈值,方便后续进行比较。
音频分解与冗余消除
由于乐音和噪声的掩蔽能力不同,可根据频谱的局部最大值将音频信号分解为乐音和噪音成分。同时,利用标准中给出的绝对阈值消除被掩蔽成分,并考虑在每个临界频带内,小于0.5Bark的距离中只保留最高功率的成分,对乐音与噪声的岩壁部分进行去除。
计算掩蔽阈值
掩蔽阈值的计算需要先对各个频率的单独掩蔽阈值进行计算,其计算由标准算法给出。
而对于各个频率的总掩蔽阈值,则可直接将频率下的绝对掩蔽阈值和单独掩蔽阈值进行加和得出,其算法公式为
公式加和中第一项即为绝对听阈,第二项为所有的乐音对 i 处的掩蔽(加和),第三项则为所有噪声对 i 处的掩蔽(加和)。
计算子带掩蔽阈值
对于之前所得出的全局掩蔽阈值,以子带为单位,分别选择各个子带中最小的阈值作为本子带的掩蔽阈值。
计算信掩比
以子带为单位,计算每个子带的信掩比 SMR=信号能量/掩蔽阈值 。至此,心理声学模型的分析过程完成,所得的子带信掩比则传输给动态比特分配模块进行比特分配。
对于所确定的可用于样本分配的码字数量,对子带进行分配时,根据子带的掩噪比 MNR=SNR-SMR 进行分配,以保证每个子带的MNR均为最小即可。循环分配至码字用完之后,即实现在满足比特率和掩蔽要求的前提下,使MNR最小。
分配结果即可直接传值子带编码过程中,子带编码即根据此分配方式进行码字分配量化。
对编码完成的数据加上头部进行装帧即可。注意由于编码过程机制,在封装头部时需要将子带的比特分配信息以及比例因子带上。
实验对 m2aenc 工程进行解读,并加入输出语句以实现编码过程中的重要信息输出。
在common.h头文件中定义编码文件中数据结构的结构体。其中包含:
typedef struct
{
int version;
int lay;
int error_protection;
int dab_extension;
int dab_length;
int bitrate_index;
int sampling_frequency;
int padding;
int extension;
int mode;
int mode_ext;
int copyright;
int original;
int emphasis;
}
frame_header;
typedef struct
{
frame_header *header; /* raw header information */
int actual_mode; /* when writing IS, may forget if 0 chs */
al_table *alloc; /* bit allocation table read in */
int tab_num; /* number of table as loaded */
int nch; /* num channels: 1 for mono, 2 for stereo */
int jsbound; /* first band of joint stereo coding */
int sblimit; /* total number of sub bands */
}
frame_info; //帧结构
typedef struct bit_stream_struc
{
FILE *pt; /* pointer to bit stream device */
unsigned char *buf; /* bit stream buffer */
int buf_size; /* size of buffer (in number of bytes) */
long totbit; /* bit counter of bit stream */
int buf_byte_idx; /* pointer to top byte in buffer */
int buf_bit_idx; /* pointer to top bit of top byte in buffer */
int mode; /* bit stream open in read or write mode */
int eob; /* end of buffer index */
int eobs; /* end of bit stream flag */
char format;
/* format of file in rd mode (BINARY/ASCII) */
}
Bit_stream_struc; //形成比特流数据时的信息
在 m2aenc.c 代码文件中,main函数即为MPEG音频编码的过程,主要编码过程以帧为单位进行。忽略函数中其余信息的判断,过程包括:
/* 数据准备 */
adb = available_bits(&header, &glopts); //预算一个数据帧所分配的比特数
lg_frame = adb / 8; //将每一帧的比特数转化为字节数
if (header.dab_extension) {
/* in 24 kHz we always have 4 bytes */
if (header.sampling_frequency == 1)
header.dab_extension = 4;
/* You must have one frame in memory if you are in DAB mode */
/* in conformity of the norme ETS 300 401 http://www.etsi.org */
/* see bitstream.c */
if (frameNum == 1)
minimum = lg_frame + MINIMUM;
adb -= header.dab_extension * 8 + header.dab_length * 8 + 16;
}
int avaliablebits = adb; //先取出一帧的比特数
/* 运行时域处理部分的工作:子带分解 */
{
int gr, bl, ch;
/* New polyphase filter
Combines windowing and filtering. Ricardo Feb'03 */
/* 用多层循环将1152个样本以32个为单位进行读取 */
for (gr = 0; gr < 3; gr++) //每帧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]); //子带分解,提取384个数据样本
}
/* 提取比例因子 */
scale_factor_calc(*sb_sample, scalar, nch, frame.sblimit); //计算各个子带的比例因子
pick_scale(scalar, &frame, max_sc); //选择出最大的比例因子
if (frame.actual_mode == MPG_MD_JOINT_STEREO) { //对不同格式的音频帧的额外操作
combine_LR(*sb_sample, *j_sample, frame.sblimit);
scale_factor_calc(j_sample, &j_scale, 1, frame.sblimit);
}
/* 开始运行声学模型 */
/* 输入:当前帧内数据,最大比例因子,smr,整个帧 */
psycho_1(buffer, max_sc, smr, &frame);
/* 根据心理声学模型运行结果 进行比特分配 */
transmission_pattern(scalar, scfsi, &frame);
main_bit_allocation(smr, scfsi, bit_alloc, &adb, &frame, &glopts); //比特分配
/* 编码过程 */
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); //封装
通过添加代码,在result.txt文件中输出编码信息,其包括:
FILE* result;
result = fopen("result.txt", "a");
fprintf(result, "*************************\n");
fprintf(result, "源文件:%s\n", inPath);
fprintf(result, "输出文件:%s\n", outPath);
fprintf(result, "采样频率:%f kHz\n", s_freq[header->version][header->sampling_frequency]);
fprintf(result, "码率:%d kbps\n", bitrate[header->version][header->bitrate_index]);
fprintf(result, "*************************\n");
fclose(result);
FILE *result = fopen("result.txt", "a");;
if (frameNum == 5) {
fprintf(result, "观测第 %d 帧\n", frameNum);
fprintf(result, "分配比特预算:%d bits\n", avaliablebits);
fprintf(result, "***************** 比例因子 *****************\n");
for (sb = 0; sb < frame.sblimit; sb++) // 每个子带
{
fprintf(result, "子带 %2d:\t", sb + 1);
fprintf(result, "%2d\t%2d\t%2d\t|\t%2d\t%2d\t%2d\n", scalar[0][0][sb], scalar[0][1][sb], scalar[0][2][sb], scalar[1][0][sb], scalar[1][1][sb], scalar[1][2][sb]);
}
fprintf(result, "\n");
fprintf(result, "******** 比特分配表 ********\n"); //输出比特分配结果
for (sb = 0; sb < frame.sblimit; sb++) {
fprintf(result, "子带 %2d:\t%2d\t|\t%2d\n", sb + 1, bit_alloc[0][sb], bit_alloc[1][sb]);
}
fprintf(result, "\n");
}
fclose(result);
实验中对每个输出文件的第五帧数据进行了输出,并对呈现效果进行了排版。其中因实验素材均为双声道,故在每个子带的比例因子及比特分配结果中双声道的数据由 “|” 分割。具体形式与数据见三。
实验使用四种素材声音MP3文件进行输入,输出MP2文件,并对输出的信息进行分析。
*************************
源文件:City.mp3
输出文件:Cityout.mp2
采样频率:44.100000 kHz
码率:192 kbps
*************************
观测第 5 帧
分配比特预算:5016 bits
***************** 比例因子 *****************
子带 1: 9 9 9 | 9 9 9
子带 2: 8 8 8 | 8 8 8
子带 3: 8 8 8 | 9 9 9
子带 4: 13 9 9 | 9 9 9
子带 5: 8 8 8 | 9 9 9
子带 6: 10 10 10 | 9 9 9
子带 7: 8 8 8 | 8 8 8
子带 8: 10 10 10 | 11 8 13
子带 9: 9 9 9 | 10 10 10
子带 10: 9 9 9 | 12 9 9
子带 11: 9 9 9 | 9 9 9
子带 12: 9 9 9 | 9 9 9
子带 13: 9 9 9 | 8 8 8
子带 14: 9 9 9 | 9 9 7
子带 15: 9 9 9 | 8 8 8
子带 16: 9 9 9 | 10 10 10
子带 17: 8 8 8 | 10 10 10
子带 18: 7 9 9 | 8 8 8
子带 19: 8 8 8 | 8 10 10
子带 20: 10 10 10 | 9 9 8
子带 21: 9 9 9 | 9 9 9
子带 22: 9 9 9 | 12 9 9
子带 23: 9 9 9 | 8 8 8
子带 24: 9 9 9 | 10 10 10
子带 25: 8 8 8 | 10 10 10
子带 26: 8 8 8 | 9 10 10
子带 27: 9 9 9 | 8 8 8
子带 28: 8 8 8 | 10 10 10
子带 29: 9 9 9 | 8 8 8
子带 30: 9 9 9 | 9 9 9
******** 比特分配表 ********
子带 1: 5 | 5
子带 2: 4 | 4
子带 3: 3 | 3
子带 4: 4 | 5
子带 5: 5 | 5
子带 6: 4 | 4
子带 7: 4 | 5
子带 8: 3 | 3
子带 9: 4 | 4
子带 10: 4 | 4
子带 11: 3 | 3
子带 12: 4 | 4
子带 13: 3 | 3
子带 14: 3 | 3
子带 15: 4 | 4
子带 16: 2 | 2
子带 17: 3 | 3
子带 18: 4 | 4
子带 19: 3 | 3
子带 20: 1 | 1
子带 21: 1 | 1
子带 22: 1 | 1
子带 23: 2 | 2
子带 24: 2 | 2
子带 25: 2 | 2
子带 26: 1 | 1
子带 27: 1 | 1
子带 28: 1 | 1
子带 29: 1 | 1
子带 30: 1 | 1
*************************
源文件:Night.mp3
输出文件:Nightout.mp2
采样频率:44.100000 kHz
码率:192 kbps
*************************
观测第 5 帧
分配比特预算:5016 bits
***************** 比例因子 *****************
子带 1: 10 10 10 | 9 9 9
子带 2: 9 9 9 | 8 8 8
子带 3: 9 9 9 | 9 9 9
子带 4: 8 8 8 | 8 8 8
子带 5: 9 9 9 | 9 9 9
子带 6: 9 9 9 | 9 9 9
子带 7: 10 7 11 | 7 7 7
子带 8: 9 9 9 | 8 8 8
子带 9: 9 9 9 | 9 9 9
子带 10: 9 9 9 | 12 11 8
子带 11: 12 9 9 | 9 9 9
子带 12: 8 8 8 | 8 8 8
子带 13: 10 10 7 | 10 10 10
子带 14: 9 9 9 | 9 9 9
子带 15: 9 9 9 | 9 9 9
子带 16: 8 8 8 | 10 10 10
子带 17: 8 11 11 | 8 8 8
子带 18: 9 9 9 | 11 10 7
子带 19: 8 8 8 | 9 9 9
子带 20: 9 9 9 | 9 9 9
子带 21: 10 10 10 | 11 11 11
子带 22: 9 9 9 | 12 8 8
子带 23: 11 11 11 | 11 8 8
子带 24: 9 9 9 | 10 10 10
子带 25: 9 9 9 | 8 8 12
子带 26: 9 9 9 | 10 10 10
子带 27: 9 9 9 | 9 9 9
子带 28: 9 9 9 | 9 9 9
子带 29: 9 9 9 | 10 10 10
子带 30: 9 9 9 | 9 9 9
******** 比特分配表 ********
子带 1: 4 | 5
子带 2: 3 | 4
子带 3: 3 | 3
子带 4: 5 | 5
子带 5: 4 | 3
子带 6: 3 | 3
子带 7: 4 | 4
子带 8: 3 | 3
子带 9: 3 | 3
子带 10: 3 | 3
子带 11: 2 | 2
子带 12: 4 | 4
子带 13: 3 | 3
子带 14: 2 | 2
子带 15: 3 | 3
子带 16: 1 | 1
子带 17: 2 | 2
子带 18: 3 | 3
子带 19: 2 | 2
子带 20: 1 | 1
子带 21: 0 | 0
子带 22: 1 | 1
子带 23: 2 | 2
子带 24: 1 | 1
子带 25: 1 | 1
子带 26: 1 | 1
子带 27: 1 | 1
子带 28: 1 | 1
子带 29: 1 | 1
子带 30: 1 | 1
*************************
源文件:Oops.mp3
输出文件:Oopsout.mp2
采样频率:44.100000 kHz
码率:192 kbps
*************************
观测第 5 帧
分配比特预算:5016 bits
***************** 比例因子 *****************
子带 1: 10 10 10 | 9 9 9
子带 2: 9 9 9 | 9 9 9
子带 3: 9 9 9 | 9 9 9
子带 4: 9 9 9 | 9 9 9
子带 5: 11 8 8 | 9 9 9
子带 6: 9 11 11 | 9 9 9
子带 7: 8 8 8 | 7 7 7
子带 8: 10 10 10 | 9 9 9
子带 9: 9 9 9 | 9 9 9
子带 10: 10 10 10 | 9 9 9
子带 11: 9 9 9 | 12 8 11
子带 12: 8 8 8 | 8 8 8
子带 13: 9 9 9 | 9 9 9
子带 14: 10 10 10 | 8 8 8
子带 15: 8 8 8 | 11 8 11
子带 16: 10 10 10 | 8 8 8
子带 17: 8 8 8 | 10 10 8
子带 18: 9 9 9 | 9 9 9
子带 19: 8 11 11 | 9 9 9
子带 20: 8 8 8 | 8 8 8
子带 21: 8 8 8 | 13 10 10
子带 22: 8 8 8 | 8 8 8
子带 23: 8 8 8 | 11 8 11
子带 24: 8 8 8 | 12 9 9
子带 25: 9 9 9 | 9 9 9
子带 26: 9 9 9 | 10 10 10
子带 27: 9 9 9 | 9 10 10
子带 28: 8 8 8 | 9 9 9
子带 29: 8 8 8 | 9 9 9
子带 30: 9 9 9 | 9 9 9
******** 比特分配表 ********
子带 1: 5 | 5
子带 2: 3 | 4
子带 3: 5 | 5
子带 4: 4 | 4
子带 5: 4 | 4
子带 6: 4 | 5
子带 7: 4 | 4
子带 8: 2 | 3
子带 9: 3 | 4
子带 10: 4 | 4
子带 11: 2 | 3
子带 12: 3 | 4
子带 13: 2 | 2
子带 14: 2 | 3
子带 15: 3 | 3
子带 16: 2 | 2
子带 17: 1 | 2
子带 18: 1 | 2
子带 19: 1 | 1
子带 20: 0 | 1
子带 21: 1 | 1
子带 22: 0 | 0
子带 23: 0 | 0
子带 24: 0 | 0
子带 25: 0 | 0
子带 26: 1 | 0
子带 27: 0 | 0
子带 28: 0 | 0
子带 29: 0 | 0
子带 30: 0 | 0
*************************
源文件:Mixed.mp3
输出文件:Mixedout.mp2
采样频率:44.100000 kHz
码率:192 kbps
*************************
观测第 5 帧
分配比特预算:5016 bits
***************** 比例因子 *****************
子带 1: 9 9 9 | 9 9 9
子带 2: 9 9 9 | 8 8 8
子带 3: 12 8 11 | 9 9 9
子带 4: 9 9 9 | 8 8 8
子带 5: 9 9 9 | 10 10 10
子带 6: 10 10 10 | 9 9 9
子带 7: 12 8 8 | 9 9 9
子带 8: 8 11 14 | 8 12 12
子带 9: 11 8 8 | 9 9 9
子带 10: 8 8 8 | 8 8 8
子带 11: 9 9 9 | 11 8 8
子带 12: 9 9 9 | 8 8 8
子带 13: 8 8 8 | 7 7 7
子带 14: 9 9 9 | 8 8 8
子带 15: 9 9 9 | 8 8 8
子带 16: 9 9 9 | 12 8 8
子带 17: 9 9 9 | 8 8 8
子带 18: 10 10 10 | 10 10 10
子带 19: 8 8 8 | 9 9 9
子带 20: 9 9 9 | 9 9 9
子带 21: 9 9 9 | 9 9 8
子带 22: 9 9 9 | 10 10 10
子带 23: 7 9 9 | 10 10 10
子带 24: 8 8 8 | 9 9 9
子带 25: 9 9 9 | 7 7 7
子带 26: 10 10 10 | 12 8 8
子带 27: 8 8 8 | 9 9 9
子带 28: 7 7 7 | 8 8 8
子带 29: 10 10 10 | 10 10 10
子带 30: 11 8 8 | 9 9 9
******** 比特分配表 ********
子带 1: 5 | 4
子带 2: 4 | 5
子带 3: 3 | 3
子带 4: 5 | 5
子带 5: 4 | 4
子带 6: 4 | 4
子带 7: 4 | 3
子带 8: 3 | 4
子带 9: 4 | 4
子带 10: 4 | 4
子带 11: 3 | 3
子带 12: 4 | 4
子带 13: 4 | 4
子带 14: 3 | 3
子带 15: 3 | 3
子带 16: 2 | 2
子带 17: 2 | 2
子带 18: 3 | 3
子带 19: 3 | 3
子带 20: 2 | 2
子带 21: 1 | 1
子带 22: 1 | 1
子带 23: 3 | 3
子带 24: 2 | 2
子带 25: 2 | 2
子带 26: 1 | 1
子带 27: 1 | 1
子带 28: 2 | 2
子带 29: 1 | 1
子带 30: 1 | 1
从输出结果中,不难看出不同情况的音频确有不同的比特分配方式,但因为音频的采样率均为44100Hz,其分配帧的比特数是一致的。在实验前,曾设想结果中白噪声的高频和低频子带的比特差距应没有音乐(单一钢琴)的差距大,但结果不尽然。怀疑应该是因为本身使用的白噪声音频,其频带较窄,所以其与音乐的分配趋势大体相同。
而另一猜想则成立,即更为单一的突发噪声,因为频率成分无疑是三者中最为单一的,其比特分配就较为集中。