windows錄音教程

一、錄音的基本流程:


這個流程圖可能不太準確,具體的看下面的分析吧

二、具體步驟

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()結束一切。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章