waveout系列API实现pcm音频播放
生活随笔
收集整理的這篇文章主要介紹了
waveout系列API实现pcm音频播放
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最近做一個播放組件,也算是折騰1周了,收獲還算不少。
回想下整個編碼過程中磕磕碰碰走了不少彎路,最大的杯具就是,太相信網上現有代碼例子。
國內網上關于waveout的文章不少,但基本就那幾篇轉載,其中的問題也沒有人指出。
為了方便大家用到時少被誤導,在此留下我的筆記(如果被我誤導了,我先道歉-,-)
代碼不多,直接上關鍵部分(本人認為多余代碼貼上去百害而無一利):
一、初始化設備
bool WinAudioPlay::DevOpen()
{
if (!m_bPalyStata)
{
WAVEFORMATEX wfx;
ZeroMemory(&wfx,sizeof(WAVEFORMATEX));
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = 44100L;
wfx.wBitsPerSample = 16;
wfx.cbSize = 0;
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * wfx.wBitsPerSample / 8;
if(::waveOutOpen (0,0,&wfx,0,0,WAVE_FORMAT_QUERY))
{
Plug::PlugMessageBox(L"wave設備初始化失敗~");
return false;
}
if (::waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &wfx, (DWORD)&WinAudioPlay::waveOutProc, (DWORD)this, CALLBACK_FUNCTION))
{
Plug::PlugMessageBox(L"wave設備初始化失敗~");
return false;
}
m_iBlockNum = 0;
m_bPalyStata = true;
m_spbrTgthr.reset(new boost::barrier(2));
m_sptrdWaveOutTgthr.reset(new boost::thread(boost::bind(&WinAudioPlay::ThrdWaveOutTogether,this)));
}
return true;
}
二、接收pcm格式數據,并加載到聲卡緩沖區
bool __stdcall WinAudioPlay::play_audio( const void* buffer, int len )
{
if (!m_bPalyStata)
return false;
if (BLOCK_MAX <= m_iBlockNum || len <= 0)
{
return true; //超過緩沖最大包,不繼續播放
}
LPWAVEHDR pWaveHeader = new WAVEHDR;
memset(pWaveHeader, 0, sizeof(WAVEHDR));
pWaveHeader->dwLoops = 1;
pWaveHeader->dwBufferLength = len;
pWaveHeader->lpData = new char[len];
if (!pWaveHeader->lpData)
{
delete pWaveHeader;
return false;
}
memcpy(pWaveHeader->lpData, buffer, len);
if (::waveOutPrepareHeader(m_hWaveOut, pWaveHeader, sizeof(WAVEHDR)))
{
delete pWaveHeader->lpData;
delete pWaveHeader;
return false;
}
if (::waveOutWrite(m_hWaveOut, pWaveHeader, sizeof(WAVEHDR)))
{
delete pWaveHeader->lpData;
delete pWaveHeader;
return false;
}
m_iBlockNum++;
return true;
}
三、回調函數
void CALLBACK WinAudioPlay::waveOutProc( HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
{
WinAudioPlay* pThis=(WinAudioPlay*)dwInstance;
if(WOM_DONE == uMsg) //播放完成
{
while(NULL != pThis->m_lpWaveHdrFromCallbackProc)
{
boost::this_thread::interruptible_wait(1);
}
pThis->m_lpWaveHdrFromCallbackProc = (LPWAVEHDR)dwParam1;
pThis->m_spbrTgthr->wait();
}
return ;
}
四、線程同步播放
void WinAudioPlay::ThrdWaveOutTogether()
{
while(!m_b_exit)
{
m_spbrTgthr->wait();
if (NULL != m_lpWaveHdrFromCallbackProc)
{
::waveOutUnprepareHeader(m_hWaveOut, m_lpWaveHdrFromCallbackProc, sizeof(WAVEHDR));
delete[] m_lpWaveHdrFromCallbackProc->lpData;
delete m_lpWaveHdrFromCallbackProc;
m_lpWaveHdrFromCallbackProc = NULL;
(m_iBlockNum > 0)?(m_iBlockNum--):(m_iBlockNum = 0);
}
}
if (m_hWaveOut != NULL)
{
::waveOutReset(m_hWaveOut);
::waveOutClose(m_hWaveOut);
}
}
五、關閉線程,釋放資源
WinAudioPlay::~WinAudioPlay()
{
m_bPalyStata = false;
while(0 != m_iBlockNum)
{
Sleep(1);
}
m_b_exit = true;
m_spbrTgthr->wait();
m_sptrdWaveOutTgthr->join();
}
我相信聰明的你,借助msdn能夠很快理解上面意思,我就不多打字了(水平有限,怕誤人子弟~)
但是需要注意以下幾點:
一、waveOutProc回調函數中絕對不能調用waveOut系列函數(可用線程同步實現通知在另一個線程調用)
二、調用waveOutReset函數時,函數執行完畢才返回,期間不可以調用hWaveOut
三、注意釋放資源
and so on...
其實還有很多,我就不多寫了,以上3點中前兩點處理不好,會發生線程死鎖。
當初我就在上面耗費了很長時間,后來多虧孫總點醒(其實msdn中有那么一句的。但是E語要加強啊。。。)
最后贈送一個免費的建議,聽不聽由你:不要太過于相信網絡上的代碼,win32平臺最有說服力的還數msdn~
歡迎各位轉載,但必須在文章頁面中給出作者和原文鏈接!
總結
以上是生活随笔為你收集整理的waveout系列API实现pcm音频播放的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 西沙群岛实际控制图(西沙群岛的实际控制现
- 下一篇: Oracle中的delete和trunc