一、录音的基本流程:
这个流程图可能不太准确,具体的看下面的分析吧
二、具体步骤
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()结束一切。