[源代码以及工程实例下载 ]
1、 语音播放API
1.1 waveOutOpen - 打开播放设备
- MMRESULT waveOutOpen(
- LPHWAVEOUT phwo, /* 一个指向接收波形音频输出设备的句柄 */
- UINT_PTR uDeviceID, /* 将要被打开的波形音频输出设备的ID */
- LPWAVEFORMATEX pwfx, /* 一个指向将被送到设备的音频数据格式的WAVEFORMATEX结构的指针 */
- DWORD_PTR dwCallback, /* 它指向一个特定的CALLBACK函数,事件柄,窗口柄... */
- DWORD_PTR dwCallbackInstance, /* 传递到CALLBACK进程的用户实例数据,如是窗口,该参数设为0 */
- DWORD fdwOpen /* 用来打开设备的标识(FLAGS) */
- );
MMRESULT waveOutOpen(
LPHWAVEOUT phwo, /* 一个指向接收波形音频输出设备的句柄 */
UINT_PTR uDeviceID, /* 将要被打开的波形音频输出设备的ID */
LPWAVEFORMATEX pwfx, /* 一个指向将被送到设备的音频数据格式的WAVEFORMATEX结构的指针 */
DWORD_PTR dwCallback, /* 它指向一个特定的CALLBACK函数,事件柄,窗口柄... */
DWORD_PTR dwCallbackInstance, /* 传递到CALLBACK进程的用户实例数据,如是窗口,该参数设为0 */
DWORD fdwOpen /* 用来打开设备的标识(FLAGS) */
);
1)phwo:一个指向接收波形音频输出设备的句柄。用句柄来区别(identify)别的波形输出设备。如果fdwOpen被设定为 WAVE_FORMAT_QUERY,那么这个参数可能为NULL 。
2)uDevideID:将要被打开的波形音频输出设备的ID ,它可以是一个设备ID,也可以是一个已经打开的波形音频输入设备句柄,你可以用以下的值来货替:
WAVE_MAPPER - 该函数选一个能够播放给定格式的波形音频输出设备
3)pwfx:一个指向将被送到设备的音频数据格式的WAVEFORMATEX结构的指针,当调用waveOutOpen 函数之后可立即释放该结构;
4)dwCallback:它指向一个特定的CALLBACK函数,事件柄,窗口柄,或一个线程ID(用于在音频回放时以便处理与回放进度相关的消息)。如果无须CALLBACK函数,可以将其设为0 。
5)dwCallbackInstance :传递到CALLBACK进程的用户实例数据。如果是窗口CALLBACK进程的话,该参数不用(设为0)
6)fwOpen:用来打开设备的标识(FLAGS),它们的定义如下:
值 | 含义 |
CALLBACK_EVENT | dwCallback 参数栏是事件句柄 |
CALLBACK_FUNCTION | dwCallback 参数栏是CALLBACK函数地址 |
CALLBACK_NULL | 默认的设置,即无CALLBACK进程 |
CALLBACK_THREAD | dwCallback 参数栏是线程ID |
CALLBACK_WINDOW | dwCallback 参数栏是窗口句柄 |
WAVE_ALLOWSYNC | 如果该项被设置,一个同步的波形音频设备能被打开。如果在打开一个同步驱动时没有用该项,设备打开将会失败。 |
WAVE_FORMAT_DIRECT | 如果设定该项,音频设备不会对输出的音频数据执行转换 |
WAVE_FORMAT_QUERY | 如果设定该项,waveOutOpen 用于询问设备是否支持给定的格式,但设备实际上并没有被打开。此时phwo参数可为NULL |
WAVE_MAPPED | 该项被设定后uDeviceID参数表示将通过波形映射器映射到一个波形格式音频设备。 |
7)返回值:成功后返回MMSYSERR_NOERROR ,否则返回以下值:
值 | 描述 |
MMSYSERR_ALLOCATED | 表示资源已存在 |
MMSYSERR_BADDEVICEID | 设备ID超出范围 |
MMSYSERR_NODRIVER | 没有驱动 |
MMSYSERR_NOMEM | 不能分配内存 |
WAVERR_BADFORMAT | 企图打开一个不被支持的格式 |
WAVERR_SYNC | 设备是可同步的,但waveOutOpen没用有WAVE_ALLOWSYNC设置。 |
注:
waveoutopen创建设备实例句柄用于,使用其他waveoutAPI时将之作为参数,用于区别不同的音频流。
可用waveOutGetNumDevs函数测定在当前系统中输出设备的数目。
如果uDeviceID参数项是一个ID,它将会表示从0 到总的数目,WAAVE_MAPPER常量也可以用作装置ID。
pwfc所指的结构能够扩展到包含某些数据格式的特殊信息,例如,对于PCM数据,一个额外的UNIT类型用来表示取样的位数目。在这个情况下用PCMWAVEFORAMT结构。对于其它的格式,用WAVEFORMATEX结构来表示额外的数据长度。
如果你选用的是一个窗口或线程来接收CALLBACK信息,如下的信息将会被送到窗口处理函数中,来表明波形音频输出进程:MM_WOM_OPEN,MM_WOM_CLOSE ,和MM_WOM_DONE,如果你选用的是一个函数来接收CALLBACK信息,如下的信息将会被传到函数中,来显示波形音频输出进程:WOM_OPEN ,WOM_CLOSE,WOM_DONE。
1.2 waveOutPrepareHeader - 准备数据块
- MMRESULT waveOutPrepareHeader(
- HWAVEOUT hwo, /* 波形音频输出设备的句柄 */
- LPWAVEHDR pwh, /* 一个WAVEHDR结构的指针,其基址必须与样本大小对齐 */
- UINT cbwh /* WAVEHDR结构的大小,单位:字节 */
- );
- 备注:
MMRESULT waveOutPrepareHeader(
HWAVEOUT hwo, /* 波形音频输出设备的句柄 */
LPWAVEHDR pwh, /* 一个WAVEHDR结构的指针,其基址必须与样本大小对齐 */
UINT cbwh /* WAVEHDR结构的大小,单位:字节 */
);
备注:
- 调用此函数之前必须设置WAVEHDR结构的lpData,dwBufferLength,dwFlags成员,dwFlags成员必须设置为0。
- 一旦准备完成,不可以修改lpData指针。
- 尝试准备一个处于准备状态的数据块,将无效果,返回0
- 不应该在同一时刻为不同的波形设备准备同一个数据,如果想从一个设备录制并在另一个设备播放数据,但不想拷贝缓存块,可以分配两块WAVEHDR并将lpData指向同一数据缓存地址,其中一个调用<STRONG>waveInPrepareHeader</STRONG>,另一个调用<STRONG>waveOutPrepareHeader</STRONG>。
调用此函数之前必须设置WAVEHDR结构的lpData,dwBufferLength,dwFlags成员,dwFlags成员必须设置为0。
一旦准备完成,不可以修改lpData指针。
尝试准备一个处于准备状态的数据块,将无效果,返回0
不应该在同一时刻为不同的波形设备准备同一个数据,如果想从一个设备录制并在另一个设备播放数据,但不想拷贝缓存块,可以分配两块WAVEHDR并将lpData指向同一数据缓存地址,其中一个调用<strong>waveInPrepareHeader</strong>,另一个调用<strong>waveOutPrepareHeader</strong>。
1.3 waveOutWrite - 将音频数据块送到指定的音频输出设备
- MMRESULT waveOutWrite(
- HWAVEOUT hwo, /* 音频输出设备句柄 */
- LPWAVEHDR pwh, /* 包含音频数据块信息的WAVEHDR结构指针 */
- UINT cbwh /* WAVEHDR结构大小,单位byte */
- );
MMRESULT waveOutWrite(
HWAVEOUT hwo, /* 音频输出设备句柄 */
LPWAVEHDR pwh, /* 包含音频数据块信息的WAVEHDR结构指针 */
UINT cbwh /* WAVEHDR结构大小,单位byte */
);
注意:
要播放的数据一般在声音文件里面获得,并填入这个结构。由于是直接播放数据。所以要播放多少数据可以通过修改这个结构来达到目的。
播放完数据后 WHDR_DONE 会设置到pwh指向的结构体中的dwFlags 成员
在调用本函数之前必须调用waveOutPrepareHeader函数
除非是恢复被waveOutPause函数暂停的设备,回调函数会在第一个数据块一送达设备的时候就开始运作.回调函数在waveOutOpen里面设置
请不要在回调函数里面调用任何的waveOut系列的函数,否则一定会造成死锁。哪怕是waveOutUnprepareHeader,waveOutClose
1.4 waveOutUnprepareHeader - 清除音频缓存数据
- MMRESULT waveOutUnprepareHeader(
- HWAVEOUT hwo,
- LPWAVEHDR pwh,
- UINT cbwh
- );
MMRESULT waveOutUnprepareHeader(
HWAVEOUT hwo,
LPWAVEHDR pwh,
UINT cbwh
);
注意:
调用时机,送到的音频输出设备的数据已经播放完成即:pwh的dwFlags中WHDR_DONE位有效
1.5 waveOutPause - 暂停音频播放
1.6 waveOutRestart - 继续播放已暂停的音频设备
1.7 waveOutReset - 重设音频输出设备,将已准备好的缓存块均设置为已播放完成
1.8 waveOutClose - 关闭音频输出设备
2、解码器开发
- class AMRFileDecoder
- {
- public:
- AMRFileDecoder(void);
- AMRFileDecoder(LPCTSTR lpszFile);
- virtual ~AMRFileDecoder(void);
- private: // 屏蔽拷贝构造函数和赋值运算
- AMRFileDecoder(const AMRFileDecoder& )
- {
- ATLASSERT(FALSE);
- }
- AMRFileDecoder& operator=(const AMRFileDecoder&)
- {
- ATLASSERT(FALSE);
- return *this;
- }
- public:
- /// 设置需解码文件路径
- virtual void SetFilePathName(LPCTSTR lpszFile);
- /// 获取总时间长度,单位ms
- virtual ULONGLONG GetTimeLength();
- /// 获取解码后的波形格式
- virtual WAVEFORMATEX GetWaveFromatX();
- /// 开始解码,初始化解码器
- virtual BOOL BeginDecode();
- /// 解码,每解码一帧,游标后移至下一帧,返回解码后的帧大小,输出解码后的波形数据
- virtual DWORD Decode(LPSTR& pData);
- /// 判断是否解码结束
- virtual bool IsEOF();
- /// 结束解码,销毁解码器
- virtual void EndDecode();
- /// 判断解码器是否正常
- virtual bool IsVaild();
- /// 获取解码后的波形数据大小,单位byte
- virtual DWORD GetDecodedMaxSize();
- /// 获取解码后的波形数据帧大小,单位byte
- virtual DWORD GetDecodedFrameMaxSize();
- private:
- DWORD GetFrameCount();
- private:
- LPSTR m_pBaseAddress; // 文件映射内存块首地址
- LONGLONG m_liFileSize; // 文件映射内存块大小
- ULONG m_dwFrameCount; // 帧总数
- LPSTR m_pCurAddress; // 解码游标,指示当前解码位置
- LPVOID m_pvDecoderState; // 解码器状态指针
- CString m_sFilePathName; // 解码文件路径
- };
class AMRFileDecoder
{
public:
AMRFileDecoder(void);
AMRFileDecoder(LPCTSTR lpszFile);
virtual ~AMRFileDecoder(void);
private: // 屏蔽拷贝构造函数和赋值运算
AMRFileDecoder(const AMRFileDecoder& )
{
ATLASSERT(FALSE);
}
AMRFileDecoder& operator=(const AMRFileDecoder&)
{
ATLASSERT(FALSE);
return *this;
}
public:
/// 设置需解码文件路径
virtual void SetFilePathName(LPCTSTR lpszFile);
/// 获取总时间长度,单位ms
virtual ULONGLONG GetTimeLength();
/// 获取解码后的波形格式
virtual WAVEFORMATEX GetWaveFromatX();
/// 开始解码,初始化解码器
virtual BOOL BeginDecode();
/// 解码,每解码一帧,游标后移至下一帧,返回解码后的帧大小,输出解码后的波形数据
virtual DWORD Decode(LPSTR& pData);
/// 判断是否解码结束
virtual bool IsEOF();
/// 结束解码,销毁解码器
virtual void EndDecode();
/// 判断解码器是否正常
virtual bool IsVaild();
/// 获取解码后的波形数据大小,单位byte
virtual DWORD GetDecodedMaxSize();
/// 获取解码后的波形数据帧大小,单位byte
virtual DWORD GetDecodedFrameMaxSize();
private:
DWORD GetFrameCount();
private:
LPSTR m_pBaseAddress; // 文件映射内存块首地址
LONGLONG m_liFileSize; // 文件映射内存块大小
ULONG m_dwFrameCount; // 帧总数
LPSTR m_pCurAddress; // 解码游标,指示当前解码位置
LPVOID m_pvDecoderState; // 解码器状态指针
CString m_sFilePathName; // 解码文件路径
};
3、解码线程
- if(m_pDecoder == NULL || !m_pDecoder->IsVaild())
- return 0;
- // 开始解码,初始化解码器
- if(!m_pDecoder->BeginDecode())
- {
- return 0;
- }
- // 申请临时内存块,存储解码后的波形数据
- DWORD dwFrameMaxSize = m_pDecoder->GetDecodedFrameMaxSize();
- LPSTR pBufferBase = (LPSTR)malloc(dwFrameMaxSize);
- ATLASSERT(pBufferBase);
- memset(pBufferBase, 0, dwFrameMaxSize);
- if(pBufferBase == NULL) return 0;
- register ThreadMsg tmsg = TMSG_ALIVE;
- DWORD dwSizeAmount = 0;
- while(!m_pDecoder->IsEOF() && tmsg)
- {
- // 解码,每帧
- DWORD dwSize = m_pDecoder->Decode(pBufferBase);
- dwSizeAmount += dwSize;
- // 将解码后数据写入缓存区,供播放线程使用
- EnterCriticalSection(&m_cs);
- memcpy(m_waveData.pData + m_waveData.dwSize, pBufferBase, dwSize);
- m_waveData.dwSize += dwSize;
- LeaveCriticalSection(&m_cs);
- // 当解码数据量操作了一个播放缓存时,发个信号,通知可以开始播放了
- if(dwSizeAmount > BLOCK_SIZE)
- {
- dwSizeAmount = 0;
- SetEvent(m_hEventDecode);
- }
- // 节省CPU时间,让CPU有时间去干别的事儿
- Sleep(1);
- // 检测线程是否将被强制退出
- EnterCriticalSection(&m_cs);
- tmsg = m_msgDecodeThread;
- LeaveCriticalSection(&m_cs);
- }
- // 如果数据量很小,根本不足一个播放缓存,也要发个信号
- if(dwSizeAmount > 0)
- {
- SetEvent(m_hEventDecode);
- }
- m_waveData.bDecodeFinished = true;
- // 解码结束
- m_pDecoder->EndDecode();
- free(pBufferBase);
- pBufferBase = NULL;
- return 0;
- }
if(m_pDecoder == NULL || !m_pDecoder->IsVaild())
return 0;
// 开始解码,初始化解码器
if(!m_pDecoder->BeginDecode())
{
return 0;
}
// 申请临时内存块,存储解码后的波形数据
DWORD dwFrameMaxSize = m_pDecoder->GetDecodedFrameMaxSize();
LPSTR pBufferBase = (LPSTR)malloc(dwFrameMaxSize);
ATLASSERT(pBufferBase);
memset(pBufferBase, 0, dwFrameMaxSize);
if(pBufferBase == NULL) return 0;
register ThreadMsg tmsg = TMSG_ALIVE;
DWORD dwSizeAmount = 0;
while(!m_pDecoder->IsEOF() && tmsg)
{
// 解码,每帧
DWORD dwSize = m_pDecoder->Decode(pBufferBase);
dwSizeAmount += dwSize;
// 将解码后数据写入缓存区,供播放线程使用
EnterCriticalSection(&m_cs);
memcpy(m_waveData.pData + m_waveData.dwSize, pBufferBase, dwSize);
m_waveData.dwSize += dwSize;
LeaveCriticalSection(&m_cs);
// 当解码数据量操作了一个播放缓存时,发个信号,通知可以开始播放了
if(dwSizeAmount > BLOCK_SIZE)
{
dwSizeAmount = 0;
SetEvent(m_hEventDecode);
}
// 节省CPU时间,让CPU有时间去干别的事儿
Sleep(1);
// 检测线程是否将被强制退出
EnterCriticalSection(&m_cs);
tmsg = m_msgDecodeThread;
LeaveCriticalSection(&m_cs);
}
// 如果数据量很小,根本不足一个播放缓存,也要发个信号
if(dwSizeAmount > 0)
{
SetEvent(m_hEventDecode);
}
m_waveData.bDecodeFinished = true;
// 解码结束
m_pDecoder->EndDecode();
free(pBufferBase);
pBufferBase = NULL;
return 0;
}
4、播放线程
- unsigned int WavePlayer::PlayThreadProcImpl()
- {
- /// 定义为寄存器变量,因为它将会被高频率的使用,用于编译器优化
- register ThreadMsg tmsg = TMSG_ALIVE;
- /// 线程循环
- while( tmsg )
- {
- // 每次循环后,交出CPU控制权,放在此处,因为下面有continue语句
- Sleep(10);
- /// 首先检查线程消息
- EnterCriticalSection( &m_cs );
- tmsg = m_msgPlayThread;
- LeaveCriticalSection( &m_cs );
- // 线程要结束,退出线程循环
- if(!tmsg) break;
- // 如果设备为空,表示还没有打开设备,需要打开设备
- if(m_hWaveoutDev == NULL)
- {
- EnterCriticalSection(&m_cs);
- MMRESULT mmres = waveOutOpen(&m_hWaveoutDev, WAVE_MAPPER, &m_waveData.wfmtx, (DWORD_PTR)WaveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
- LeaveCriticalSection(&m_cs);
- if(mmres != MMSYSERR_NOERROR)
- {
- // failed, try again.
- continue;
- }
- }
- // 检查空闲缓存块
- EnterCriticalSection( &m_cs );
- int free = m_wBlock.wfreeblock;
- LeaveCriticalSection( &m_cs );
- // 如果没有空闲的缓存了,等待...
- if(free < BP_TURN)
- {
- continue;
- }
- /////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////
- /// < 播放主循环 > ///
- /////////////////////////////////////////////////////////////////////////
- /////////////////////////////////////////////////////////////////////////
- WAVEHDR *current = NULL;
- /// BP_TURN为每次写入播放队列的块数
- for( unsigned int m = 0; m < BP_TURN; m++ )
- {
- /// 当前空闲播放缓存块
- current = &m_wBlock.pWaveHdr[m_wBlock.wcurrblock];
- // 首先需要检查有没有被Unprepare掉
- if( current->dwFlags & WHDR_PREPARED )
- {
- waveOutUnprepareHeader( m_hWaveoutDev, current, sizeof(WAVEHDR) );
- }
- /// 计算剩余需要播放的数据
- EnterCriticalSection(&m_cs);
- unsigned long left = m_waveData.dwSize - m_wBlock.wpos;
- unsigned int bDecodeFinished = m_waveData.bDecodeFinished;
- LeaveCriticalSection(&m_cs);
- unsigned long chunk = 0;
- if( left >= BLOCK_SIZE )
- {
- chunk = BLOCK_SIZE;
- }
- else if(!bDecodeFinished)
- {
- // 如果解码还没有结束,现有的数据量有不足以填满一个缓存块,先不写入缓存
- break;
- }
- else if( left && left < BLOCK_SIZE)
- {
- chunk = left;
- }
- else
- {
- //////////////////////////////////////////////////////////////////////
- /// < 播放完成> ///
- //////////////////////////////////////////////////////////////////////
- /// 获取空闲缓存块数量
- EnterCriticalSection( &m_cs );
- int free = m_wBlock.wfreeblock;
- LeaveCriticalSection( &m_cs );
- /// 当所有的缓存块都播放完了,才意味着播放真正完成
- if( free == BLOCK_COUNT )
- {
- /// Unprepare缓存块
- for( int j = 0; j < m_wBlock.wfreeblock; j++)
- {
- if( m_wBlock.pWaveHdr[j].dwFlags & WHDR_PREPARED )
- {
- waveOutUnprepareHeader(m_hWaveoutDev, &m_wBlock.pWaveHdr[j], sizeof(WAVEHDR));
- }
- }
- // 此时,才算真正的播放完成,关闭线程
- tmsg = TMSG_CLOSE;
- // 处理播放完成事件
- OnPlayFinished();
- }
- // 此break仅跳出该循环,没有跳出线程循环
- break;
- }
- /// prepare current wave data block header
- EnterCriticalSection(&m_cs);
- memcpy( current->lpData, &m_waveData.pData[m_wBlock.wpos], chunk );
- LeaveCriticalSection(&m_cs);
- current->dwBufferLength = chunk; // sizeof block
- m_wBlock.wpos += chunk; // update position
- /// prepare for playback
- waveOutPrepareHeader( m_hWaveoutDev, current, sizeof(WAVEHDR) );
- /// push to the queue
- waveOutWrite(m_hWaveoutDev, current, sizeof(WAVEHDR));
- /// 减小空闲块计数
- EnterCriticalSection( &m_cs );
- m_wBlock.wfreeblock--;
- LeaveCriticalSection( &m_cs );
- /// 使当前空闲块指向下一个
- m_wBlock.wcurrblock++;
- m_wBlock.wcurrblock %= BLOCK_COUNT;
- }
- }/// thread
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- ///
- /// < force to close device which are still playing >
- ///////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- if(m_hWaveoutDev)
- {
- waveOutReset( m_hWaveoutDev );
- /// unprepare any blocks that are still prepared
- for( int j = 0; j < BLOCK_COUNT; j++)
- {
- if( m_wBlock.pWaveHdr[j].dwFlags & WHDR_PREPARED )
- {
- waveOutUnprepareHeader(m_hWaveoutDev, &m_wBlock.pWaveHdr[j], sizeof(WAVEHDR));
- }
- }
- waveOutClose(m_hWaveoutDev);
- m_hWaveoutDev = NULL;
- }
- return THREAD_EXIT;
- }
unsigned int WavePlayer::PlayThreadProcImpl()
{
/// 定义为寄存器变量,因为它将会被高频率的使用,用于编译器优化
register ThreadMsg tmsg = TMSG_ALIVE;
/// 线程循环
while( tmsg )
{
// 每次循环后,交出CPU控制权,放在此处,因为下面有continue语句
Sleep(10);
/// 首先检查线程消息
EnterCriticalSection( &m_cs );
tmsg = m_msgPlayThread;
LeaveCriticalSection( &m_cs );
// 线程要结束,退出线程循环
if(!tmsg) break;
// 如果设备为空,表示还没有打开设备,需要打开设备
if(m_hWaveoutDev == NULL)
{
EnterCriticalSection(&m_cs);
MMRESULT mmres = waveOutOpen(&m_hWaveoutDev, WAVE_MAPPER, &m_waveData.wfmtx, (DWORD_PTR)WaveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
LeaveCriticalSection(&m_cs);
if(mmres != MMSYSERR_NOERROR)
{
// failed, try again.
continue;
}
}
// 检查空闲缓存块
EnterCriticalSection( &m_cs );
int free = m_wBlock.wfreeblock;
LeaveCriticalSection( &m_cs );
// 如果没有空闲的缓存了,等待...
if(free < BP_TURN)
{
continue;
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
/// < 播放主循环 > ///
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
WAVEHDR *current = NULL;
/// BP_TURN为每次写入播放队列的块数
for( unsigned int m = 0; m < BP_TURN; m++ )
{
/// 当前空闲播放缓存块
current = &m_wBlock.pWaveHdr[m_wBlock.wcurrblock];
// 首先需要检查有没有被Unprepare掉
if( current->dwFlags & WHDR_PREPARED )
{
waveOutUnprepareHeader( m_hWaveoutDev, current, sizeof(WAVEHDR) );
}
/// 计算剩余需要播放的数据
EnterCriticalSection(&m_cs);
unsigned long left = m_waveData.dwSize - m_wBlock.wpos;
unsigned int bDecodeFinished = m_waveData.bDecodeFinished;
LeaveCriticalSection(&m_cs);
unsigned long chunk = 0;
if( left >= BLOCK_SIZE )
{
chunk = BLOCK_SIZE;
}
else if(!bDecodeFinished)
{
// 如果解码还没有结束,现有的数据量有不足以填满一个缓存块,先不写入缓存
break;
}
else if( left && left < BLOCK_SIZE)
{
chunk = left;
}
else
{
//////////////////////////////////////////////////////////////////////
/// < 播放完成> ///
//////////////////////////////////////////////////////////////////////
/// 获取空闲缓存块数量
EnterCriticalSection( &m_cs );
int free = m_wBlock.wfreeblock;
LeaveCriticalSection( &m_cs );
/// 当所有的缓存块都播放完了,才意味着播放真正完成
if( free == BLOCK_COUNT )
{
/// Unprepare缓存块
for( int j = 0; j < m_wBlock.wfreeblock; j++)
{
if( m_wBlock.pWaveHdr[j].dwFlags & WHDR_PREPARED )
{
waveOutUnprepareHeader(m_hWaveoutDev, &m_wBlock.pWaveHdr[j], sizeof(WAVEHDR));
}
}
// 此时,才算真正的播放完成,关闭线程
tmsg = TMSG_CLOSE;
// 处理播放完成事件
OnPlayFinished();
}
// 此break仅跳出该循环,没有跳出线程循环
break;
}
/// prepare current wave data block header
EnterCriticalSection(&m_cs);
memcpy( current->lpData, &m_waveData.pData[m_wBlock.wpos], chunk );
LeaveCriticalSection(&m_cs);
current->dwBufferLength = chunk; // sizeof block
m_wBlock.wpos += chunk; // update position
/// prepare for playback
waveOutPrepareHeader( m_hWaveoutDev, current, sizeof(WAVEHDR) );
/// push to the queue
waveOutWrite(m_hWaveoutDev, current, sizeof(WAVEHDR));
/// 减小空闲块计数
EnterCriticalSection( &m_cs );
m_wBlock.wfreeblock--;
LeaveCriticalSection( &m_cs );
/// 使当前空闲块指向下一个
m_wBlock.wcurrblock++;
m_wBlock.wcurrblock %= BLOCK_COUNT;
}
}/// thread
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///
/// < force to close device which are still playing >
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
if(m_hWaveoutDev)
{
waveOutReset( m_hWaveoutDev );
/// unprepare any blocks that are still prepared
for( int j = 0; j < BLOCK_COUNT; j++)
{
if( m_wBlock.pWaveHdr[j].dwFlags & WHDR_PREPARED )
{
waveOutUnprepareHeader(m_hWaveoutDev, &m_wBlock.pWaveHdr[j], sizeof(WAVEHDR));
}
}
waveOutClose(m_hWaveoutDev);
m_hWaveoutDev = NULL;
}
return THREAD_EXIT;
}
5、主播放线程
- // 如果已经有播放的了,先停止
- if(m_ePlayStat != Play_Stop)
- {
- Stop();
- }
- // 设置解码器
- if(m_pDecoder == NULL)
- {
- m_pDecoder = new AMRFileDecoder(lpszFile);
- }
- else
- {
- m_pDecoder->SetFilePathName(lpszFile);
- }
- // 取播放时间
- if(pLength)
- {
- *pLength = (DWORD)m_pDecoder->GetTimeLength();
- }
- // 申请解码后的数据堆内存块
- DWORD dwWaveMaxSize = m_pDecoder->GetDecodedMaxSize();
- EnterCriticalSection(&m_cs);
- m_waveData.wfmtx = m_pDecoder->GetWaveFromatX();
- m_waveData.pData = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwWaveMaxSize);
- LeaveCriticalSection(&m_cs);
- // 设置回调函数
- // 创建解码线程
- if(m_hThreadDecode == NULL)
- {
- m_msgDecodeThread = TMSG_ALIVE;
- m_hThreadDecode = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DecodeThread, (LPVOID)this, CREATE_SUSPENDED, NULL);
- ATLASSERT(m_hThreadDecode);
- ResumeThread(m_hThreadDecode);
- }
- // 等待解码缓存信号
- WaitForSingleObject(m_hEventDecode, INFINITE);
- // 创建播放线程
- if(m_hThreadPlay == NULL)
- {
- m_msgPlayThread = TMSG_ALIVE;
- m_hThreadPlay = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)PlayThread, (LPVOID)this, CREATE_SUSPENDED, NULL );
- ATLASSERT(m_hThreadPlay);
- ResumeThread(m_hThreadPlay);
- }
- m_ePlayStat = Play_Playing;