一、錄音的基本流程:
這個流程圖可能不太準確,具體的看下面的分析吧
二、具體步驟
1.使用waveInOpen()函數打開一個音頻設備:
HWAVEIN hWavein;
WAVEFORMATEX waveFormat; //音頻格式
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nChannels = 1; //單聲道
waveFormat.nSamplesPerSec = SAMPLESPERSEC; //採樣率
waveFormat.nAvgBytesPerSec = SAMPLESPERSEC * 2;
waveFormat.nBlockAlign = 2; //最小的塊大小
waveFormat.wBitsPerSample = BITSPERSAMPLE; //採樣精度
DWORD nThreadID;
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)waveInProc, NULL, 0, &nThreadID);
if (hThread == INVALID_HANDLE_VALUE)
{
printf("創建線程失敗!\n");
longjmp(jmpBuf, GetLastError());
}
MMRESULT ret = waveInOpen(&hWavein, 0, &waveFormat, nThreadID, 0, CALLBACK_THREAD); //這裏我們採用線程回調的方式
我們需要在waveInOpen()函數的第二個參數中指定設備的ID,這裏我們給了0表示打開系統中第一個麥克風
2.使用waveInPrepareHeader()和waveInAddBuffer()添加緩衝區:
for (int i = 0; i < 4; ++i) //這裏我們初始化4塊緩衝區
{
WAVEHDR* pWaveHdr = malloc(sizeof(WAVEHDR));
RtlZeroMemory(pWaveHdr, sizeof(WAVEHDR));
pWaveHdr->lpData = malloc(DATASIZE);
pWaveHdr->dwBufferLength = DATASIZE;
pWaveHdr->dwFlags = 0;
ret = waveInPrepareHeader(hWavein, pWaveHdr, sizeof(WAVEHDR));
if (ret != MMSYSERR_NOERROR)
{
printf("準備緩衝區失敗!\n");
longjmp(jmpBuf, GetLastError());
}
ret = waveInAddBuffer(hWavein, pWaveHdr, sizeof(WAVEHDR));
if (ret != MMSYSERR_NOERROR)
{
printf("加入緩衝區失敗!\n");
longjmp(jmpBuf, GetLastError());
}
}
建議在這裏至少添加4塊緩衝區,注意結構體WAVEHDR中有三個參數是需要我們進行初始化的,lpData表示存放音頻塊的大小,這裏我們給的是6400個字節,dwBufferLength表示lpData的大小,dwFlags給0即可,之後即可將該結構體添加進去。注意:這裏的結構體需要在堆上申請內存,不要在這裏使用局部變量。
3.使用waveInStart()
waveInStart()之後,程序就開始錄音了,當一塊音頻區滿後,會產生MM_WIM_DATA消息(如果你在waveInOpen那裏使用的是回調函數,這個消息是WIM_DATA),在這個消息中我們將音頻塊的內容取出,並重新將這個音頻塊加入到設備中。
case MM_WIM_DATA: //緩衝區錄滿的消息,在這裏取數據,並重新添加緩衝區
hWavein = (HWAVEIN)msg.wParam;
pWavehdr = (WAVEHDR*)msg.lParam;
int nWrite = fwrite(pWavehdr->lpData, 1,pWavehdr->dwBytesRecorded, g_pFile);
printf("dwBytesRecorded:%d, dwBufferLength: %d\n", pWavehdr->dwBytesRecorded, pWavehdr->dwBufferLength);
if (g_bWaveStatus)
{
ret = waveInAddBuffer(hWavein, pWavehdr, sizeof(WAVEHDR));
if (ret != MMSYSERR_NOERROR)
{
printf("再次加入緩衝區失敗!\n");
longjmp(callBackJmp, GetLastError());
}
}
這樣我們就可以源源不斷的一直在錄音了,那麼如何結束錄音並釋放緩衝區暱,下面開始進行結束錄音:
4.使用waveInStop()、waveInReset()和waveInUnprepareHeader()
先使用waveInStop(),它會立即停止錄音,停止錄音的同時,肯定有一塊緩衝區正在被使用,它不管這塊緩衝區有沒有滿,直接將這塊緩衝區發送給相應的接收程序(就是我們可以在MM_WIM_DATA中收到這塊緩衝區),那麼我們如何知道這塊緩衝區到底被使用了多少暱?在WAVEHDR結構體中,有一個dwBytesRecorded變量會指示這塊緩衝區被使用了多少,一般這個值是等於dwBufferLength的,但是當我們使用waveInStop()後,我們可以通過dwBytesRecorded瞭解緩衝區被使用的大小,所以我們應該儘量以dwBytesRecorded爲準。之後我們就可以使用waveInUnprepareHeader()來釋放掉這塊緩衝區,也不要在把這塊緩衝區加入進去了。
這樣我們就釋放掉一塊緩衝區了,但是我們在開始的時候一共申請了4塊緩衝區,還有3塊怎麼辦暱?
這時我們需要使用waveInReset()將其他沒有被使用的緩衝區直接發送給接收程序(就是我們可以在MM_WIM_DATA中收到這些緩衝區),之後在調用waveInUnprepareHeader()。
下面這張圖充分的說明了這一切:
5.使用waveInClose()結束一切。