您的位置:首页 > 运维架构

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

2012-10-15 15:09 603 查看
打造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现

本人应用场景:播放采集设备输出的PCM音频,参考了两种网上实现方法,接口做了些调整,因为播放的音频属性经常需要改变,在播放初始化函数Start传入W***EFORMATEX参数,这样支持的播放音频种类多,经测试,均能正常播放,使用方法:

CViWavePlay* m_pWavPlay;
m_pWavPlay = new CViWavePlay;
m_pWavPlay->Start(PW***EFORMATEX(pbFormat));
m_pWavPlay->PlayAudio((char*)pPData->m_pData,pPData->m_nData);
m_pWavPlay->Stop();

Start函数参数W***EFORMATEX设置方法:

参见头文件的定义:

typedef struct tW***EFORMATEX

{

WORD wFormatTag; /* format type */// 波形声音的格式,本人此处设置为 W***E_FORMAT_PCM

WORD nChannels; /* number of channels (i.e. mono, stereo...) *///音频文件的通道数量,单声道为1,立体声为2.

DWORD nSamplesPerSec; /* sample rate *///样本采样率,对于 W***E_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// 额外信息的大小,以字节为单位,添加在W***EFORMATEX的结尾。如果不需要额外的信息,此值必为0

extra information (after cbSize) */

} W***EFORMATEX;

典型设置示例:

W***EFORMATEX _wfx;

_wfx.nSamplesPerSec = 44100; /* sample rate */

_wfx.wBitsPerSample = 16; /* sample size */

_wfx.nChannels = 2; /* channels */

_wfx.cbSize = 0; /* size of _extra_ info */

_wfx.wFormatTag = W***E_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 _W***EPLAY_4FR567H6_H_
#define _W***EPLAY_4FR567H6_H_

#include <mmsystem.h>
#include "mthread.h"

//音频播放
class CViWavePlay
{
public:
	CViWavePlay();
	~CViWavePlay();
public:
	BOOL Start(PW***EFORMATEX pWaveformat);
	BOOL PlayAudio(char* buf,unsigned int nSize);
	void Stop();
public:
	UINT GetDeviceNum();
	W***EOUTCAPS* GetDeviceCap();

private:
	static DWORD WINAPI ThreadProc(LPVOID lpParameter);
	inline int GetBufferNum();
	inline void AddBuffer();
	inline void SubBuffer();

	BOOL Open(PW***EFORMATEX pWaveformat);
	void Close();
	BOOL StartThread();
	void StopThread();

private:
	W***EOUTCAPS	m_waveCaps;
	BOOL		m_bDevOpen;
	BOOL		m_bThread;
	HW***EOUT	m_hWave;
	HANDLE		m_hThread;
	DWORD		m_ThreadID;

	W***EFORMATEX m_Waveformat;

	M_Critical_Section	m_Lock;
	int			m_BufferQueue;
};

#endif //_W***EPLAY_4FR567H6_H_

实现方法一的cpp文件:

#include "stdafx.h"
#include "WavPlay.h"
#pragma comment(lib,"Winmm")

CViWavePlay::CViWavePlay()
{
	ZeroMemory(&m_Waveformat,sizeof(W***EFORMATEX)); 
	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();
}

W***EOUTCAPS* CViWavePlay::GetDeviceCap()
{
	MMRESULT mRet = waveOutGetDevCaps(W***E_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      = W***E_FORMAT_PCM;
// 	_wfx.nBlockAlign     = (_wfx.wBitsPerSample * _wfx.nChannels) >> 3;
// 	_wfx.nAvgBytesPerSec = _wfx.nBlockAlign * _wfx.nSamplesPerSec;
BOOL CViWavePlay::Open(PW***EFORMATEX pWaveformat)
{
	if( m_bDevOpen )
	{
		return FALSE;
	}
	memcpy(&m_Waveformat,pWaveformat,sizeof(W***EFORMATEX));
	m_Waveformat.nBlockAlign     = (m_Waveformat.wBitsPerSample * m_Waveformat.nChannels) >> 3;
	m_Waveformat.nAvgBytesPerSec = m_Waveformat.nBlockAlign * m_Waveformat.nSamplesPerSec;

	MMRESULT mRet;
	W***EFORMATEX wfx;

	//lphWaveOut: PHWaveOut;   {用于返回设备句柄的指针; 如果 dwFlags=W***E_FORMAT_QUERY, 这里应是 nil}
	//uDeviceID: UINT;         {设备ID; 可以指定为: W***E_MAPPER, 这样函数会根据给定的波形格式选择合适的设备}
	//lpFormat: PWaveFormatEx; {TWaveFormat 结构的指针; TWaveFormat 包含要申请的波形格式}
	//dwCallback: DWORD        {回调函数地址或窗口句柄; 若不使用回调机制, 设为 nil}
	//dwInstance: DWORD        {给回调函数的实例数据; 不用于窗口}
	//dwFlags: DWORD           {打开选项}// long120823
	mRet = waveOutOpen(0,W***E_MAPPER,&m_Waveformat,0,0,W***E_FORMAT_QUERY);
	if( mRet != MMSYSERR_NOERROR )
	{
		return FALSE;
	}

	mRet = waveOutOpen(&m_hWave,W***E_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:
			W***EHDR* pWaveHead = (W***EHDR*)msg.lParam;
			waveOutUnprepareHeader((HW***EOUT)msg.wParam,pWaveHead,sizeof(W***EHDR));
			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;
			}
			else
				Sleep(10);
			t--;
		}
		if(!bEnd)
		{
			TerminateThread(m_hThread,0);
		}
		m_hThread = 0;
	}
	m_bThread = FALSE;
}

BOOL CViWavePlay::Start(PW***EFORMATEX 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;
	W***EHDR* pWaveHead = new W***EHDR;

	ZeroMemory(pWaveHead,sizeof(W***EHDR));

	lpData = new char[nSize];

	pWaveHead->dwBufferLength = nSize;
	memcpy(lpData,buf,nSize);
	pWaveHead->lpData = lpData;

	mRet = waveOutPrepareHeader(m_hWave,pWaveHead,sizeof(W***EHDR));
  	if( mRet != MMSYSERR_NOERROR )
	{
		return FALSE;
	}

	mRet = waveOutWrite(m_hWave,pWaveHead,sizeof(W***EHDR));
  	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_W***PLAYER_ER56782036__INCLUDED_)
#define AFX_W***PLAYER_ER56782036__INCLUDED_

class CViWavePlay
{
public:
	CViWavePlay();
	~CViWavePlay();
	bool Start(PW***EFORMATEX pWavHead);
	void PlayAudio(LPSTR data, int size);
	void WaveMsg(HW***EOUT hwo, UINT uMsg, DWORD dwParam1, DWORD dwParam2);

private:
	bool m_bPalyState;
private:
	CRITICAL_SECTION _waveCriticalSection;
	W***EHDR*         _waveBlocks;	
	int              _nWaveCurrentBlock;
	HW***EOUT _hWaveOut;
	W***EFORMATEX _wfx;  
	volatile int     _nWaveFreeBlockCount;	
protected:
	bool CloseDevice();
	W***EHDR* allocateBlocks(int size, int count);
	void freeBlocks(W***EHDR* blockArray);
};

#endif //AFX_W***PLAYER_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( HW***EOUT 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(W***EFORMATEX)); 
	_waveBlocks          = NULL;
	_nWaveFreeBlockCount = 0;
	_nWaveCurrentBlock   = 0;

	InitializeCriticalSection(&_waveCriticalSection);

}
//关闭线程,释放资源
CViWavePlay::~CViWavePlay() 
{   
	DeleteCriticalSection(&_waveCriticalSection);
	CloseDevice();
	m_bPalyState = false; 
} 

bool CViWavePlay::CloseDevice()
{
	ZeroMemory(&_wfx,sizeof(W***EFORMATEX)); 
	 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(W***EHDR));
		}
	}
	freeBlocks(_waveBlocks);
	waveOutClose(_hWaveOut);
	return true;
}

bool CViWavePlay::Start(PW***EFORMATEX 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(W***EFORMATEX)); 
// 	_wfx.nSamplesPerSec  = 44100;  /* sample rate */
// 	_wfx.wBitsPerSample  = 16;     /* sample size */
// 	_wfx.nChannels       = 2;      /* channels    */
// 	_wfx.cbSize          = 0;      /* size of _extra_ info */
// 	_wfx.wFormatTag      = W***E_FORMAT_PCM;
// 	_wfx.nBlockAlign     = (_wfx.wBitsPerSample * _wfx.nChannels) >> 3;
// 	_wfx.nAvgBytesPerSec = _wfx.nBlockAlign * _wfx.nSamplesPerSec;
	memcpy(&_wfx,pWavHead,sizeof(W***EFORMATEX));
	_wfx.nBlockAlign     = (_wfx.wBitsPerSample * _wfx.nChannels) >> 3;
	_wfx.nAvgBytesPerSec = _wfx.nBlockAlign * _wfx.nSamplesPerSec;

	if(::waveOutOpen (0,0,&_wfx,0,0,W***E_FORMAT_QUERY)) //W***E_FORMAT_QUERY = $0001;{只是判断设备是否支持给定的格式, 并不打开}
	{ 
		TRACE_ERR2("wave设备初始化失败~"); 
		return false; 
	} 
	//lphWaveOut: PHWaveOut;   {用于返回设备句柄的指针; 如果 dwFlags=W***E_FORMAT_QUERY, 这里应是 nil}
	//uDeviceID: UINT;         {设备ID; 可以指定为: W***E_MAPPER, 这样函数会根据给定的波形格式选择合适的设备}
	//lpFormat: PWaveFormatEx; {TWaveFormat 结构的指针; TWaveFormat 包含要申请的波形格式}
	//dwCallback: DWORD        {回调函数地址或窗口句柄; 若不使用回调机制, 设为 nil}
	//dwInstance: DWORD        {给回调函数的实例数据; 不用于窗口}
	//dwFlags: DWORD           {打开选项}
	if(waveOutOpen(&_hWaveOut, W***E_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(HW***EOUT hwo, UINT uMsg, DWORD dwParam1, DWORD dwParam2) 
{
	EnterCriticalSection(&_waveCriticalSection);
	_nWaveFreeBlockCount++;
	LeaveCriticalSection(&_waveCriticalSection);
	return;
}

void CViWavePlay::PlayAudio(LPSTR data, int size)
{
	if (!m_bPalyState) 
		return ; 

    W***EHDR* current;
    int remain;

    current = &_waveBlocks[_nWaveCurrentBlock];
    
    while(size > 0) {
        /* 
         * 首先确定使用的header 是 unprepared
         */
        if(current->dwFlags & WHDR_PREPARED) 
            waveOutUnprepareHeader(_hWaveOut, current, sizeof(W***EHDR));

        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(W***EHDR));
        waveOutWrite(_hWaveOut, current, sizeof(W***EHDR));
        
        EnterCriticalSection(&_waveCriticalSection);
        _nWaveFreeBlockCount--;
        LeaveCriticalSection(&_waveCriticalSection);
        
        /*
         * 等待free一个block
         */
        while(!_nWaveFreeBlockCount)
            Sleep(10);

        /*
         * 指向下一个block
         */
        _nWaveCurrentBlock++;
        _nWaveCurrentBlock %= BLOCK_COUNT;

        current = &_waveBlocks[_nWaveCurrentBlock];
        current->dwUser = 0;
    }
}

W***EHDR* CViWavePlay::allocateBlocks(int size, int count)
{
	unsigned char* buffer;
	W***EHDR* blocks;
	DWORD totalBufferSize = (size + sizeof(W***EHDR)) * count;

	if((buffer = (unsigned char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, totalBufferSize)) == NULL) 
	{
		fprintf(stderr, "Memory allocation error\n");
		ExitProcess(1);
	}
	blocks = (W***EHDR*)buffer;
	buffer += sizeof(W***EHDR) * count;
	for(int i = 0; i < count; i++) {
		blocks[i].dwBufferLength = size;
		blocks[i].lpData = (LPSTR)buffer;
		buffer += size;
	}

	return blocks;
}

void CViWavePlay::freeBlocks(W***EHDR* blockArray)
{
	HeapFree(GetProcessHeap(), 0, blockArray);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: