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~

你可能感兴趣的:(api)