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

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

1、 语音播放API

1.1 waveOutOpen - 打开播放设备

  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 - 准备数据块

  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结构的大小,单位:字节 */
    );
备注:
  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 - 将音频数据块送到指定的音频输出设备

  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 - 清除音频缓存数据

  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、解码器开发

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

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

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

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