现在的位置: 首页 > 综合 > 正文

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

2013年09月19日 ⁄ 综合 ⁄ 共 11075字 ⁄ 字号 评论关闭

打造自己的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:
	WAVEOUTCAPS	m_waveCaps;
	BOOL		m_bDevOpen;
	BOOL		m_bThread;
	HWAVEOUT	m_hWave;
	HANDLE		m_hThread;
	DWORD		m_ThreadID;

	WAVEFORMATEX m_Waveformat;

	M_Critical_Section	m_Lock;
	int			m_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           {打开选项}// long120823
	mRet = 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;
			}
			else
				Sleep(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);
}

抱歉!评论已关闭.