数据压缩(十三)——MPEG音频编码原理及编码器调试


一、MPEG音频编码原理

在这里插入图片描述

1.1 基本思想

  分析信号,去掉不能被感知的部分【声音压缩算法可以确立这种特性的模型来取消更多的冗余数据】

  • 子带分析滤波器组:使信号具有高的时间分辨率【短暂冲击信号情况下,编码的声音信号具有足够高的质量】
  • FFT运算:使信号具有高的频率分辨率
  • 比特分配:低频子带分配较多的位数【保护音调和共振峰的结构】;高频自带分配较少的位数【摩擦音和类似噪声的声音】

1.2 心理声学模型(Psychoacoustic model)

  • 生理(Physiological)感知极限(传感极限)
  • 心理(Psychological)感知极限(信号处理极限)

1.2.1 听觉阈值

听觉系统中存在一个听觉阈值电平,低于这个电平的声音信号就听不到

在这里插入图片描述

  • 听觉阈值的大小随声音频率的改变而改变
  • 一个人是否听到声音取决于声音的频率,以及声音的幅度是否高于这种频率下的听觉阈值。

1.2.2 频域掩蔽

听觉阈值电平是自适应的,会随听到的不同频率声音而发生变化

在这里插入图片描述
  如果有多个频率成分的复杂信号存在,那么频谱的总掩蔽阈值与频率的关系取决于各掩蔽音的强度、频率和它们之间的距离。

1.3 临界频带(Critical Band)

  临界频带是指当某个纯音被以它为中心频率、且具有一定带 宽的连续噪声所掩蔽时,如果该纯音刚好被听到时的功率等于这一频带内的噪声功率,这个带宽为临界频带宽度。
1 B a r k = { f 100 , f o r f r e q u e n c i e s f < 500 H z 9 + 4 l o g ( f 1000 ) , f o r f r e q u e n c i e s f ≥ 500 H z 1 Bark=\left\{ \begin{aligned} &\frac{f}{100},&&for frequencies&& f<500Hz \\ &9+4log(\frac{f}{1000}),&& for frequencies&& f \geq 500Hz\\ \end{aligned} \right. 1Bark=100f,9+4log(1000f),forfrequenciesforfrequenciesf<500Hzf500Hz

1.4 人耳听觉系统

  人类听觉系统大致等效于一个信号通过一组并联的不同中心频率的带通滤波器。

  • 听音者在噪声中听某一纯音信号时,只启用中心频率与信号频率相同的那个听觉滤波器。纯音信号通过该滤波器,而噪声信号只有通带范围内的部分信号能通过,通带以外的频率成分则被抑制,只有通过该滤波器的噪声才对掩蔽起作用。
  • 聆听复音时启动多个听觉滤波器。听觉能够计算各滤波器输出端的信噪比。当信噪比达到或者超过听阈因子时,即可听到该频率成分。
    在这里插入图片描述

1.5 掩蔽效果的加和

  Lutfi对多个掩蔽音同时存在时的综合掩蔽效果进行了研究: 每个掩蔽音的掩蔽效果先独立变换然后再线性相加。
F ( M A B = F ( M A ) + F ( M B ) F(M_{AB}=F(M_A)+F(M_B) F(MAB=F(MA)+F(MB) F ( M A ) = ( 1 0 M A / 10 ) p F(M_A)=(10^{M_A/10})^p F(MA)=(10MA/10)p

  1. 当两个信号重叠并落在一个临界频带中时,二者的掩蔽分量可以线 性相加。
  2. 对于复杂音频信号可将其频谱分割成一系列离散段,每段就是一个掩蔽信号。各掩蔽音互不重叠,即以一个临界带为单位。各掩蔽音的声压级则通过将对应的临界频带上的短时功率谱密度线性相加得到 。

二、MPEG音频压缩编码器

输入声音信号经过一个多相滤波器组,变换到多个子带,同时经过“心理声学模型”计算以频率为自变量的噪声掩蔽阈值。量化和编码部分用信掩比SMR决定分配给子带信号的量化位数,使量化噪声<掩蔽阈值。最后通过数据帧包装 将量化的子带样本和其它数据按照规定的帧格式组装成比特数据流。

2.1 多相滤波器组(Polyphase Filter Bank)

  先分成32个相等的子带。
在这里插入图片描述
  512个输入样本空间的FIFO。
  新输入的32个样本在最低端。
多相滤波器组的缺点

  • 等带宽的滤波器组与人类听觉系统的临界频带不对应【在低频区域,单个子带会覆盖多个临界频带。】。
  • 滤波器组与其逆过程不是无失真的【但滤波器组引入的误差很小,且听不到】
  • 子带间频率有混叠【滤波后的相邻子带有频率混叠现象,一个子带中的信号可以影响相邻子带的输出】

2.2 心理声学模型

  MPEG-1标准定义了两个模型。
心理声学模型 1:

  • 计算复杂度低
  • 但对假设用户听不到的部分压缩太严重

心理声学模型 2:

  • 提供了适合Layer 3编码的更多特征

  实习实现的模型复杂度取决于所需要的压缩因子。

2.3 量化和编码

2.3.1 比例因子的取值和编码

  对各个子带每12个样点进行一次比例因子计算【查表】。
  第二层中一帧对应36个子带样值,原则上要传送3个比例因子。
  为了降低比例因子的传输码率,每帧中每个子带的三个比例因子被划分成特定的几种模式,根据这些模式,1个、2个或3个比例因子和比例因子选择信息(每子带2比特)一起被传送。如 果一个比例因子和下一个只有很小的差别,就只传送大的一个【这种情况在稳态信号中经常出现】。

2.3.2 比特分配及编码

  • 对每个子带计算掩蔽-噪声比MNR M N R = S N R ( 信 噪 比 ) − S M R ( 信 掩 比 ) MNR=SNR(信噪比)-SMR(信掩比) MNR=SNR()SMR()
  • 使整个一帧和每个子带的总噪声-掩蔽比最小
    循环过程:每一次循环给获益最大的子带分配一个比特】
    第一层 1帧用4比特给每个子带的比特分配信息编码;第二层只在低频段用4比特,高频段用2比特】

  比例因子的作用是充分利用量化器的量化范围, 通过比特分配和比例因子相配合,可以表示动态范围超过120dB的样本 。

2.3.3 数据帧包装

  以层2为例:
在这里插入图片描述

  • 帧头(Header):每帧开始的头32个比特,包含有同步和状态比特流信息,在所有层都相同,同步码字为12bit全1码。
  • 误码检测CRC:使用一种16bit奇偶校验字,可供在比特流中作检测误码用。
  • 声音数据:由比特分配表、比例因子选择信息、比例因子和子带样值组成,其中子带样值是声音数据的最大部分。
  • 辅助数据:用作辅助数据比特流。

三、MPEG音频编码器调试

  调试的第一步自然是跑通代码弄清楚输入参数。代码在第一次编译的时候有报错,是编译环境的问题,直接复制报错信息google一下就可以解决,在此不进行说明了。
  然后运行一下代码,得到如下结果:
在这里插入图片描述
  按照提示信息所说,必须要输入的就只有输入文件的名字,所以,如下图所示输入命令并得到结果:
在这里插入图片描述

3.1 理解程序设计的整体框架

  通过main()函数上方的注释可以很方便的知道程序设计的框架。


/************************************************************************
*
* main
*
* PURPOSE:  MPEG II Encoder with
* psychoacoustic models 1 (MUSICAM) and 2 (AT&T)//心理声学模型
*
* SEMANTICS:  One overlapping frame of audio of up to 2 channels are
* processed at a time in the following order:
* (associated routines are in parentheses)//最多按以下顺序一次处理最多2个通道的一个重叠音频帧
*
* 1.  Filter sliding window of data to get 32 subband
* samples per channel.
* (window_subband,filter_subband)//过滤数据滑动窗口以获取每个通道32个子带样本
*
* 2.  If joint stereo mode, combine left and right channels
* for subbands above #jsbound#.//如果是联合立体声模式,请为#jsbound#以上的子带组合左右声道
* (combine_LR)
*
* 3.  Calculate scalefactors for the frame, and 
* also calculate scalefactor select information.//计算比例因子,并计算比例因子选择信息。
* (*_scale_factor_calc)
*
* 4.  Calculate psychoacoustic masking levels using selected
* psychoacoustic model.//使用选定的心理声学模型计算心理声学掩蔽电平。
* (psycho_i, psycho_ii)
*
* 5.  Perform iterative bit allocation for subbands with low
* mask_to_noise ratios using masking levels from step 4.//使用步骤4中的掩蔽电平,对具有低掩蔽比的子带执行迭代位分配。
* (*_main_bit_allocation)
*
* 6.  If error protection flag is active, add redundancy for
* error protection.//若是需要,就添加CRC纠错
* (*_CRC_calc)
*
* 7.  Pack bit allocation, scalefactors, and scalefactor select
*headerrmation onto bitstream.//将比特分配,比例因子和比例因子打包到比特流中。
* (*_encode_bit_alloc,*_encode_scale,transmission_pattern)
*
* 8.  Quantize subbands and pack them into bitstream//量化子带并将其打包成比特流
* (*_subband_quantization, *_sample_encoding)
*
************************************************************************/


整理成有序列表如下

  1. 对滑动窗口内的音频信号进行了32子带滤波;
  2. 如果有立体声,就加入左右声道;
  3. 计算这一帧的比例因子及比例因子选择信息‘
  4. 使用选定的心理声学模型计算心理声学掩蔽水平;
  5. 使用步骤4中的掩蔽电平,对具有低掩蔽比的子带执行迭代位分配;
  6. 若是需要,就添加CRC纠错;
  7. 将比特分配,比例因子和比例因子信息打包到比特流中;
  8. 量化子带并将其打包成比特流

  音频编码和视频编码不同,没有反馈的机制,只是一条顺序过程走下去,所以比之视频编码来的相对较为容易。

main()函数框架分析:

int main(int argc, char** argv)
{
	/********************************以上都是初始化,不需要过多关注***************************************************/
	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);//解析命令行参数
	print_config(&frame, &model, original_file_name, encoded_file_name);//输出一些配置信息

	/* this will load the alloc tables and do some other stuff */
	hdr_to_frps(&frame);//把从信息头中解压出来的信息加载
	nch = frame.nch;
	error_protection = header.error_protection;


	//while里面是获取的每一帧的信息
	while (get_audio(musicin, buffer, num_samples, nch, &header) > 0) {
		if (glopts.verbosity > 1)
			if (++frameNum % 10 == 0)
				fprintf(stderr, "[%4u]\r", frameNum);
		fflush(stderr);
		win_buf[0] = &buffer[0][0];
		win_buf[1] = &buffer[1][0];

		adb = available_bits(&header, &glopts);//bit预算
		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 gr, bl, ch;
			/* New polyphase filter
		   Combines windowing and filtering. Ricardo Feb'03 */
			for (gr = 0; gr < 3; gr++)
				for (bl = 0; bl < 12; bl++)
					for (ch = 0; ch < nch; ch++)
						WindowFilterSubband(&buffer[ch][gr * 12 * 32 + 32 * bl], ch,
							&(*sb_sample)[ch][gr][bl][0]);//多相滤波器组
		}
		scale_factor_calc(*sb_sample, scalar, nch, frame.sblimit);//计算比例因子
		pick_scale(scalar, &frame, max_sc);
		if (frame.actual_mode == MPG_MD_JOINT_STEREO) {
			/* this way we calculate more mono than we need */
			/* but it is cheap */
			combine_LR(*sb_sample, *j_sample, frame.sblimit);
			scale_factor_calc(j_sample, &j_scale, 1, frame.sblimit);//计算比例因子选择信息
		}

		if ((glopts.quickmode == TRUE) && (++psycount % glopts.quickcount != 0)) {
			/* We're using quick mode, so we're only calculating the model every
			   'quickcount' frames. Otherwise, just copy the old ones across */
			for (ch = 0; ch < nch; ch++) {
				for (sb = 0; sb < SBLIMIT; sb++)
					smr[ch][sb] = smrdef[ch][sb];
			}
		}
		else
		{
			/* calculate the psymodel */
			//根据心理声学模型计算掩蔽电平
			switch (model) {
			case -1:
				psycho_n1(smr, nch);
				break;
			case 0:	/* Psy Model A */ //心理声学模型A计算掩蔽电平
				psycho_0(smr, nch, scalar, (FLOAT)s_freq[header.version][header.sampling_frequency] * 1000);//心理声学模型的输出一定是smr
				break;
			case 1:
				psycho_1(buffer, max_sc, smr, &frame);
				break;
			case 2:
				for (ch = 0; ch < nch; ch++) {
					psycho_2(&buffer[ch][0], &sam[ch][0], ch, &smr[ch][0], //snr32,
						(FLOAT)s_freq[header.version][header.sampling_frequency] *
						1000, &glopts);
				}
				break;
			case 3:
				/* Modified psy model 1 */
				psycho_3(buffer, max_sc, smr, &frame, &glopts);
				break;
			case 4:
				/* Modified Psycho Model 2 */
				for (ch = 0; ch < nch; ch++) {
					psycho_4(&buffer[ch][0], &sam[ch][0], ch, &smr[ch][0], // snr32,
						(FLOAT)s_freq[header.version][header.sampling_frequency] *
						1000, &glopts);
				}
				break;
			case 5:
				/* Model 5 comparse model 1 and 3 */
				psycho_1(buffer, max_sc, smr, &frame);
				fprintf(stdout, "1 ");
				smr_dump(smr, nch);
				psycho_3(buffer, max_sc, smr, &frame, &glopts);
				fprintf(stdout, "3 ");
				smr_dump(smr, nch);
				break;
			case 6:
				/* Model 6 compares model 2 and 4 */
				for (ch = 0; ch < nch; ch++)
					psycho_2(&buffer[ch][0], &sam[ch][0], ch, &smr[ch][0], //snr32,
						(FLOAT)s_freq[header.version][header.sampling_frequency] *
						1000, &glopts);
				fprintf(stdout, "2 ");
				smr_dump(smr, nch);
				for (ch = 0; ch < nch; ch++)
					psycho_4(&buffer[ch][0], &sam[ch][0], ch, &smr[ch][0], // snr32,
						(FLOAT)s_freq[header.version][header.sampling_frequency] *
						1000, &glopts);
				fprintf(stdout, "4 ");
				smr_dump(smr, nch);
				break;
			case 7:
				fprintf(stdout, "Frame: %i\n", frameNum);
				/* Dump the SMRs for all models */
				psycho_1(buffer, max_sc, smr, &frame);
				fprintf(stdout, "1");
				smr_dump(smr, nch);
				psycho_3(buffer, max_sc, smr, &frame, &glopts);
				fprintf(stdout, "3");
				smr_dump(smr, nch);
				for (ch = 0; ch < nch; ch++)
					psycho_2(&buffer[ch][0], &sam[ch][0], ch, &smr[ch][0], //snr32,
						(FLOAT)s_freq[header.version][header.sampling_frequency] *
						1000, &glopts);
				fprintf(stdout, "2");
				smr_dump(smr, nch);
				for (ch = 0; ch < nch; ch++)
					psycho_4(&buffer[ch][0], &sam[ch][0], ch, &smr[ch][0], // snr32,
						(FLOAT)s_freq[header.version][header.sampling_frequency] *
						1000, &glopts);
				fprintf(stdout, "4");
				smr_dump(smr, nch);
				break;
			case 8:
				/* Compare 0 and 4 */
				psycho_n1(smr, nch);
				fprintf(stdout, "0");
				smr_dump(smr, nch);

				for (ch = 0; ch < nch; ch++)
					psycho_4(&buffer[ch][0], &sam[ch][0], ch, &smr[ch][0], // snr32,
						(FLOAT)s_freq[header.version][header.sampling_frequency] *
						1000, &glopts);
				fprintf(stdout, "4");
				smr_dump(smr, nch);
				break;
			default:
				fprintf(stderr, "Invalid psy model specification: %i\n", model);
				exit(0);
			}

			if (glopts.quickmode == TRUE)
				/* copy the smr values and reuse them later */
				for (ch = 0; ch < nch; ch++) {
					for (sb = 0; sb < SBLIMIT; sb++)
						smrdef[ch][sb] = smr[ch][sb];
				}

			if (glopts.verbosity > 4)
				smr_dump(smr, nch);
		}
		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);//如果需要就添加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);//将子带打包到比特流中
		//后面是一些输出信息,也不需要过多关注
		...
  exit (0);
}

3.2 理解感知音频编码的设计思想

3.2.1 两条线

在这里插入图片描述
  从流程图来看,MPEG音频编码分为清晰的上下两条线。上面一条线是音频编码的主线部分,下面一条线的本质是为了帮助计算线性量化器的量化比特数。其中,下面一条线最为出彩的部分是心理声学模型的应用,它对去除冗余信息起到了极大的作用。

3.2.2 时-频分析的矛盾

  要感知时域的变化,就要增加时域的分辨力,但同时就会对频域的分辨力有所限制,反之亦然,时域分辨力和频域分辨力有着天然的矛盾,我们所能做的就是这两者中取一个中间方案。

3.3 理解心理声学模型的实现过程

3.3.1 临界频带的概念

  临界频带的概念在上文理论部分已经进行过说明,在此不再进行赘述。

3.3.2 比例因子的计算

  在上文中已经讲解过比例因子的计算过程,下面是其实现代码:

void scale_factor_calc(double sb_sample[][3][SCALE_BLOCK][SBLIMIT],
	unsigned int scalar[][3][SBLIMIT], int nch,
	int sblimit)
{
	//scalar就是得到的比例因子
	  /* Optimized to use binary search instead of linear scan through the
		 scalefactor table; guarantees to find scalefactor in only 5
		 jumps/comparisons and not in {0 (lin. best) to 63 (lin. worst)}.
		 Scalefactors for subbands > sblimit are no longer computed.
		 Uses a single sblimit-loop.
		 Patrick De Smet Oct 1999.
	   */
	int k, t;
	/* Using '--' loops to avoid possible "cmp value + bne/beq" compiler  */
	/* inefficiencies. Below loops should compile to "bne/beq" only code  */
	for (k = nch; k--;)
		for (t = 3; t--;) {
			int i;
			for (i = sblimit; i--;) {//寻找子带中的最大值
				int j;
				unsigned int l;
				register double temp;
				unsigned int scale_fac;
				/* Determination of max. over each set of 12 subband samples:  */
				/* PDS TODO: maybe this could/should ??!! be integrated into   */
				/* the subband filtering routines?                             */
				register double cur_max = fabs(sb_sample[k][t][SCALE_BLOCK - 1][i]);
				for (j = SCALE_BLOCK - 1; j--;) {
					if ((temp = fabs(sb_sample[k][t][j][i])) > cur_max)
						cur_max = temp;
				}
				/* PDS: binary search in the scalefactor table: */
				/* This is the real speed up: */
				for (l = 16, scale_fac = 32; l; l >>= 1) {//查找比例因子表中比这个最大值大的最小值作为比例因子
					if (cur_max <= multiple[scale_fac])
						scale_fac += l;
					else
						scale_fac -= l;
				}
				if (cur_max > multiple[scale_fac])
					scale_fac--;
				scalar[k][t][i] = scale_fac;//得到比例因子
			}
		}
}

3.3.3 掩蔽值计算的思路

  掩蔽值的计算是通过大量实验得到的经验计算函数,并没有什么严密的数学推导,是实验结果。下面是以心理声学模型A为例的掩蔽值计算代码:

void psycho_0(double SMR[2][SBLIMIT], int nch, unsigned int scalar[2][3][SBLIMIT], FLOAT sfreq) {
	int ch, sb, gr;
	int minscaleindex[2][SBLIMIT]; /* Smaller scale indexes mean bigger scalefactors */
	//较小的指标意味着较大的比例因子
	static FLOAT ath_min[SBLIMIT];
	int i;
	static int init = 0;

	if (!init) {
		FLOAT freqperline = sfreq / 1024.0;
		for (sb = 0; sb < SBLIMIT; sb++) {
			ath_min[sb] = 1000; /* set it huge */
		}

		/* Find the minimum ATH in each subband */
		//在每个子带中找到最小的ATH
		for (i = 0; i < 512; i++) {
			FLOAT thisfreq = i * freqperline;
			FLOAT ath_val = ATH_dB(thisfreq, 0);
			if (ath_val < ath_min[i >> 4])
				ath_min[i >> 4] = ath_val;
		}
		init++;
	}

	/* Find the minimum scalefactor index for each ch/sb */
	//找到这32个子带中最小的比例因子
	for (ch = 0; ch < nch; ch++)
		for (sb = 0; sb < SBLIMIT; sb++)
			minscaleindex[ch][sb] = scalar[ch][0][sb];

	for (ch = 0; ch < nch; ch++)
		for (gr = 1; gr < 3; gr++)
			for (sb = 0; sb < SBLIMIT; sb++)
				if (minscaleindex[ch][sb] > scalar[ch][gr][sb])
					minscaleindex[ch][sb] = scalar[ch][gr][sb];

	/* Oh yeah. Fudge the hell out of the SMR calculations
	   by combining the scalefactor table index and the min ATH in that subband
	   There are probably more elegant/correct ways of combining these values,
	   but who cares? It works pretty well
	   MFC Mar 03 */
	for (ch = 0; ch < nch; ch++)
		for (sb = 0; sb < SBLIMIT; sb++)
			SMR[ch][sb] = 2.0 * (30.0 - minscaleindex[ch][sb]) - ath_min[sb];
}

3.4 理解码率分配的实现思路

  比特分配的具体过程也已经在上文原理部分中有过提及,不再详述。以下是main()函数中实现码率分配的函数体定义和关键代码解释:

void main_bit_allocation(double perm_smr[2][SBLIMIT],
	unsigned int scfsi[2][SBLIMIT],
	unsigned int bit_alloc[2][SBLIMIT], int* adb,
	frame_info* frame, options* glopts)
{
	...
	//以上都是初始化,不需要过多关注
	/* decide on which bit allocation method to use */
	if (glopts->vbr == FALSE) {
		/* Just do the old bit allocation method */
		noisy_sbs = a_bit_allocation(perm_smr, scfsi, bit_alloc, adb, frame);
	}
	else {
		/* do the VBR bit allocation method */
		frame->header->bitrate_index = lower;
		*adb = available_bits(frame->header, glopts);
		{
			int brindex;
			int found = FALSE;

			/* Work out how many bits are needed for there to be no noise (ie all MNR > 0.0 + VBRLEVEL) */
			int req =
				VBR_bits_for_nonoise(perm_smr, scfsi, frame, glopts->vbrlevel);

			/* Look up this value in the bitrateindextobits table to find what bitrate we should use for
			   this frame */
			for (brindex = lower; brindex <= upper; brindex++) {
				if (bitrateindextobits[brindex] > req) {
					/* this method always *overestimates* the bits that are needed
					   i.e. it will usually  guess right but
					   when it's wrong it'll guess a higher bitrate than actually required.
					   e.g. on "messages from earth" track 6, the guess was
					   wrong on 75/36341 frames. each time it guessed higher.
					   MFC Feb 2003 */
					guessindex = brindex;
					found = TRUE;
					break;
				}
			}
			/* Just for sanity */
			if (found == FALSE)
				guessindex = upper;
		}

		frame->header->bitrate_index = guessindex;
		*adb = available_bits(frame->header, glopts);

		/* update the statistics */
		vbrstats[frame->header->bitrate_index]++;

		if (glopts->verbosity > 2) {
			/* print out the VBR stats every 1000th frame */
			static int count = 0;
			int i;
			if ((count++ % 1000) == 0) {
				for (i = 1; i < 15; i++)
					fprintf(stdout, "%4i ", vbrstats[i]);
				fprintf(stdout, "\n");
			}

			/* Print out *every* frames bitrateindex, bits required, and bits available at this bitrate */
			if (glopts->verbosity > 5)
				fprintf(stdout,
					"> bitrate index %2i has %i bits available to encode the %i bits\n",
					frame->header->bitrate_index, *adb,
					VBR_bits_for_nonoise(perm_smr, scfsi, frame,
						glopts->vbrlevel));

		}

		noisy_sbs =
			VBR_bit_allocation(perm_smr, scfsi, bit_alloc, adb, frame, glopts);
	}
}

  通过调试可以发现,本实验使用的比特分配的函数是a_bit_allocation,以下是这个函数体的定义和关键代码解释:

int a_bit_allocation(double perm_smr[2][SBLIMIT],
	unsigned int scfsi[2][SBLIMIT],
	unsigned int bit_alloc[2][SBLIMIT], int* adb,
	frame_info* frame)
{
	int i, min_ch, min_sb, oth_ch, k, increment, scale, seli, ba;
	int bspl, bscf, bsel, ad, bbal = 0;
	double mnr[2][SBLIMIT];
	char used[2][SBLIMIT];
	int nch = frame->nch;
	int sblimit = frame->sblimit;
	int jsbound = frame->jsbound;
	al_table* alloc = frame->alloc;
	static char init = 0;
	static int banc = 32, berr = 0;
	static int sfsPerScfsi[] = { 3, 2, 1, 2 };	/* lookup # sfs per scfsi */

	if (!init) {
		init = 1;
		if (frame->header->error_protection)
			berr = 16;		/* added 92-08-11 shn */
	}

	for (i = 0; i < jsbound; ++i)
		bbal += nch * (*alloc)[i][0].bits;
	for (i = jsbound; i < sblimit; ++i)
		bbal += (*alloc)[i][0].bits;
	*adb -= bbal + berr + banc;
	ad = *adb;

	for (i = 0; i < sblimit; i++)
		for (k = 0; k < nch; k++) {
			mnr[k][i] = snr[0] - perm_smr[k][i];
			bit_alloc[k][i] = 0;
			used[k][i] = 0;
		}
	bspl = bscf = bsel = 0;

	do {
		/* locate the subband with minimum SMR */
		//找到拥有最大MNR的子带
		maxmnr(mnr, used, sblimit, nch, &min_sb, &min_ch);

		if (min_sb > -1) {		/* there was something to find */
		  /* find increase in bit allocation in subband [min] */
			//计算拥有最大MNR的子带需要分配多少比特位才不是最大
			increment =
				SCALE_BLOCK * ((*alloc)[min_sb][bit_alloc[min_ch][min_sb] + 1].group *
					(*alloc)[min_sb][bit_alloc[min_ch][min_sb] + 1].bits);
			if (used[min_ch][min_sb])
				increment -=
				SCALE_BLOCK * ((*alloc)[min_sb][bit_alloc[min_ch][min_sb]].group *
					(*alloc)[min_sb][bit_alloc[min_ch][min_sb]].bits);

			/* scale factor bits required for subband [min] */
			oth_ch = 1 - min_ch;	/* above js bound, need both chans */
			if (used[min_ch][min_sb])
				scale = seli = 0;
			else {			/* this channel had no bits or scfs before */
				seli = 2;
				scale = 6 * sfsPerScfsi[scfsi[min_ch][min_sb]];
				if (nch == 2 && min_sb >= jsbound) {
					/* each new js sb has L+R scfsis */
					seli += 2;
					scale += 6 * sfsPerScfsi[scfsi[oth_ch][min_sb]];
				}
			}

			//检查剩下的可用比特位是否能够给出足够的比特位
			/* check to see enough bits were available for */
			/* increasing resolution in the minimum band */
			if (ad >= bspl + bscf + bsel + seli + scale + increment) {
				//如果够的话就给这个子带分配比特
				ba = ++bit_alloc[min_ch][min_sb];	/* next up alloc */
				bspl += increment;	/* bits for subband sample */
				bscf += scale;		/* bits for scale factor */
				bsel += seli;		/* bits for scfsi code */
				used[min_ch][min_sb] = 1;	/* subband has bits */
				mnr[min_ch][min_sb] =
					-perm_smr[min_ch][min_sb] + snr[(*alloc)[min_sb][ba].quant + 1];
				/* Check if subband has been fully allocated max bits */ //检查该子带已分配的比特数是否已经到达了最大值
				if (ba >= (1 << (*alloc)[min_sb][0].bits) - 1)
					used[min_ch][min_sb] = 2;	/* don't let this sb get any more bits */
												//如果已分配的比特数到达了最大值,那么就不能给他分配比特了
			}
			else
				used[min_ch][min_sb] = 2;	/* can't increase this alloc */ //不能分配比特位

			if (min_sb >= jsbound && nch == 2) {
				/* above jsbound, alloc applies L+R */
				ba = bit_alloc[oth_ch][min_sb] = bit_alloc[min_ch][min_sb];
				used[oth_ch][min_sb] = used[min_ch][min_sb];
				mnr[oth_ch][min_sb] =
					-perm_smr[oth_ch][min_sb] + snr[(*alloc)[min_sb][ba].quant + 1];
			}

		}
	} while (min_sb > -1);		/* until could find no channel */

	/* Calculate the number of bits left */
	//计算剩下的比特位的数目
	ad -= bspl + bscf + bsel;
	*adb = ad;
	for (k = 0; k < nch; k++)
		for (i = sblimit; i < SBLIMIT; i++)
			bit_alloc[k][i] = 0;
	return 0;
}

3.5 输出音频的采样率和目标码率

  先去原代码的输出函数中看看有没有需要的数据,可以发现音频采样率和目标码率都已经被输出了:

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");
}

在这里插入图片描述
  由上图可发现,音频的采样率为44.1KHz,目标码率为192Kbps。

3.6 某个数据帧,输出

  在进入循环前打开文件,main()函数末尾关闭文件。

int main(int argc, char** argv)
{
	...
	/* this will load the alloc tables and do some other stuff */
	hdr_to_frps(&frame);//把从信息头中解压出来的信息加载
	nch = frame.nch;
	error_protection = header.error_protection;

	//打开输出文件
	FILE* result;
	result = fopen("result.txt", "w+");

	//while里面是获取的每一帧的信息
	while (get_audio(musicin, buffer, num_samples, nch, &header) > 0) {
	...
	}
	...
	//关闭文件
	fclose(result);
	exit(0);
}

3.6.1 该帧所分配的比特数

  我们知道,在比特预算时,把计算所得的该帧总分配比特数赋给了adb,在adb获得值以后直接进行输出。

int main(int argc, char** argv)
{
	...
		adb = available_bits(&header, &glopts);//bit预算
		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;
		}
		
		if (frameNum == 1)
		{
			//输出可用比特数
			fprintf(result, "可用比特数:%d\n", adb);
		}
	...
}

3.6.2 该帧的比例因子

  通过调试,我们知道,实验中比例因子存放在scalar[][][]中,每个声道有SBLIMIT个子带,每个子带有3个比例因子。
  值得注意的是,比例因子存放数组的定义为scalar[2][3][SBLIMIT],所以在写循环的时候需要格外注意顺序。

int main(int argc, char** argv)
{
		...
		scale_factor_calc(*sb_sample, scalar, nch, frame.sblimit);//计算比例因子
		pick_scale(scalar, &frame, max_sc);
		if (frame.actual_mode == MPG_MD_JOINT_STEREO) {
			/* this way we calculate more mono than we need */
			/* but it is cheap */
			combine_LR(*sb_sample, *j_sample, frame.sblimit);
			scale_factor_calc(j_sample, &j_scale, 1, frame.sblimit);//计算比例因子选择信息
		}
		if (frameNum == 1)
		{
			//输出比例因子
			fprintf(result, "\n比例因子:\n");
			for (int k = 0; k < nch; k++)//声道
			{
				fprintf(result, "声道%d:\n", k + 1);
				for (int i = 0; i < SBLIMIT; i++)//每个子带有三个比例因子
				{
					fprintf(result, "子带%d:\t", i);
					for (int j = 0; j < 3; j++)
					{
						fprintf(result, "%d\t", scalar[k][j][i]);
					}
					fprintf(result, "\n");
				}
			}
		}
		...
}

3.6.3 该帧的比特分配结果

  通过调试,我们知道,比特分配的结果存放在bit_alloc[][]中。每个声道有SBLIMIT个子带,每个子带有一个比特分配的结果。

int main(int argc, char** argv)
{
		...
		transmission_pattern(scalar, scfsi, &frame);
		main_bit_allocation(smr, scfsi, bit_alloc, &adb, &frame, &glopts);//进行比特分配
		if (frameNum == 1)
		{
			//输出比特分配结果
			fprintf(result, "\n比特分配\n");
			for (int k = 0; k < nch; k++)//声道
			{
				fprintf(result, "声道%d:\n", k + 1);
				for (int i = 0; i < SBLIMIT; i++)//每个子带有一个比特分配结果
				{
					fprintf(result, "子带%d:\t", i);
					fprintf(result, "%d\n", bit_alloc[k][i]);
				}
			}
		}
		...
}

3.6.4 输出result文件结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  至此,实验结束。

  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值