日本a√视频在线,久久青青亚洲国产,亚洲一区欧美二区,免费g片在线观看网站

        <style id="k3y6c"><u id="k3y6c"></u></style>
        <s id="k3y6c"></s>
        <mark id="k3y6c"></mark>
          
          

          <mark id="k3y6c"></mark>

          "); //-->

          博客專欄

          EEPW首頁 > 博客 > 多媒體文件格式(五):PCM / WAV 格式

          多媒體文件格式(五):PCM / WAV 格式

          發(fā)布人:電子禪石 時間:2024-08-11 來源:工程師 發(fā)布文章

          本文目錄:

          • 一、名詞解析

          • 二、PCM 

          • 三、WAV

          • 三、PCM & WAV 開發(fā)實踐

          • 參考資料:


          一、名詞解析

          PCM(Pulse Code Modulation)也被稱為脈碼編碼調(diào)制,PCM中的聲音數(shù)據(jù)沒有被壓縮,它是由模擬信號經(jīng)過采樣、量化、編碼轉(zhuǎn)換成的標準的數(shù)字音頻數(shù)據(jù)。采樣轉(zhuǎn)換方式參考下圖進行了解:

          音頻采樣包含以下幾大要素:

          1. 采樣率

          采樣率表示音頻信號每秒的數(shù)字快照數(shù)。該速率決定了音頻文件的頻率范圍。采樣率越高,數(shù)字波形的形狀越接近原始模擬波形。低采樣率會限制可錄制的頻率范圍,這可導(dǎo)致錄音表現(xiàn)原始聲音的效果不佳。根據(jù)奈奎斯特采樣定理,為了重現(xiàn)給定頻率,采樣率必須至少是該頻率的兩倍。例如,一般CD唱片的采樣率為每秒 44,100 個采樣,因此可重現(xiàn)最高為 22,050 Hz 的頻率,此頻率剛好超過人類的聽力極限 20,000 Hz。

           

          圖中A是低采樣率的音頻信號,其效果已經(jīng)將原始聲波進行了扭曲,B則是完全重現(xiàn)原始聲波的高采樣率的音頻信號。

          數(shù)字音頻常用的采樣率如下:

          2. 位深度 

          位深度決定動態(tài)范圍。采樣聲波時,為每個采樣指定最接近原始聲波振幅的振幅值。較高的位深度可提供更多可能的振幅值,產(chǎn)生更大的動態(tài)范圍、更低的噪聲基準和更高的保真度。

          位深度越高,提供的動態(tài)范圍越大。 

          二、PCM 

          在上面的名詞解析中我們應(yīng)該對PCM有了一定的理解和認識,下面我們將對PCM做更多的講解。

          1. PCM音頻數(shù)據(jù)存儲方式

          如果是單聲道的文件,采樣數(shù)據(jù)按時間的先后順序依次存入。如果是單聲道的音頻文件,采樣數(shù)據(jù)按時間的先后順序依次存入(也可能采用 LRLRLR 方式存儲,只是另一個聲道的數(shù)據(jù)為 0)。

          如果是雙聲道的話通常按照 LRLRLR 的方式存儲,存儲的時候還和機器的大小端有關(guān)。(關(guān)于字節(jié)序大小端的相關(guān)內(nèi)容可參考《字節(jié)序問題之大小端模式講解》進行了解)

          PCM的存儲方式為小端模式,存儲Data數(shù)據(jù)排列如下圖所示:

          2. PCM 音頻數(shù)據(jù)的參數(shù)

          描述 PCM 音頻數(shù)據(jù)的參數(shù)的時候有如下描述方式:

          44100HZ 16bit stereo: 每秒鐘有 44100 次采樣, 采樣數(shù)據(jù)用 16 位(2 字節(jié))記錄, 雙聲道(立體聲)
          22050HZ 8bit  mono: 每秒鐘有 22050 次采樣, 采樣數(shù)據(jù)用 8 位(1 字節(jié))記錄, 單聲道
          48000HZ 32bit 51ch: 每秒鐘有 48000 次采樣, 采樣數(shù)據(jù)用 32 位(4 字節(jié)浮點型)記錄, 5.1 聲道

          44100Hz 指的是采樣率,它的意思是每秒取樣 44100 次。采樣率越大,存儲數(shù)字音頻所占的空間就越大。

          16bit 指的是采樣精度,意思是原始模擬信號被采樣后,每一個采樣點在計算機中用 16 位(兩個字節(jié))來表示。采樣精度越高越能精細地表示模擬信號的差異。

          Stereo 指的是聲道數(shù),也即采樣時用到的麥克風(fēng)的數(shù)量,麥克風(fēng)越多就越能還原真實的采樣環(huán)境(當然麥克風(fēng)的放置位置也是有規(guī)定的)。

          三、WAV

          WAV 是 Microsoft 和 IBM 為 PC 開發(fā)的一種聲音文件格式,它符合 RIFF(Resource Interchange File Format)文件規(guī)范,用于保存 Windows 平臺的音頻信息資源,被 Windows 平臺及其應(yīng)用程序所廣泛支持。WAVE 文件通常只是一個具有單個 “WAVE” 塊的 RIFF 文件,該塊由兩個子塊(”fmt” 子數(shù)據(jù)塊和 ”data” 子數(shù)據(jù)塊),它的格式如下圖所示:

          WAV 格式定義

          該格式的實質(zhì)就是在 PCM 文件的前面加了一個文件頭,每個字段的的含義如下:

          復(fù)制代碼
          typedef struct {    char          ChunkID[4]; //內(nèi)容為"RIFF"
              unsigned long ChunkSize;  //存儲文件的字節(jié)數(shù)(不包含ChunkID和ChunkSize這8個字節(jié))
              char          Format[4];  //內(nèi)容為"WAVE“} WAVE_HEADER;
           
          typedef struct {   char           Subchunk1ID[4]; //內(nèi)容為"fmt"
             unsigned long  Subchunk1Size;  //存儲該子塊的字節(jié)數(shù)(不含前面的Subchunk1ID和Subchunk1Size這8個字節(jié))
             unsigned short AudioFormat;    //存儲音頻文件的編碼格式,例如若為PCM則其存儲值為1。
             unsigned short NumChannels;    //聲道數(shù),單聲道(Mono)值為1,雙聲道(Stereo)值為2,等等
             unsigned long  SampleRate;     //采樣率,如8k,44.1k等
             unsigned long  ByteRate;       //每秒存儲的bit數(shù),其值 = SampleRate * NumChannels * BitsPerSample / 8
             unsigned short BlockAlign;     //塊對齊大小,其值 = NumChannels * BitsPerSample / 8
             unsigned short BitsPerSample;  //每個采樣點的bit數(shù),一般為8,16,32等。} WAVE_FMT;
           
          typedef struct {   char          Subchunk2ID[4]; //內(nèi)容為“data”
             unsigned long Subchunk2Size;  //接下來的正式的數(shù)據(jù)部分的字節(jié)數(shù),其值 = NumSamples * NumChannels * BitsPerSample / 8} WAVE_DATA;
          復(fù)制代碼

          WAV 文件頭解析

          這里是一個 WAVE 文件的開頭 72 字節(jié),字節(jié)顯示為十六進制數(shù)字:

          復(fù)制代碼
          52 49 46 46 | 24 08 00 00 | 57 41 56 4566 6d 74 20 | 10 00 00 00 | 01 00 02 00 22 56 00 00 | 88 58 01 00 | 04 00 10 0064 61 74 61 | 00 08 00 00 | 00 00 00 00 24 17 1E F3 | 3C 13 3C 14 | 16 F9 18 F934 E7 23 A6 | 3C F2 24 F2 | 11 CE 1A 0D
          復(fù)制代碼

          字段解析如下圖:

          三、PCM & WAV 開發(fā)實踐

          1. PCM格式轉(zhuǎn)為WAV格式(基于C語言)
          復(fù)制代碼
          int simplest_pcm16le_to_wave(const char *pcmpath,int channels,int sample_rate,const char *wavepath)
          {
              typedef struct WAVE_HEADER{  
                  char         fccID[4];        
                  unsigned   long    dwSize;            
                  char         fccType[4];    
              }WAVE_HEADER;  
              typedef struct WAVE_FMT{  
                  char         fccID[4];        
                  unsigned   long       dwSize;            
                  unsigned   short     wFormatTag;    
                  unsigned   short     wChannels;  
                  unsigned   long       dwSamplesPerSec;  
                  unsigned   long       dwAvgBytesPerSec;  
                  unsigned   short     wBlockAlign;  
                  unsigned   short     uiBitsPerSample;  
              }WAVE_FMT;  
              typedef struct WAVE_DATA{  
                  char       fccID[4];          
                  unsigned long dwSize;              
              }WAVE_DATA;  
              if(channels==0||sample_rate==0){
              channels = 2;
              sample_rate = 44100;
              }    int bits = 16;
              WAVE_HEADER   pcmHEADER;  
              WAVE_FMT   pcmFMT;  
              WAVE_DATA   pcmDATA;  
           
              unsigned   short   m_pcmData;
              FILE   *fp,*fpout;  
              fp=fopen(pcmpath, "rb");    if(fp == NULL) {  
                  printf("open pcm file error\n");        return -1;  
              }
              fpout=fopen(wavepath,   "wb+");    if(fpout == NULL) {    
                  printf("create wav file error\n");  
                  return -1; 
              }        
              //WAVE_HEADER
              memcpy(pcmHEADER.fccID,"RIFF",strlen("RIFF"));                    
              memcpy(pcmHEADER.fccType,"WAVE",strlen("WAVE"));  
              fseek(fpout,sizeof(WAVE_HEADER),1); 
              //WAVE_FMT
              pcmFMT.dwSamplesPerSec=sample_rate;  
              pcmFMT.dwAvgBytesPerSec=pcmFMT.dwSamplesPerSec*sizeof(m_pcmData);  
              pcmFMT.uiBitsPerSample=bits;
              memcpy(pcmFMT.fccID,"fmt ",strlen("fmt "));  
              pcmFMT.dwSize=16;  
              pcmFMT.wBlockAlign=2;  
              pcmFMT.wChannels=channels;  
              pcmFMT.wFormatTag=1;  
           
              fwrite(&pcmFMT,sizeof(WAVE_FMT),1,fpout); 
              //WAVE_DATA;
              memcpy(pcmDATA.fccID,"data",strlen("data"));  
              pcmDATA.dwSize=0;
              fseek(fpout,sizeof(WAVE_DATA),SEEK_CUR);
              fread(&m_pcmData,sizeof(unsigned short),1,fp);    while(!feof(fp)){  
                  pcmDATA.dwSize+=2;
                  fwrite(&m_pcmData,sizeof(unsigned short),1,fpout);
                  fread(&m_pcmData,sizeof(unsigned short),1,fp);
              }  
              pcmHEADER.dwSize=44+pcmDATA.dwSize;
              rewind(fpout);
              fwrite(&pcmHEADER,sizeof(WAVE_HEADER),1,fpout);
              fseek(fpout,sizeof(WAVE_FMT),SEEK_CUR);
              fwrite(&pcmDATA,sizeof(WAVE_DATA),1,fpout);
              
              fclose(fp);
              fclose(fpout);    return 0;
          }
          復(fù)制代碼

           

          注意:函數(shù)里聲明的數(shù)據(jù)類型unsigned long在有些C編譯器上是64位的,這時候要改成unsigned int才可以,否則wav頭有88bytes,標準的是44bytes,改完就正常了,對C還不熟悉的人小小的心得,另外,聲道數(shù)和采樣率也要注意,一般采樣率有44100/16000/8000,要確認是哪個,聲道是1還是2,這兩個參數(shù)要設(shè)置好才會有正確的轉(zhuǎn)換結(jié)果。 

          2. PCM降低某個聲道的音量(基于C語言)

          一般來說 PCM 數(shù)據(jù)中的波形幅值越大,代表音量越大,對于 PCM 音頻數(shù)據(jù)而言,它的幅值(即該采樣點采樣值的大?。┐硪袅康拇笮?。

          如果我們需要降低某個聲道的音量,可以通過減小某個聲道的數(shù)據(jù)的值來實現(xiàn)降低某個聲道的音量。

          復(fù)制代碼
          int pcm16le_half_volume_left( char *url ) {
              FILE *fp_in = fopen( url, "rb+" );
              FILE *fp_out = fopen( "output_half_left.pcm", "wb+" );
              unsigned char *sample = ( unsigned char * )malloc(4); // 一次讀取一個sample,因為是2聲道,所以是4字節(jié) 
              while ( !feof( fp_in ) ){
                  fread( sample, 1, 4, fp_in );        short* sample_num = ( short* )sample; // 轉(zhuǎn)成左右聲道兩個short數(shù)據(jù)
                  *sample_num = *sample_num / 2; // 左聲道數(shù)據(jù)減半
                  fwrite( sample, 1, 2, fp_out ); // L
                  fwrite( sample + 2, 1, 2, fp_out ); // R    }    free( sample );
              fclose( fp_in );
              fclose( fp_out );    return 0;
          }
          復(fù)制代碼

          上述代碼做的事情是:在讀出左聲道的 2 Byte 的取樣值之后,將其轉(zhuǎn)成了 C 語言中的一個 short 類型的變量。將該數(shù)值除以 2 之后寫回到了 PCM 文件中。

          3. 分離PCM音頻數(shù)據(jù)左右聲道的數(shù)據(jù)

          因為PCM音頻數(shù)據(jù)是按照LRLRLR的方式來存儲左右聲道的音頻數(shù)據(jù)的,所以我們可以通過將它們交叉的讀出來的方式來分離左右聲道的數(shù)據(jù):

          復(fù)制代碼
          int simplest_pcm16le_split(char *url) {
              FILE *fp=fopen(url,"rb+");
              FILE *fp1=fopen("output_l.pcm","wb+");
              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;
          }
          復(fù)制代碼

          4. 從PCM16LE單聲道音頻采樣數(shù)據(jù)中截取一部分數(shù)據(jù)

          本程序中的函數(shù)可以從PCM16LE單聲道數(shù)據(jù)中截取一段數(shù)據(jù),并輸出截取數(shù)據(jù)的樣值。函數(shù)的代碼如下所示:

          復(fù)制代碼
          /**
           * Cut a 16LE PCM single channel file.
           * @param url        Location of PCM file.
           * @param start_num  start point
           * @param dur_num    how much point to cut */int simplest_pcm16le_cut_singlechannel(char *url,int start_num,int dur_num){
              FILE *fp=fopen(url,"rb+");
              FILE *fp1=fopen("output_cut.pcm","wb+");
              FILE *fp_stat=fopen("output_cut.txt","wb+");
           
              unsigned char *sample=(unsigned char *)malloc(2); 
              int cnt=0;    while(!feof(fp)){
                  fread(sample,1,2,fp);        if(cnt>start_num&&cnt<=(start_num+dur_num)){
                      fwrite(sample,1,2,fp1); 
                      short samplenum=sample[1];
                      samplenum=samplenum*256;
                      samplenum=samplenum+sample[0];
           
                      fprintf(fp_stat,"%6d,",samplenum);            if(cnt%10==0)
                          fprintf(fp_stat,"\n",samplenum);
                  }
                  cnt++;
              } 
              free(sample);
              fclose(fp);
              fclose(fp1);
              fclose(fp_stat);    return 0;
          }
          復(fù)制代碼

          5. 將PCM16LE雙聲道音頻采樣數(shù)據(jù)轉(zhuǎn)換為PCM8音頻采樣數(shù)據(jù)

          本程序中的函數(shù)可以通過計算的方式將PCM16LE雙聲道數(shù)據(jù)16bit的采樣位數(shù)轉(zhuǎn)換為8bit。函數(shù)的代碼如下所示:

          復(fù)制代碼
          /**
           * 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","wb+"); 
              int cnt=0;
           
              unsigned char *sample=(unsigned char *)malloc(4); 
              while(!feof(fp)){ 
                  short *samplenum16=NULL;        char samplenum8=0;
                  unsigned char samplenum8_u=0;
                  fread(sample,1,4,fp);        //(-32768-32767)
                  samplenum16=(short *)sample;
                  samplenum8=(*samplenum16)>>8;        //(0-255)
                  samplenum8_u=samplenum8+128;        //L
                  fwrite(&samplenum8_u,1,1,fp1);
           
                  samplenum16=(short *)(sample+2);
                  samplenum8=(*samplenum16)>>8;
                  samplenum8_u=samplenum8+128;        //R
                  fwrite(&samplenum8_u,1,1,fp1);
                  cnt++;
              }
              printf("Sample Cnt:%d\n",cnt); 
              free(sample);
              fclose(fp);
              fclose(fp1);    return 0;
          }
          復(fù)制代碼

          PCM16LE格式的采樣數(shù)據(jù)的取值范圍是-32768到32767,而PCM8格式的采樣數(shù)據(jù)的取值范圍是0到255。所以PCM16LE轉(zhuǎn)換到PCM8需要經(jīng)過兩個步驟:第一步是將-32768到32767的16bit有符號數(shù)值轉(zhuǎn)換為-128到127的8bit有符號數(shù)值,第二步是將-128到127的8bit有符號數(shù)值轉(zhuǎn)換為0到255的8bit無符號數(shù)值。在本程序中,16bit采樣數(shù)據(jù)是通過short類型變量存儲的,而8bit采樣數(shù)據(jù)是通過unsigned char類型存儲的。

          6. 將PCM16LE雙聲道音頻采樣數(shù)據(jù)的聲音速度提高一倍

          本程序中的函數(shù)可以通過抽象的方式將PCM16LE雙聲道數(shù)據(jù)的速度提高一倍,采用采樣每個聲道奇(偶)數(shù)點的樣值的方式,函數(shù)的代碼如下所示:

          復(fù)制代碼
          /**
           * 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;
          }
          復(fù)制代碼

            

          參考資料:

          視音頻數(shù)據(jù)處理入門:PCM音頻采樣數(shù)據(jù)處理   -->  致敬雷神!


          *博客內(nèi)容為網(wǎng)友個人發(fā)布,僅代表博主個人觀點,如有侵權(quán)請聯(lián)系工作人員刪除。



          關(guān)鍵詞: pcm

          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉