最近刚写好一段声纹识别代码,想写一个MFC界面来做一个语音采集,搭建一个人机交互的声纹识别界面,于是开始玩一玩MFC的语音采集,保存文件,播放。
网上搜相关内容,发现https://blog.csdn.net/shufac/article/details/20649303该博客较为完善,本文代码参考该博客,对其中细节进行补充。
步骤一:新建工程
我使用的软件是VS2012,新建MFC工程-->基于对话框-->完成,放置3个Button
ID分别为 IDC_RECORD_START,IDC_RECORD_STOP, IDC_RECORD_PLAY
并给这三个按钮分别添加消息处理函数:OnRecordStart(),OnRecordStop(),OnRecordPlay()。
步骤二:添加头文件
在MFCApplication1Dlg.cpp中添加头文件
//声音相关
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "WINMM.LIB")
添加一些宏定义以及全局变量,后面程序会用到
HWAVEIN hWaveIn;//波形音频数据格式Wave_audio数据格式
WAVEFORMATEX waveform;
HWAVEOUT hWaveOut;//打开回放设备函数
WAVEHDR *pWaveHdr1;
WAVEHDR *pWaveHdr2;
#define BUFFER_SIZE (44100*16*2/8*5) // 录制声音长度
#define FRAGMENT_SIZE 1024*4 // 缓存区大小
#define FRAGMENT_NUM 4 // 缓存区个数
//static unsigned char pSaveBuffer[BUFFER_SIZE] = {0};
PBYTE pBuffer1;
PBYTE pBuffer2;
PBYTE pSaveBuffer;
PBYTE pNewBuffer;
int dwDataLength=FRAGMENT_SIZE;
bool bEnding;
步骤三:在初始化函数OnInitDialog()中分配内存
在OnInitDialog()中加入以下代码:
//allocate memory for wave header
pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
pWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
//allocate memory for save buffer
pSaveBuffer = reinterpret_cast<PBYTE>(malloc(1));
步骤四:完善消息处理函数
完善OnBnClickedRecordStart(),OnBnClickedRecordStop(),OnBnClickedRecordPlay()代码如下:
void CMFCApplication1Dlg::OnBnClickedRecordStart()
{
// TODO: 在此添加控件通知处理程序代码
//allocate buffer memory
pBuffer1=(PBYTE)malloc(dwDataLength);
pBuffer2=(PBYTE)malloc(dwDataLength);
if (!pBuffer1 || !pBuffer2) {
if (pBuffer1) free(pBuffer1);
if (pBuffer2) free(pBuffer2);
MessageBeep(MB_ICONEXCLAMATION);
AfxMessageBox(_T("Memory erro!"));
return;
}
//open waveform audo for input
waveform.wFormatTag=WAVE_FORMAT_PCM;
waveform.nChannels=2;
waveform.nSamplesPerSec=44100;
waveform.nAvgBytesPerSec=176400;
waveform.nBlockAlign=4;
waveform.wBitsPerSample=16;
waveform.cbSize=0;
if (waveInOpen(&hWaveIn,WAVE_MAPPER,&waveform,(DWORD)this->m_hWnd,NULL,CALLBACK_WINDOW)) {
free(pBuffer1);
free(pBuffer2);
MessageBeep(MB_ICONEXCLAMATION);
AfxMessageBox(_T("Audio can not be open!"));
}
pWaveHdr1->lpData=(LPSTR)pBuffer1;
pWaveHdr1->dwBufferLength=dwDataLength;
pWaveHdr1->dwBytesRecorded=0;
pWaveHdr1->dwUser=0;
pWaveHdr1->dwFlags=0;
pWaveHdr1->dwLoops=1;
pWaveHdr1->lpNext=NULL;
pWaveHdr1->reserved=0;
waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));
pWaveHdr2->lpData=(LPSTR)pBuffer2; //
pWaveHdr2->dwBufferLength=dwDataLength;
pWaveHdr2->dwBytesRecorded=0;
pWaveHdr2->dwUser=0;
pWaveHdr2->dwFlags=0;
pWaveHdr2->dwLoops=1;
pWaveHdr2->lpNext=NULL;
pWaveHdr2->reserved=0;
waveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
pSaveBuffer = (PBYTE)realloc (pSaveBuffer, 1) ;
// Add the buffers
waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
waveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
// Begin sampling
bEnding=FALSE;
dwDataLength = 0 ;
waveInStart (hWaveIn) ;
}
void CMFCApplication1Dlg::OnBnClickedRecordStop()
{
// TODO: 在此添加控件通知处理程序代码
bEnding=TRUE;
//停止录音
waveInReset(hWaveIn);
//存储声音文件
CFile m_file;
CFileException fileException;
SYSTEMTIME sys2; //获取系统时间确保文件的保存不出现重名
GetLocalTime(&sys2);
//以下实现将录入的声音转换为wave格式文件
//查找当前目录中有没有Voice文件夹 没有就先创建一个,有就直接存储
TCHAR szPath[MAX_PATH];
GetModuleFileName(NULL, szPath, MAX_PATH);
CString PathName(szPath);
//获取exe目录
CString PROGRAM_PATH = PathName.Left(PathName.ReverseFind(_T('\\')) + 1);
//Debug目录下RecordVoice文件夹中
PROGRAM_PATH+=_T("RecordVoice\\");
if (!(GetFileAttributes(PROGRAM_PATH)==FILE_ATTRIBUTE_DIRECTORY))
{
if (!CreateDirectory(PROGRAM_PATH,NULL))
{
AfxMessageBox(_T("Make Dir Error"));
}
}
//kn_string strFilePath = _T("RecordVoice\\");
//GetFilePath(strFilePath);
CString m_csFileName=PROGRAM_PATH+_T("\\audio");//strVoiceFilePath
//CString m_csFileName= _T("D:\\audio");
wchar_t s[30] = {0};
_stprintf_s(s,_T("%d%d%d%d%d%d"),sys2.wYear,sys2.wMonth,sys2.wDay,sys2.wHour,sys2.wMinute,sys2.wSecond);
m_csFileName.Append(s);
m_csFileName.Append(_T(".wav"));
m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);
DWORD m_WaveHeaderSize = 38;
DWORD m_WaveFormatSize = 18;
m_file.SeekToBegin();
m_file.Write("RIFF",4);
//unsigned int Sec=(sizeof + m_WaveHeaderSize);
unsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);
m_file.Write(&Sec,sizeof(Sec));
m_file.Write("WAVE",4);
m_file.Write("fmt ",4);
m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));
m_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));
m_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));
m_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));
m_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
m_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
m_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));
m_file.Write("data",4);
m_file.Write(&dwDataLength,sizeof(dwDataLength));
m_file.Write(pSaveBuffer,dwDataLength);
m_file.Seek(dwDataLength,CFile::begin);
m_file.Close();
}
void CMFCApplication1Dlg::OnBnClickedRecordPlay()
{
// TODO: 在此添加控件通知处理程序代码
//open waveform audio for output
waveform.wFormatTag = WAVE_FORMAT_PCM;
//设置不同的声音采样格式
waveform.nChannels=2;
waveform.nSamplesPerSec=44100;
waveform.nAvgBytesPerSec=176400;
waveform.nBlockAlign=4;
waveform.wBitsPerSample=16;
if (waveOutOpen(&hWaveOut,WAVE_MAPPER,&waveform,(DWORD)this->m_hWnd,NULL,CALLBACK_WINDOW)) {
MessageBeep(MB_ICONEXCLAMATION);
AfxMessageBox(_T("Audio output erro"));
}
return ;
}
步骤五:添加几个自定义消息
在Dialog页面右击鼠标,选择"类向导",选择消息,添加自定义消息,输入“自定义Windows消息”,“消息处理程序名称”,确定,选择刚添加的函数OnMM_WIM_OPEN,点击编辑代码,如下图所示:
在OnMM_WIM_OPEN中添加下列代码:
afx_msg LRESULT CMFCApplication1Dlg::OnMM_WIM_OPEN(WPARAM wParam, LPARAM lParam)
{
((CWnd *)(this->GetDlgItem(IDC_RECORD_START)))->EnableWindow(FALSE);
((CWnd *)(this->GetDlgItem(IDC_RECORD_STOP)))->EnableWindow(TRUE);
((CWnd *)(this->GetDlgItem(IDC_RECORD_PLAY)))->EnableWindow(FALSE);
return 0;
}
同样步骤,依次添加OnMM_WIM_DATA,OnMM_WIM_CLOSE,OnMM_WOM_OPEN,OnMM_WOM_DONE,OnMM_WOM_CLOSE等消息,代码如下:
afx_msg LRESULT CMFCApplication1Dlg::OnMM_WIM_DATA(WPARAM wParam, LPARAM lParam)
{
// Reallocate save buffer memory
pNewBuffer = (PBYTE)realloc (pSaveBuffer, dwDataLength +((PWAVEHDR) lParam)->dwBytesRecorded);
if (pNewBuffer == NULL)
{
waveInClose (hWaveIn) ;
MessageBeep (MB_ICONEXCLAMATION);
AfxMessageBox(_T("erro memory"));
return TRUE;
}
pSaveBuffer = pNewBuffer ;
CopyMemory (pSaveBuffer + dwDataLength, ((PWAVEHDR) lParam)->lpData,
((PWAVEHDR) lParam)->dwBytesRecorded) ;
dwDataLength += ((PWAVEHDR) lParam)->dwBytesRecorded ;
if (bEnding)
{
waveInClose (hWaveIn) ;
return TRUE;
}
waveInAddBuffer (hWaveIn, (PWAVEHDR) lParam, sizeof (WAVEHDR)) ;
return 0;
}
afx_msg LRESULT CMFCApplication1Dlg::OnMM_WIM_CLOSE(WPARAM wParam, LPARAM lParam)
{
KillTimer(1);
if (0==dwDataLength) {
return TRUE;
}
waveInUnprepareHeader (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
waveInUnprepareHeader (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
free (pBuffer1) ;
free (pBuffer2) ;
if (dwDataLength > 0)
{
//enable play
((CWnd *)(this->GetDlgItem(IDC_RECORD_START)))->EnableWindow(TRUE);
((CWnd *)(this->GetDlgItem(IDC_RECORD_STOP)))->EnableWindow(FALSE);
((CWnd *)(this->GetDlgItem(IDC_RECORD_PLAY)))->EnableWindow(TRUE);
}
((CWnd *)(this->GetDlgItem(IDC_RECORD_START)))->EnableWindow(TRUE);
((CWnd *)(this->GetDlgItem(IDC_RECORD_STOP)))->EnableWindow(FALSE);
return 0;
}
afx_msg LRESULT CMFCApplication1Dlg::OnMM_WOM_OPEN(WPARAM wParam, LPARAM lParam)
{
// Set up header
pWaveHdr1->lpData = (LPSTR)pSaveBuffer ; //???
pWaveHdr1->dwBufferLength = dwDataLength ;
pWaveHdr1->dwBytesRecorded = 0 ;
pWaveHdr1->dwUser = 0 ;
pWaveHdr1->dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP ;
pWaveHdr1->dwLoops = 1;
pWaveHdr1->lpNext = NULL;
pWaveHdr1->reserved = 0;
// Prepare and write
waveOutPrepareHeader (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
waveOutWrite (hWaveOut, pWaveHdr1, sizeof (WAVEHDR)) ;
((CWnd *)(this->GetDlgItem(IDC_RECORD_START)))->EnableWindow(TRUE);
((CWnd *)(this->GetDlgItem(IDC_RECORD_STOP)))->EnableWindow(FALSE);
((CWnd *)(this->GetDlgItem(IDC_RECORD_PLAY)))->EnableWindow(FALSE);
return 0;
}
afx_msg LRESULT CMFCApplication1Dlg::OnMM_WOM_DONE(WPARAM wParam, LPARAM lParam)
{
waveOutUnprepareHeader (hWaveOut, pWaveHdr1, sizeof (WAVEHDR));
waveOutClose (hWaveOut);
return NULL;
return 0;
}
afx_msg LRESULT CMFCApplication1Dlg::OnMM_WOM_CLOSE(WPARAM wParam, LPARAM lParam)
{
((CWnd *)(this->GetDlgItem(IDC_RECORD_START)))->EnableWindow(TRUE);
((CWnd *)(this->GetDlgItem(IDC_RECORD_STOP)))->EnableWindow(FALSE);
((CWnd *)(this->GetDlgItem(IDC_RECORD_PLAY)))->EnableWindow(TRUE);
return 0;
}
步骤六:运行程序,成功。
本文代码参考:https://blog.csdn.net/shufac/article/details/20649303