MPEG-1 Audio Layer II編碼原理及編碼器調試

MPEG-1 Audio Layer II編碼原理及編碼器調試

模擬音頻信號數字化後通常具有極高的碼率,例如對於取樣頻率爲44.1 kHz的雙聲道CD數字音頻而言,採用16 bit量化,無壓縮碼率可達1.41 Mbps,非常不利於存儲和傳輸。因此,爲了節省存儲空間、提高傳輸效率,必須要對數字音頻信號進行壓縮編碼。

一. 數字音頻信號壓縮的可能性

無壓縮的數字音頻信號中主要存在兩方面的冗餘信息:

一是聲音信號中本身存在的冗餘:信號幅度分佈是非均勻的(小幅度的樣值比大幅度樣值出現的概率高),且樣本之間存在相關性(時域冗餘);

二是根據人耳的聽覺特性,可不對與聽覺無關的部分編碼(具體內容見後)。

二. 人類聽覺系統的感知特性

1. 等響度曲線


等響度曲線

圖中每條曲線代表的(人耳感受的)響度相同,最下方一條曲線代表最小可聽閾,其下方區域的聲音人耳無法察覺,因此可以不編碼。

2. 頻域掩蔽效應

一個高強度純音會使得該頻率附近的最小可聽閾曲線提升,掩蔽掉在其下的(原本能聽到的)聲音。這個純音稱爲掩蔽聲,掩蔽聲存在時,音調音剛剛能被聽到時的閾值稱爲掩蔽閾值


頻域掩蔽示意圖

因此在編碼時,可以去除掩蔽閾值以下的信號分量,並忽略可能會被掩蔽的量化噪聲

掩蔽曲線具有以下特點:

  • 不對稱,高頻一端的斜率絕對值更小;
  • 低頻音易對高頻音產生掩蔽;
  • 掩蔽聲強度越高、頻率越高,掩蔽的頻率範圍越寬。

3. 臨界頻帶(Critical Band)

臨界頻帶具有兩種等價的定義:

  • 定義一:當某個純音被以它爲中心頻率、且具有一定帶寬的連續噪聲所掩蔽時,如果該純音剛好被聽到時的功率等於這一頻帶內的噪聲功率,這個帶寬爲臨界頻帶寬度;

  • 定義二:如果增大掩蔽信號覆蓋的頻率範圍,超過到某個值後掩蔽效應不再隨着帶寬的增大而增強,這個臨界的帶寬就稱爲臨界頻帶。

其單位爲Bark,代表一個臨界頻帶的寬度。

通常認爲,在20 Hz—16 kHz的範圍內有25個臨界頻帶(500 Hz以下每個臨界頻帶的帶寬大約是100 Hz,500 Hz以上的臨界頻帶帶寬逐漸增加)。

4. 人類聽覺系統

人類聽覺系統可以大致等效爲在0 Hz—20 kHz範圍內的25個重疊的帶通濾波器組。

  • 人耳在噪聲中聽某一純音信號時,只啓用中心頻率與信號頻率相同的那個聽覺濾波器,且只有純音信號和在通帶範圍內的部分信號可通過該濾波器。只有通過該濾波器的噪聲纔對掩蔽起作用;

  • 聆聽複音時啓動多個聽覺濾波器。聽覺能夠計算各濾波器輸出端的信噪比。當信噪比達到或者超過聽閾因子時,即可聽到該頻率成分。


    人耳聽覺系統

5. 時域掩蔽效應

由於在時間上相鄰的聲音之間存在超前掩蔽(5—20 ms)和滯後掩蔽(50—200 ms),因此在編碼時,可將時間上相繼的一些樣值歸併成塊,並計算每塊內最大樣值的比例因子

三. MPEG-1音頻壓縮編碼

MPEG-1中採用了感知音頻編碼,Layer II的編碼器框圖如下。


MPEG-1 Audio Layer II編碼器框圖
可以看到,編碼器具有兩條脈絡:一是上方藍色框內的子帶編碼部分,這也是編碼的主線;二是下方的部分,是MPEG音頻編碼的亮點。

下面以Layer II爲基準,簡要說明編碼器的組成和編碼過程。

1. 多相濾波器

數字音頻信號通過一個多相濾波器組,變換成32個等寬頻帶子帶,使得信號具有較高的時間分辨率,確保在短暫衝擊信號的情況下,編碼的聲音信號具有足夠高的質量。

但需要說明的是,高時域分辨率和高頻域分辨率是不可兼得的,我們需要做出權衡。

濾波器組的輸出是臨界頻帶經過量化的係數樣值。若一個子帶覆蓋多個臨界頻帶,則選擇具有最小NMR的臨界頻帶來計算分配給子帶的比特數。

2. 心理聲學模型

心理聲學模型決定了各個子帶中允許的最大量化噪聲,小於它的量化噪聲都會被掩蔽。若子帶內的信號功率小於掩蔽閾值,則不進行編碼;否則,確定要編碼的係數所需的比特數,使量化引起的噪聲低於掩蔽效應;

  • 使用FFT將音頻樣值變換到頻域。這裏Layer II採用1024點FFT(Layer I中採用512點),提高了頻率分辨率,可以得到更準確的頻譜特性;

  • 將得到的頻率組成臨界頻帶;

  • 由於樂音和非樂音(噪聲)信號的掩蔽能力不同,因此在臨界頻帶的譜值中,將兩種信號分離:可將局部峯值視爲樂音,然後將本臨界頻帶內的剩餘頻譜組成一個代表噪聲頻率(無調成份);


    樂音和非樂音(噪聲)的掩蔽能力不同
  • 計算每個子帶的全局掩蔽閾值(以頻率爲自變量);


    全局掩蔽閾值的計算公式
  • 計算每個子帶的SMR(每8 ms計算一個);

3. 量化編碼

(1) 比特分配

比特分配:對每個子帶計算**掩噪比MNR (dB) = 信噪比SNR - 信掩比SMR,然後找出其中具有最低MNR的子帶,並給該子帶多分配一些比特,然後重新計算MNR,繼續分配,重複該步驟,直至沒有比特可以分配。這樣可以使得在滿足比特率和掩蔽要求的前提下,使MNR最小**;


掩噪比MNR = 信噪比SNR - 信掩比SMR

(2) 計算比例因子

  • 對各個子帶每36個樣點(Layer I爲12個樣點)進行一次比例因子的計算,先確定12個連續樣值中的最大值,查Layer II、Layer I比例因子表中比這它大的最小值作爲量化比例因子;
  • 每12個樣值計算出一個比例因子,Layer II中將每個子帶分爲3組,每組各有12個取樣值,因此36個樣值具有3個比例因子;
  • 比例因子可以使得比較準確地計算出子帶的聲壓級;
  • 一般比例因子從低頻子帶到高頻子帶連續下降;

(3) 子帶樣值量化

將子帶樣值除以比例因子(結果爲XX),根據所分配的比特數查表得AABB,量化結果爲AX+BAX+B

(4) 顆粒形成

對量化級別在3、5、9級時,採用“顆粒” 優化。例如:

  • 採用顆粒量化:3個樣本 @ 3個量化水平 = 27種可能的值 → 5 bit
  • 不採用顆粒量化:1個樣本 @ 3個量化水平 = 2bit → 3個樣本6 bit
  • 經過大量實驗,使用顆粒量化可將壓縮比從4:1增加到6:1乃至8:1

4. 幀比特流形成

  • 其中每個子帶中前後相鄰的連續36個樣值(3組12個樣值)共用在這個子帶比特分配值,36個樣值中的同一組樣值共用該組的比例因子;
  • 同一時刻的32個子帶樣值放在一起;
  • Layer II每幀包含1152個PCM樣值(爲Layer I的三倍);
  • 若取樣頻率爲48 kHz,一幀相當於1152 / 48k = 24 ms的聲音樣值,因此Layer II的精確度爲24 ms(爲Layer I的三倍,因而更精確)。

MPEG-1 Audio Layer II的幀結構

四. MPEG-1 Audio Layer II編碼器調試

1. 編碼流程

下面是編碼器main()函數部分的代碼,可以結合上面的編碼步驟,並結合註釋進行理解。

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];	// 存放雙聲道各個子帶的比特分配表
	static unsigned int scfsi[2][SBLIMIT];
	static unsigned int scalar[2][3][SBLIMIT];	// 存放雙聲道3組12個樣值的各個子帶的比例因子
	static unsigned int 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;
	int nch;	// 聲道數
	int error_protection;
	static unsigned int crc;
	int sb, ch;
	int adb;	// 比特預算 (i.e., number of bits available)
	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];    // exe文件名稱
	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);	// print文件參數到窗口

	/* this will load the alloc tables and do some other stuff */
	hdr_to_frps(&frame);
	nch = frame.nch;
	error_protection = header.error_protection;

	/* 從數據流獲取音頻 */
	while (get_audio(musicin, buffer, num_samples, nch, &header) > 0) {
		/* 從輸入的文件讀取數據到buffer */
		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);	// 計算比特預算

		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++)   /* 36個樣值分爲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]);    /* 多相濾波器組 */
		}

#ifdef REFERENCECODE
		{
			/* Old code. left here for reference */
			int gr, bl, ch;
			for (gr = 0; gr < 3; gr++)
				for (bl = 0; bl < SCALE_BLOCK; bl++)
					for (ch = 0; ch < nch; ch++) {
						window_subband(&win_buf[ch], &(*win_que)[ch][0], ch);
						filter_subband(&(*win_que)[ch][0], &(*sb_sample)[ch][gr][bl][0]);
					}
		}
#endif


#ifdef NEWENCODE
		scalefactor_calc_new(*sb_sample, scalar, nch, frame.sblimit);
		find_sf_max(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_new(*sb_sample, *j_sample, frame.sblimit);
			scalefactor_calc_new(j_sample, &j_scale, 1, frame.sblimit);
		}
#else
		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);
		}
#endif


		/* 選擇心理聲學模型,計算SMR */
		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 */
				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);
		}

#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);

	exit(0);
}

2. 命令行設置

最基本的命令行參數如下:

輸入文件名 輸出文件名

此時使用默認的輸出比特率(192 kbps)。

此外,我們還可以參考usage()函數來自定額外的輸出參數,如:

-b 48 -a 輸入文件名 輸出文件名

可將輸出比特率設爲48 kbps,並輸出單聲道音頻。

3. 輸出一幀的比例因子和比特分配表

定義文件指針:FILE* infoFp;

爲了保證程序的簡明與運行流暢,將需要觀測的內容輸出到TRACE文件,因此在文件開頭位置定義宏:#define FRAME_TRACE 1,這樣設爲1時就打開數據幀的TRACE文件。

先在print_config()函數中輸出一些輸入、輸出文件的主要參數(參考函數中的寫法即可):

#if FRAME_TRACE
	fprintf(infoFp, "========== 基本信息 ==========\n");
	fprintf(infoFp, "輸入文件:%s\n", inPath);
	fprintf(infoFp, "輸出文件:%s\n", outPath);
	fprintf(infoFp, "採樣頻率:%.1f kHz\n", s_freq[header->version][header->sampling_frequency]);
	fprintf(infoFp, "輸出文件碼率:%d kbps\n", bitrate[header->version][header->bitrate_index]);
#endif // FRAME_TRACE

然後輸出比例因子和比特分配表,在main()中添加:

		/* 打開文件等步驟略 */
		...
#else
		scale_factor_calc(*sb_sample, scalar, nch, frame.sblimit); // 計算比例因子
		pick_scale(scalar, &frame, max_sc);    // pick比例因子

				/********** Added by S.Z.Zheng **********/
#if FRAME_TRACE
		if (frameNum == 2) {
			fprintf(infoFp, "聲道數:%d\n", nch);
			fprintf(infoFp, "目前觀測第 %d 幀\n", frameNum);
			fprintf(infoFp, "本幀比特預算:%d bits\n", adb);
			fprintf(infoFp, "\n");

			/* 比例因子 */
			fprintf(infoFp, "========== 比例因子 ==========\n");
			for (ch = 0; ch < nch; ch++)	// 每個聲道單獨輸出
			{
				fprintf(infoFp, "------ 聲道%2d ------\n", ch + 1);
				for (sb = 0; sb < frame.sblimit; sb++)	// 每個子帶
				{
					fprintf(infoFp, "子帶[%2d]:\t", sb + 1);
					for (int gr = 0; gr < 3; gr++) {
						fprintf(infoFp, "%2d\t", scalar[ch][gr][sb]);
					}
					fprintf(infoFp, "\n");
				}
			}
			fprintf(infoFp, "\n");

			/* 比特分配表 */
			fprintf(infoFp, "========== 比特分配表 ==========\n");  //輸出比特分配結果
			for (ch = 0; ch < nch; ch++) {
				fprintf(infoFp, "------ 聲道%2d ------\n", ch + 1); //按聲道分配
				for (sb = 0; sb < frame.sblimit; sb++) {
					fprintf(infoFp, "子帶[%2d]:\t%2d\n", sb + 1, bit_alloc[ch][sb]);
				}
				fprintf(infoFp, "\n");
			}
		}
#endif // FRAME_TRACE
		/********** Addition ended **********/
		...

五. 測試結果

1. 樂音

========== 基本信息 ==========
輸入文件:music.wav
輸出文件:music_192k.mp2
採樣頻率:44.1 kHz
輸出文件碼率:192 kbps
聲道數:2
目前觀測第 2 幀
本幀比特預算:5016 bits

========== 比例因子 ==========
------ 聲道 1 ------
子帶[ 1]:	33	35	35	
子帶[ 2]:	44	41	41	
子帶[ 3]:	47	46	46	
子帶[ 4]:	47	46	45	
子帶[ 5]:	46	44	47	
子帶[ 6]:	50	47	48	
子帶[ 7]:	46	49	49	
子帶[ 8]:	47	48	48	
子帶[ 9]:	49	48	48	
子帶[10]:	49	46	47	
子帶[11]:	48	47	48	
子帶[12]:	46	48	48	
子帶[13]:	49	48	48	
子帶[14]:	47	51	50	
子帶[15]:	49	49	48	
子帶[16]:	50	46	49	
子帶[17]:	51	50	48	
子帶[18]:	51	50	51	
子帶[19]:	52	49	51	
子帶[20]:	51	49	49	
子帶[21]:	49	51	50	
子帶[22]:	51	49	52	
子帶[23]:	48	49	50	
子帶[24]:	54	52	55	
子帶[25]:	54	56	54	
子帶[26]:	55	54	54	
子帶[27]:	53	54	53	
子帶[28]:	54	54	54	
子帶[29]:	54	56	53	
子帶[30]:	55	56	55	
------ 聲道 2 ------
子帶[ 1]:	34	34	36	
子帶[ 2]:	43	43	44	
子帶[ 3]:	44	46	45	
子帶[ 4]:	45	47	47	
子帶[ 5]:	46	48	47	
子帶[ 6]:	47	49	51	
子帶[ 7]:	47	49	49	
子帶[ 8]:	49	48	48	
子帶[ 9]:	51	50	46	
子帶[10]:	49	49	49	
子帶[11]:	48	50	48	
子帶[12]:	50	48	48	
子帶[13]:	48	48	50	
子帶[14]:	47	49	50	
子帶[15]:	50	50	49	
子帶[16]:	48	49	49	
子帶[17]:	49	50	49	
子帶[18]:	50	50	50	
子帶[19]:	51	51	50	
子帶[20]:	51	49	50	
子帶[21]:	53	53	51	
子帶[22]:	53	50	50	
子帶[23]:	49	49	48	
子帶[24]:	51	51	53	
子帶[25]:	53	57	53	
子帶[26]:	57	53	56	
子帶[27]:	53	53	55	
子帶[28]:	54	53	54	
子帶[29]:	55	54	55	
子帶[30]:	56	57	54	

========== 比特分配表 ==========
------ 聲道 1 ------
子帶[ 1]:	 4
子帶[ 2]:	 4
子帶[ 3]:	 3
子帶[ 4]:	 5
子帶[ 5]:	 5
子帶[ 6]:	 5
子帶[ 7]:	 4
子帶[ 8]:	 5
子帶[ 9]:	 4
子帶[10]:	 5
子帶[11]:	 3
子帶[12]:	 6
子帶[13]:	 5
子帶[14]:	 3
子帶[15]:	 2
子帶[16]:	 2
子帶[17]:	 4
子帶[18]:	 4
子帶[19]:	 3
子帶[20]:	 3
子帶[21]:	 3
子帶[22]:	 3
子帶[23]:	 1
子帶[24]:	 1
子帶[25]:	 0
子帶[26]:	 1
子帶[27]:	 0
子帶[28]:	 1
子帶[29]:	 1
子帶[30]:	 0

------ 聲道 2 ------
子帶[ 1]:	 3
子帶[ 2]:	 4
子帶[ 3]:	 4
子帶[ 4]:	 5
子帶[ 5]:	 5
子帶[ 6]:	 5
子帶[ 7]:	 4
子帶[ 8]:	 5
子帶[ 9]:	 4
子帶[10]:	 5
子帶[11]:	 3
子帶[12]:	 6
子帶[13]:	 5
子帶[14]:	 3
子帶[15]:	 2
子帶[16]:	 2
子帶[17]:	 4
子帶[18]:	 4
子帶[19]:	 3
子帶[20]:	 3
子帶[21]:	 3
子帶[22]:	 3
子帶[23]:	 1
子帶[24]:	 1
子帶[25]:	 0
子帶[26]:	 1
子帶[27]:	 0
子帶[28]:	 1
子帶[29]:	 1
子帶[30]:	 0

輸出比特率爲48 kbps時:

========== 基本信息 ==========
輸入文件:music.wav
輸出文件:music_48k.mp2
採樣頻率:44.1 kHz
輸出文件碼率:48 kbps
聲道數:2
目前觀測第 2 幀
本幀比特預算:1256 bits

========== 比例因子 ==========
------ 聲道 1 ------
子帶[ 1]:	33	35	35	
子帶[ 2]:	44	41	41	
子帶[ 3]:	47	46	46	
子帶[ 4]:	47	46	45	
子帶[ 5]:	46	44	47	
子帶[ 6]:	50	47	48	
子帶[ 7]:	46	49	49	
子帶[ 8]:	47	48	48	
------ 聲道 2 ------
子帶[ 1]:	34	34	36	
子帶[ 2]:	43	43	44	
子帶[ 3]:	44	46	45	
子帶[ 4]:	45	47	47	
子帶[ 5]:	46	48	47	
子帶[ 6]:	47	49	51	
子帶[ 7]:	47	49	49	
子帶[ 8]:	49	48	48	

========== 比特分配表 ==========
------ 聲道 1 ------
子帶[ 1]:	 3
子帶[ 2]:	 2
子帶[ 3]:	 1
子帶[ 4]:	 1
子帶[ 5]:	 1
子帶[ 6]:	 1
子帶[ 7]:	 1
子帶[ 8]:	 1

------ 聲道 2 ------
子帶[ 1]:	 1
子帶[ 2]:	 2
子帶[ 3]:	 2
子帶[ 4]:	 1
子帶[ 5]:	 1
子帶[ 6]:	 1
子帶[ 7]:	 1
子帶[ 8]:	 1

2. 噪聲

噪聲爲使用MATLAB產生的高斯白噪聲。

========== 基本信息 ==========
輸入文件:agwn.wav
輸出文件:agwn_192k.mp2
採樣頻率:44.1 kHz
輸出文件碼率:192 kbps
聲道數:1
目前觀測第 2 幀
本幀比特預算:5016 bits

========== 比例因子 ==========
------ 聲道 1 ------
子帶[ 1]:	24	23	22	
子帶[ 2]:	25	23	25	
子帶[ 3]:	24	27	24	
子帶[ 4]:	24	26	25	
子帶[ 5]:	24	25	24	
子帶[ 6]:	27	25	25	
子帶[ 7]:	25	24	24	
子帶[ 8]:	25	24	24	
子帶[ 9]:	25	22	25	
子帶[10]:	25	25	25	
子帶[11]:	23	26	27	
子帶[12]:	25	24	24	
子帶[13]:	24	23	24	
子帶[14]:	23	24	24	
子帶[15]:	26	24	25	
子帶[16]:	24	25	23	
子帶[17]:	23	25	24	
子帶[18]:	25	23	23	
子帶[19]:	24	25	25	
子帶[20]:	23	26	24	
子帶[21]:	24	25	23	
子帶[22]:	24	25	23	
子帶[23]:	23	23	24	
子帶[24]:	26	25	24	
子帶[25]:	23	25	24	
子帶[26]:	24	23	26	
子帶[27]:	25	24	25	
子帶[28]:	24	26	23	
子帶[29]:	23	25	23	
子帶[30]:	26	24	22	

========== 比特分配表 ==========
------ 聲道 1 ------
子帶[ 1]:	 6
子帶[ 2]:	 5
子帶[ 3]:	 5
子帶[ 4]:	 7
子帶[ 5]:	 7
子帶[ 6]:	 7
子帶[ 7]:	 7
子帶[ 8]:	 7
子帶[ 9]:	 6
子帶[10]:	 7
子帶[11]:	 6
子帶[12]:	 6
子帶[13]:	 6
子帶[14]:	 6
子帶[15]:	 6
子帶[16]:	 4
子帶[17]:	 6
子帶[18]:	 6
子帶[19]:	 5
子帶[20]:	 5
子帶[21]:	 5
子帶[22]:	 5
子帶[23]:	 4
子帶[24]:	 1
子帶[25]:	 1
子帶[26]:	 1
子帶[27]:	 1
子帶[28]:	 1
子帶[29]:	 1
子帶[30]:	 0

3. 樂音疊加噪聲

使用MATLAB在樂音波形上疊加高斯白噪聲。

========== 基本信息 ==========
輸入文件:music+agwn.wav
輸出文件:music+agwn_192k.mp2
採樣頻率:44.1 kHz
輸出文件碼率:192 kbps
聲道數:2
目前觀測第 2 幀
本幀比特預算:5016 bits

========== 比例因子 ==========
------ 聲道 1 ------
子帶[ 1]:	25	24	25	
子帶[ 2]:	26	25	24	
子帶[ 3]:	25	24	23	
子帶[ 4]:	23	24	24	
子帶[ 5]:	24	25	24	
子帶[ 6]:	25	24	26	
子帶[ 7]:	25	24	26	
子帶[ 8]:	24	23	24	
子帶[ 9]:	24	24	23	
子帶[10]:	23	26	22	
子帶[11]:	26	23	24	
子帶[12]:	23	25	26	
子帶[13]:	24	23	23	
子帶[14]:	25	25	24	
子帶[15]:	24	27	25	
子帶[16]:	27	23	26	
子帶[17]:	24	26	23	
子帶[18]:	26	23	23	
子帶[19]:	25	25	24	
子帶[20]:	25	26	23	
子帶[21]:	24	24	26	
子帶[22]:	23	26	24	
子帶[23]:	26	25	24	
子帶[24]:	24	23	27	
子帶[25]:	24	25	23	
子帶[26]:	25	24	23	
子帶[27]:	24	25	27	
子帶[28]:	22	23	23	
子帶[29]:	24	24	23	
子帶[30]:	23	23	24	
------ 聲道 2 ------
子帶[ 1]:	25	24	25	
子帶[ 2]:	26	25	24	
子帶[ 3]:	25	24	23	
子帶[ 4]:	23	24	24	
子帶[ 5]:	24	25	24	
子帶[ 6]:	25	24	26	
子帶[ 7]:	25	24	26	
子帶[ 8]:	24	23	24	
子帶[ 9]:	24	24	23	
子帶[10]:	23	26	22	
子帶[11]:	26	23	24	
子帶[12]:	23	25	26	
子帶[13]:	24	23	23	
子帶[14]:	25	25	24	
子帶[15]:	24	27	25	
子帶[16]:	27	23	26	
子帶[17]:	24	26	23	
子帶[18]:	26	23	23	
子帶[19]:	25	25	24	
子帶[20]:	25	26	23	
子帶[21]:	24	24	26	
子帶[22]:	23	26	24	
子帶[23]:	26	25	24	
子帶[24]:	24	23	27	
子帶[25]:	24	25	23	
子帶[26]:	25	24	23	
子帶[27]:	24	25	27	
子帶[28]:	22	23	23	
子帶[29]:	24	24	23	
子帶[30]:	23	23	24	

========== 比特分配表 ==========
------ 聲道 1 ------
子帶[ 1]:	 3
子帶[ 2]:	 3
子帶[ 3]:	 3
子帶[ 4]:	 4
子帶[ 5]:	 4
子帶[ 6]:	 3
子帶[ 7]:	 3
子帶[ 8]:	 4
子帶[ 9]:	 3
子帶[10]:	 3
子帶[11]:	 2
子帶[12]:	 3
子帶[13]:	 1
子帶[14]:	 3
子帶[15]:	 1
子帶[16]:	 1
子帶[17]:	 1
子帶[18]:	 2
子帶[19]:	 2
子帶[20]:	 1
子帶[21]:	 1
子帶[22]:	 1
子帶[23]:	 1
子帶[24]:	 0
子帶[25]:	 0
子帶[26]:	 0
子帶[27]:	 0
子帶[28]:	 0
子帶[29]:	 0
子帶[30]:	 0

------ 聲道 2 ------
子帶[ 1]:	 3
子帶[ 2]:	 3
子帶[ 3]:	 3
子帶[ 4]:	 3
子帶[ 5]:	 4
子帶[ 6]:	 3
子帶[ 7]:	 3
子帶[ 8]:	 4
子帶[ 9]:	 3
子帶[10]:	 3
子帶[11]:	 2
子帶[12]:	 3
子帶[13]:	 1
子帶[14]:	 3
子帶[15]:	 1
子帶[16]:	 1
子帶[17]:	 1
子帶[18]:	 2
子帶[19]:	 2
子帶[20]:	 1
子帶[21]:	 1
子帶[22]:	 1
子帶[23]:	 0
子帶[24]:	 0
子帶[25]:	 0
子帶[26]:	 0
子帶[27]:	 0
子帶[28]:	 0
子帶[29]:	 0
子帶[30]:	 0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章