• waveout系列API实现pcm音频播放


    最近做一个播放组件,也算是折腾1周了,收获还算不少。

    回想下整个编码过程中磕磕碰碰走了不少弯路,最大的杯具就是,太相信网上现有代码例子。

    国内网上关于waveout的文章不少,但基本就那几篇转载,其中的问题也没有人指出。

    为了方便大家用到时少被误导,在此留下我的笔记(如果被我误导了,我先道歉-,-)

    代码不多,直接上关键部分(本人认为多余代码贴上去百害而无一利):

    一、初始化设备

    bool WinAudioPlay::DevOpen()
    {
        if (!m_bPalyStata)
        {
            WAVEFORMATEX    wfx;
            ZeroMemory(&wfx,sizeof(WAVEFORMATEX));
        
            wfx.wFormatTag = WAVE_FORMAT_PCM;
            wfx.nChannels = 2;
            wfx.nSamplesPerSec = 44100L;
            wfx.wBitsPerSample = 16;
            wfx.cbSize = 0;
            wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;    
            wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * wfx.wBitsPerSample / 8;
        
            if(::waveOutOpen (0,0,&wfx,0,0,WAVE_FORMAT_QUERY))
            {
                Plug::PlugMessageBox(L"wave设备初始化失败~");
                return false;
            }
            if (::waveOutOpen(&m_hWaveOut, WAVE_MAPPER, &wfx, (DWORD)&WinAudioPlay::waveOutProc, (DWORD)this, CALLBACK_FUNCTION))
            {
                Plug::PlugMessageBox(L"wave设备初始化失败~");
                return false;
            }
            m_iBlockNum        = 0;
            m_bPalyStata    = true;
            m_spbrTgthr.reset(new boost::barrier(2));
            m_sptrdWaveOutTgthr.reset(new boost::thread(boost::bind(&WinAudioPlay::ThrdWaveOutTogether,this)));
        } 
        return true;
    }

    二、接收pcm格式数据,并加载到声卡缓冲区

    bool __stdcall WinAudioPlay::play_audio( const void* buffer, int len )
    {
        if (!m_bPalyStata)
            return false;
    
        if (BLOCK_MAX <= m_iBlockNum || len <= 0)
        {
            return true;        //超过缓冲最大包,不继续播放
        }
    
        LPWAVEHDR pWaveHeader = new WAVEHDR;
    
        memset(pWaveHeader, 0, sizeof(WAVEHDR));
        pWaveHeader->dwLoops = 1;
        pWaveHeader->dwBufferLength = len;
        pWaveHeader->lpData = new char[len];
        if (!pWaveHeader->lpData)
        {
            delete pWaveHeader;
            return false;
        }
        memcpy(pWaveHeader->lpData, buffer, len);
    
        if (::waveOutPrepareHeader(m_hWaveOut, pWaveHeader, sizeof(WAVEHDR)))
        {
            delete pWaveHeader->lpData;
            delete pWaveHeader;
            return false;
        }
        if (::waveOutWrite(m_hWaveOut, pWaveHeader, sizeof(WAVEHDR)))
        {
            delete pWaveHeader->lpData;
            delete pWaveHeader;
            return false;
        }
    
        m_iBlockNum++;
        return true;
    }

    三、回调函数

    void CALLBACK WinAudioPlay::waveOutProc( HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 )
    {
        WinAudioPlay* pThis=(WinAudioPlay*)dwInstance;
    
        if(WOM_DONE == uMsg)    //播放完成
        { 
            while(NULL != pThis->m_lpWaveHdrFromCallbackProc)
            {
                boost::this_thread::interruptible_wait(1);
            }
            pThis->m_lpWaveHdrFromCallbackProc = (LPWAVEHDR)dwParam1;
            pThis->m_spbrTgthr->wait();
        }
        return ;
    }

    四、线程同步播放

    void WinAudioPlay::ThrdWaveOutTogether()
    {
        while(!m_b_exit)
        {
            m_spbrTgthr->wait();
            if (NULL != m_lpWaveHdrFromCallbackProc)
            {    
                ::waveOutUnprepareHeader(m_hWaveOut, m_lpWaveHdrFromCallbackProc, sizeof(WAVEHDR));
                delete[] m_lpWaveHdrFromCallbackProc->lpData;        
                delete     m_lpWaveHdrFromCallbackProc;
                m_lpWaveHdrFromCallbackProc = NULL;
    
                (m_iBlockNum > 0)?(m_iBlockNum--):(m_iBlockNum = 0);            
            }
        }
        if (m_hWaveOut != NULL)
        {
            ::waveOutReset(m_hWaveOut);
            ::waveOutClose(m_hWaveOut);
        }
    }

    五、关闭线程,释放资源

    WinAudioPlay::~WinAudioPlay()
    {    
        m_bPalyStata = false;
        while(0 != m_iBlockNum)
        {
            Sleep(1);
        }
        m_b_exit = true;
        m_spbrTgthr->wait();
        m_sptrdWaveOutTgthr->join();
    }

    我相信聪明的你,借助msdn能够很快理解上面意思,我就不多打字了(水平有限,怕误人子弟~)

    但是需要注意以下几点:

    一、waveOutProc回调函数中绝对不能调用waveOut系列函数(可用线程同步实现通知在另一个线程调用)

    二、调用waveOutReset函数时,函数执行完毕才返回,期间不可以调用hWaveOut

    三、注意释放资源

    and so on...

    其实还有很多,我就不多写了,以上3点中前两点处理不好,会发生线程死锁。

    当初我就在上面耗费了很长时间,后来多亏孙总点醒(其实msdn中有那么一句的。但是E语要加强啊。。。)

    最后赠送一个免费的建议,听不听由你:不要太过于相信网络上的代码,win32平台最有说服力的还数msdn~

  • 相关阅读:
    Android碰到的怪问题
    Linux转换文件格式
    Android 打包签名 从生成keystore到完成签名
    重写SimpleCursorAdapter
    sqlite的数据导入 导出
    日语单词检索WebService 账户API和示例更新
    日语单词检索 WebService 试运行 和简单说明
    使用扩展方法,让菜单也可以Clone
    接口里面的静态方法痒啊
    创业难,守业更难
  • 原文地址:https://www.cnblogs.com/wuyaSama/p/1889623.html
Copyright © 2020-2023  润新知