本人应用场景:播放采集设备输出的PCM音频,参考了两种网上实现方法,接口做了些调整,因为播放的音频属性经常需要改变,在播放初始化函数Start传入WAVEFORMATEX参数,这样支持的播放音频种类多,经测试,均能正常播放,使用方法:
- CViWavePlay* m_pWavPlay;
- m_pWavPlay = new CViWavePlay;
- m_pWavPlay->Start(PWAVEFORMATEX(pbFormat));
- m_pWavPlay->PlayAudio((char*)pPData->m_pData,pPData->m_nData);
- m_pWavPlay->Stop();
Start函数参数WAVEFORMATEX设置方法:
参见头文件的定义:
typedef struct tWAVEFORMATEX
{
WORD wFormatTag; /* format type */// 波形声音的格式,本人此处设置为 WAVE_FORMAT_PCM
WORD nChannels; /* number of channels (i.e. mono, stereo...) *///音频文件的通道数量,单声道为1,立体声为2.
DWORD nSamplesPerSec; /* sample rate *///样本采样率,对于 WAVE_FORMAT_PCM通常为8.0 kHz, 11.025 kHz, 22.05 kHz和44.1 kHz
DWORD nAvgBytesPerSec; /* for buffer estimation */
WORD nBlockAlign; /* block size of data */
WORD wBitsPerSample; /* Number of bits per sample of mono data *//每个样本的BIT数目,一般为16
WORD cbSize; /* The count in bytes of the size of// 额外信息的大小,以字节为单位,添加在WAVEFORMATEX的结尾。如果不需要额外的信息,此值必为0
extra information (after cbSize) */
} WAVEFORMATEX;
典型设置示例:
WAVEFORMATEX _wfx;
_wfx.nSamplesPerSec = 44100; /* sample rate */
_wfx.wBitsPerSample = 16; /* sample size */
_wfx.nChannels = 2; /* channels */
_wfx.cbSize = 0; /* size of _extra_ info */
_wfx.wFormatTag = WAVE_FORMAT_PCM;
_wfx.nBlockAlign = (_wfx.wBitsPerSample * _wfx.nChannels) >> 3;
_wfx.nAvgBytesPerSec = _wfx.nBlockAlign * _wfx.nSamplesPerSec;
m_pWavPlay->Start(&_wfx);
实现方法一的头文件:
M_Critical_Section 是自用封装好的线程锁,可用afxmt.h中的CCriticalSection来代替
- #ifndef _WAVEPLAY_4FR567H6_H_
- #define _WAVEPLAY_4FR567H6_H_
- #include <mmsystem.h>
- #include "mthread.h"
- //音频播放
- class CViWavePlay
- {
- public:
- CViWavePlay();
- ~CViWavePlay();
- public:
- BOOL Start(PWAVEFORMATEX pWaveformat);
- BOOL PlayAudio(char* buf,unsigned int nSize);
- void Stop();
- public:
- UINT GetDeviceNum();
- WAVEOUTCAPS* GetDeviceCap();
- private:
- static DWORD WINAPI ThreadProc(LPVOID lpParameter);
- inline int GetBufferNum();
- inline void AddBuffer();
- inline void SubBuffer();
- BOOL Open(PWAVEFORMATEX pWaveformat);
- void Close();
- BOOL StartThread();
- void StopThread();
- private:
- WAVEOUTCAPS m_waveCaps;
- BOOL m_bDevOpen;
- BOOL m_bThread;
- HWAVEOUT m_hWave;
- HANDLE m_hThread;
- DWORD m_ThreadID;
- WAVEFORMATEX m_Waveformat;
- M_Critical_Section m_Lock;
- int m_BufferQueue;
- };
- #endif //_WAVEPLAY_4FR567H6_H_
实现方法一的cpp文件:
- #include "stdafx.h"
- #include "WavPlay.h"
- #pragma comment(lib,"Winmm")
- CViWavePlay::CViWavePlay()
- {
- ZeroMemory(&m_Waveformat,sizeof(WAVEFORMATEX));
- memset(&m_waveCaps,0,sizeof(m_waveCaps));
- m_bDevOpen = FALSE;
- m_bThread = FALSE;
- m_hWave = 0;
- m_hThread = 0;
- m_ThreadID = 0;
- m_BufferQueue = 0;
- m_Lock.init();
- }
- CViWavePlay::~CViWavePlay()
- {
- Stop();
- m_Lock.cleanup();
- }
- UINT CViWavePlay::GetDeviceNum()
- {
- return waveOutGetNumDevs();
- }
- WAVEOUTCAPS* CViWavePlay::GetDeviceCap()
- {
- MMRESULT mRet = waveOutGetDevCaps(WAVE_MAPPER,&m_waveCaps,sizeof(m_waveCaps));
- if( mRet == MMSYSERR_NOERROR )
- return &m_waveCaps;
- return NULL;
- }
- // 典型参数设置方法 longf120823
- // _wfx.nSamplesPerSec = 44100; /* sample rate */
- // _wfx.wBitsPerSample = 16; /* sample size */
- // _wfx.nChannels = 2; /* channels */
- // _wfx.cbSize = 0; /* size of _extra_ info */
- // _wfx.wFormatTag = WAVE_FORMAT_PCM;
- // _wfx.nBlockAlign = (_wfx.wBitsPerSample * _wfx.nChannels) >> 3;
- // _wfx.nAvgBytesPerSec = _wfx.nBlockAlign * _wfx.nSamplesPerSec;
- BOOL CViWavePlay::Open(PWAVEFORMATEX pWaveformat)
- {
- if( m_bDevOpen )
- {
- return FALSE;
- }
- memcpy(&m_Waveformat,pWaveformat,sizeof(WAVEFORMATEX));
- m_Waveformat.nBlockAlign = (m_Waveformat.wBitsPerSample * m_Waveformat.nChannels) >> 3;
- m_Waveformat.nAvgBytesPerSec = m_Waveformat.nBlockAlign * m_Waveformat.nSamplesPerSec;
- MMRESULT mRet;
- WAVEFORMATEX wfx;
- //lphWaveOut: PHWaveOut; {用于返回设备句柄的指针; 如果 dwFlags=WAVE_FORMAT_QUERY, 这里应是 nil}
- //uDeviceID: UINT; {设备ID; 可以指定为: WAVE_MAPPER, 这样函数会根据给定的波形格式选择合适的设备}
- //lpFormat: PWaveFormatEx; {TWaveFormat 结构的指针; TWaveFormat 包含要申请的波形格式}
- //dwCallback: DWORD {回调函数地址或窗口句柄; 若不使用回调机制, 设为 nil}
- //dwInstance: DWORD {给回调函数的实例数据; 不用于窗口}
- //dwFlags: DWORD {打开选项}// long120823
- mRet = waveOutOpen(0,WAVE_MAPPER,&m_Waveformat,0,0,WAVE_FORMAT_QUERY);
- if( mRet != MMSYSERR_NOERROR )
- {
- return FALSE;
- }
- mRet = waveOutOpen(&m_hWave,WAVE_MAPPER,&m_Waveformat,m_ThreadID,0,CALLBACK_THREAD);
- if( mRet != MMSYSERR_NOERROR )
- {
- return FALSE;
- }
- m_bDevOpen = TRUE;
- return TRUE;
- }
- void CViWavePlay::Close()
- {
- if (!m_bDevOpen)
- {
- return;
- }
- if(!m_hWave)
- {
- return;
- }
- MMRESULT mRet = waveOutClose(m_hWave);
- if( mRet != MMSYSERR_NOERROR )
- {
- return;
- }
- m_hWave = 0;
- m_bDevOpen = FALSE;
- }
- DWORD WINAPI CViWavePlay::ThreadProc(LPVOID lpParameter)
- {
- CViWavePlay *pWaveOut;
- pWaveOut = (CViWavePlay *)lpParameter;
- MSG msg;
- while(GetMessage(&msg,0,0,0))
- {
- switch(msg.message )
- {
- case WOM_OPEN:
- break;
- case WOM_CLOSE:
- break;
- case WOM_DONE:
- WAVEHDR* pWaveHead = (WAVEHDR*)msg.lParam;
- waveOutUnprepareHeader((HWAVEOUT)msg.wParam,pWaveHead,sizeof(WAVEHDR));
- pWaveOut->SubBuffer();
- delete []pWaveHead->lpData;
- delete pWaveHead;
- break;
- }
- }
- return msg.wParam;
- }
- BOOL CViWavePlay::StartThread()
- {
- if( m_bThread )
- {
- return FALSE;
- }
- m_hThread = CreateThread(0,0,ThreadProc,this,0,&m_ThreadID);
- if( !m_hThread )
- {
- return FALSE;
- }
- m_bThread = TRUE;
- return TRUE;
- }
- void CViWavePlay::StopThread()
- {
- if (!m_bThread)
- {
- return;
- }
- if(m_hThread)
- {
- int t=50;
- DWORD ExitCode;
- BOOL bEnd=FALSE;
- PostThreadMessage(m_ThreadID,WM_QUIT,0,0);
- while(t)
- {
- GetExitCodeThread(m_hThread,&ExitCode);
- if(ExitCode!= STILL_ACTIVE)
- {
- bEnd=TRUE;
- break;
- }
- else
- Sleep(10);
- t--;
- }
- if(!bEnd)
- {
- TerminateThread(m_hThread,0);
- }
- m_hThread = 0;
- }
- m_bThread = FALSE;
- }
- BOOL CViWavePlay::Start(PWAVEFORMATEX pWaveformat)
- {
- if (NULL==pWaveformat)
- {
- return FALSE;
- }
- if( !StartThread())
- {
- return FALSE;
- }
- if( !Open(pWaveformat))
- {
- StopThread();
- return FALSE;
- }
- return TRUE;
- }
- BOOL CViWavePlay::PlayAudio(char* buf,unsigned int nSize)
- {
- if( !m_bDevOpen )
- {
- return FALSE;
- }
- if( GetBufferNum() >= 5 )//超过缓冲最大包,不继续播放
- {
- return FALSE;
- }
- MMRESULT mRet;
- char* lpData = NULL;
- WAVEHDR* pWaveHead = new WAVEHDR;
- ZeroMemory(pWaveHead,sizeof(WAVEHDR));
- lpData = new char[nSize];
- pWaveHead->dwBufferLength = nSize;
- memcpy(lpData,buf,nSize);
- pWaveHead->lpData = lpData;
- mRet = waveOutPrepareHeader(m_hWave,pWaveHead,sizeof(WAVEHDR));
- if( mRet != MMSYSERR_NOERROR )
- {
- return FALSE;
- }
- mRet = waveOutWrite(m_hWave,pWaveHead,sizeof(WAVEHDR));
- if( mRet != MMSYSERR_NOERROR )
- {
- return FALSE;
- }
- AddBuffer();
- return TRUE;
- }
- void CViWavePlay::Stop()
- {
- Close();
- StopThread();
- }
- int CViWavePlay::GetBufferNum()
- {
- int nRet = 5;
- m_Lock.lock();
- nRet = m_BufferQueue;
- m_Lock.unlock();
- return nRet;
- }
- void CViWavePlay::AddBuffer()
- {
- m_Lock.lock();
- m_BufferQueue++;
- m_Lock.unlock();
- }
- void CViWavePlay::SubBuffer()
- {
- m_Lock.lock();
- m_BufferQueue--;
- m_Lock.unlock();
- }
方法二播放音频,但未经过严格测试
方法二头文件:
- #if !defined(AFX_WAVPLAYER_ER56782036__INCLUDED_)
- #define AFX_WAVPLAYER_ER56782036__INCLUDED_
- class CViWavePlay
- {
- public:
- CViWavePlay();
- ~CViWavePlay();
- bool Start(PWAVEFORMATEX pWavHead);
- void PlayAudio(LPSTR data, int size);
- void WaveMsg(HWAVEOUT hwo, UINT uMsg, DWORD dwParam1, DWORD dwParam2);
- private:
- bool m_bPalyState;
- private:
- CRITICAL_SECTION _waveCriticalSection;
- WAVEHDR* _waveBlocks;
- int _nWaveCurrentBlock;
- HWAVEOUT _hWaveOut;
- WAVEFORMATEX _wfx;
- volatile int _nWaveFreeBlockCount;
- protected:
- bool CloseDevice();
- WAVEHDR* allocateBlocks(int size, int count);
- void freeBlocks(WAVEHDR* blockArray);
- };
- #endif //AFX_WAVPLAYER_ER56782036__INCLUDED_
方法二cpp文件:
- #include "stdafx.h"
- #include "WavPlay_.h"
- #include <mmsystem.h>
- #define BLOCK_SIZE 8192
- #define BLOCK_COUNT 20
- #define BLOCK_MAX 8192
- //回调函数
- //如果选择窗口接受回调信息, 可能会发送到窗口的消息有:
- //MM_WOM_OPEN = $3BB;
- // MM_WOM_CLOSE = $3BC;
- // MM_WOM_DONE = $3BD;
- // 如果选择函数接受回调信息, 可能会发送给函数的消息有:
- // WOM_OPEN = MM_WOM_OPEN;
- // WOM_CLOSE = MM_WOM_CLOSE;
- // WOM_DONE = MM_WOM_DONE;
- void CALLBACK callback_waveOutProc( HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
- {
- /*
- * 忽略打开关闭设备操作
- */
- if(uMsg != WOM_DONE)
- return;
- CViWavePlay* pThis=(CViWavePlay*)dwInstance;
- if (NULL==pThis)
- {
- return;
- }
- pThis->WaveMsg(hwo,uMsg,dwParam1,dwParam2);
- return;
- }
- CViWavePlay::CViWavePlay()
- {
- m_bPalyState = false;
- ZeroMemory(&_wfx,sizeof(WAVEFORMATEX));
- _waveBlocks = NULL;
- _nWaveFreeBlockCount = 0;
- _nWaveCurrentBlock = 0;
- InitializeCriticalSection(&_waveCriticalSection);
- }
- //关闭线程,释放资源
- CViWavePlay::~CViWavePlay()
- {
- DeleteCriticalSection(&_waveCriticalSection);
- CloseDevice();
- m_bPalyState = false;
- }
- bool CViWavePlay::CloseDevice()
- {
- ZeroMemory(&_wfx,sizeof(WAVEFORMATEX));
- while(_nWaveFreeBlockCount < BLOCK_COUNT)
- Sleep(10);
- /*
- * unprepare any blocks that are still prepared
- */
- for(int i = 0; i < _nWaveFreeBlockCount; i++)
- {
- if(_waveBlocks[i].dwFlags & WHDR_PREPARED)
- {
- waveOutUnprepareHeader(_hWaveOut, &_waveBlocks[i], sizeof(WAVEHDR));
- }
- }
- freeBlocks(_waveBlocks);
- waveOutClose(_hWaveOut);
- return true;
- }
- bool CViWavePlay::Start(PWAVEFORMATEX pWavHead)
- {
- if (m_bPalyState)
- {
- return false;
- }
- if (NULL==pWavHead)
- {
- return false;
- }
- //CloseDevice();
- _waveBlocks = allocateBlocks(BLOCK_SIZE, BLOCK_COUNT);
- _nWaveFreeBlockCount = BLOCK_COUNT;
- _nWaveCurrentBlock = 0;
- // ZeroMemory(&_wfx,sizeof(WAVEFORMATEX));
- // _wfx.nSamplesPerSec = 44100; /* sample rate */
- // _wfx.wBitsPerSample = 16; /* sample size */
- // _wfx.nChannels = 2; /* channels */
- // _wfx.cbSize = 0; /* size of _extra_ info */
- // _wfx.wFormatTag = WAVE_FORMAT_PCM;
- // _wfx.nBlockAlign = (_wfx.wBitsPerSample * _wfx.nChannels) >> 3;
- // _wfx.nAvgBytesPerSec = _wfx.nBlockAlign * _wfx.nSamplesPerSec;
- memcpy(&_wfx,pWavHead,sizeof(WAVEFORMATEX));
- _wfx.nBlockAlign = (_wfx.wBitsPerSample * _wfx.nChannels) >> 3;
- _wfx.nAvgBytesPerSec = _wfx.nBlockAlign * _wfx.nSamplesPerSec;
- if(::waveOutOpen (0,0,&_wfx,0,0,WAVE_FORMAT_QUERY)) //WAVE_FORMAT_QUERY = $0001;{只是判断设备是否支持给定的格式, 并不打开}
- {
- TRACE_ERR2("wave设备初始化失败~");
- return false;
- }
- //lphWaveOut: PHWaveOut; {用于返回设备句柄的指针; 如果 dwFlags=WAVE_FORMAT_QUERY, 这里应是 nil}
- //uDeviceID: UINT; {设备ID; 可以指定为: WAVE_MAPPER, 这样函数会根据给定的波形格式选择合适的设备}
- //lpFormat: PWaveFormatEx; {TWaveFormat 结构的指针; TWaveFormat 包含要申请的波形格式}
- //dwCallback: DWORD {回调函数地址或窗口句柄; 若不使用回调机制, 设为 nil}
- //dwInstance: DWORD {给回调函数的实例数据; 不用于窗口}
- //dwFlags: DWORD {打开选项}
- if(waveOutOpen(&_hWaveOut, WAVE_MAPPER, &_wfx, (DWORD_PTR)callback_waveOutProc, (DWORD)this, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
- {
- TRACE_ERR2("wave设备打开失败~");
- return false;
- }
- m_bPalyState = true;
- _nWaveCurrentBlock = 0;
- return true;
- }
- void CViWavePlay::WaveMsg(HWAVEOUT hwo, UINT uMsg, DWORD dwParam1, DWORD dwParam2)
- {
- EnterCriticalSection(&_waveCriticalSection);
- _nWaveFreeBlockCount++;
- LeaveCriticalSection(&_waveCriticalSection);
- return;
- }
- void CViWavePlay::PlayAudio(LPSTR data, int size)
- {
- if (!m_bPalyState)
- return ;
- WAVEHDR* current;
- int remain;
- current = &_waveBlocks[_nWaveCurrentBlock];
- while(size > 0) {
- /*
- * 首先确定使用的header 是 unprepared
- */
- if(current->dwFlags & WHDR_PREPARED)
- waveOutUnprepareHeader(_hWaveOut, current, sizeof(WAVEHDR));
- if(size < (int)(BLOCK_SIZE - current->dwUser)) {
- memcpy(current->lpData + current->dwUser, data, size);
- current->dwUser += size;
- break;
- }
- remain = BLOCK_SIZE - current->dwUser;
- memcpy(current->lpData + current->dwUser, data, remain);
- size -= remain;
- data += remain;
- current->dwBufferLength = BLOCK_SIZE;
- waveOutPrepareHeader(_hWaveOut, current, sizeof(WAVEHDR));
- waveOutWrite(_hWaveOut, current, sizeof(WAVEHDR));
- EnterCriticalSection(&_waveCriticalSection);
- _nWaveFreeBlockCount--;
- LeaveCriticalSection(&_waveCriticalSection);
- /*
- * 等待free一个block
- */
- while(!_nWaveFreeBlockCount)
- Sleep(10);
- /*
- * 指向下一个block
- */
- _nWaveCurrentBlock++;
- _nWaveCurrentBlock %= BLOCK_COUNT;
- current = &_waveBlocks[_nWaveCurrentBlock];
- current->dwUser = 0;
- }
- }
- WAVEHDR* CViWavePlay::allocateBlocks(int size, int count)
- {
- unsigned char* buffer;
- WAVEHDR* blocks;
- DWORD totalBufferSize = (size + sizeof(WAVEHDR)) * count;
- if((buffer = (unsigned char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, totalBufferSize)) == NULL)
- {
- fprintf(stderr, "Memory allocation error\n");
- ExitProcess(1);
- }
- blocks = (WAVEHDR*)buffer;
- buffer += sizeof(WAVEHDR) * count;
- for(int i = 0; i < count; i++) {
- blocks[i].dwBufferLength = size;
- blocks[i].lpData = (LPSTR)buffer;
- buffer += size;
- }
- return blocks;
- }
- void CViWavePlay::freeBlocks(WAVEHDR* blockArray)
- {
- HeapFree(GetProcessHeap(), 0, blockArray);
- }