录音和播放线程类






3.73/5 (8投票s)
这是一个派生自 CWinThread 类的类。它用于在特定线程中录制和播放声音。

简介与背景
该演示展示了一种使用线程录制和播放声音的方法。我创建了一个派生自 CWinThread
的类,它用于在线程中播放和录制声音。 这是一个可重用的类,因此您可以在您的项目中使用它。 我在 MFC 上构建了它,所以我不确定它在其他条件下是否能很好地工作。 它可能有一些错误,我还没有找到解决它们的方法。 我编写这个类的目的是因为我想帮助您简化您的繁重工作。
Using the Code
使用代码很简单,只需遵循几个步骤
首先,将两个文件添加到您的项目中:“NewThread.h”和“NewThread.cpp”。 请先在文章顶部下载它。
其次,在代码的适当位置声明对象。
#include "NewThread.h"
...
CNewThread *pThread;
...
pThread=new CNewThread();
pThread->CreateThread ();
...
//Okay, it does work now.
我将在下面详细讨论如何创建该类。
类的原理
当我想录制和播放声音时,首先必须解决的问题。 我应该选择什么 API。 有时,我们使用 sndPlaySound()
函数播放声音文件或使用 MCI 函数。 但这一次,我使用 waveInOpen()
和 waveOutOpen()
。 所以,第一件事是检查它的头文件和库,并将其添加到这个类的头文件中。
#include "mmsystem.h"
#pragma comment(lib,"winmm.lib")
好的,让我们继续讨论这个类,从主菜单点击“插入”,插入一个派生自 MFC CWinThread
的新类。 将以上代码片段添加到类的头文件中。
class CNewThread : public CWinThread
{
DECLARE_DYNCREATE(CNewThread)
public:
CNewThread(); // protected constructor used by dynamic creation
// Attributes
public:
virtual ~CNewThread();
// Operations
public:
CString m_strState; //Its a CString object that indicates the state of the thread.
CString GetState(); //Get the state of the thread, which it's playing or recording...
int GetRecSecond(); //Get tick counts of the recording by seconds.
void StopPlay(); //Stop playing.
void StartToPlay(); //Begin playing.
void StopRec(); //Stop record.
void StartToRec(); //Begin recording.
private:
WAVEFORMATEX wf; //It's a structure of WAVEFORMATX,
//will be passed into waveInOpen(...)
HWAVEIN hWaveIn; //The handle of the input device
HWAVEOUT hWaveOut; //Then handle of the output device
WAVEHDR *waveHdr; //The pointer of structure
//will be initialized in initialization method
BYTE *Buffer; //A buffer to store data per once recording
BYTE *pTotalBuf,*pBuf; //The buffer to store the total data have been recorded.
BOOL bRec; //The state of recording
UINT uLength; //The length of recording
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CNewThread)
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
virtual BOOL PreTranslateMessage(MSG* pMsg);
//}}AFX_VIRTUAL
// Implementation
protected:
// Generated message map functions
//{{AFX_MSG(CNewThread)
afx_msg void ON_WOMCLOSE(); //These functions are used to deal
//with the messages will be sent by waveInOpen and waveOutOpen.
afx_msg void ON_WOMDATA();
afx_msg void ON_WOMOPEN();
afx_msg void ON_WIMDATA(UINT wParam, LONG lParam);
afx_msg void ON_WIMOPEN(UINT wParam, LONG lParam);
afx_msg void ON_WIMCLOSE(UINT wParam, LONG lParam);
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
UINT m_nRecTime; //The time, has been recorded.
void InitSound(); //Alloc memory for the buffers.
};
这些代码片段是由 ClassWizard
创建的,所以不要全部手动输入。 右键单击您刚刚插入的新类,添加函数和成员变量是我们所需要的。
waveInOpen
waveInOpen
函数打开给定的波形音频输入设备以进行录音。
MMRESULT waveInOpen(
LPHWAVEIN phwi,
UINT uDeviceID,
LPWAVEFORMATEX pwfx,
DWORD dwCallback,
DWORD dwCallbackInstance,
DWORD fdwOpen
);
从 MSDN 我们得知,waveInOpen
需要 WAVEFORMATX
结构的地址,以便我们可以在初始化函数中创建它
void CNewThread::InitSound()
{
bRec=false;
wf.cbSize=0;
wf.wFormatTag=WAVE_FORMAT_PCM;
wf.nChannels=1;
wf.nSamplesPerSec=11025;
wf.wBitsPerSample =8;
wf.nBlockAlign=1;
wf.nAvgBytesPerSec =11025;
waveHdr=(WAVEHDR*)malloc(sizeof(WAVEHDR)); //Alloc memory for WAVEHDR structure.
}
从代码片段中,我们还发现它已经为缓冲区 “waveHdr
” 分配了内存。
再次查看 “waveInOpen
” 函数,关注最后一个参数,它非常重要。
它决定了我们应该使用哪种回调机制。 我们需要选择三个:CALLBACK_FUNCTION
、CALLBACK_THREAD
和 CALLBACK_WINDOW
,对于这个类,我选择 CALLBACK_THREAD
,这意味着我将在线程中处理它的消息。 如果您选择 CALLBACK_WINDOW
,我们应该使用 ON_MESSAGE(...)
宏来映射消息。 与窗口不同,当我们选择 CALLBACK_THREAD
时 “要小心”,我们应该使用宏 ON_THREAD_MESSAGE
而不是 ON_MESSAGE
。 看看下面的代码片段
BEGIN_MESSAGE_MAP(CNewThread, CWinThread)
//{{AFX_MSG_MAP(CNewThread)
// NOTE - the ClassWizard will add and remove mapping macros here.
ON_THREAD_MESSAGE(MM_WIM_OPEN,ON_WIMOPEN)
ON_THREAD_MESSAGE(MM_WIM_DATA,ON_WIMDATA)
ON_THREAD_MESSAGE(MM_WIM_CLOSE,ON_WIMCLOSE)
ON_THREAD_MESSAGE(MM_WOM_OPEN,ON_WOMOPEN)
ON_THREAD_MESSAGE(MM_WOM_DONE,ON_WOMDATA)
ON_THREAD_MESSAGE(MM_WOM_CLOSE,ON_WOMCLOSE)
//}}AFX_MSG_MAP
备注:ON_WIMOPEN
是类成员函数的名称。 右键单击刚刚插入到项目中的类,添加成员函数,就可以了。
如何进行连续录音? 这正是一个好问题。 看看下面的代码
void CNewThread::ON_WIMDATA(UINT wParam, LONG lParam)
{
pBuf=(BYTE*)realloc(pTotalBuf,uLength+((PWAVEHDR)lParam)->dwBytesRecorded );
pTotalBuf=pBuf;
memcpy(pTotalBuf+uLength,((PWAVEHDR)lParam)->lpData ,
((PWAVEHDR)lParam)->dwBytesRecorded);
uLength+=((PWAVEHDR)lParam)->dwBytesRecorded;
/**/
if(bRec)
::waveInAddBuffer (hWaveIn,((PWAVEHDR)lParam),sizeof(WAVEHDR));
}
当缓冲区已满时,将发送 MM_WIM_DATA
消息,因此将调用 ON_WIMDATA()
函数。 将数据从缓冲区 “Buffer
” 复制到缓冲区 “pTotalBuf
”,并重新分配 buffer pTotalBuf
的内存,扩大其空间。
::waveInAddBuffer (hWaveIn,((PWAVEHDR)lParam),sizeof(WAVEHDR));
这一行将添加一个新缓冲区来填充,换句话说,录音数据将存储在添加的缓冲区中。 因此,缓冲区 “Buffer
” 可以再次用于存储新数据,当再次已满时,将数据复制到总缓冲区。 播放很简单,只需播放总缓冲区 “pTotalBuf
” 就行了。
关注点
对我来说,这是一个使用线程和声音进行编程的机会。 我通过编写一些代码学到了很多东西。 希望它对你有所帮助。
历史
- 2010 年 1 月 17 日:首次发布