[源代码以及工程实例下载 ]

1、 语音播放API

1.1 waveOutOpen - 打开播放设备

[cpp] view plaincopyprint?
  1. MMRESULT waveOutOpen(
  2. LPHWAVEOUT      phwo,               /* 一个指向接收波形音频输出设备的句柄 */
  3. UINT_PTR        uDeviceID,          /* 将要被打开的波形音频输出设备的ID */
  4. LPWAVEFORMATEX  pwfx,               /* 一个指向将被送到设备的音频数据格式的WAVEFORMATEX结构的指针 */
  5. DWORD_PTR       dwCallback,         /* 它指向一个特定的CALLBACK函数,事件柄,窗口柄... */
  6. DWORD_PTR       dwCallbackInstance, /* 传递到CALLBACK进程的用户实例数据,如是窗口,该参数设为0 */
  7. DWORD           fdwOpen             /* 用来打开设备的标识(FLAGS) */
  8. );
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 - 准备数据块

[cpp] view plaincopyprint?
  1. MMRESULT waveOutPrepareHeader(
  2. HWAVEOUT hwo,  /* 波形音频输出设备的句柄 */
  3. LPWAVEHDR pwh, /* 一个WAVEHDR结构的指针,其基址必须与样本大小对齐 */
  4. UINT cbwh      /* WAVEHDR结构的大小,单位:字节 */
  5. );
  6. 备注:
MMRESULT waveOutPrepareHeader(
HWAVEOUT hwo,  /* 波形音频输出设备的句柄 */
LPWAVEHDR pwh, /* 一个WAVEHDR结构的指针,其基址必须与样本大小对齐 */
UINT cbwh      /* WAVEHDR结构的大小,单位:字节 */
);
备注:
[cpp] view plaincopyprint?
  1. 调用此函数之前必须设置WAVEHDR结构的lpData,dwBufferLength,dwFlags成员,dwFlags成员必须设置为0。
  2. 一旦准备完成,不可以修改lpData指针。
  3. 尝试准备一个处于准备状态的数据块,将无效果,返回0
  4. 不应该在同一时刻为不同的波形设备准备同一个数据,如果想从一个设备录制并在另一个设备播放数据,但不想拷贝缓存块,可以分配两块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 - 将音频数据块送到指定的音频输出设备

[cpp] view plaincopyprint?
  1. MMRESULT waveOutWrite(
  2. HWAVEOUT hwo,    /* 音频输出设备句柄 */   
  3. LPWAVEHDR pwh,  /* 包含音频数据块信息的WAVEHDR结构指针 */ 
  4. UINT cbwh      /* WAVEHDR结构大小,单位byte */
  5. );
MMRESULT waveOutWrite(
HWAVEOUT hwo,    /* 音频输出设备句柄 */   
LPWAVEHDR pwh,  /* 包含音频数据块信息的WAVEHDR结构指针 */ 
UINT cbwh      /* WAVEHDR结构大小,单位byte */
); 

注意:
    要播放的数据一般在声音文件里面获得,并填入这个结构。由于是直接播放数据。所以要播放多少数据可以通过修改这个结构来达到目的。
    播放完数据后 WHDR_DONE 会设置到pwh指向的结构体中的dwFlags 成员
    在调用本函数之前必须调用waveOutPrepareHeader函数
    除非是恢复被waveOutPause函数暂停的设备,回调函数会在第一个数据块一送达设备的时候就开始运作.回调函数在waveOutOpen里面设置
    请不要在回调函数里面调用任何的waveOut系列的函数,否则一定会造成死锁。哪怕是waveOutUnprepareHeader,waveOutClose

1.4 waveOutUnprepareHeader - 清除音频缓存数据

[cpp] view plaincopyprint?
  1. MMRESULT waveOutUnprepareHeader(
  2. HWAVEOUT hwo,
  3. LPWAVEHDR pwh,
  4. UINT cbwh
  5. );
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、解码器开发

[cpp] view plaincopyprint?
  1. class AMRFileDecoder
  2. {
  3. public:
  4. AMRFileDecoder(void);
  5. AMRFileDecoder(LPCTSTR lpszFile);
  6. virtual ~AMRFileDecoder(void);
  7. private: // 屏蔽拷贝构造函数和赋值运算
  8. AMRFileDecoder(const AMRFileDecoder& )
  9. {
  10. ATLASSERT(FALSE);
  11. }
  12. AMRFileDecoder& operator=(const AMRFileDecoder&)
  13. {
  14. ATLASSERT(FALSE);
  15. return *this;
  16. }
  17. public:
  18. /// 设置需解码文件路径
  19. virtual void SetFilePathName(LPCTSTR lpszFile);
  20. /// 获取总时间长度,单位ms
  21. virtual ULONGLONG GetTimeLength();
  22. /// 获取解码后的波形格式
  23. virtual WAVEFORMATEX GetWaveFromatX();
  24. /// 开始解码,初始化解码器
  25. virtual BOOL BeginDecode();
  26. /// 解码,每解码一帧,游标后移至下一帧,返回解码后的帧大小,输出解码后的波形数据
  27. virtual DWORD Decode(LPSTR& pData);
  28. /// 判断是否解码结束
  29. virtual bool IsEOF();
  30. /// 结束解码,销毁解码器
  31. virtual void EndDecode();
  32. /// 判断解码器是否正常
  33. virtual bool IsVaild();
  34. /// 获取解码后的波形数据大小,单位byte
  35. virtual DWORD GetDecodedMaxSize();
  36. /// 获取解码后的波形数据帧大小,单位byte
  37. virtual DWORD GetDecodedFrameMaxSize();
  38. private:
  39. DWORD GetFrameCount();
  40. private:
  41. LPSTR       m_pBaseAddress;         // 文件映射内存块首地址
  42. LONGLONG    m_liFileSize;           // 文件映射内存块大小
  43. ULONG       m_dwFrameCount;         // 帧总数
  44. LPSTR       m_pCurAddress;          // 解码游标,指示当前解码位置
  45. LPVOID      m_pvDecoderState;       // 解码器状态指针
  46. CString     m_sFilePathName;        // 解码文件路径
  47. };
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、解码线程

[cpp] view plaincopyprint?
  1. if(m_pDecoder == NULL || !m_pDecoder->IsVaild())
  2. return 0;
  3. // 开始解码,初始化解码器
  4. if(!m_pDecoder->BeginDecode())
  5. {
  6. return 0;
  7. }
  8. // 申请临时内存块,存储解码后的波形数据
  9. DWORD dwFrameMaxSize = m_pDecoder->GetDecodedFrameMaxSize();
  10. LPSTR pBufferBase = (LPSTR)malloc(dwFrameMaxSize);
  11. ATLASSERT(pBufferBase);
  12. memset(pBufferBase, 0, dwFrameMaxSize);
  13. if(pBufferBase == NULL) return 0;
  14. register ThreadMsg tmsg = TMSG_ALIVE;
  15. DWORD dwSizeAmount = 0;
  16. while(!m_pDecoder->IsEOF() && tmsg)
  17. {
  18. // 解码,每帧
  19. DWORD dwSize = m_pDecoder->Decode(pBufferBase);
  20. dwSizeAmount += dwSize;
  21. // 将解码后数据写入缓存区,供播放线程使用
  22. EnterCriticalSection(&m_cs);
  23. memcpy(m_waveData.pData + m_waveData.dwSize, pBufferBase, dwSize);
  24. m_waveData.dwSize += dwSize;
  25. LeaveCriticalSection(&m_cs);
  26. // 当解码数据量操作了一个播放缓存时,发个信号,通知可以开始播放了
  27. if(dwSizeAmount > BLOCK_SIZE)
  28. {
  29. dwSizeAmount = 0;
  30. SetEvent(m_hEventDecode);
  31. }
  32. // 节省CPU时间,让CPU有时间去干别的事儿
  33. Sleep(1);
  34. // 检测线程是否将被强制退出
  35. EnterCriticalSection(&m_cs);
  36. tmsg = m_msgDecodeThread;
  37. LeaveCriticalSection(&m_cs);
  38. }
  39. // 如果数据量很小,根本不足一个播放缓存,也要发个信号
  40. if(dwSizeAmount > 0)
  41. {
  42. SetEvent(m_hEventDecode);
  43. }
  44. m_waveData.bDecodeFinished = true;
  45. // 解码结束
  46. m_pDecoder->EndDecode();
  47. free(pBufferBase);
  48. pBufferBase = NULL;
  49. return 0;
  50. }
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、播放线程

[cpp] view plaincopyprint?
  1. unsigned int WavePlayer::PlayThreadProcImpl()
  2. {
  3. /// 定义为寄存器变量,因为它将会被高频率的使用,用于编译器优化
  4. register    ThreadMsg       tmsg  = TMSG_ALIVE;
  5. /// 线程循环
  6. while( tmsg )
  7. {
  8. // 每次循环后,交出CPU控制权,放在此处,因为下面有continue语句
  9. Sleep(10);
  10. /// 首先检查线程消息
  11. EnterCriticalSection( &m_cs );
  12. tmsg = m_msgPlayThread;
  13. LeaveCriticalSection( &m_cs );
  14. // 线程要结束,退出线程循环
  15. if(!tmsg)   break;
  16. // 如果设备为空,表示还没有打开设备,需要打开设备
  17. if(m_hWaveoutDev == NULL)
  18. {
  19. EnterCriticalSection(&m_cs);
  20. MMRESULT mmres = waveOutOpen(&m_hWaveoutDev, WAVE_MAPPER, &m_waveData.wfmtx, (DWORD_PTR)WaveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
  21. LeaveCriticalSection(&m_cs);
  22. if(mmres != MMSYSERR_NOERROR)
  23. {
  24. // failed, try again.
  25. continue;
  26. }
  27. }
  28. // 检查空闲缓存块
  29. EnterCriticalSection( &m_cs );
  30. int free = m_wBlock.wfreeblock;
  31. LeaveCriticalSection( &m_cs );
  32. // 如果没有空闲的缓存了,等待...
  33. if(free < BP_TURN)
  34. {
  35. continue;
  36. }
  37. /
  38. /
  39. ///                       < 播放主循环 >                              ///
  40. /
  41. /
  42. WAVEHDR     *current = NULL;
  43. /// BP_TURN为每次写入播放队列的块数
  44. for( unsigned int m = 0; m < BP_TURN; m++ )
  45. {
  46. /// 当前空闲播放缓存块
  47. current = &m_wBlock.pWaveHdr[m_wBlock.wcurrblock];
  48. // 首先需要检查有没有被Unprepare掉
  49. if( current->dwFlags & WHDR_PREPARED )
  50. {
  51. waveOutUnprepareHeader( m_hWaveoutDev, current, sizeof(WAVEHDR) );
  52. }
  53. /// 计算剩余需要播放的数据
  54. EnterCriticalSection(&m_cs);
  55. unsigned long left  = m_waveData.dwSize - m_wBlock.wpos;
  56. unsigned int bDecodeFinished = m_waveData.bDecodeFinished;
  57. LeaveCriticalSection(&m_cs);
  58. unsigned long chunk = 0;
  59. if( left  >= BLOCK_SIZE )
  60. {
  61. chunk  = BLOCK_SIZE;
  62. }
  63. else if(!bDecodeFinished)
  64. {
  65. // 如果解码还没有结束,现有的数据量有不足以填满一个缓存块,先不写入缓存
  66. break;
  67. }
  68. else if( left && left < BLOCK_SIZE)
  69. {
  70. chunk  = left;
  71. }
  72. else
  73. {
  74. //
  75. ///                 < 播放完成>                                    ///
  76. //
  77. /// 获取空闲缓存块数量
  78. EnterCriticalSection( &m_cs );
  79. int free = m_wBlock.wfreeblock;
  80. LeaveCriticalSection( &m_cs );
  81. /// 当所有的缓存块都播放完了,才意味着播放真正完成
  82. if( free == BLOCK_COUNT )
  83. {
  84. /// Unprepare缓存块
  85. for( int j = 0; j < m_wBlock.wfreeblock; j++)
  86. {
  87. if( m_wBlock.pWaveHdr[j].dwFlags & WHDR_PREPARED )
  88. {
  89. waveOutUnprepareHeader(m_hWaveoutDev, &m_wBlock.pWaveHdr[j], sizeof(WAVEHDR));
  90. }
  91. }
  92. // 此时,才算真正的播放完成,关闭线程
  93. tmsg = TMSG_CLOSE;
  94. // 处理播放完成事件
  95. OnPlayFinished();
  96. }
  97. // 此break仅跳出该循环,没有跳出线程循环
  98. break;
  99. }
  100. /// prepare current wave data block header
  101. EnterCriticalSection(&m_cs);
  102. memcpy( current->lpData, &m_waveData.pData[m_wBlock.wpos], chunk );
  103. LeaveCriticalSection(&m_cs);
  104. current->dwBufferLength  = chunk;   // sizeof block
  105. m_wBlock.wpos           += chunk;   // update position
  106. /// prepare for playback
  107. waveOutPrepareHeader( m_hWaveoutDev, current, sizeof(WAVEHDR) );
  108. /// push to the queue
  109. waveOutWrite(m_hWaveoutDev, current, sizeof(WAVEHDR));
  110. /// 减小空闲块计数
  111. EnterCriticalSection( &m_cs );
  112. m_wBlock.wfreeblock--;
  113. LeaveCriticalSection( &m_cs );
  114. /// 使当前空闲块指向下一个
  115. m_wBlock.wcurrblock++;
  116. m_wBlock.wcurrblock %= BLOCK_COUNT;
  117. }
  118. }/// thread
  119. ///
  120. ///
  121. ///
  122. ///            < force to close device which are still playing >
  123. ///
  124. ///
  125. if(m_hWaveoutDev)
  126. {
  127. waveOutReset( m_hWaveoutDev );
  128. /// unprepare any blocks that are still prepared
  129. for( int j = 0; j < BLOCK_COUNT; j++)
  130. {
  131. if( m_wBlock.pWaveHdr[j].dwFlags & WHDR_PREPARED )
  132. {
  133. waveOutUnprepareHeader(m_hWaveoutDev, &m_wBlock.pWaveHdr[j], sizeof(WAVEHDR));
  134. }
  135. }
  136. waveOutClose(m_hWaveoutDev);
  137. m_hWaveoutDev = NULL;
  138. }
  139. return THREAD_EXIT;
  140. }
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、主播放线程

[cpp] view plaincopyprint?
  1. // 如果已经有播放的了,先停止
  2. if(m_ePlayStat != Play_Stop)
  3. {
  4. Stop();
  5. }
  6. // 设置解码器
  7. if(m_pDecoder == NULL)
  8. {
  9. m_pDecoder = new AMRFileDecoder(lpszFile);
  10. }
  11. else
  12. {
  13. m_pDecoder->SetFilePathName(lpszFile);
  14. }
  15. // 取播放时间
  16. if(pLength)
  17. {
  18. *pLength = (DWORD)m_pDecoder->GetTimeLength();
  19. }
  20. // 申请解码后的数据堆内存块
  21. DWORD dwWaveMaxSize = m_pDecoder->GetDecodedMaxSize();
  22. EnterCriticalSection(&m_cs);
  23. m_waveData.wfmtx = m_pDecoder->GetWaveFromatX();
  24. m_waveData.pData = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwWaveMaxSize);
  25. LeaveCriticalSection(&m_cs);
  26. // 设置回调函数
  27. // 创建解码线程
  28. if(m_hThreadDecode == NULL)
  29. {
  30. m_msgDecodeThread = TMSG_ALIVE;
  31. m_hThreadDecode = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)DecodeThread, (LPVOID)this, CREATE_SUSPENDED, NULL);
  32. ATLASSERT(m_hThreadDecode);
  33. ResumeThread(m_hThreadDecode);
  34. }
  35. // 等待解码缓存信号
  36. WaitForSingleObject(m_hEventDecode, INFINITE);
  37. // 创建播放线程
  38. if(m_hThreadPlay == NULL)
  39. {
  40. m_msgPlayThread = TMSG_ALIVE;
  41. m_hThreadPlay = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)PlayThread, (LPVOID)this, CREATE_SUSPENDED, NULL );
  42. ATLASSERT(m_hThreadPlay);
  43. ResumeThread(m_hThreadPlay);
  44. }
  45. m_ePlayStat = Play_Playing;

多线程应用---使用WaveOut* API开发AMR音频播放器(含源码下载)相关推荐

  1. arcgis开发 多版本之间如何兼容_arcgis api 4.x for js 结合 react 入门开发系列初探篇(附源码下载)...

    你还在使用 JQuery 或者 Dojo 框架开发 arcgis api 4.x for js 吗?想试试模块化开发吗?随着前端技术的发展,arcgis api 4.x for js 也有了结合 re ...

  2. ASP.NET 3.5 新特性开发向导实践(附项目源码下载)

    ASP.NET 3.5 新特性开发向导实践(附项目源码下载) 本篇文章将演示ASP.NET 3.5 部分新功能.新特性,包括LINQ.ListView控件.LinqDataSource.DataPag ...

  3. html5游戏开发 网页版-捕鱼达人游戏源码下载

    html5游戏开发 网页版-捕鱼达人游戏源码下载 来玩一把! 转载于:https://www.cnblogs.com/jsfoot/p/3215371.html

  4. 修复采集接口版云开发表情包微信小程序源码下载增加制作等功能

    大家好,相信很多人对这个界面的表情包小程序肯定不陌生吧 不错之前该款小程序是属于独立后端的,不管今天所发的这款是云开发的哟 运营着这个表情包的用户应该发现了,最近很多表情包图片都失效了 所以呢,今天小 ...

  5. 小程序源码:全新独家云开发微群人脉小程序源码下载社群空间站

    今天给大家带来一款云开发版本的微群人脉小程序源码 该版本属于采集版本(群二维码自动采集) 该版本属于云开发版本(免服务器和域名) 这是一款不怕封小程序版本 PS:支持用户自主发布那一款还是有点危险因为 ...

  6. 云开发表白墙微信小程序源码下载免服务器和域名支持流量主收益

    这是一款云开发的表白墙微信小程序 特点是云开发,所以也就无需服务器和域名的支持了 安装特别的简单 首先呢小程序账号开通云开发权限 然后把源码上传到微信开发者工具里面 然后点击开发者工具软件上面的云开发 ...

  7. JAVA毕业设计vue开发一个简单音乐播放器计算机源码+lw文档+系统+调试部署+数据库

    JAVA毕业设计vue开发一个简单音乐播放器计算机源码+lw文档+系统+调试部署+数据库 JAVA毕业设计vue开发一个简单音乐播放器计算机源码+lw文档+系统+调试部署+数据库 本源码技术栈: 项目 ...

  8. 音乐播放器开发实例(可毕设含源码)

    一.开发基础 最近很多大学生都快毕业了在整理毕业论文以及毕业作品,其实在大三的时候就可以开始考虑了并且可以提前和自己的导师进行沟通,让自己的作品直接成为论文选题,在国产操作系统上开发一个简易音乐播放器 ...

  9. Springboot+vue开发的图书借阅管理系统项目源码下载-P0029

    前言 图书借阅管理系统项目是基于SpringBoot+Vue技术开发而来,功能相对比较简单,分为两个角色即管理员和学生用户,核心业务功能就是图书的发布.借阅与归还,相比于一些复杂的系统,该项目具备简单 ...

最新文章

  1. 12306 背后的技术大牛:我不跟人拼智商,我就跟他们拼狠!
  2. 算法----------找到 K 个最接近的元素
  3. 死磕Java并发:J.U.C之阻塞队列:LinkedBlockingDeque
  4. 公开羞辱邻座大码乘客 美国一女乘客被逐下客机
  5. MySQL查看数据表
  6. SQL语言之DQL语言学习(九)多表查询/链接查询 SQL99学习
  7. ehlib中dbgrideh的多选框设置
  8. ASP.NET程序中常用代码汇总(四)
  9. mysql数据库开发的36条军规
  10. layer.open组件获取弹出层页面变量、函数
  11. Linux 下如何查找木马并处理
  12. 华三 h3c 交换机RSTP配置
  13. CSDN寄送的礼物,博客评选的阳光普照奖
  14. 上位机开发实用语言软件分析
  15. Unbuntu ./btest : cannot execute binary file: Exec format error解决方案(CSAPP)
  16. 扫地机器人朋友圈文案_扫地机器人的简单文案
  17. 农历公历万年历互查系统
  18. 安卓使用usb连接外设
  19. JavaWeb基本概念与术语
  20. 第1章 沉沦在大学里——《逆袭大学》连载

热门文章

  1. 贝乐机器人俱乐部_贝乐机器人编程俱乐部机器人课程介绍
  2. java设计一个排队叫号系统_一个简易的叫号系统实现方案
  3. Nutanix携手世纪互联 推出超融合解决方案
  4. Java中Double保留自定义小数位的几种方法
  5. Vue 前端对接第三方平台扫码登录(Oauth2)
  6. 微信小程序app.json配置
  7. orcale 数据库语句(一)
  8. 图像处理——图像类型的转换
  9. U盘进PE时,快捷启动,没有usb启动选项
  10. SpiderFoot(信息收集)