1、初始化
memset(&hWaveIn, 0, sizeof(HWAVEIN));
memset(&waveForm, 0, sizeof(WAVEFORMATEX));
if (getAudioDevices()) //判断能否获取设备
{
//设置采样频率
setWaveFormat(&waveForm, 2, 44100, 16);
//初始化设备缓冲区
for (int i = 0; i < 2; ++i)
{
whdr[i].lpData = (CHAR*)malloc(bufLength);
memset(whdr[i].lpData, 0, bufLength);
whdr[i].dwBufferLength = bufLength; //缓冲区大小
whdr[i].dwBytesRecorded = 0; //已填充字节数,结束时未填充的自动处理
whdr[i].dwUser = 0; //用户自定义数据
whdr[i].dwFlags = 0; //用不着
whdr[i].dwLoops = 0; //用不着
whdr[i].lpNext = NULL; //用不着
whdr[i].reserved = 0; //用不着
}
}
bool InputSound::getAudioDevices()
{
//获取音频设备数量
int count = 0;
count = waveInGetNumDevs();
if (count == 0)
{
printf("未获取到音频设备,请检查设备\n");
return false;
}
printf("音频输入设备数据量:%d\n", count);
//获取设备的信息
WAVEINCAPS waveIncaps;
MMRESULT mmResult = waveInGetDevCaps(0, &waveIncaps, sizeof(WAVEINCAPS));
if (mmResult != MMSYSERR_NOERROR)
{
printf("获取设备信息异常\n");
return false;
}
//printf("音频输入设备:%s\n", waveIncaps.szPname);
std::cout << "音频输入设备:" << waveIncaps.szPname << std::endl;
return true;
}
2、设置录音的参数
void InputSound::setWaveFormat(LPWAVEFORMATEX waveformat, WORD nCh, DWORD nSampleRate, WORD BitsPerSample)
{
waveformat->wFormatTag = WAVE_FORMAT_PCM;
waveformat->nChannels = nCh; //声道数
waveformat->nSamplesPerSec = nSampleRate; //采样率
waveformat->nAvgBytesPerSec = nSampleRate * nCh * BitsPerSample / 8; //每秒数据量
waveformat->nBlockAlign = nCh * BitsPerSample / 8; //单帧数据量
waveformat->wBitsPerSample = BitsPerSample; //采样精度
waveformat->cbSize = 0;
}
3、开始录音
void InputSound::startRecording()
{
try
{
MMRESULT mmResult = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveForm, (DWORD)waveInProc, (DWORD)this, CALLBACK_FUNCTION);
if (mmResult != MMSYSERR_NOERROR)
{
printf("设备启动失败\n");
return;
}
waveInPrepareHeader(hWaveIn, &whdr[0], sizeof(WAVEHDR)); //配置数据块
waveInPrepareHeader(hWaveIn, &whdr[1], sizeof(WAVEHDR));
//部署缓存
waveInAddBuffer(hWaveIn, &whdr[0], sizeof(WAVEHDR)); //压入缓冲区
waveInAddBuffer(hWaveIn, &whdr[1], sizeof(WAVEHDR));
//发送录音开始消息
waveInStart(hWaveIn);
isStop = FALSE;
printf("开始录音\n");
}
catch (...)
{
printf("启动失败\n");
}
return;
}
4、回调处理
需要注意,将数据写入文件后,需要经缓冲区清空,重新加到录音设备上。
DWORD CALLBACK InputSound::waveInProc(HWAVEIN hwavein, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
InputSound* inputSound = (InputSound*)dwInstance;
char buf[bufLength] = {0};
switch (uMsg)
{
case WIM_OPEN: //设备成功打开
printf("设备启动成功\n");
break;
case WIM_DATA: //缓冲区数据填充完毕
memcpy(&buf, ((LPWAVEHDR)dwParam1)->lpData, bufLength); //拷贝数据内容
FILE *pf;
fopen_s(&pf, "./RecordSounds.pcm", "ab+");
if (strlen(buf))
{
//写入文件
fwrite(buf, 1, ((LPWAVEHDR)dwParam1)->dwBufferLength, pf);
}
if (inputSound->isStop != TRUE)
{
//清空数据内容
memset(((LPWAVEHDR)dwParam1)->lpData, 0, bufLength);
((LPWAVEHDR)dwParam1)->dwBytesRecorded = 0;
waveInAddBuffer(hwavein, (PWAVEHDR)dwParam1, sizeof(WAVEHDR)); //重新添加缓存
}
fclose(pf);
break;
case WIM_CLOSE: //操作完成
printf("设备关闭成功\n");
break;
default:
break;
}
return 0;
}
5、停止录音
void InputSound::stopRocording()
{
try
{
isStop = TRUE;
//设备停止
waveInStop(hWaveIn);
waveInReset(hWaveIn);
//释放缓冲区
waveInUnprepareHeader(hWaveIn, &whdr[0], sizeof(WAVEHDR));
waveInUnprepareHeader(hWaveIn, &whdr[1], sizeof(WAVEHDR));
printf("停止录音\n");
//关闭设备
waveInClose(hWaveIn);
}
catch (...)
{
printf("停止失败\n");
}
return;
}
1、初始化
这里初始化的时候,将dwUser 这个变量作为了一个数据是否播放完的标记。
getAudioDevices()、setWaveFormat()两个函数与录音中处理类似。
if (getAudioDevices())
{
//设置播放频率
setWaveFormat(&waveForm, 2, 44100, 16);
for (int i = 0; i < 2; ++i)
{
whdr[i].lpData = (CHAR*)malloc(bufLength);
memset(whdr[i].lpData, 0, bufLength);
whdr[i].dwBufferLength = bufLength; //缓冲区大小
whdr[i].dwBytesRecorded = 0; //已填充字节数,结束时未填充的自动处理
whdr[i].dwUser = 0; //用户自定义数据,0表示无内容,1表示有内容
whdr[i].dwFlags = 0; //用不着
whdr[i].dwLoops = 0; //用不着
whdr[i].lpNext = NULL; //用不着
whdr[i].reserved = 0; //用不着
}
}
2、开始播放
void OutputSound::startPlaying()
{
try
{
MMRESULT mmResult = waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveForm, (DWORD)waveOutProc, (DWORD)this, CALLBACK_FUNCTION);
if (mmResult != MMSYSERR_NOERROR)
{
printf("设备启动失败\n");
return;
}
FILE *pf;
fopen_s(&pf, "./RecordSounds.pcm", "rb");
while (true)
{
for (int i = 0; i < 2; ++i)
{
if (whdr[i].dwUser != 1) //读取数据,发送给设备
{
char buf[bufLength] = { 0 };
//whdr[i].dwBufferLength = fread(&buf, sizeof(char), bufLength, pf);
fread(&buf, bufLength, 1, pf);
memcpy(whdr[i].lpData, &buf, bufLength);
whdr[i].dwUser = 1;
waveOutPrepareHeader(hWaveOut, &whdr[i], sizeof(WAVEHDR));//准备缓冲
waveOutWrite(hWaveOut, &whdr[i], sizeof(WAVEHDR)); //发送给设备
}
}
if (feof(pf))
{
printf("播放结束\n");
Sleep(1000);
waveOutClose(hWaveOut);
break;
}
}
fclose(pf);
}
catch (...)
{
printf("播放失败\n");
}
return;
}
3、回调处理
DWORD CALLBACK OutputSound::waveOutProc(HWAVEOUT hwaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
LPWAVEHDR phdr = (LPWAVEHDR)dwParam1;
switch (uMsg)
{
case WOM_OPEN:
printf("播放设备启动\n");
break;
case WOM_DONE:
//播放完了,是缓冲块可以重新读取数据
phdr->dwUser = 0;
break;
case WOM_CLOSE:
printf("播放设备关闭\n");
break;
default:
break;
}
return 0;
}