製造自己的wave音頻播放器-使用waveOutOpen與waveOutWrite實現

來源:互聯網
上載者:User

打造自己的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);}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.