一、MPEG音频编码思想
1、整体概述
(1)基本思想
分析信号,去掉不能被感知的部分
(2)MPEG-1声音的主要性能
输入为PCM信号,采样率为32,44.1或48kHz,输出为32kbps到384kbps。
(3)三个独立的压缩层次
- Layer1:编码器最简单,384kbps(4:1, 用于小型数字盒带DCC,Compact Cassette)
- Layer2:编码器复杂程度中等,256kbps~192kbps(6: 1~8:1, 用于DAB、CD-I和VCD)
- Layer3:编码器最为复杂,64kbps,用于ISDN,网络 音频。
2、多相滤波器组(Polyphase Filter Bank)
分割子带。将PCM样本变换到32个子带的频域信号。
如果输入的采样频率为48kHz,那么子带的频率宽度为48/(2*32)=0.75Hz
(1)多项滤波器思路:
(2)缺点:
- 等带宽的滤波器组与人类听觉系统的临界频带不对应
在低频区域,单个子带会覆盖多个临界频带。在这种情况下,量化比特数不能兼每个临界频带 - 滤波器组与其逆过程不是无失真的
但滤波器组引入的误差差很小,且听不到 - 子带间频率有混叠
滤波后的相邻子带有频率混叠现象,一个子带中的信号可以影响相邻子带的输出
混叠:一个单频正弦信号输入可能在两个子带中产生非零信号
(3)与临界频带比较
3、心理声学模型(Psychoacoustic Model)
计算信号中不可听觉感知的部分,计算噪声遮蔽效应
MPEG-I 标准定义了两个模型
- 心理声学模型1
计算复杂度低,但对假设用户听不到的部分压缩太严重 - 心理声学模型2
提供了适合Layer III编码的更多特征 - 实际实现的模型复杂度取决所需要的压缩因子,如大的压缩因子不重要,则可以完全不用心理声学模型。此时位分配算法不使用SMR( Signal Mask Ratio ),而是使用SNR
4、比特分配器(Bit Allocator)
根据心理声学模型的计算结果,为每个子带信号分配比特数
(1)在调整到固定的码率之前
先确定可用于样值编码的有效比特数。这个数值取决于比例因子、比例因子选择信息、比特分配信息以及辅助数据所需比特数
(2)比特分配的过程
对每个子带计算掩蔽-噪声比MNR,是信噪比SNR与信掩比SMR之差,
即:MNR = SNR–SMR
噪声-掩蔽比(noise-to-mask ratio, NMR):NMR = SMR – SNR (dB)
其中SNR 由MPEG-I标准给定 (为量化水平的函数) ,NMR表示波形误差与感知测量之间的误差
(3)使整帧和每个子带的总噪声—掩蔽比最小
这是一个循环过程,每一次循环使获益最大的子带的量化级别增加一级,当然所用比特数不能超过一帧所能提供的最大数目
第1层一帧用4比特给每个子带的比特分配信息编码;而第2层只在低频段用4比特,高频段则用2比特
(4)循环,直到没有比特可用
对最高NMR的子带分配比特,使获益最大的子带的量化级别增加一级;
重新计算分配了更多比特子带的NMR。
5、装帧(Frame Creation):
产生MPEG-I兼容的比特流
(1) LayerⅠ装帧
- 每帧含384个样本数据。每帧由32个子带分别输出的12个样本值组成
- 以48kHz采样,一帧长为32×12×20.83µs =8ms。
(2)LayerⅡ装帧
每帧包含1152个样本。低、中、高频段对比特分配不同,分别用4、3、2比特。比特流中增加了一个比
特因子选择信息域,解码器根据这个域的信息可知道是否需要以及如何共享比例因子。
3 组/帧 x 12个样本/子带 x 32个子带/帧 = 1152个样本/帧 → 每个样本的overhead更少
6、量化
- Layer I:每个子带从相同的量化集合中选择
- Layer II: 根据采样和码率量化,不同子带可以从不同的量化器集合中选择,对量化级别在3、5、9级时,采用“颗粒” 优化 .
颗粒= 3 个样本,根据颗粒选择量化水平
某些(高频)子带的比特数可能为0
二、心理声学模型实现过程
1、将样本变换到频域
32个等分的子带信号并不能精确地反映人耳的听觉特性。引入FFT补偿频率分辨率不足的问题。
此处采用Hann加权和DFT
- Hann加权减少频域中的边界效应
- 此变换不同于多相滤波器组,因为模型需要更精细的频率分辨率,而且计算掩蔽阈值也需要每个频率 的幅值
模型1:采用512 (Layer I) 或1024 (Layers II and III)样本窗口
- Layer I:每帧384个样本点,512个样本点足够覆盖
- Layer II 和Layer III:每帧1152个样本点,每帧两次计算,
模型1选择两个信号掩蔽比(SMR)中较小的一个
2、确定声压级别
3、考虑安静时阈值
即绝对阈值。在标准中有根据输入PCM信号的采样率编制的“频率、临界频带率和绝对阈值”表。
此表为多位科学家经多次心理声学实验所得。
4、音频信号分解
将音频信号分解成“乐音(tones)” 和“非乐音/噪声”部分:因为两种信号的掩蔽能力不同
5、音调和非音调掩蔽成分的消除
利用标准中给出的绝对阈值消除被掩蔽成分;考虑在每个临界频带内,小于0.5Bark的距离中只保留最高功率的成分
6、单个掩蔽阈值的计算
音调成分和非音调成分单个掩蔽阈值根据标准中给出的算法求得
7、全局掩蔽阈值的计算
还要考虑别的临界频带的影响。一个掩蔽信号会对其它频带上的信号产生掩蔽效应。这种掩蔽效应称为掩蔽扩散。
8、每个子带的掩蔽阈值
选择出本子带中最小的阈值作为子带阈值
对高频不正确——高频区的临界频带很宽,可能跨越多个子带,从而导致模型1将临界带宽内所有的非音调部分集中为一个代表频率,当一个子带在很宽的频带内却远离代表频率时,无法得到准确的非音调掩蔽值。但计算量低
9、计算每个子带信号掩蔽比(signal-to-mask ratio, SMR)
SMR = 信号能量 / 掩蔽阈值(SMR传递给编码单元)
三、人耳听觉系统
人类听觉系统大致等效于一个信号通过一组并联的不同中心频率的带通滤波器
➢ 中心频率与信号频率相同的滤波器具有最大响应;中心频率偏离信号频率较多的滤波器不会产生响应。
➢ 在0Hz到20KHz频率范围内由25个重叠的带通滤波器组成的滤波器组
➢ 听音者在噪声中听某一纯音信号时,只启用中心频率与信号频率相同的那个听觉滤波器,纯音信号通过该滤波器,而噪声信号只有通带范围内的部分信号能通过,通带以外的频率成分则被抑制,只有通过该滤波器的噪声才对掩蔽起作用。
➢ 聆听复音时启动多个听觉滤波器。听觉能够计算各滤波器输出端的信噪比。当信噪比达到或者超过听阈因子时,即可听到该频率成分
1、掩蔽
掩蔽声对被掩蔽声的掩蔽效应,取决于两者的频率与强度的关系
- 噪声掩蔽纯音时,只有以纯音频率为中心的,一定频带宽度内的噪声能量起掩蔽作用,超出该频带的噪声能量无掩蔽效应,称掩蔽的临界带宽。掩蔽说明了频率选择性的极限。
- 掩蔽纯音信号,理论上使用带宽范围等于其临界带宽1/3倍频程的窄带噪声,这是因为掩蔽的临界带宽稍窄,听起来接近于纯音,患者常常混淆纯音与掩蔽噪声,而以纯音频率为中心频率的1/3倍频程噪声,宽度略大于临界带宽,同样可以起到很好的掩蔽效果。但实际应用中根据IEC645规定,临床听力计上常使用约4/10倍频程的窄带噪声。
- 掩蔽言语声和短声等宽频谱信号,则常用白噪声。因此,在听觉诱发反应测试中,往往在健侧耳施加白噪声作为掩蔽噪声
- 由上图可以看出:
(1)在相同条件下,乐音掩蔽噪声比噪声掩蔽乐音要难
(2)在一个固定的频率下的音信号易被比该频率低的信号掩蔽
- 音乐与语音信号大都由一系列复杂的频谱分量构成,相应的这些多个掩蔽分量也会相互影响并最终获得一个整体的掩蔽阈值。
- 对于多个掩蔽分量的综合掩蔽效果,目前有多种模型给出了描述方案 。
Lutfi 对多个掩蔽音同时存在时的综合掩蔽效果进行了研究:
每个掩蔽音的掩蔽效果先独立变换然后再线性相加
综合掩蔽效果与各掩蔽信号的声音级以及掩蔽信号的个数都有关系。如何整合掩蔽音?
➢ 当两个信号重叠并落在一个临界频带中时,二者的掩蔽分量可以线性相加。
➢对于复杂音频信号可将其频谱分割成一系列离散段,每段就是一个掩蔽信号。各掩蔽音互不重叠,即以一个临界带为单位。各掩蔽音的声压级则通过将对应的临界频带上的短时功率谱密度线性相加得到
2、听觉阈值
我们可以得出以下结论:
- 两个声音响度级相同,但强度不一定相同,还与频率有关;
- 声压级越高,等响度曲线趋于平坦;
- 人耳对3~4KHz的声音感觉最灵敏
3、频域掩蔽效应
频域掩蔽域随声压级变化曲线
如果有多个频率成分的复杂信号存在,那么频谱的总掩蔽阈值与频率的关系取决于各掩蔽音的强度、频率和它们之间的距离。
4、临界频带
**临界频带(Critical Band)**是指当某个纯音被以它为中心频率、且具有一定带宽的连续噪声所掩蔽时,如果该纯音刚好被听到时的功率等于这一频带内的噪声功率,这个带宽为临界频带宽度。
通常认为从20Hz到16kHz有25个临界频带,单位为bark,
1 Bark = 一个临界频带的宽度
四、实验代码
1、定义宏
#define FRAME_TRACE 1
2、进行音频信息打印
打印输入文件、输出文件、输出音频的采样率和目标码率等信息
void print_config (frame_info * frame, int *psy, char *inPath,
char *outPath)
{
frame_header *header = frame->header;
if (glopts.verbosity == 0)
return;
fprintf (stderr, "--------------------------------------------\n");
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]);
fprintf (stderr, "%s ", version_names[header->version]);
if (header->mode != MPG_MD_JOINT_STEREO)
fprintf (stderr, "Layer II %s Psycho model=%d (Mode_Extension=%d)\n",
mode_names[header->mode], *psy, header->mode_ext);
else
fprintf (stderr, "Layer II %s Psy model %d \n", mode_names[header->mode],
*psy);
fprintf (stderr, "[De-emph:%s\tCopyright:%s\tOriginal:%s\tCRC:%s]\n",
((header->emphasis) ? "On" : "Off"),
((header->copyright) ? "Yes" : "No"),
((header->original) ? "Yes" : "No"),
((header->error_protection) ? "On" : "Off"));
fprintf (stderr, "[Padding:%s\tByte-swap:%s\tChanswap:%s\tDAB:%s]\n",
((glopts.usepadbit) ? "Normal" : "Off"),
((glopts.byteswap) ? "On" : "Off"),
((glopts.channelswap) ? "On" : "Off"),
((glopts.dab) ? "On" : "Off"));
if (glopts.vbr == TRUE)
fprintf (stderr, "VBR Enabled. Using MNR boost of %f\n", glopts.vbrlevel);
fprintf(stderr,"ATH adjustment %f\n",glopts.athlevel);
fprintf (stderr, "--------------------------------------------\n");
#if FRAME_TRACE
FILE* output;
output = fopen("output.txt", "a");
fprintf(output, "========== 基本信息 ==========\n");
fprintf(output, "输入文件:%s\n", inPath);
fprintf(output, "输出文件:%s\n", outPath);
fprintf(output, "采样频率:%.1f kHz\n", s_freq[header->version][header->sampling_frequency]);
fprintf(output, "输出文件码率:%d kbps\n", bitrate[header->version][header->bitrate_index]);
fclose(output);
#endif // FRAME_TRACE
}
3、输出数据帧的信息
对于某个数据帧,输出
- 该帧所分配的比特数
- 该帧的比例因子
- 该帧的比特分配结果
在main()
中添加FRAME_TRACE
执行时的代码行,见if FRAME_TRACE
int main (int argc, char **argv)
{
typedef double SBS[2][3][SCALE_BLOCK][SBLIMIT];
SBS *sb_sample;
typedef double JSBS[3][SCALE_BLOCK][SBLIMIT];
JSBS *j_sample;
typedef double IN[2][HAN_SIZE];
IN *win_que;
typedef unsigned int SUB[2][3][SCALE_BLOCK][SBLIMIT];
SUB *subband;
frame_info frame;
frame_header header;
char original_file_name[MAX_NAME_SIZE];
char encoded_file_name[MAX_NAME_SIZE];
short **win_buf;
static short buffer[2][1152];
static unsigned int bit_alloc[2][SBLIMIT], scfsi[2][SBLIMIT];
static unsigned int scalar[2][3][SBLIMIT], j_scale[3][SBLIMIT];
static double smr[2][SBLIMIT], lgmin[2][SBLIMIT], max_sc[2][SBLIMIT];
// FLOAT snr32[32];
short sam[2][1344]; /* was [1056]; */
int model, nch, error_protection;
static unsigned int crc;
int sb, ch, adb;
unsigned long frameBits, sentBits = 0;
unsigned long num_samples;
int lg_frame;
int i;
/* Used to keep the SNR values for the fast/quick psy models */
static FLOAT smrdef[2][32];
static int psycount = 0;
extern int minimum;
time_t start_time, end_time;
int total_time;
sb_sample = (SBS *) mem_alloc (sizeof (SBS), "sb_sample");
j_sample = (JSBS *) mem_alloc (sizeof (JSBS), "j_sample");
win_que = (IN *) mem_alloc (sizeof (IN), "Win_que");
subband = (SUB *) mem_alloc (sizeof (SUB), "subband");
win_buf = (short **) mem_alloc (sizeof (short *) * 2, "win_buf");
/* clear buffers */
memset ((char *) buffer, 0, sizeof (buffer));
memset ((char *) bit_alloc, 0, sizeof (bit_alloc));
memset ((char *) scalar, 0, sizeof (scalar));
memset ((char *) j_scale, 0, sizeof (j_scale));
memset ((char *) scfsi, 0, sizeof (scfsi));
memset ((char *) smr, 0, sizeof (smr));
memset ((char *) lgmin, 0, sizeof (lgmin));
memset ((char *) max_sc, 0, sizeof (max_sc));
//memset ((char *) snr32, 0, sizeof (snr32));
memset ((char *) sam, 0, sizeof (sam));
global_init ();
header.extension = 0;
frame.header = &header;
frame.tab_num = -1; /* no table loaded */
frame.alloc = NULL;
header.version = MPEG_AUDIO_ID; /* Default: MPEG-1 */
total_time = 0;
time(&start_time);
programName = argv[0];
if (argc == 1) /* no command-line args */
short_usage ();
else
parse_args (argc, argv, &frame, &model, &num_samples, original_file_name,
encoded_file_name);
prin