音視訊資料處理(6)--- PCM音訊取樣資料處理程式碼實現
音訊取樣資料在視訊播放器的解碼流程中的位置如下圖所示。
分離PCM16LE雙聲道音訊取樣資料的左聲道和右聲道
將PCM16LE雙聲道音訊取樣資料中左聲道的音量降一半
將PCM16LE雙聲道音訊取樣資料的聲音速度提高一倍
將PCM16LE雙聲道音訊取樣資料轉換為PCM8音訊取樣資料
從PCM16LE單聲道音訊取樣資料中擷取一部分資料
將PCM16LE雙聲道音訊取樣資料轉換為WAVE格式音訊資料
注:PCM音訊資料可以使用音訊編輯軟體匯入檢視。例如收費的專業音訊編輯軟體Adobe Audition,或者免費開源的音訊編輯軟體
函式列表
(1)分離PCM16LE雙聲道音訊取樣資料的左聲道和右聲道
本程式中的函式可以將PCM16LE雙聲道資料中左聲道和右聲道的資料分離成兩個檔案。函式的程式碼如下所示。- /**
- * Split Left and Right channel of 16LE PCM file.
- * @param url Location of PCM file.
- *
- */
- int simplest_pcm16le_split(char *url){
- FILE *fp=fopen(url,"rb+");
-
FILE
- FILE *fp2=fopen("output_r.pcm","wb+");
- unsigned char *sample=(unsigned char *)malloc(4);
- while(!feof(fp)){
- fread(sample,1,4,fp);
- //L
- fwrite(sample,1,2,fp1);
- //R
- fwrite(sample+2,1,2,fp2);
-
}
- free(sample);
- fclose(fp);
- fclose(fp1);
- fclose(fp2);
- return 0;
- }
呼叫上面函式的方法如下所示。
- simplest_pcm16le_split("NocturneNo2inEflat_44.1k_s16le.pcm");
從程式碼可以看出,PCM16LE雙聲道資料中左聲道和右聲道的取樣值是間隔儲存的。每個取樣值佔用2Byte空間。程式碼執行後,會把NocturneNo2inEflat_44.1k_s16le.pcm的PCM16LE格式的資料分離為兩個單聲道資料:
output_l.pcm:左聲道資料。注:本文中聲音樣值的取樣頻率一律是44100Hz,取樣格式一律為16LE。“16”代表取樣位數是16bit。由於1Byte=8bit,所以一個聲道的一個取樣值佔用2Byte。“LE”代表Little Endian,代表2 Byte取樣值的儲存方式為高位存在高地址中。output_r.pcm:右聲道資料。
下圖為輸入的雙聲道PCM資料的波形圖。上面的波形圖是左聲道的圖形,下面的波形圖是右聲道的波形。圖中的橫座標是時間,總長度為22秒;縱座標是取樣值,取值範圍從-32768到32767。
下圖為分離後左聲道資料output_l.pcm的音訊波形圖。
下圖為分離後右聲道資料output_r.pcm的音訊波形圖。
(2)將PCM16LE雙聲道音訊取樣資料中左聲道的音量降一半
本程式中的函式可以將PCM16LE雙聲道資料中左聲道的音量降低一半。函式的程式碼如下所示。- /**
- * Halve volume of Left channel of 16LE PCM file
- * @param url Location of PCM file.
- */
- int simplest_pcm16le_halfvolumeleft(char *url){
- FILE *fp=fopen(url,"rb+");
- FILE *fp1=fopen("output_halfleft.pcm","wb+");
- int cnt=0;
- unsigned char *sample=(unsigned char *)malloc(4);
- while(!feof(fp)){
- short *samplenum=NULL;
- fread(sample,1,4,fp);
- samplenum=(short *)sample;
- *samplenum=*samplenum/2;
- //L
- fwrite(sample,1,2,fp1);
- //R
- fwrite(sample+2,1,2,fp1);
- cnt++;
- }
- printf("Sample Cnt:%d\n",cnt);
- free(sample);
- fclose(fp);
- fclose(fp1);
- return 0;
- }
呼叫上面函式的方法如下所示。
- simplest_pcm16le_halfvolumeleft("NocturneNo2inEflat_44.1k_s16le.pcm");
從原始碼可以看出,本程式在讀出左聲道的2 Byte的取樣值之後,將其當成了C語言中的一個short型別的變數。將該數值除以2之後寫回到了PCM檔案中。下圖為輸入PCM雙聲道音訊取樣資料的波形圖。
下圖為輸出的左聲道經過處理後的波形圖。可以看出左聲道的波形幅度降低了一半。
(3)將PCM16LE雙聲道音訊取樣資料的聲音速度提高一倍
本程式中的函式可以通過抽象的方式將PCM16LE雙聲道資料的速度提高一倍。函式的程式碼如下所示。- /**
- * Re-sample to double the speed of 16LE PCM file
- * @param url Location of PCM file.
- */
- int simplest_pcm16le_doublespeed(char *url){
- FILE *fp=fopen(url,"rb+");
- FILE *fp1=fopen("output_doublespeed.pcm","wb+");
- int cnt=0;
- unsigned char *sample=(unsigned char *)malloc(4);
- while(!feof(fp)){
- fread(sample,1,4,fp);
- if(cnt%2!=0){
- //L
- fwrite(sample,1,2,fp1);
- //R
- fwrite(sample+2,1,2,fp1);
- }
- cnt++;
- }
- printf("Sample Cnt:%d\n",cnt);
- free(sample);
- fclose(fp);
- fclose(fp1);
- return 0;
- }
呼叫上面函式的方法如下所示。
- simplest_pcm16le_doublespeed("NocturneNo2inEflat_44.1k_s16le.pcm");
從原始碼可以看出,本程式只採樣了每個聲道奇數點的樣值。處理完成後,原本22秒左右的音訊變成了11秒左右。音訊的播放速度提高了2倍,音訊的音調也變高了很多。下圖為輸入PCM雙聲道音訊取樣資料的波形圖。
下圖為輸出的PCM雙聲道音訊取樣資料的波形圖。通過時間軸可以看出音訊變短了很多。
(4)將PCM16LE雙聲道音訊取樣資料轉換為PCM8音訊取樣資料
本程式中的函式可以通過計算的方式將PCM16LE雙聲道資料16bit的取樣位數轉換為8bit。函式的程式碼如下所示。
- /**
- * Convert PCM-16 data to PCM-8 data.
- * @param url Location of PCM file.
- */
- int simplest_pcm16le_to_pcm8(char *url){
- FILE *fp=fopen(url,"rb+");
- FILE *fp1=fopen("output_8.pcm"