1. 程式人生 > >Windows下wave API 音訊採集

Windows下wave API 音訊採集

目的:Windows下wave API採集PCM

環境:

系統:Win10
環境:VS2015 64bit

操作步驟:

1. 匯入系統wave標頭檔案及庫

#include <mmsystem.h>   
#pragma comment(lib, "winmm.lib") 

2. 獲取音訊裝置資訊 - 確認是否有音訊輸入裝置

1. UINT WINAPI waveInGetNumDevs(void); //返回音訊輸入裝置的數量

2. MMRESULT WINAPI waveInGetDevCaps(   //獲取輸入裝置資訊
    _In_ UINT_PTR uDeviceID,
    _Out_writes_bytes_(cbwic) LPWAVEINCAPSW pwic,
    _In_ UINT cbwic
    );

3.
示例: int iAudioDev = waveInGetNumDevs(); //獲取裝置數量 printf("[Capture] input device Num:[%d]\n", iAudioDev); for (int i = 0; i < iAudioDev; i++) //迴圈列印裝置的資訊 { WAVEINCAPS wic; waveInGetDevCaps(i, &wic, sizeof(WAVEINCAPS)); //注意,i即為DeviceID printf("[Capture] DeviceNum:[%d], DevicePID:[%d], DeviceName:%s\n"
, i, wic.wPid, wic.szPname); } ps: 實際測試筆記本可以獲取到兩個音訊輸入裝置,DeviceID分別為01。當在waveInOpen()開啟0的時候是系統聲音+麥克風採集;當在waveInOpen()開啟1的時候是麥克風採集,沒有系統音。

3. 開啟音訊裝置

1. 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; 2. WINMMAPI MMRESULT WINAPI waveInOpen( //開啟音訊裝置 _Out_opt_ LPHWAVEIN phwi, _In_ UINT uDeviceID, _In_ LPCWAVEFORMATEX pwfx, _In_opt_ DWORD_PTR dwCallback, _In_opt_ DWORD_PTR dwInstance, _In_ DWORD fdwOpen ); 3. 示例: HWAVEIN hWave; WAVEFORMATEX waveForm; memset(&waveForm, 0, sizeof(WAVEFORMATEX)); waveForm.wFormatTag = WAVE_FORMAT_PCM; waveForm.nChannels = 1; waveForm.nSamplesPerSec = 8000; waveForm.nAvgBytesPerSec = waveForm.nChannels*waveForm.nSamplesPerSec*waveForm.wBitsPerSample / 8; waveForm.nBlockAlign = 4; waveForm.wBitsPerSample = 16; waveForm.cbSize = 0; waveInOpen(&hWave, WAVE_MAPPER, &waveForm, (DWORD_PTR)waveCaptureProc, 0, CALLBACK_FUNCTION); //waveInProc音訊採集回掉,在設定的回掉函式中獲取音訊資料,DeviceID可以預設設定為WAVE_MAPPER,自動選擇採集(系統音+麥克風,類似於 0), DeviceID設定為非0值,即是對應的麥克風聲音。 //回撥函式 void CALLBACK waveCaptureProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { LPWAVEHDR waveH = (LPWAVEHDR)dwParam1; if ((WIM_DATA == uMsg)) { fwrite(waveH->lpData, 1, waveH->dwBytesRecorded, fpInPCM); //寫出資料 printf("Capture:[%d]\n", waveH->dwBytesRecorded); waveInAddBuffer(hwi, waveH, sizeof(WAVEHDR)); //buffer重新放入採集佇列 } } ps:回掉或者主執行緒可以做策略來實現錄音的停止控制

4. 裝置音訊緩衝區,並開始錄音

1. 填充WAVEHDR結構體。
2. WINMMAPI MMRESULT WINAPI waveInPrepareHeader( // 準備緩衝區
    _In_ HWAVEIN hwi,
    _Inout_updates_bytes_(cbwh) LPWAVEHDR pwh,
    _In_ UINT cbwh
    );
3. WINMMAPI MMRESULT WINAPI waveInAddBuffer( //新增buffer到音訊採集
    _In_ HWAVEIN hwi,
    _Inout_updates_bytes_(cbwh) LPWAVEHDR pwh,
    _In_ UINT cbwh
    );
4. WINMMAPI MMRESULT WINAPI waveInStart(_In_ HWAVEIN hwi);//開始錄音

示例:
#define FRAGMENT_SIZE 1024        // 設定快取區大小  
#define FRAGMENT_NUM 4            // 設定快取區個數  
static WAVEHDR waveHDR[FRAGMENT_NUM];  
for (int i = 0; i<FRAGMENT_NUM; i++)
{
    waveHDR[i].lpData = new char[FRAGMENT_SIZE];
    waveHDR[i].dwBufferLength = FRAGMENT_SIZE;
    waveHDR[i].dwBytesRecorded = 0;
    waveHDR[i].dwUser = NULL;
    waveHDR[i].dwFlags = 0;
    waveHDR[i].dwLoops = 1;
    waveHDR[i].lpNext = NULL;
    waveHDR[i].reserved = 0;

    waveInPrepareHeader(hWave, &waveHDR[i], sizeof(WAVEHDR));
    waveInAddBuffer(hWave, &waveHDR[i], sizeof(WAVEHDR));
}

waveInStart(hWave);

5. 錄音結束關閉

1. WINMMAPI MMRESULT WINAPI waveInStop(_In_ HWAVEIN hwi);
2. WINMMAPI MMRESULT WINAPI waveInReset(_In_ HWAVEIN hwi);
3. 釋放新增的緩衝區
4. WINMMAPI MMRESULT WINAPI waveInClose(_In_ HWAVEIN hwi);

示例:
waveInStop(hWave);
waveInReset(hWave);

for (int i = 0; i < FRAGMENT_NUM; i++)
{
    waveInUnprepareHeader(hWave, &waveHDR[i], sizeof(WAVEHDR));
    delete waveHDR[i].lpData;
    delete waveHDR[i].lpData = NULL;
}

waveInClose(hWave);