MFC利用麦克风录音、保存语音文件、播放wav

最近刚写好一段声纹识别代码,想写一个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/*,sys2.wMilliseconds*/);
    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      =   1;  
    waveform.nSamplesPerSec =11025;  
    waveform.nAvgBytesPerSec=11025;  
    waveform.nBlockAlign    =1;  
    waveform.wBitsPerSample =8; */ 
 
    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

 

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值