【数据压缩】第八次作业——MPEG音频编码
MPEG音頻編碼實驗
文章目錄
- MPEG音頻編碼實驗
 - MPEG介紹
 - MPEG-1聲音的主要性能
 
- 心理聲學模型
 - 人耳聽覺特性
 - 臨界頻帶
 - 掩蔽值
 - 心理聲學模型Ⅰ
 
- MPEG-1 編碼原理
 - 基本思想
 - 整體框架
 - 兩條線
 - 時-頻分析的矛盾
 - 重要模塊分析
 - 多相濾波器組
 - 比例因子選擇
 - 心理聲學模型
 - 動態比特分配
 - 裝幀
 
- layerⅡ
 
- 關鍵代碼分析
 - main的參數
 - 代碼注釋
 
- 實驗結果
 - 音頻文件
 - 音頻的采樣率和目標碼率
 - 樂音test
 - 持續性噪聲longnoise
 - 突發性噪聲burstnoise
 - 混合音merge
 
- 輸出相關內容
 - 結果
 - 壓縮率
 
- 實驗問題及總結
 
MPEG介紹
MPEG標準主要有以下五個,MPEG-1、MPEG-2、MPEG-4、MPEG-7及MPEG-21等。該專家組建于1988年,專門負責為CD建立視頻和音頻標準,而成員都是視頻、音頻及系統領域的技術專家。及后,他們成功將聲音和影像的記錄脫離了傳統的模擬方式,建立了ISO/IEC11172壓縮編碼標準,并制定出MPEG-格式,令視聽傳播方面進入了數字化時代。因此,大家現在泛指的MPEG-X版本,就是由ISO (InternationalOrganization for Standardization) 所制定而發布的視頻、音頻、數據的壓縮標準。
MPEG-1聲音的主要性能
輸入為PCM信號,采樣率為32、44.1或48kHz,輸出為32kbps~384kbps
具有三個獨立的壓縮層次:layerⅠ(編碼器最簡單)、layerⅡ(編碼器復雜度中等)、layerⅢ(編碼器最復雜)
心理聲學模型
心理聲學模型用來計算信號中不可被聽覺感知的部分
人耳聽覺特性
人耳聽覺系統大致等效于一個信號通過一組并聯的不同中心頻率的帶通濾波器,中心頻率與信號頻率相同的濾波器具有最大響應,中心頻率偏離信號頻率較多的濾波器不會產生響應。在0Hz到20kHz頻率范圍內由25個重疊的帶通濾波器組成濾波器組。
聽覺系統中存在一個聽覺閾值電平,低于這個電平的聲音信號就聽不到。
- 聽覺閾值電平是自適應的,也就是說聽覺閾值的大小隨聲音頻率的改變而改變。
 - 一個人能否聽到這種聲音取決于聲音的頻率以及聲音的幅度是否高于這種頻率下的聽覺閾值
 - 掩蔽效應: 一個較弱的聲音的聽覺感受被另一個較強的聲音影響的現象稱為人耳的聽覺掩蔽效應。掩蔽作用與信號頻率和強度有關。掩蔽效應在一定頻率范圍內不隨帶寬增大而改變,直至超過某個頻率值。
 
如果有多個頻率成分的復雜信號存在,那么頻譜的總掩蔽閾值與頻率的關系取決于各掩蔽音的強度、頻率和它們之間的距離。
 
臨界頻帶
-  
臨界頻帶是指當某個純音被以它為中心頻率,且具有一定帶寬的連續噪聲所掩蔽時,如果該純音剛好被聽到時的功率等于這一頻帶內的噪聲功率,這個帶寬為臨界頻帶寬度
 -  
通常認為從20Hz到16kHz有25個臨界頻帶,單位為bark,1bark=一個臨界頻帶的寬度
 
掩蔽值
- 掩蔽音與被掩蔽音的組合方式有四種,即它們分別可以是樂音信號或窄帶噪聲。
 - 音樂與語音信號大都由一系列復雜的頻譜分量構成,相應的這些多個掩蔽分量也會相互影響并最終獲得一個整體的掩蔽閾值。
 - 多個掩蔽音同時存在時的綜合掩蔽效果可以理解為每個掩蔽音的掩蔽效果先獨立變化然后再線性相加 
- 當兩個信號重疊并落在一個臨界頻帶中時,二者的掩蔽分量可以線性相加
 - 對于復雜音頻信號,可以將其頻譜分割成一系列離散段,每段就是一個掩蔽信號,各掩蔽音互不重疊,即以一個臨界帶為單位。各掩蔽音的聲壓級則通過將對應的臨界頻帶上的短時功率譜密度線性相加得到。
 
 - 將輸入信號變換到頻域,再將結果分解成一些盡量與臨界頻帶盡可能相似的子帶,然后對每個子帶進行量化,量化方式應當使得量化噪聲聽不見
 
心理聲學模型Ⅰ
該模型的計算復雜度低,但對假設用戶聽不到的部分壓縮太嚴重。
實際實現的模型復雜度取決于所需要的壓縮因子,如大的壓縮因子不重要,則可以不用心理聲學模型,此時位分配算法不使用SMR而使用SNR
32個等分的子帶信號并不能精確地反映人耳的聽覺特性,因此引入FFT補償頻率分辨率不足的問題。
layer1每幀有384個樣本點,因此采用512點的樣本窗口;layer2和layer3每幀有1152個樣本點,采用1024點的樣本窗口,每幀兩次計算,選擇兩個信掩比(SMR)中較小的一個
即絕對閾值,在標準中有根據輸入PCM信號的采樣率編制的“頻率、臨界頻帶率和絕對閾值”表
根據音頻頻譜的局部功率最大值確定樂音成分,局部峰值為樂音,之后將本臨界頻帶內的剩余頻譜合在一起,組成一個代表噪聲頻率
利用標準中給出的絕對閾值消除被掩蔽成分,考慮在每個臨界頻帶內,小于0.5bark的距離中只保留最高功率的成分
音調成分和非音調成分單個掩蔽閾值根據標準中的算法求得
還要考慮其他臨界頻帶的影響,因為一個掩蔽信號會對其他頻帶上的信號產生掩蔽效應,稱為掩蔽擴散
選擇出本子帶中最小的閾值作為子帶閾值
SMR=信號能量/掩蔽閾值
將SMR傳遞給編碼單元
MPEG-1 編碼原理
基本思想
分析信號,去掉不能被感知的部分
整體框架
輸入聲音信號經過一個多相濾波器組,變換到多個子帶。同時,經過“心理聲學模型”計算以頻率為自變量的噪聲掩蔽閾值、量化和編碼部分用信掩比SMR決定分配給子帶信號的量化位數,使量化噪聲<掩蔽閾值。最后通過數據幀包裝將量化的子帶樣本和其他數據按照規定的幀格式組裝成比特數據流
兩條線
MPEG音頻壓縮編碼框架主體分為兩條線,一條是紅框中的內容,一條是綠框中的內容
紅框部分:PCM碼流經多相濾波器組變換為32個子帶的頻域信號,在實際中可以理解為每32個樣點做一次子帶分解,連續做12次,此時得到的每個子帶中都有12個樣點,這樣的過程再連續做3次,所以最終每個子帶上會有12×3=3612\times 3=3612×3=36個樣本點。之后利用第二條線的結果對數據進行量化。
綠框部分:對PCM信號進行FFT,輸入到心理聲學模型中,同時,紅框中子帶分解后,根據各個子帶的樣本點計算提取相應子帶的比例因子,也將其輸入到心理聲學模型中,由心理聲學模型計算以頻率為自變量的噪聲掩蔽閾值,通過信號掩蔽比SMR確定比例因子選擇信息,另外,與目標碼率結合確定動態比特分配,決定給子帶分配多少量化比特數。
將量化后的子帶樣本和邊信息編碼數據以及輔助數據按照規定的幀格式組裝成幀比特流輸出。
時-頻分析的矛盾
之所以要分成上下兩條線進行壓縮編碼,是因為時-頻之間存在矛盾。
對于傅里葉變換過程中,窗的長度決定頻譜圖的時間分辨率和頻率分辨率,窗長越長,截取的信號越長,傅里葉變換后頻率分辨率越高,時間分辨率越低;相反,窗長越短,截取的信號就越短,頻率分辨率越低,時間分辨率越高
因此,MPEG音頻壓縮編碼的第一條線,利用子帶分析濾波器組使信號具有高的時間分辨率,確保在短暫沖擊信號情況下,編碼的聲音信號仍具有足夠高的質量。第二條線利用FFT運算,使信號具有高的頻率分辨率,因為掩蔽閾值是從功率譜密度推出來的
重要模塊分析
多相濾波器組
-  
子帶編碼:將原始信號分解為若干個子頻帶,對其分別進行編碼處理后再合成為全頻帶信號
 -  
多相濾波器組用來分割子帶,將PCM樣本變換到32個子帶的頻域信號
 -  
缺點:
- 等帶寬的濾波器組與人類聽覺系統的臨界頻帶不對應,在低頻區域,單個子帶會覆蓋多個臨界頻帶,在這種情況下,量化比特數不能兼顧每個臨界頻帶
 - 濾波器組與其逆過程不是無失真的,但是濾波器組引入的誤差很小,且聽不到
 - 子帶間頻率有混疊,濾波后的相鄰子帶有頻率混疊現象,一個子帶中的信號可以影響相鄰子帶的輸出
 
 
比例因子選擇
- 比例因子:對各個子帶每12個樣點進行一次比例因子計算,先定出12個樣點中絕對值的最大值,查比例因子表中比這個最大值大的最小值作為比例因子。用6bit表示。
 - 比例因子選擇:每幀中每個子帶的三個比例因子被一起考慮,劃分成特定的幾種模式,根據這些模式,1個、2個或3個比例因子和比例因子選擇信息(每子帶2bit)一起被傳送。如果一個比例因子和下一個只有很小的差別,就只傳送大的一個,這種情況對于穩態信號經常出現。
 
心理聲學模型
輸入為比例因子和FFT變換的結果,輸出為信掩比,用于動態比特分配
動態比特分配
layer Ⅰ
輸入為(目標)碼率和信掩比,輸出為量化比特數
- 在調整到固定的碼率之前,要先確定可用于樣值編碼的有效比特數,這個數值取決于比例因子、比例因子選擇信息、比特分配信息以及輔助數據所需比特數
 - 目標:使整幀和每個子帶的總噪聲-掩蔽比最小
 - 比特分配的過程 
- 計算噪聲-掩蔽比NMR=信掩比SMR-信噪比SNR
 - 其中SNR由MPEG-1標準給定,NMR表示波形誤差與感知測量之間的誤差
 - 比特分配時的最好情況:信噪比=信掩比
 
 - 碼率分配的實現思路: 
- 初始還未分配bit時,信噪比為0,噪掩比等于信掩比。
 - 根據(目標)碼率計算預估比特數
 - 優先對噪掩比高的子帶分配比特(如果噪掩比是負數則不考慮),使獲益最大的子帶的量化級別增加一級
 - 量化比特數每增加1bit,信噪比會上升6db,噪掩比會下降6db
 - 分配比特后重新計算該子帶的噪掩比,之后重復上述過程,直到沒有比特可用或每個子帶的噪掩比都為0
 
 
裝幀
layer Ⅰ
 
layerⅡ
與layer Ⅰ類似但有所增強
-  
幀:3組/幀×12個樣本/子帶×32個子帶/幀=1152個樣本/幀
 -  
縮放因子:每個子帶的3個組盡量使用共同的縮放因子
- 1/2/3個縮放因子和縮放因子選擇信息(每子帶2bit)一起傳送
 - 如果縮放因子和下一個只有很小的差別,就只傳送大的一個,這種情況對于穩態信號經常出現
 - 如果要給瞬態信號編碼,則要在瞬態的前、后沿傳送兩個或所有三個比例因子
 
 -  
量化:每子帶12個連續的樣值都除以比例因子進行歸一化,得到的值為XXX,進行量化計算:A×X+BA\times X+BA×X+B,其中AAA和BBB是量化系數,根據bit分配信息得到量化級數,根據量化級數查量化表得到AAA和BBB
- 根據采樣和碼率量化,不同子帶可以從不同的量化器集合中選擇
 - 對量化級別在3,5,9級,采用“顆粒”優化
 
 -  
裝幀:
 
關鍵代碼分析
main的參數
首先在命令行輸入-h指令,查看代碼的輸入格式要求:
 
 結果如下:
修改調試中main函數的輸入格式:
 
代碼注釋
在main函數上方的注釋中詳細說明了程序設計的整體框架
/************************************************************************ * * main * * PURPOSE: MPEG II Encoder with * psychoacoustic models 1 (MUSICAM) and 2 (AT&T) //mpeg2編碼+心理聲學模型1和2 * * 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) * * 1. Filter sliding window of data to get 32 subband * samples per channel. //滑動窗一次獲得32個子帶的樣本 * (window_subband,filter_subband) * * 2. If joint stereo mode, combine left and right channels * for subbands above #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. //迭代分配bit * (*_main_bit_allocation) * * 6. If error protection flag is active, add redundancy for * error protection. //可以添加冗余校驗 * (*_CRC_calc) * * 7. Pack bit allocation, scalefactors, and scalefactor select * headerrmation onto bitstream. //將bit分配、比例因子和比例因子選擇信息進行打包 * (*_encode_bit_alloc,*_encode_scale,transmission_pattern) * * 8. Quantize subbands and pack them into bitstream //對子帶進行量化并打包 * (*_subband_quantization, *_sample_encoding) * ************************************************************************/過程如下:
m2aenc.c
int frameNum = 0;int main (int argc, char **argv) {typedef double SBS[2][3][SCALE_BLOCK][SBLIMIT]; // SCALE_BLOCK=12,SBLIMIT=32SBS *sb_sample;typedef double JSBS[3][SCALE_BLOCK][SBLIMIT];JSBS *j_sample;typedef double IN[2][HAN_SIZE]; //HAN_SIZE=512IN *win_que;typedef unsigned int SUB[2][3][SCALE_BLOCK][SBLIMIT];SUB *subband;frame_info frame; //音頻幀信息結構體變量,包含頭信息、bit分配表、聲道數、子帶數等音頻幀信息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]; //心理聲學模型得到smr// FLOAT snr32[32];short sam[2][1344]; /* was [1056]; */int model, nch, error_protection; //nch指聲道數static unsigned int crc;int sb, ch, adb; //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 */ //MPEG_AUDIO_ID=1total_time = 0;time(&start_time); programName = argv[0]; if (argc == 1) /* no command-line args */ //命令行沒有參數short_usage ();elseparse_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 (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 totalbit = adb;{int gr, bl, ch;/* New polyphase filterCombines windowing and filtering. Ricardo Feb'03 */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] ); //多相濾波器組,子帶分解}#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 NEWENCODEscalefactor_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);} #elsescale_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);} #endifif ((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++) { //nch表示通道數for (sb = 0; sb < SBLIMIT; sb++) //SBLIMIT=32smr[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); break;case 1:psycho_1 (buffer, max_sc, smr, &frame); //送入buffer和最大比例因子,返回smrbreak;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 NEWENCODEsf_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); #elsetransmission_pattern (scalar, scfsi, &frame); //對于給定的子帶,決定是否發送1、2或全部3個比例因子,并相應地填寫比例因子選擇信息,scfsi是比例因子選擇信息main_bit_allocation (smr, scfsi, bit_alloc, &adb, &frame, &glopts); //比特分配if (error_protection) //如果需要就添加CRCCRC_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); //剩余未分配比特數寫入stackif (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 NEWENCODEextern int vbrstats_new[15]; #elseextern int vbrstats[15]; #endiffprintf (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 NEWENCODEfprintf (stdout,"%4i ",vbrstats_new[i]); #elsefprintf (stdout, "%4i ", vbrstats[i]); #endiffprintf (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); }實驗結果
音頻文件
噪音是自己錄制的,樂音采用了所給代碼中的樣例音頻,混合音就將上述兩個音頻混合在一起了
本次實驗中使用的音頻如下:
 
音頻的采樣率和目標碼率
可以看到在原代碼中音頻采樣率和目標碼率都已經被輸出了
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);elsefprintf (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"); }樂音test
 可以看到音頻的采樣率是44.1kHz,目標碼率是192kbps
持續性噪聲longnoise
 可以看到音頻的采樣率是48.0kHz,目標碼率是192kbps
突發性噪聲burstnoise
 可以看到音頻的采樣率是48.0kHz,目標碼率是192kbps
混合音merge
可以看到音頻的采樣率是44.1kHz,目標碼率是192kbps
輸出相關內容
在main函數中添加文件的打開和寫入語句,將結果相關結果寫到文件中
實驗中輸出每段音頻第5幀的比特分配數、比例因子和比特分配結果
在main函數中打開文件并在結尾處關閉
FILE* result = NULL;result = fopen("result.txt", "wb");........fclose(result);通過代碼分析可以知道
- 由available_bits函數計算得到的adb就是比特預算數
 - 由scale_factor_calc (*sb_sample, scalar, nch, frame.sblimit); pick_scale (scalar, &frame, max_sc);兩步計算得到的scalar就是比例因子,需要注意scalar的定義為static unsigned int scalar[2][3][SBLIMIT]; //存放雙聲道3組12個樣本的各個子帶的比例因子,其中SBLIMIT=32
 - 由transmission_pattern (scalar, scfsi, &frame); main_bit_allocation (smr, scfsi, bit_alloc, &adb, &frame, &glopts);計算得到的bit_alloc就是比特分配信息,需要注意bit_alloc的定義為static unsigned int bit_alloc[2][SBLIMIT]; //存放雙聲道各個子帶的比特分配信息,其中SBLIMIT=32
 
在main函數的while循環中添加代碼將上述信息寫入結果文件:
if (frameNum == 5) {//輸出提示信息fprintf(result, "當前幀是第%d幀\n", frameNum);//輸出可用比特數fprintf(result, "所分配比特數(可用比特數)為:%d\n", totalbit);//輸出比例因子fprintf(result, "比例因子:\n");for (int i = 0; i < nch; i++) { //第一層,循環聲道數fprintf(result, "聲道[%d]:\n", i + 1);for (int j = 0; j < frame.sblimit; j++) { //第二層,循環子帶數if (j % 4 == 0 && j != 0) {fprintf(result, "\n");}fprintf(result, "子帶%d:\t", j);for (int k = 0; k < 3; k++) { //第三層,循環組數(每個子帶比例因子數)fprintf(result, "%d\t", scalar[i][k][j]);}fprintf(result, "\t");}fprintf(result, "\n");}fprintf(result, "\n");//輸出比特分配結果fprintf(result, "\n比特分配結果:\n");for (int i = 0; i < nch; i++) { //第一層,循環聲道fprintf(result, "聲道[%d]:\n", i + 1);for (int j = 0; j < frame.sblimit; j++) { //第二層,循環比特分配結果if (j % 4 == 0 && j != 0) {fprintf(result, "\n");}fprintf(result, "子帶%d:\t%d\t", j, bit_alloc[i][j]);}fprintf(result, "\n");}fflush(result);}結果
| 樂音test.wav | |||
| 持續性噪音longnoise.wav | |||
| 突發性噪音burstnoise.wav | |||
| 混合音merge.wav | 
壓縮率
壓縮率是文件壓縮后的大小與壓縮前的大小之比
| 樂音test | 2908KB | 792KB | 0.272 | 
| 持續噪音longnoise | 5810KB | 727KB | 0.125 | 
| 突發性噪音burstnoise | 514KB | 65KB | 0.126 | 
| 混合音merge | 5353KB | 729KB | 0.136 | 
實驗問題及總結
-  
實驗開始前需要選中項目,右擊,選擇重定目標解決方案,否則無法運行
 -  
實驗開始時遇到如下錯誤:
解決方法:
或
參考鏈接:https://blog.csdn.net/ql1053927633/article/details/113879844
 
總結
以上是生活随笔為你收集整理的【数据压缩】第八次作业——MPEG音频编码的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: java mediainfo.dll_m
 - 下一篇: 黑客反跟踪