录制声音并且播放录取的声音
录制声音一般采用两种机制:
第一种: 直接在UI下采用消息;
第二种: 开辟一个线程单独进行;
下面先介绍第一种如何进行,DEMO是网上的,挺零碎的,我费了点功夫,调试能直接使用的.
<1> : wav族函数进行声音采集和播放时,会以下几个消息:
MM_WIM_OPEN、 MM_WIM_DATA、MM_WIM_CLOSE、MM_WOM_OPEN、MM_WOM_DONE、MM_WOM_CLOSE
所以新建一个MFC工程,并且添加如下三个消息:
MM_WIM_OPEN、 MM_WIM_DATA、MM_WIM_CLOSE
当然也可以将另外三个播放的消息也一并添加.
假设新建了Recorde MFC Dialog工程.
<2> : 添加消息:
// Generated message map functions
//{{AFX_MSG(CRecordeDlg)
virtual BOOL OnInitDialog();
...
afx_msg LRESULT OnMM_WIM_OPEN(UINT wParam,LONG lParam);
afx_msg LRESULT OnMM_WIM_DATA(UINT wParam,LONG lParam);
afx_msg LRESULT OnMM_WIM_CLOSE(UINT wParam,LONG lParam);
afx_msg LRESULT OnMM_WOM_OPEN(UINT wParam,LONG lParam);
afx_msg LRESULT OnMM_WOM_DONE(UINT wParam,LONG lParam);
afx_msg LRESULT OnMM_WOM_CLOSE(UINT wParam,LONG lParam);
...
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CRecordeDlg, CDialog)
//{{AFX_MSG_MAP(CRecordeDlg)
...
ON_MESSAGE(MM_WIM_OPEN,OnMM_WIM_OPEN)
ON_MESSAGE(MM_WIM_DATA,OnMM_WIM_DATA)
ON_MESSAGE(MM_WIM_CLOSE,OnMM_WIM_CLOSE)
ON_MESSAGE(MM_WOM_OPEN,OnMM_WOM_OPEN)
ON_MESSAGE(MM_WOM_DONE,OnMM_WOM_DONE)
ON_MESSAGE(MM_WOM_CLOSE,OnMM_WOM_CLOSE)
...
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
<3> : 添加头文件:
#include<mmsystem.h>
#pragma comment(lib, "Winmm.lib")
全局变量:
#define INP_BUFFER_SIZE 2048//该值越大,录取的数据就越多.
#define WAVE_HEADER_SIZE 44
enum et{
RECORD_STATE_INIT,
RECORD_STATE_STOP,
RECORD_STATE_STOPING,
RECORD_STATE_PLAYING,
RECORD_STATE_RECING
};
类中自定义变量:
int m_nRecordState;
DWORD m_dwDataLength; //数据长度
WAVEFORMATEX m_waveform; //声音格式
HWAVEIN m_hWaveIn; //音频输入句柄
HWAVEOUT m_hWaveOut; //音频输出句柄
PBYTE m_pSaveBuffer; //音频存储内存
WAVEHDR m_WAVEHDR1;
WAVEHDR m_WAVEHDR2;
char m_cbBuffer1[INP_BUFFER_SIZE]; //声音临时缓存1
char m_cbBuffer2[INP_BUFFER_SIZE]; //声音临时缓存2
新添加录音button1播放button2按钮.
<4> : 录音按钮如下:
void CRecordeDlg::OnButton1()
{
// TODO: Add your control notification handler code here
//打开声音设备
if(waveInOpen(&m_hWaveIn,WAVE_MAPPER,&m_waveform,
(DWORD)m_hWnd,NULL,CALLBACK_WINDOW)){
MessageBeep(MB_ICONEXCLAMATION);
MessageBox(_T("录制声音失败!"),
_T("错误"),MB_ICONEXCLAMATION|MB_OK);
return;
}
//设置缓冲区
m_WAVEHDR1.lpData = (LPTSTR)m_cbBuffer1;
m_WAVEHDR1.dwBufferLength = INP_BUFFER_SIZE;
m_WAVEHDR1.dwBytesRecorded = 0;
m_WAVEHDR1.dwUser=0;
m_WAVEHDR1.dwFlags=0;
m_WAVEHDR1.dwLoops=1;
m_WAVEHDR1.lpNext=NULL;
m_WAVEHDR1.reserved=0;
m_WAVEHDR2.lpData=(LPTSTR)m_cbBuffer2;
m_WAVEHDR2.dwBufferLength = INP_BUFFER_SIZE;
m_WAVEHDR2.dwBytesRecorded = 0;
m_WAVEHDR2.dwUser=0;
m_WAVEHDR2.dwFlags=0;
m_WAVEHDR2.dwLoops=1;
m_WAVEHDR2.lpNext=NULL;
m_WAVEHDR2.reserved=0;
//设置双缓冲
waveInPrepareHeader(m_hWaveIn,&m_WAVEHDR1,sizeof(WAVEHDR));
waveInPrepareHeader(m_hWaveIn,&m_WAVEHDR2,sizeof(WAVEHDR));
waveInAddBuffer(m_hWaveIn,&m_WAVEHDR1,sizeof(WAVEHDR));
waveInAddBuffer(m_hWaveIn,&m_WAVEHDR2,sizeof(WAVEHDR));
//先随便分配个内存
if(m_pSaveBuffer == NULL){
free(m_pSaveBuffer);
m_pSaveBuffer = NULL;
}
m_pSaveBuffer = (PBYTE)malloc(1);
m_dwDataLength = 0;
//开始录音
// Begin sampling
waveInStart(m_hWaveIn);
}
播音按钮如下:
void CRecordeDlg::OnButton2()
{
// TODO: Add your control notification handler code here
//打开声音播放句柄
if(waveOutOpen(&m_hWaveOut,WAVE_MAPPER,&m_waveform,(DWORD)m_hWnd, NULL, CALLBACK_WINDOW)){
MessageBeep(MB_ICONEXCLAMATION);
MessageBox(_T("录制声音失败!"),_T("错误"),MB_ICONEXCLAMATION|MB_OK);
return;
}
}
<4> :添加响应消息函数(附带了功能代码):
LRESULT CRecordeDlg::OnMM_WIM_CLOSE (UINT wParam,LONG lParam){
waveInUnprepareHeader(m_hWaveIn, &m_WAVEHDR1, sizeof (WAVEHDR)) ;
waveInUnprepareHeader(m_hWaveIn, &m_WAVEHDR2, sizeof (WAVEHDR)) ;
m_nRecordState = RECORD_STATE_STOP;
//SetButtonState();
return NULL;
}
LRESULT CRecordeDlg::OnMM_WIM_DATA (UINT wParam,LONG lParam){
PWAVEHDR pWaveHdr = (PWAVEHDR)lParam;
if(pWaveHdr->dwBytesRecorded > 0){
m_pSaveBuffer = (PBYTE)realloc(m_pSaveBuffer,m_dwDataLength + pWaveHdr->dwBytesRecorded);
if(m_pSaveBuffer == NULL){
waveInClose (m_hWaveIn);
MessageBeep (MB_ICONEXCLAMATION) ;
AfxMessageBox("erro memory");
return NULL;
}
memcpy(m_pSaveBuffer+m_dwDataLength , pWaveHdr->lpData,pWaveHdr->dwBytesRecorded);
m_dwDataLength += pWaveHdr->dwBytesRecorded;
}
if(m_nRecordState == RECORD_STATE_STOPING){
waveInClose(m_hWaveIn);
}
waveInAddBuffer(m_hWaveIn, pWaveHdr, sizeof (WAVEHDR));
return NULL;
}
LRESULT CRecordeDlg::OnMM_WIM_OPEN (UINT wParam,LONG lParam){
m_nRecordState = RECORD_STATE_RECING;
//SetButtonState();
return NULL;
}
LRESULT CRecordeDlg::OnMM_WOM_CLOSE (UINT wParam,LONG lParam){
m_nRecordState = RECORD_STATE_STOP;
//SetButtonState();
return NULL;
}
LRESULT CRecordeDlg::OnMM_WOM_DONE (UINT wParam,LONG lParam){
PWAVEHDR pWaveHdr = (PWAVEHDR)lParam;
waveOutUnprepareHeader (m_hWaveOut, pWaveHdr, sizeof (WAVEHDR)) ;
waveOutClose (m_hWaveOut);
return NULL;
}
LRESULT CRecordeDlg::OnMM_WOM_OPEN (UINT wParam,LONG lParam){
memset(&m_WAVEHDR1,0,sizeof(WAVEHDR));
m_WAVEHDR1.lpData = (char *)m_pSaveBuffer;//指向要播放的内存
m_WAVEHDR1.dwBufferLength = m_dwDataLength;//播放的长度
m_WAVEHDR1.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
m_WAVEHDR1.dwLoops = 1;
waveOutPrepareHeader(m_hWaveOut,&m_WAVEHDR1,sizeof(WAVEHDR));
waveOutWrite(m_hWaveOut,&m_WAVEHDR1,sizeof(WAVEHDR));
m_nRecordState = RECORD_STATE_PLAYING;
//SetButtonState();
return NULL;
}