日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

多线程应用---使用WaveOut* API开发AMR音频播放器(含源码下载)

發布時間:2024/3/26 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 多线程应用---使用WaveOut* API开发AMR音频播放器(含源码下载) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

[源代碼以及工程實例下載 ]

1、 語音播放API

1.1 waveOutOpen - 打開播放設備

[cpp] view plaincopyprint?
  • MMRESULT?waveOutOpen(???
  • ????LPHWAVEOUT??????phwo,???????????????/*?一個指向接收波形音頻輸出設備的句柄?*/???
  • ????UINT_PTR????????uDeviceID,??????????/*?將要被打開的波形音頻輸出設備的ID?*/???
  • ????LPWAVEFORMATEX??pwfx,???????????????/*?一個指向將被送到設備的音頻數據格式的WAVEFORMATEX結構的指針?*/???
  • ????DWORD_PTR???????dwCallback,?????????/*?它指向一個特定的CALLBACK函數,事件柄,窗口柄...?*/??
  • ????DWORD_PTR???????dwCallbackInstance,?/*?傳遞到CALLBACK進程的用戶實例數據,如是窗口,該參數設為0?*/???
  • ????DWORD???????????fdwOpen?????????????/*?用來打開設備的標識(FLAGS)?*/??
  • ????);??
  • MMRESULT waveOutOpen( LPHWAVEOUT phwo, /* 一個指向接收波形音頻輸出設備的句柄 */ UINT_PTR uDeviceID, /* 將要被打開的波形音頻輸出設備的ID */ LPWAVEFORMATEX pwfx, /* 一個指向將被送到設備的音頻數據格式的WAVEFORMATEX結構的指針 */ DWORD_PTR dwCallback, /* 它指向一個特定的CALLBACK函數,事件柄,窗口柄... */DWORD_PTR dwCallbackInstance, /* 傳遞到CALLBACK進程的用戶實例數據,如是窗口,該參數設為0 */ DWORD fdwOpen /* 用來打開設備的標識(FLAGS) */);

    ??? 1)phwo:一個指向接收波形音頻輸出設備的句柄。用句柄來區別(identify)別的波形輸出設備。如果fdwOpen被設定為 WAVE_FORMAT_QUERY,那么這個參數可能為NULL 。
    ??? 2)uDevideID:將要被打開的波形音頻輸出設備的ID ,它可以是一個設備ID,也可以是一個已經打開的波形音頻輸入設備句柄,你可以用以下的值來貨替:
    WAVE_MAPPER - 該函數選一個能夠播放給定格式的波形音頻輸出設備
    ??? 3)pwfx:一個指向將被送到設備的音頻數據格式的WAVEFORMATEX結構的指針,當調用waveOutOpen 函數之后可立即釋放該結構;
    ??? 4)dwCallback:它指向一個特定的CALLBACK函數,事件柄,窗口柄,或一個線程ID(用于在音頻回放時以便處理與回放進度相關的消息)。如果無須CALLBACK函數,可以將其設為0 。
    ??? 5)dwCallbackInstance :傳遞到CALLBACK進程的用戶實例數據。如果是窗口CALLBACK進程的話,該參數不用(設為0)
    ????6)fwOpen:用來打開設備的標識(FLAGS),它們的定義如下:

    含義
    CALLBACK_EVENTdwCallback 參數欄是事件句柄
    CALLBACK_FUNCTIONdwCallback 參數欄是CALLBACK函數地址
    CALLBACK_NULL默認的設置,即無CALLBACK進程
    CALLBACK_THREADdwCallback 參數欄是線程ID
    CALLBACK_WINDOWdwCallback 參數欄是窗口句柄
    WAVE_ALLOWSYNC如果該項被設置,一個同步的波形音頻設備能被打開。如果在打開一個同步驅動時沒有用該項,設備打開將會失敗。
    WAVE_FORMAT_DIRECT如果設定該項,音頻設備不會對輸出的音頻數據執行轉換
    WAVE_FORMAT_QUERY如果設定該項,waveOutOpen 用于詢問設備是否支持給定的格式,但設備實際上并沒有被打開。此時phwo參數可為NULL
    WAVE_MAPPED該項被設定后uDeviceID參數表示將通過波形映射器映射到一個波形格式音頻設備。

    ??? 7)返回值:成功后返回MMSYSERR_NOERROR ,否則返回以下值:

    描述
    MMSYSERR_ALLOCATED表示資源已存在
    MMSYSERR_BADDEVICEID設備ID超出范圍
    MMSYSERR_NODRIVER沒有驅動
    MMSYSERR_NOMEM不能分配內存
    WAVERR_BADFORMAT企圖打開一個不被支持的格式
    WAVERR_SYNC設備是可同步的,但waveOutOpen沒用有WAVE_ALLOWSYNC設置。

    注:
    ??? waveoutopen創建設備實例句柄用于,使用其他waveoutAPI時將之作為參數,用于區別不同的音頻流。
    ??? 可用waveOutGetNumDevs函數測定在當前系統中輸出設備的數目。
    ??? 如果uDeviceID參數項是一個ID,它將會表示從0 到總的數目,WAAVE_MAPPER常量也可以用作裝置ID。
    ??? pwfc所指的結構能夠擴展到包含某些數據格式的特殊信息,例如,對于PCM數據,一個額外的UNIT類型用來表示取樣的位數目。在這個情況下用PCMWAVEFORAMT結構。對于其它的格式,用WAVEFORMATEX結構來表示額外的數據長度。
    ??? 如果你選用的是一個窗口或線程來接收CALLBACK信息,如下的信息將會被送到窗口處理函數中,來表明波形音頻輸出進程:MM_WOM_OPEN,MM_WOM_CLOSE ,和MM_WOM_DONE,如果你選用的是一個函數來接收CALLBACK信息,如下的信息將會被傳到函數中,來顯示波形音頻輸出進程:WOM_OPEN ,WOM_CLOSE,WOM_DONE。

    1.2 waveOutPrepareHeader - 準備數據塊

    [cpp] view plaincopyprint?
  • MMRESULT?waveOutPrepareHeader(??
  • ????HWAVEOUT?hwo,??/*?波形音頻輸出設備的句柄?*/??
  • ????LPWAVEHDR?pwh,?/*?一個WAVEHDR結構的指針,其基址必須與樣本大小對齊?*/??
  • ????UINT?cbwh??????/*?WAVEHDR結構的大小,單位:字節?*/??
  • ????);??
  • 備注:??
  • MMRESULT waveOutPrepareHeader(HWAVEOUT hwo, /* 波形音頻輸出設備的句柄 */LPWAVEHDR pwh, /* 一個WAVEHDR結構的指針,其基址必須與樣本大小對齊 */UINT cbwh /* WAVEHDR結構的大小,單位:字節 */); 備注: [cpp] view plaincopyprint?
  • 調用此函數之前必須設置WAVEHDR結構的lpData,dwBufferLength,dwFlags成員,dwFlags成員必須設置為0。??
  • 一旦準備完成,不可以修改lpData指針。??
  • 嘗試準備一個處于準備狀態的數據塊,將無效果,返回0??
  • 不應該在同一時刻為不同的波形設備準備同一個數據,如果想從一個設備錄制并在另一個設備播放數據,但不想拷貝緩存塊,可以分配兩塊WAVEHDR并將lpData指向同一數據緩存地址,其中一個調用<STRONG>waveInPrepareHeader</STRONG>,另一個調用<STRONG>waveOutPrepareHeader</STRONG>。??
  • 調用此函數之前必須設置WAVEHDR結構的lpData,dwBufferLength,dwFlags成員,dwFlags成員必須設置為0。一旦準備完成,不可以修改lpData指針。嘗試準備一個處于準備狀態的數據塊,將無效果,返回0不應該在同一時刻為不同的波形設備準備同一個數據,如果想從一個設備錄制并在另一個設備播放數據,但不想拷貝緩存塊,可以分配兩塊WAVEHDR并將lpData指向同一數據緩存地址,其中一個調用<strong>waveInPrepareHeader</strong>,另一個調用<strong>waveOutPrepareHeader</strong>。

    1.3 waveOutWrite - 將音頻數據塊送到指定的音頻輸出設備

    [cpp] view plaincopyprint?
  • MMRESULT?waveOutWrite(??
  • ????HWAVEOUT?hwo,????/*?音頻輸出設備句柄?*/?  ??
  • ????LPWAVEHDR?pwh,? /*?包含音頻數據塊信息的WAVEHDR結構指針?*/ ??
  • ????UINT?cbwh?  ???/*?WAVEHDR結構大小,單位byte?*/??
  • ????);???
  • MMRESULT waveOutWrite(HWAVEOUT hwo, /* 音頻輸出設備句柄 */   LPWAVEHDR pwh,  /* 包含音頻數據塊信息的WAVEHDR結構指針 */ UINT cbwh    /* WAVEHDR結構大小,單位byte */);

    注意:
    ??? 要播放的數據一般在聲音文件里面獲得,并填入這個結構。由于是直接播放數據。所以要播放多少數據可以通過修改這個結構來達到目的。
    ??? 播放完數據后 WHDR_DONE 會設置到pwh指向的結構體中的dwFlags 成員
    ??? 在調用本函數之前必須調用waveOutPrepareHeader函數
    ??? 除非是恢復被waveOutPause函數暫停的設備,回調函數會在第一個數據塊一送達設備的時候就開始運作.回調函數在waveOutOpen里面設置
    ??? 請不要在回調函數里面調用任何的waveOut系列的函數,否則一定會造成死鎖。哪怕是waveOutUnprepareHeader,waveOutClose

    1.4 waveOutUnprepareHeader - 清除音頻緩存數據

    [cpp] view plaincopyprint?
  • MMRESULT?waveOutUnprepareHeader(??
  • ??HWAVEOUT?hwo,???
  • ??LPWAVEHDR?pwh,???
  • ??UINT?cbwh???
  • );??
  • MMRESULT waveOutUnprepareHeader(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh );

    注意:
    ??? 調用時機,送到的音頻輸出設備的數據已經播放完成即:pwh的dwFlags中WHDR_DONE位有效

    1.5 waveOutPause - 暫停音頻播放

    1.6 waveOutRestart - 繼續播放已暫停的音頻設備

    1.7 waveOutReset - 重設音頻輸出設備,將已準備好的緩存塊均設置為已播放完成

    1.8 waveOutClose - 關閉音頻輸出設備

    2、解碼器開發

    [cpp] view plaincopyprint?
  • class?AMRFileDecoder??
  • {??
  • public:??
  • ????AMRFileDecoder(void);??
  • ????AMRFileDecoder(LPCTSTR?lpszFile);??
  • ????virtual?~AMRFileDecoder(void);??
  • ??
  • private:?//?屏蔽拷貝構造函數和賦值運算 ??
  • ????AMRFileDecoder(const?AMRFileDecoder&?)??
  • ????{??
  • ????????ATLASSERT(FALSE);??
  • ????}??
  • ????AMRFileDecoder&?operator=(const?AMRFileDecoder&)??
  • ????{??
  • ????????ATLASSERT(FALSE);??
  • ????????return?*this;??
  • ????}??
  • ??
  • public:??
  • ????///?設置需解碼文件路徑 ??
  • ????virtual?void?SetFilePathName(LPCTSTR?lpszFile);??
  • ????///?獲取總時間長度,單位ms ??
  • ????virtual?ULONGLONG?GetTimeLength();??
  • ????///?獲取解碼后的波形格式 ??
  • ????virtual?WAVEFORMATEX?GetWaveFromatX();??
  • ????///?開始解碼,初始化解碼器 ??
  • ????virtual?BOOL?BeginDecode();??
  • ????///?解碼,每解碼一幀,游標后移至下一幀,返回解碼后的幀大小,輸出解碼后的波形數據 ??
  • ????virtual?DWORD?Decode(LPSTR&?pData);??
  • ????///?判斷是否解碼結束 ??
  • ????virtual?bool?IsEOF();??
  • ????///?結束解碼,銷毀解碼器 ??
  • ????virtual?void?EndDecode();??
  • ????///?判斷解碼器是否正常 ??
  • ????virtual?bool?IsVaild();??
  • ????///?獲取解碼后的波形數據大小,單位byte ??
  • ????virtual?DWORD?GetDecodedMaxSize();??
  • ????///?獲取解碼后的波形數據幀大小,單位byte ??
  • ????virtual?DWORD?GetDecodedFrameMaxSize();??
  • ??
  • private:??
  • ????DWORD?GetFrameCount();??
  • ??
  • private:??
  • ????LPSTR???????m_pBaseAddress;?????????//?文件映射內存塊首地址 ??
  • ????LONGLONG????m_liFileSize;???????????//?文件映射內存塊大小 ??
  • ????ULONG???????m_dwFrameCount;?????????//?幀總數 ??
  • ????LPSTR???????m_pCurAddress;??????????//?解碼游標,指示當前解碼位置 ??
  • ????LPVOID??????m_pvDecoderState;???????//?解碼器狀態指針 ??
  • ????CString?????m_sFilePathName;????????//?解碼文件路徑 ??
  • };??
  • class AMRFileDecoder { public:AMRFileDecoder(void);AMRFileDecoder(LPCTSTR lpszFile);virtual ~AMRFileDecoder(void);private: // 屏蔽拷貝構造函數和賦值運算AMRFileDecoder(const AMRFileDecoder& ){ATLASSERT(FALSE);}AMRFileDecoder& operator=(const AMRFileDecoder&){ATLASSERT(FALSE);return *this;}public:/// 設置需解碼文件路徑virtual void SetFilePathName(LPCTSTR lpszFile);/// 獲取總時間長度,單位msvirtual ULONGLONG GetTimeLength();/// 獲取解碼后的波形格式virtual WAVEFORMATEX GetWaveFromatX();/// 開始解碼,初始化解碼器virtual BOOL BeginDecode();/// 解碼,每解碼一幀,游標后移至下一幀,返回解碼后的幀大小,輸出解碼后的波形數據virtual DWORD Decode(LPSTR& pData);/// 判斷是否解碼結束virtual bool IsEOF();/// 結束解碼,銷毀解碼器virtual void EndDecode();/// 判斷解碼器是否正常virtual bool IsVaild();/// 獲取解碼后的波形數據大小,單位bytevirtual DWORD GetDecodedMaxSize();/// 獲取解碼后的波形數據幀大小,單位bytevirtual DWORD GetDecodedFrameMaxSize();private:DWORD GetFrameCount();private:LPSTR m_pBaseAddress; // 文件映射內存塊首地址LONGLONG m_liFileSize; // 文件映射內存塊大小ULONG m_dwFrameCount; // 幀總數LPSTR m_pCurAddress; // 解碼游標,指示當前解碼位置LPVOID m_pvDecoderState; // 解碼器狀態指針CString m_sFilePathName; // 解碼文件路徑 };

    3、解碼線程

    [cpp] view plaincopyprint?
  • if(m_pDecoder?==?NULL?||?!m_pDecoder->IsVaild())??
  • ????????return?0;??
  • ??
  • ????//?開始解碼,初始化解碼器 ??
  • ????if(!m_pDecoder->BeginDecode())??
  • ????{??
  • ????????return?0;??
  • ????}??
  • ??
  • ????//?申請臨時內存塊,存儲解碼后的波形數據 ??
  • ????DWORD?dwFrameMaxSize?=?m_pDecoder->GetDecodedFrameMaxSize();??
  • ????LPSTR?pBufferBase?=?(LPSTR)malloc(dwFrameMaxSize);??
  • ????ATLASSERT(pBufferBase);??
  • ????memset(pBufferBase,?0,?dwFrameMaxSize);??
  • ????if(pBufferBase?==?NULL)?return?0;??
  • ??
  • ????register?ThreadMsg?tmsg?=?TMSG_ALIVE;??
  • ??
  • ????DWORD?dwSizeAmount?=?0;??
  • ????while(!m_pDecoder->IsEOF()?&&?tmsg)??
  • ????{??
  • ????????//?解碼,每幀 ??
  • ????????DWORD?dwSize?=?m_pDecoder->Decode(pBufferBase);??
  • ????????dwSizeAmount?+=?dwSize;??
  • ??
  • ????????//?將解碼后數據寫入緩存區,供播放線程使用 ??
  • ????????EnterCriticalSection(&m_cs);?????????????
  • ????????memcpy(m_waveData.pData?+?m_waveData.dwSize,?pBufferBase,?dwSize);??
  • ????????m_waveData.dwSize?+=?dwSize;??
  • ????????LeaveCriticalSection(&m_cs);??
  • ??
  • ????????//?當解碼數據量操作了一個播放緩存時,發個信號,通知可以開始播放了 ??
  • ????????if(dwSizeAmount?>?BLOCK_SIZE)??
  • ????????{??
  • ????????????dwSizeAmount?=?0;??
  • ????????????SetEvent(m_hEventDecode);??
  • ????????}??
  • ??
  • ????????//?節省CPU時間,讓CPU有時間去干別的事兒 ??
  • ????????Sleep(1);??
  • ??
  • ????????//?檢測線程是否將被強制退出 ??
  • ????????EnterCriticalSection(&m_cs);??
  • ????????tmsg?=?m_msgDecodeThread;??
  • ????????LeaveCriticalSection(&m_cs);??
  • ????}??
  • ??
  • ????//?如果數據量很小,根本不足一個播放緩存,也要發個信號 ??
  • ????if(dwSizeAmount?>?0)??
  • ????{??
  • ????????SetEvent(m_hEventDecode);??
  • ????}??
  • ??
  • ????m_waveData.bDecodeFinished?=?true;??
  • ??
  • ????//?解碼結束 ??
  • ????m_pDecoder->EndDecode();??
  • ??
  • ????free(pBufferBase);??
  • ????pBufferBase?=?NULL;??
  • ??
  • ????return?0;??
  • }??
  • if(m_pDecoder == NULL || !m_pDecoder->IsVaild())return 0;// 開始解碼,初始化解碼器if(!m_pDecoder->BeginDecode()){return 0;}// 申請臨時內存塊,存儲解碼后的波形數據DWORD dwFrameMaxSize = m_pDecoder->GetDecodedFrameMaxSize();LPSTR pBufferBase = (LPSTR)malloc(dwFrameMaxSize);ATLASSERT(pBufferBase);memset(pBufferBase, 0, dwFrameMaxSize);if(pBufferBase == NULL) return 0;register ThreadMsg tmsg = TMSG_ALIVE;DWORD dwSizeAmount = 0;while(!m_pDecoder->IsEOF() && tmsg){// 解碼,每幀DWORD dwSize = m_pDecoder->Decode(pBufferBase);dwSizeAmount += dwSize;// 將解碼后數據寫入緩存區,供播放線程使用EnterCriticalSection(&m_cs); memcpy(m_waveData.pData + m_waveData.dwSize, pBufferBase, dwSize);m_waveData.dwSize += dwSize;LeaveCriticalSection(&m_cs);// 當解碼數據量操作了一個播放緩存時,發個信號,通知可以開始播放了if(dwSizeAmount > BLOCK_SIZE){dwSizeAmount = 0;SetEvent(m_hEventDecode);}// 節省CPU時間,讓CPU有時間去干別的事兒Sleep(1);// 檢測線程是否將被強制退出EnterCriticalSection(&m_cs);tmsg = m_msgDecodeThread;LeaveCriticalSection(&m_cs);}// 如果數據量很小,根本不足一個播放緩存,也要發個信號if(dwSizeAmount > 0){SetEvent(m_hEventDecode);}m_waveData.bDecodeFinished = true;// 解碼結束m_pDecoder->EndDecode();free(pBufferBase);pBufferBase = NULL;return 0; }

    4、播放線程

    [cpp] view plaincopyprint?
  • unsigned?int?WavePlayer::PlayThreadProcImpl()??
  • {??
  • ????///?定義為寄存器變量,因為它將會被高頻率的使用,用于編譯器優化 ??
  • ????register????ThreadMsg???????tmsg??=?TMSG_ALIVE;???????????
  • ??
  • ????///?線程循環 ??
  • ????while(?tmsg?)??
  • ????{??
  • ????????//?每次循環后,交出CPU控制權,放在此處,因為下面有continue語句 ??
  • ????????Sleep(10);??
  • ??
  • ????????///?首先檢查線程消息 ??
  • ????????EnterCriticalSection(?&m_cs?);??
  • ????????tmsg?=?m_msgPlayThread;??
  • ????????LeaveCriticalSection(?&m_cs?);??
  • ????????//?線程要結束,退出線程循環 ??
  • ????????if(!tmsg)???break;??
  • ??
  • ????????//?如果設備為空,表示還沒有打開設備,需要打開設備 ??
  • ????????if(m_hWaveoutDev?==?NULL)??
  • ????????{??
  • ????????????EnterCriticalSection(&m_cs);??
  • ????????????MMRESULT?mmres?=?waveOutOpen(&m_hWaveoutDev,?WAVE_MAPPER,?&m_waveData.wfmtx,?(DWORD_PTR)WaveOutProc,?(DWORD_PTR)this,?CALLBACK_FUNCTION);??
  • ????????????LeaveCriticalSection(&m_cs);??
  • ????????????if(mmres?!=?MMSYSERR_NOERROR)??
  • ????????????{??
  • ????????????????//?failed,?try?again. ??
  • ????????????????continue;??
  • ????????????}??
  • ????????}??
  • ??????????
  • ????????//?檢查空閑緩存塊 ??
  • ????????EnterCriticalSection(?&m_cs?);??
  • ????????int?free?=?m_wBlock.wfreeblock;??
  • ????????LeaveCriticalSection(?&m_cs?);??
  • ??
  • ????????//?如果沒有空閑的緩存了,等待... ??
  • ????????if(free?<?BP_TURN)??
  • ????????{??
  • ????????????continue;??
  • ????????}??
  • ??
  • ????????/ ??
  • ????????/ ??
  • ????????///???????????????????????<?播放主循環?>??????????????????????????????/// ??
  • ????????/ ??
  • ????????/ ??
  • ??
  • ????????WAVEHDR?????*current?=?NULL;??
  • ??
  • ????????///?BP_TURN為每次寫入播放隊列的塊數 ??
  • ????????for(?unsigned?int?m?=?0;?m?<?BP_TURN;?m++?)??
  • ????????{?????
  • ????????????///?當前空閑播放緩存塊 ??
  • ????????????current?=?&m_wBlock.pWaveHdr[m_wBlock.wcurrblock];???
  • ??
  • ????????????//?首先需要檢查有沒有被Unprepare掉 ??
  • ????????????if(?current->dwFlags?&?WHDR_PREPARED?)??
  • ????????????{??
  • ????????????????waveOutUnprepareHeader(?m_hWaveoutDev,?current,?sizeof(WAVEHDR)?);??
  • ????????????}??
  • ??
  • ????????????///?計算剩余需要播放的數據 ??
  • ????????????EnterCriticalSection(&m_cs);??
  • ????????????unsigned?long?left??=?m_waveData.dwSize?-?m_wBlock.wpos;??
  • ????????????unsigned?int?bDecodeFinished?=?m_waveData.bDecodeFinished;??
  • ????????????LeaveCriticalSection(&m_cs);??
  • ????????????unsigned?long?chunk?=?0;??
  • ??
  • ????????????if(?left??>=?BLOCK_SIZE?)??
  • ????????????{??
  • ????????????????chunk??=?BLOCK_SIZE;??
  • ????????????}??
  • ????????????else?if(!bDecodeFinished)??
  • ????????????{??
  • ????????????????//?如果解碼還沒有結束,現有的數據量有不足以填滿一個緩存塊,先不寫入緩存 ??
  • ????????????????break;??
  • ????????????}??
  • ????????????else?if(?left?&&?left?<?BLOCK_SIZE)??
  • ????????????{??
  • ????????????????chunk??=?left;??
  • ????????????}??
  • ????????????else??
  • ????????????{?????
  • ????????????????// ??
  • ????????????????///?????????????????<?播放完成>????????????????????????????????????/// ??
  • ????????????????// ??
  • ??
  • ????????????????///?獲取空閑緩存塊數量 ??
  • ????????????????EnterCriticalSection(?&m_cs?);??
  • ????????????????int?free?=?m_wBlock.wfreeblock;??
  • ????????????????LeaveCriticalSection(?&m_cs?);??
  • ??
  • ????????????????///?當所有的緩存塊都播放完了,才意味著播放真正完成 ??
  • ????????????????if(?free?==?BLOCK_COUNT?)??
  • ????????????????{??
  • ????????????????????///?Unprepare緩存塊 ??
  • ????????????????????for(?int?j?=?0;?j?<?m_wBlock.wfreeblock;?j++)???
  • ????????????????????{??
  • ????????????????????????if(?m_wBlock.pWaveHdr[j].dwFlags?&?WHDR_PREPARED?)??
  • ????????????????????????{??
  • ????????????????????????????waveOutUnprepareHeader(m_hWaveoutDev,?&m_wBlock.pWaveHdr[j],?sizeof(WAVEHDR));??
  • ????????????????????????}??
  • ????????????????????}??
  • ??
  • ????????????????????//?此時,才算真正的播放完成,關閉線程 ??
  • ????????????????????tmsg?=?TMSG_CLOSE;??
  • ????????????????????//?處理播放完成事件 ??
  • ????????????????????OnPlayFinished();??
  • ????????????????}??
  • ??
  • ????????????????//?此break僅跳出該循環,沒有跳出線程循環 ??
  • ????????????????break;??
  • ????????????}??
  • ??
  • ????????????///?prepare?current?wave?data?block?header ??
  • ????????????EnterCriticalSection(&m_cs);??
  • ????????????memcpy(?current->lpData,?&m_waveData.pData[m_wBlock.wpos],?chunk?);??
  • ????????????LeaveCriticalSection(&m_cs);??
  • ??
  • ????????????current->dwBufferLength??=?chunk;???//?sizeof?block ??
  • ????????????m_wBlock.wpos???????????+=?chunk;???//?update?position ??
  • ??
  • ????????????///?prepare?for?playback ??
  • ????????????waveOutPrepareHeader(?m_hWaveoutDev,?current,?sizeof(WAVEHDR)?);??
  • ??
  • ????????????///?push?to?the?queue ??
  • ????????????waveOutWrite(m_hWaveoutDev,?current,?sizeof(WAVEHDR));??
  • ??
  • ????????????///?減小空閑塊計數 ??
  • ????????????EnterCriticalSection(?&m_cs?);??
  • ????????????m_wBlock.wfreeblock--;??
  • ????????????LeaveCriticalSection(?&m_cs?);??
  • ??
  • ????????????///?使當前空閑塊指向下一個 ??
  • ????????????m_wBlock.wcurrblock++;??
  • ????????????m_wBlock.wcurrblock?%=?BLOCK_COUNT;??
  • ????????}??
  • ??
  • ????}///?thread ??
  • ??
  • ????/// ??
  • ????/// ??
  • ????/// ??
  • ????///????????????<?force?to?close?device?which?are?still?playing?>? ??
  • ????/// ??
  • ????/// ??
  • ????if(m_hWaveoutDev)??
  • ????{??
  • ????????waveOutReset(?m_hWaveoutDev?);??
  • ??
  • ????????///?unprepare?any?blocks?that?are?still?prepared ??
  • ????????for(?int?j?=?0;?j?<?BLOCK_COUNT;?j++)???
  • ????????{??
  • ????????????if(?m_wBlock.pWaveHdr[j].dwFlags?&?WHDR_PREPARED?)??
  • ????????????{??
  • ????????????????waveOutUnprepareHeader(m_hWaveoutDev,?&m_wBlock.pWaveHdr[j],?sizeof(WAVEHDR));??
  • ????????????}??
  • ????????}??
  • ??
  • ????????waveOutClose(m_hWaveoutDev);??
  • ????????m_hWaveoutDev?=?NULL;??
  • ????}??
  • ??
  • ????return?THREAD_EXIT;??
  • }??
  • unsigned int WavePlayer::PlayThreadProcImpl() {/// 定義為寄存器變量,因為它將會被高頻率的使用,用于編譯器優化register ThreadMsg tmsg = TMSG_ALIVE; /// 線程循環while( tmsg ){// 每次循環后,交出CPU控制權,放在此處,因為下面有continue語句Sleep(10);/// 首先檢查線程消息EnterCriticalSection( &m_cs );tmsg = m_msgPlayThread;LeaveCriticalSection( &m_cs );// 線程要結束,退出線程循環if(!tmsg) break;// 如果設備為空,表示還沒有打開設備,需要打開設備if(m_hWaveoutDev == NULL){EnterCriticalSection(&m_cs);MMRESULT mmres = waveOutOpen(&m_hWaveoutDev, WAVE_MAPPER, &m_waveData.wfmtx, (DWORD_PTR)WaveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION);LeaveCriticalSection(&m_cs);if(mmres != MMSYSERR_NOERROR){// failed, try again.continue;}}// 檢查空閑緩存塊EnterCriticalSection( &m_cs );int free = m_wBlock.wfreeblock;LeaveCriticalSection( &m_cs );// 如果沒有空閑的緩存了,等待...if(free < BP_TURN){continue;}///// < 播放主循環 > /////WAVEHDR *current = NULL;/// BP_TURN為每次寫入播放隊列的塊數for( unsigned int m = 0; m < BP_TURN; m++ ){ /// 當前空閑播放緩存塊current = &m_wBlock.pWaveHdr[m_wBlock.wcurrblock]; // 首先需要檢查有沒有被Unprepare掉if( current->dwFlags & WHDR_PREPARED ){waveOutUnprepareHeader( m_hWaveoutDev, current, sizeof(WAVEHDR) );}/// 計算剩余需要播放的數據EnterCriticalSection(&m_cs);unsigned long left = m_waveData.dwSize - m_wBlock.wpos;unsigned int bDecodeFinished = m_waveData.bDecodeFinished;LeaveCriticalSection(&m_cs);unsigned long chunk = 0;if( left >= BLOCK_SIZE ){chunk = BLOCK_SIZE;}else if(!bDecodeFinished){// 如果解碼還沒有結束,現有的數據量有不足以填滿一個緩存塊,先不寫入緩存break;}else if( left && left < BLOCK_SIZE){chunk = left;}else{ ///// < 播放完成> //////// 獲取空閑緩存塊數量EnterCriticalSection( &m_cs );int free = m_wBlock.wfreeblock;LeaveCriticalSection( &m_cs );/// 當所有的緩存塊都播放完了,才意味著播放真正完成if( free == BLOCK_COUNT ){/// Unprepare緩存塊for( int j = 0; j < m_wBlock.wfreeblock; j++) {if( m_wBlock.pWaveHdr[j].dwFlags & WHDR_PREPARED ){waveOutUnprepareHeader(m_hWaveoutDev, &m_wBlock.pWaveHdr[j], sizeof(WAVEHDR));}}// 此時,才算真正的播放完成,關閉線程tmsg = TMSG_CLOSE;// 處理播放完成事件OnPlayFinished();}// 此break僅跳出該循環,沒有跳出線程循環break;}/// prepare current wave data block headerEnterCriticalSection(&m_cs);memcpy( current->lpData, &m_waveData.pData[m_wBlock.wpos], chunk );LeaveCriticalSection(&m_cs);current->dwBufferLength = chunk; // sizeof blockm_wBlock.wpos += chunk; // update position/// prepare for playbackwaveOutPrepareHeader( m_hWaveoutDev, current, sizeof(WAVEHDR) );/// push to the queuewaveOutWrite(m_hWaveoutDev, current, sizeof(WAVEHDR));/// 減小空閑塊計數EnterCriticalSection( &m_cs );m_wBlock.wfreeblock--;LeaveCriticalSection( &m_cs );/// 使當前空閑塊指向下一個m_wBlock.wcurrblock++;m_wBlock.wcurrblock %= BLOCK_COUNT;}}/// thread//////////// < force to close device which are still playing > //////if(m_hWaveoutDev){waveOutReset( m_hWaveoutDev );/// unprepare any blocks that are still preparedfor( int j = 0; j < BLOCK_COUNT; j++) {if( m_wBlock.pWaveHdr[j].dwFlags & WHDR_PREPARED ){waveOutUnprepareHeader(m_hWaveoutDev, &m_wBlock.pWaveHdr[j], sizeof(WAVEHDR));}}waveOutClose(m_hWaveoutDev);m_hWaveoutDev = NULL;}return THREAD_EXIT; }

    5、主播放線程

    [cpp] view plaincopyprint?
  • //?如果已經有播放的了,先停止 ??
  • ????if(m_ePlayStat?!=?Play_Stop)??
  • ????{??
  • ????????Stop();??
  • ????}??
  • ??
  • ????//?設置解碼器 ??
  • ????if(m_pDecoder?==?NULL)??
  • ????{??
  • ????????m_pDecoder?=?new?AMRFileDecoder(lpszFile);??
  • ????}??
  • ????else??
  • ????{??
  • ????????m_pDecoder->SetFilePathName(lpszFile);??
  • ????}??
  • ??
  • ????//?取播放時間 ??
  • ????if(pLength)??
  • ????{??
  • ????????*pLength?=?(DWORD)m_pDecoder->GetTimeLength();??
  • ????}??
  • ??
  • ????//?申請解碼后的數據堆內存塊 ??
  • ????DWORD?dwWaveMaxSize?=?m_pDecoder->GetDecodedMaxSize();??
  • ????EnterCriticalSection(&m_cs);??
  • ????m_waveData.wfmtx?=?m_pDecoder->GetWaveFromatX();??
  • ????m_waveData.pData?=?(LPSTR)HeapAlloc(GetProcessHeap(),?HEAP_ZERO_MEMORY,?dwWaveMaxSize);??
  • ????LeaveCriticalSection(&m_cs);??
  • ??
  • ????//?設置回調函數 ??
  • ??
  • ????//?創建解碼線程 ??
  • ????if(m_hThreadDecode?==?NULL)??
  • ????{??
  • ????????m_msgDecodeThread?=?TMSG_ALIVE;??
  • ????????m_hThreadDecode?=?CreateThread(NULL,?0,?(LPTHREAD_START_ROUTINE)DecodeThread,?(LPVOID)this,?CREATE_SUSPENDED,?NULL);??
  • ????????ATLASSERT(m_hThreadDecode);??
  • ????????ResumeThread(m_hThreadDecode);??
  • ????}??
  • ??
  • ????//?等待解碼緩存信號 ??
  • ????WaitForSingleObject(m_hEventDecode,?INFINITE);??
  • ??
  • ????//?創建播放線程 ??
  • ????if(m_hThreadPlay?==?NULL)??
  • ????{??
  • ????????m_msgPlayThread?=?TMSG_ALIVE;??
  • ????????m_hThreadPlay?=?CreateThread(?NULL,?0,?(LPTHREAD_START_ROUTINE)PlayThread,?(LPVOID)this,?CREATE_SUSPENDED,?NULL?);??
  • ????????ATLASSERT(m_hThreadPlay);??
  • ????????ResumeThread(m_hThreadPlay);??
  • ????}??
  • ??
  • ????m_ePlayStat?=?Play_Playing;
  • 總結

    以上是生活随笔為你收集整理的多线程应用---使用WaveOut* API开发AMR音频播放器(含源码下载)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。