Windows下wave API 音訊採集
阿新 • • 發佈:2019-01-02
目的: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分別為0和1。當在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);