1. 程式人生 > >錄音播放系統(實現pcm轉碼mp3,wav)

錄音播放系統(實現pcm轉碼mp3,wav)

本文介紹了基於waveX低階音訊API採集音訊,然後播放的技術,也支援實時的播放。對於將錄音和播放分開的做法原因是為了儲存pcm檔案(未經壓縮過的音訊檔案),

然後轉碼wav和mp3格式。

PCM(Pulse code modulation):脈衝編碼調製 ,即對波形按照固定週期頻率取樣。為了保證取樣後資料質量,取樣頻率必須是樣本聲音最高頻率的兩倍,這就是 Nyquist 頻率 .


任務:C++開發---錄音播放系統

實現的功能:

1,麥克風錄入聲音,儲存為pcm(未壓縮的音訊檔案格式)檔案

2,播放pcm聲音檔案

3,將pcm檔案轉碼為mp3,wav格式。

一:基礎知識點

關於音訊相關的知識點以及音訊格式推薦兩篇文章給大家看

第一篇:音訊格式 第二篇:音訊檔案

二:錄入和播放流程

對於音訊的錄入和播放我們採用waveX的API介面,在介紹這些函式介面之前,我們必須明確聲音在採集(錄音)和播放的時需要有一些統一的格式,包括音訊格式型別,聲道,取樣率等資訊。下面的資料結構具體描述了該格式:

typedef struct tWAVEFORMATEX
{
    WORD        wFormatTag;         /* format type */
    WORD        nChannels;          /* number of channels (i.e. mono, stereo...) */
    DWORD       nSamplesPerSec;     /* sample rate */
    DWORD       nAvgBytesPerSec;    /* for buffer estimation */
    WORD        nBlockAlign;        /* block size of data */
    WORD        wBitsPerSample;     /* number of bits per sample of mono data */
    WORD        cbSize;             /* the count in bytes of the size of */
                                    /* extra information (after cbSize) */
} WAVEFORMATEX, *PWAVEFORMATEX, NEAR *NPWAVEFORMATEX, FAR *LPWAVEFORMATEX;
其中,wFormatTag是音訊格式型別,一般預設為PCM_FORMAT_PCM,nChannels是聲道數,nSamplesPerSec是取樣頻率,nAvgBytesPerSec是每秒鐘的位元組數,nBlockAlign是每個樣本的位元組數,wBitsPerSample是每個樣本的量化位數,cbSize是附加資訊的位元組大小。

無論在錄入和播放的時候,必須有存音訊檔案的結構,其內容如下

typedef struct { 
    LPSTR  lpData; //記憶體指標
    DWORD  dwBufferLength;//長度 
    DWORD  dwBytesRecorded; //已錄音的位元組長度
    DWORD  dwUser; 
    DWORD  dwFlags; 
    DWORD  dwLoops; //迴圈次數
    struct wavehdr_tag * lpNext; 
    DWORD  reserved; 
} WAVEHDR;  
其中,lpData是指定的緩衝塊地址,dwBufferLength是指定的緩衝塊大小,dwBytesRecorded是已錄音資料大小,dwUser是使用者資料,dwFlags是控制標誌,表明緩衝的使用狀態,dwLoops是音訊輸出時緩衝資料塊迴圈的次數,lpNext和reserved是系統保留資料。在程式實現時,通過設定或修改這個結構的相關引數來實現對音訊輸入和輸出緩衝區的控制。

音效卡輸入和輸出的音訊屬性可定義如下:

mywaveform.wFormatTag = WAVE_FORMAT_PCM;
mywaveform.nChannels = 1;
mywaveform.nSamplesPerSec = 8000;
mywaveform.wBitsPerSample = 16;
mywaveform.cbSize = 0;
mywaveform.nBlockAlign = (mywaveform.wBitsPerSample * mywaveform.nChannels) >> 3;
mywaveform.nAvgBytesPerSec = mywaveform.nBlockAlign * mywaveform.nSamplesPerSec;

設定的音訊格式型別是PCM格式,單通道,8000HZ的取樣率。

音訊資料塊頭結構定義如下

pWaveHdr->lpData            = pDataBuf;     // 指定緩衝的地址;
pWaveHdr->dwBufferLength    = bufferLength; //指定緩衝的大小;
pWaveHdr->dwBytesRecorded   = 0 ;
pWaveHdr->dwUser            = 0 ;
pWaveHdr->dwFlags           = 0 ;
pWaveHdr->dwLoops           = 1 ;
pWaveHdr->lpNext            = NULL;
pWaveHdr->reserved          = 0;

呼叫WaveX低階音訊函式API啟動音效卡錄音的基本操作步驟如下圖所示:

              開啟錄音裝置: waveInOpen
                            ↓
         為錄音裝置準備快取: waveInPrepareHeader
                            ↓
         為輸入裝置增加快取: waveInAddBuffer
                            ↓
                   啟動錄音: waveInStart
                            ↓
                   清除快取: waveInUnprepareHeader
                            ↓
                   停止錄音: waveInReset
                            ↓
               關閉錄音裝置: waveInClose
錄音流程
        在這個過程中,會產生很多WM_WIM_***格式的WINDOWS訊息。程式通過捕獲這些訊息對快取,資料和裝置進行處理,具體可見後面章節。錄音裝置開啟時,可以指定訊息的響應方式:回掉函式,執行緒ID,WINDOWS視窗控制代碼或事件控制代碼等。

MMRESULT waveInOpen(
    LPHWAVEIN phwi,             //輸入裝置控制代碼
    UINT uDeviceID,             //輸入裝置ID
    LPWAVEFORMATEX pwfx,        //錄音格式指標
    DWORD dwCallback,           //處理訊息的回撥函式或視窗控制代碼,執行緒ID等
    DWORD dwCallbackInstance,   //通常為0
    DWORD fdwOpen               //處理訊息方式的符號位
);

呼叫WaveX低階音訊函式API啟動音效卡輸出的基本操作步驟如下圖所示:

               開啟輸出裝置: waveOutOpen
                            ↓
         為輸出裝置準備快取: waveOutPrepareHeader
                            ↓
       寫資料導輸出裝置快取: waveOutWrite
                            ↓
               清除輸出快取: waveOutUnprepareHeader
                            ↓
                   停止輸出: waveOutReset
                            ↓
               關閉輸出裝置: waveOutClose       

WINDOWS下提供訊息對映實現事件的處理。低階音訊函式處理聲音資料塊也正要歸功於WINDOWS的訊息對映機制。

我們的代買中主要是運用所提供的回撥函式,來處理一些訊息。

分別描述了音效卡錄音和播放過程中產生的訊息:

        WIM_OPEN
            │     ↘
            │       音訊輸入裝置開啟訊息
            ↓
        WIM_DATA
            │     ↘
            │       緩衝錄滿或停止錄音訊息
            ↓
        WIM_CLOSE
            │      ↘
            ↓       音訊輸入裝置關閉訊息
 




        WOM_OPEN
            │     ↘
            │       音訊輸出 裝置開啟訊息
            ↓
        WOM_DONE
            │     ↘
            │       緩衝播放完或停止輸出訊息
            ↓
        WOM_CLOSE
            │      ↘
            ↓       音訊輸出裝置關閉訊息

三:運用lame庫實現檔案的轉換pcm-->mp3

pcm-->wav只需要新增wav檔案的一些頭格式,然後把pcm檔案內容全部寫進去,就生成了wav檔案。這是因為wav檔案本來就是未經壓縮的pcm檔案構成,知識添加了用以標示wav檔案的頭格式而已。所以當你生成wav檔案後你就會發現,其實它的大小沒有多大改變。MP3格式則不同,它會小得多。而且必須注意的是,MP3檔案在設定格式的時候好像必須是雙聲道,取樣率是44100。

我們轉MP3檔案時,用到的就是libmp3lame這個庫檔案,所以你必須在網上下載這個庫,並且配置好標頭檔案lame.h,動態庫,靜態庫路徑,這樣才會編譯過去。具體的配置問問度娘就好了。或者你直接去下載我的原始碼,把檔案中的庫檔案拿出來就好,不過路徑還得你自己配置了。

關於lame庫API的介紹詳情請參考原始碼。

四:難點和重點

完成這個系統後,感覺最大的難點和重點就是對waveX函式簇的理解,包括各種API的引數,音訊配置格式(這決定了你播放時聽到的音訊質量),還有回撥函式裡訊息處理的理解,還有如何解決播放時的實時性和連續性(一般採用多緩衝技術)。對於後面的轉碼函式,你必須清楚地瞭解你的原始檔pcm音訊檔案的格式,還有所需要轉碼成的MP3,wav音訊檔案的格式。還有系統開發完成後使用者測試時的一些問題。比如,如何停止錄音,實時播放錄音。錄音檔案的非追加性等等。

雙/多緩衝技術可以很好的實現聲音的快速連續採集和實時順暢播放。採集聲音時,緩衝滿了會有一個訊息,程式在響應這個訊息需要幾毫秒~幾十毫秒甚至更多的時間,假設為Xms,如果只使用一個緩衝,程式必須在響應完該訊息才再次採集聲音,那麼在這Xms的時間裡,沒有采集到任何聲音;聲音的播放也是一樣的道理,這樣聲音就會不連續。因此雙緩衝或多緩衝技術是必要的,讓輸入和輸出裝置可以迴圈使用這些緩衝,當程式在響應某塊緩衝資料已滿或播放完畢訊息時,音效卡可以繼續往下一塊緩衝新增資料或播放下一塊緩衝的資料,如此迴圈保障聲音的連續性。

五:原始碼

具體操作介面:


必須注意的是每次2選項播放檔案後,必須4停止播放。然後通過6,7,選項即可生成mp3,wav格式

已生成具體的檔案:


測試已成功!

博主水平有限,系統還存在很多問題,還請各位讀者不吝賜教!!!

以下是些參考資料,讀者可能會看到很多內容都是抄襲,博主只是覺得人家總結的很清楚,對於你們的理解有幫助,所以還請各位嘴下留情!