制造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现

来源:互联网 发布:醉仙武坐骑进阶数据 编辑:程序博客网 时间:2024/05/17 20:56
打造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现


 本人应用场景:播放采集设备输出的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:WAVEOUTCAPSm_waveCaps;BOOLm_bDevOpen;BOOLm_bThread;HWAVEOUTm_hWave;HANDLEm_hThread;DWORDm_ThreadID;WAVEFORMATEX m_Waveformat;M_Critical_Sectionm_Lock;intm_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           {打开选项}// long120823mRet = 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;}elseSleep(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);}

原创粉丝点击