中文字幕 另类精品,亚洲欧美一区二区蜜桃,日本在线精品视频免费,孩交精品乱子片免费

<sup id="3hn2b"></sup>

    1. <sub id="3hn2b"><ol id="3hn2b"></ol></sub><legend id="3hn2b"></legend>

      1. <xmp id="3hn2b"></xmp>

      2. "); //-->

        博客專欄

        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ù)字波形的形狀越接近原始模擬波形。低采樣率會限制可錄制的頻率范圍,這可導致錄音表現(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 

        在上面的名詞解析中我們應該對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ù),也即采樣時用到的麥克風的數(shù)量,麥克風越多就越能還原真實的采樣環(huán)境(當然麥克風的放置位置也是有規(guī)定的)。

        三、WAV

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

        WAV 格式定義

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

        復制代碼
        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;
        復制代碼

        WAV 文件頭解析

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

        復制代碼
        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
        復制代碼

        字段解析如下圖:

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

        1. PCM格式轉(zhuǎn)為WAV格式(基于C語言)
        復制代碼
        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;
        }
        復制代碼

         

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

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

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

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

        復制代碼
        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;
        }
        復制代碼

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

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

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

        復制代碼
        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;
        }
        復制代碼

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

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

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

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

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

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

        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ù)的代碼如下所示:

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

          

        參考資料:

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


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



        關(guān)鍵詞: pcm

        相關(guān)推薦

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

        關(guān)閉