制造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
2012-10-15 15:09
603 查看
打造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
本人应用场景:播放采集设备输出的PCM音频,参考了两种网上实现方法,接口做了些调整,因为播放的音频属性经常需要改变,在播放初始化函数Start传入W***EFORMATEX参数,这样支持的播放音频种类多,经测试,均能正常播放,使用方法:
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来代替
实现方法一的cpp文件:
方法二播放音频,但未经过严格测试
方法二头文件:
方法二cpp文件:
本人应用场景:播放采集设备输出的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); }
相关文章推荐
- 制造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- 制造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- 打造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- 打造自己的wave音频播放器-使用waveOutOpen与waveOutWrite实现
- 全双工音频播放器在c#中使用waveIn / waveOut api
- c++实现waveOutOpen音频播放功能
- 自己做的视音频播放器,绿色版,欢迎使用
- WIN32平台利用WaveIn-Out实现音频数据采集
- waveOutOpen、waveOutWrite系统函数用法编程实现声音播放
- 使用FFMPEG实现音频播放器
- 实例解析使用Java实现基本的音频播放器的编写要点
- waveout**实现音频播放
- 使用WaveOut API播放WAV音频文件(解决卡顿)
- vc WaveOutOpen 回调函数资料
- Android学习笔记---19_采用ListView实现数据列表显示,以及各种适配器使用,和如何写自己的适配器
- Lock的使用、ReentrantReadWriteLock实现读写分离
- 使用单例模式实现自己的HttpClient工具类
- 自己实现的水版MPI_Bcast(使用binomial tree,跟mpich2实现思路一样)
- 使用nginx+lua实现自己的logserver | 星期八的博客 web & game
- Owc的使用---自己封装好的类可以实现14种分析图