1. 程式人生 > >音視訊資料處理(6)--- PCM音訊取樣資料處理程式碼實現

音視訊資料處理(6)--- PCM音訊取樣資料處理程式碼實現

音訊取樣資料在視訊播放器的解碼流程中的位置如下圖所示。


本文分別介紹如下幾個PCM音訊取樣資料處理函式:
分離PCM16LE雙聲道音訊取樣資料的左聲道和右聲道
將PCM16LE雙聲道音訊取樣資料中左聲道的音量降一半
將PCM16LE雙聲道音訊取樣資料的聲音速度提高一倍
將PCM16LE雙聲道音訊取樣資料轉換為PCM8音訊取樣資料
從PCM16LE單聲道音訊取樣資料中擷取一部分資料
將PCM16LE雙聲道音訊取樣資料轉換為WAVE格式音訊資料

注:PCM音訊資料可以使用音訊編輯軟體匯入檢視。例如收費的專業音訊編輯軟體Adobe Audition,或者免費開源的音訊編輯軟體

Audacity

函式列表


(1)分離PCM16LE雙聲道音訊取樣資料的左聲道和右聲道

本程式中的函式可以將PCM16LE雙聲道資料中左聲道和右聲道的資料分離成兩個檔案。函式的程式碼如下所示。
  1. /** 
  2.  * Split Left and Right channel of 16LE PCM file. 
  3.  * @param url  Location of PCM file. 
  4.  * 
  5.  */
  6. int simplest_pcm16le_split(char *url){  
  7.     FILE *fp=fopen(url,"rb+");  
  8.     FILE
     *fp1=fopen("output_l.pcm","wb+");  
  9.     FILE *fp2=fopen("output_r.pcm","wb+");  
  10.     unsigned char *sample=(unsigned char *)malloc(4);  
  11.     while(!feof(fp)){  
  12.         fread(sample,1,4,fp);  
  13.         //L
  14.         fwrite(sample,1,2,fp1);  
  15.         //R
  16.         fwrite(sample+2,1,2,fp2);  
  17.     }  
  18.     free(sample);  
  19.     fclose(fp);  
  20.     fclose(fp1);  
  21.     fclose(fp2);  
  22.     return 0;  
  23. }  

呼叫上面函式的方法如下所示。
  1. simplest_pcm16le_split("NocturneNo2inEflat_44.1k_s16le.pcm");  

從程式碼可以看出,PCM16LE雙聲道資料中左聲道和右聲道的取樣值是間隔儲存的。每個取樣值佔用2Byte空間。程式碼執行後,會把NocturneNo2inEflat_44.1k_s16le.pcm的PCM16LE格式的資料分離為兩個單聲道資料:

output_l.pcm:左聲道資料。

output_r.pcm:右聲道資料。

注:本文中聲音樣值的取樣頻率一律是44100Hz,取樣格式一律為16LE。“16”代表取樣位數是16bit。由於1Byte=8bit,所以一個聲道的一個取樣值佔用2Byte。“LE”代表Little Endian,代表2 Byte取樣值的儲存方式為高位存在高地址中。

下圖為輸入的雙聲道PCM資料的波形圖。上面的波形圖是左聲道的圖形,下面的波形圖是右聲道的波形。圖中的橫座標是時間,總長度為22秒;縱座標是取樣值,取值範圍從-32768到32767。


下圖為分離後左聲道資料output_l.pcm的音訊波形圖。

 

下圖為分離後右聲道資料output_r.pcm的音訊波形圖。

 

(2)將PCM16LE雙聲道音訊取樣資料中左聲道的音量降一半

本程式中的函式可以將PCM16LE雙聲道資料中左聲道的音量降低一半。函式的程式碼如下所示。
  1. /** 
  2.  * Halve volume of Left channel of 16LE PCM file 
  3.  * @param url  Location of PCM file. 
  4.  */
  5. int simplest_pcm16le_halfvolumeleft(char *url){  
  6.     FILE *fp=fopen(url,"rb+");  
  7.     FILE *fp1=fopen("output_halfleft.pcm","wb+");  
  8.     int cnt=0;  
  9.     unsigned char *sample=(unsigned char *)malloc(4);  
  10.     while(!feof(fp)){  
  11.         short *samplenum=NULL;  
  12.         fread(sample,1,4,fp);  
  13.         samplenum=(short *)sample;  
  14.         *samplenum=*samplenum/2;  
  15.         //L
  16.         fwrite(sample,1,2,fp1);  
  17.         //R
  18.         fwrite(sample+2,1,2,fp1);  
  19.         cnt++;  
  20.     }  
  21.     printf("Sample Cnt:%d\n",cnt);  
  22.     free(sample);  
  23.     fclose(fp);  
  24.     fclose(fp1);  
  25.     return 0;  
  26. }  

呼叫上面函式的方法如下所示。
  1. simplest_pcm16le_halfvolumeleft("NocturneNo2inEflat_44.1k_s16le.pcm");  

從原始碼可以看出,本程式在讀出左聲道的2 Byte的取樣值之後,將其當成了C語言中的一個short型別的變數。將該數值除以2之後寫回到了PCM檔案中。下圖為輸入PCM雙聲道音訊取樣資料的波形圖。


下圖為輸出的左聲道經過處理後的波形圖。可以看出左聲道的波形幅度降低了一半。



(3)將PCM16LE雙聲道音訊取樣資料的聲音速度提高一倍

本程式中的函式可以通過抽象的方式將PCM16LE雙聲道資料的速度提高一倍。函式的程式碼如下所示。
  1. /** 
  2.  * Re-sample to double the speed of 16LE PCM file 
  3.  * @param url  Location of PCM file. 
  4.  */
  5. int simplest_pcm16le_doublespeed(char *url){  
  6.     FILE *fp=fopen(url,"rb+");  
  7.     FILE *fp1=fopen("output_doublespeed.pcm","wb+");  
  8.     int cnt=0;  
  9.     unsigned char *sample=(unsigned char *)malloc(4);  
  10.     while(!feof(fp)){  
  11.         fread(sample,1,4,fp);  
  12.         if(cnt%2!=0){  
  13.             //L
  14.             fwrite(sample,1,2,fp1);  
  15.             //R
  16.             fwrite(sample+2,1,2,fp1);  
  17.         }  
  18.         cnt++;  
  19.     }  
  20.     printf("Sample Cnt:%d\n",cnt);  
  21.     free(sample);  
  22.     fclose(fp);  
  23.     fclose(fp1);  
  24.     return 0;  
  25. }  

呼叫上面函式的方法如下所示。
  1. simplest_pcm16le_doublespeed("NocturneNo2inEflat_44.1k_s16le.pcm");  

從原始碼可以看出,本程式只採樣了每個聲道奇數點的樣值。處理完成後,原本22秒左右的音訊變成了11秒左右。音訊的播放速度提高了2倍,音訊的音調也變高了很多。下圖為輸入PCM雙聲道音訊取樣資料的波形圖。


下圖為輸出的PCM雙聲道音訊取樣資料的波形圖。通過時間軸可以看出音訊變短了很多。



(4)將PCM16LE雙聲道音訊取樣資料轉換為PCM8音訊取樣資料

本程式中的函式可以通過計算的方式將PCM16LE雙聲道資料16bit的取樣位數轉換為8bit。函式的程式碼如下所示。

  1. /** 
  2.  * Convert PCM-16 data to PCM-8 data. 
  3.  * @param url  Location of PCM file. 
  4.  */
  5. int simplest_pcm16le_to_pcm8(char *url){  
  6.     FILE *fp=fopen(url,"rb+");  
  7.     FILE *fp1=fopen("output_8.pcm"