• 多线程应用---使用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;
  • 相关阅读:
    2016年会有感之测试解决方案
    APP测试走过的那些坑
    2016年终总结——测试基础篇(二)
    2016年终总结——测试基础篇(一)
    分享篇——我的Java学习路线
    selenium使用笔记(三)——元素定位
    selenium使用笔记(二)——Tesseract OCR
    selenium使用笔记(一)——selenium你该知道的
    对新手学习自动化的一些感想
    Maven的配置和使用(三)
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318219.html
Copyright © 2020-2023  润新知