制造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
来源:互联网 发布:醉仙武坐骑进阶数据 编辑:程序博客网 时间:2024/05/17 20:56
本人应用场景:播放采集设备输出的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:WAVEOUTCAPSm_waveCaps;BOOLm_bDevOpen;BOOLm_bThread;HWAVEOUTm_hWave;HANDLEm_hThread;DWORDm_ThreadID;WAVEFORMATEX m_Waveformat;M_Critical_Sectionm_Lock;intm_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 {打开选项}// long120823mRet = 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;}elseSleep(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);}
- 制造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- 制造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- 制造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- 打造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- 打造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- waveOutOpen、waveOutWrite系统函数用法编程实现声音播放
- c++实现waveOutOpen音频播放功能
- VB实现远程读取音频(Wave格式)的播放时间
- 自己做的视音频播放器,绿色版,欢迎使用
- 波形音频(WAVE)底层接口的学习与使用
- 波形音频(WAVE)底层接口的学习与使用
- 关于语音聊天(wave系列函数播放文件、网络音频)的实现方法
- 自己开发的EiPlayer音频播放器
- iOS音频播放 (六):简单的音频播放器实现
- iOS音频播放 (六):简单的音频播放器实现
- iOS音频播放 (六):简单的音频播放器实现
- iOS音频播放 (六):简单的音频播放器实现
- iOS音频播放 (六):简单的音频播放器实现
- Android创建逐帧动画的方式
- MyEclipse 8.6 download 官方下载地址
- zoj 1151.Word Reversal
- Python Pycurl 多线程段错误
- POJ2230
- 制造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- 刺青的伤感爱情空间日志推荐:再见了,我的曾经
- apache2.2.22+tomcat6.0.35负载均衡和session复制
- Myeclipse的各个操作总结
- 用Javamail写的邮件接收程序
- SQL Server 问题集
- Tomcat配置远程调试
- 结构体定义及使用
- JS扩展