我用libmad解码mp3以后,然后用waveOutWrite播放,如果用一个buffer(WAVEHDR),会出现“kaka”的噪音,在网上找到原因,说每一帧之间的数据不能小于50~70分之一秒,采用双缓冲或者循环缓冲可以解决,现在我开了4个缓冲,callback的方式采用函数(waveOutProc),可是还是有问题,代码如下:/*
创建一个新的线程用来播放解码后的数据,播放线程和解码线程分开
*/
DWORD WINAPI PlayThd::ThreadProc(LPVOID lpParameter)
{
PlayThd * pcm_out_ptr = static_cast<PlayThd *>(lpParameter);
pcm_out_ptr->PcmOutProc() ; 
return 0 ; 
}
/*
PcmOutProc()函数用来输出波形文件
*/
void PlayThd::PcmOutProc()
{
//Sleep(0);
const int BUFF_NUMBER = 4;
int i=0; for (i=0; i<BUFF_NUMBER; i++)
{
ZeroMemory(&m_wave_header[i], sizeof(WAVEHDR));
m_wave_header[i].lpData                  = NULL;
m_wave_header[i].dwBufferLength          = OUTPUT_BUFFER_SIZE;
m_wave_header[i].dwUser                  = 0;
m_wave_header[i].dwFlags                 = 0;
m_wave_header[i].dwLoops                 = 0;
m_wave_header[i].dwBytesRecorded         = 0;
m_wave_header[i].lpNext                  = 0;
m_wave_header[i].reserved                = 0;
b_buffer_full[i]                         = false;
} /*初始化4个buffer*/
for (i=0; i<BUFF_NUMBER; i++)
{
b_buffer_full[i] = get_pcm_data(m_wave_header[i]); if (!b_buffer_full[i])
continue;
if (MMSYSERR_NOERROR != waveOutPrepareHeader(m_wave_out_hdl, &m_wave_header[i], sizeof(WAVEHDR)))
break;
} /*播放buffer中的数据*/
for (i=0; i<BUFF_NUMBER; i++)
{
if (!b_buffer_full[i])
continue;
if (MMSYSERR_NOERROR != waveOutWrite(m_wave_out_hdl, &m_wave_header[i], sizeof(WAVEHDR)))
break; ZeroMemory(&m_wave_header[i], sizeof(WAVEHDR));
b_buffer_full[i]=false;
}
}/*
get_pcm_data()函数从公共数据缓冲中得到数据
m_pq_ptr为指向公共数据缓冲区的指针
得去数据这里没有问题
*/
bool PlayThd::get_pcm_data(WAVEHDR &current_hdr)
{
PCM_BLOCK * pcm_blk; EnterCriticalSection(m_cs_ptr) ;
pcm_blk = m_pq_ptr->GetDataBlock() ;
LeaveCriticalSection(m_cs_ptr) ; if (!pcm_blk)
return false;
ZeroMemory(&current_hdr, sizeof(WAVEHDR)) ; current_hdr.lpData = (LPSTR)(pcm_blk->data);
current_hdr.dwBufferLength = pcm_blk->length;//OUTPUT_BUFFER_SIZE;
current_hdr.dwUser = 0;
current_hdr.dwFlags = 0;
current_hdr.dwLoops = 0;
current_hdr.dwBytesRecorded = 0;
current_hdr.lpNext = 0;
current_hdr.reserved = 0; return true;
}
/*
CALLBACK函数
out_buffer_ptr为LPVOID类型的全局变量,PlayThd的构造函数中,将this指针传递给out_buffer_pt,
所以pcm_out_ptr相当于PlayThd类的this指针
*/
void CALLBACK WaveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
PlayThd * pcm_out_ptr = static_cast<PlayThd *>(out_buffer_ptr); switch(uMsg)
{
case WOM_OPEN:
break; case WOM_DONE:
{
 LPWAVEHDR pWaveHeader = (LPWAVEHDR)dwParam1;
 waveOutUnprepareHeader(hwo, pWaveHeader, sizeof(WAVEHDR) );
 if (!pcm_out_ptr->get_pcm_data(*pWaveHeader))
break;
 waveOutPrepareHeader(hwo, pWaveHeader, sizeof(WAVEHDR));
 waveOutWrite(hwo, pWaveHeader, sizeof(WAVEHDR));
 ZeroMemory(pWaveHeader, sizeof(WAVEHDR));
}
break; case WOM_CLOSE:
break;
}
}/*然而这样做,播放时,仍然有“kaka”的声音,似乎还是一个缓冲的样子(播放的mp3长度为只有5秒),如果播放歌曲比较长的时候,就只有刚开始的时候有问题,似乎是我刚开始的几帧处理的有问题,但是却不知道怎么处理才是正确的*//*还有就是我在PcmOutProc()中最前面,如果加上一句Sleep(0);就会出现很严重的变调(也是短歌曲中才会出现),请各位帮忙解决,谢谢*/

解决方案 »

  1.   

    如果我改成const int BUFF_NUMBER = 1;那就肯定时“咔咔”的声音一直下去了,但是这样,我听着,数据没有乱,刚开始的几帧也不会变调
    然而不管加大成几,都会出现变调的情况
      

  2.   

    没看代码在写数据之前,把buffer初始化为0试试
      

  3.   

    可是我在写数据之前已经有初始化啊~for (i=0; i <BUFF_NUMBER; i++)
    {
    ZeroMemory(&m_wave_header[i], sizeof(WAVEHDR));
    m_wave_header[i].lpData                  = NULL;
    m_wave_header[i].dwBufferLength          = OUTPUT_BUFFER_SIZE;
    m_wave_header[i].dwUser                  = 0;
    m_wave_header[i].dwFlags                 = 0;
    m_wave_header[i].dwLoops                 = 0;
    m_wave_header[i].dwBytesRecorded         = 0;
    m_wave_header[i].lpNext                  = 0;
    m_wave_header[i].reserved                = 0;
    b_buffer_full[i]                         = false;
    }/*初始化4个buffer*/
    for (i=0; i <BUFF_NUMBER; i++)
    {
    b_buffer_full[i] = get_pcm_data(m_wave_header[i]);if (!b_buffer_full[i])
    continue;
    if (MMSYSERR_NOERROR != waveOutPrepareHeader(m_wave_out_hdl, &m_wave_header[i], sizeof(WAVEHDR)))
    break;
    }/*如果说是在清零以后,不从公共数据缓冲中拿数据,而直接写的话,那样做,就一直没有声音*/
    /*就是改成下面一段代买*/
    for (i=0; i <BUFF_NUMBER; i++)
    {
    ZeroMemory(&m_wave_header[i], sizeof(WAVEHDR));
    m_wave_header[i].lpData                  = NULL;
    m_wave_header[i].dwBufferLength          = 0;//OUTPUT_BUFFER_SIZE;
    m_wave_header[i].dwUser                  = 0;
    m_wave_header[i].dwFlags                 = 0;
    m_wave_header[i].dwLoops                 = 0;
    m_wave_header[i].dwBytesRecorded         = 0;
    m_wave_header[i].lpNext                  = 0;
    m_wave_header[i].reserved                = 0;
    b_buffer_full[i]                         = false;
    }/*初始化4个buffer*/
    for (i=0; i <BUFF_NUMBER; i++)
    {
    /*b_buffer_full[i] = get_pcm_data(m_wave_header[i]);if (!b_buffer_full[i])
    continue;*/
    if (MMSYSERR_NOERROR != waveOutPrepareHeader(m_wave_out_hdl, &m_wave_header[i], sizeof(WAVEHDR)))
    break;
    }/*这样的代码我也试过了,压根就没有声音了*/
      

  4.   

    我在怀疑是不是我前面的几帧处理的有问题,如果歌曲就只有那么几帧,那么听起来就会有问题,但是如果歌曲比较长的时候,一个循环以后应该就没有开了,而我现在的状况差不多就是这样的,短的歌曲(仅仅几秒钟的)就会有问题,而长的歌曲,只有刚开始几帧有问题是不是刚开始的buffer中填满数据以后,不应该用for循环直接写,而要等一会儿呢?
      

  5.   

    直接使用mciSendCommand播放mp3,没有杂音
      

  6.   

    因为我做的是WinCE上的程序,不能用mciSendCommand的
      

  7.   

    本来我的东西年前的时候都已经弄完了,那个时候用的就是d-show,在CE下面一点儿问题都没有,但是结果发现做好的播放器在windows mobile的机子上面不能跑,查资料最后才知道,windows mobile上面根本就没有d-show的组件,而为了兼容性,所以,d-show也不能用了,于是就只好自己写了,然后就用libmad的解码器,可是播放的时候,一直没有处理好~
      

  8.   

    我提出一点疑问,不知是不是这个问题?
    回调函数中,当接收到【WOM_DONE】消息时,参数【dwParam1】为【PWAVEHDR】;可以这样用:
    1、分配数据
    CopyMemory( xxx,   ((PWAVEHDR)dwParam1)->lpData    ,   size );2、播放
    waveOutWrite(hwo, (PWAVEHDR)dwParam1, sizeof(WAVEHDR)); 
    红字部分,是双缓冲播放的精华!
      

  9.   

    zaodt,能不能稍微说的详细一些啊,你的意思是不是说在waveOutWrite之前,现将数据拷贝到一个临时的缓冲中?可是这样有什么意义吗?后面调用waveOutWrite的时候,用的还是 (PWAVEHDR)dwParam1 这个参数啊?
      

  10.   

    看来楼主对【waveOut】系列的函数没有研究透彻。
    1、每当一个给定缓冲区的内容被播放完毕后,会收到【WOM_DONE】消息;2、这时参数【dwParam1】为已经播放完毕的【WAVEHDR】;3、需要把其中的内容【((PWAVEHDR)dwParam1)->lpData】更新为新的音频内容;4、然后调用【waveOutWrite】函数把这个【WAVEHDR】放入播放队列中。=============================================如果只用一个缓冲区,这时就会卡一下;因为当播放完这个缓冲区时,需要更新音频内容,这需要一点时间;如果改用两个缓冲区,当第一个缓冲区播放完毕后,开始播放第二个缓冲区;这时,第一个缓冲区正好更新内容,所以不会卡一下。
      

  11.   


    可以分两步来:1、先保证你直接播放波形文件【*.wav】没有问题;2、对【MP3】文件进行解码,然后保存成【*.wav】波形文件;3、播放保存后的波形文件,如果没有问题说明解码成功;4、把解码和播放连起来,形成实时解码后播放。
      

  12.   

    楼主,你的libmad库有没有,发一份给我,先谢了,[email protected]
      

  13.   

    不知道wince下可不可以用dsound,waveOutXXX控制太烦了,容易死锁和出问题。
      

  14.   

    曾经我在MSDN60里看到过一段 Audio Streaming Sample Code 的代码,也是用waveOutXXX从流里播放的,我用过它放在自己的一个项目里,没有卡卡声音,不知道能帮你。贴出来给你哦,希望能帮你。The following code sample demonstrates how to stream audio data using the IAudioMediaStream, IAudioStreamSample, IMemoryData, and IAudioData interfaces.#include <windows.h>
    #include <mmsystem.h>
    #include <amstream.h>/********************************************************************   Trivial wave player stuff ********************************************************************/class CWaveBuffer;class CWaveBuffer {
        public:
            CWaveBuffer();
            ~CWaveBuffer();
            BOOL Init(HWAVEOUT hWave, int Size);
            void Done();
            BOOL Write(PBYTE pData, int nBytes, int& BytesWritten);
            void Flush();    private:
            WAVEHDR      m_Hdr;
            HWAVEOUT     m_hWave;
            int          m_nBytes;
    };class CWaveOut {
        public:
            CWaveOut(LPCWAVEFORMATEX Format, int nBuffers, int BufferSize);
            ~CWaveOut();
            void Write(PBYTE Data, int nBytes);
            void Flush();
            void Wait();
            void Reset();
        private:
            const HANDLE       m_hSem;
            const int          m_nBuffers;
            int          m_CurrentBuffer;
            BOOL         m_NoBuffer;
            CWaveBuffer *m_Hdrs;
            HWAVEOUT     m_hWave;
    };/*
        CWaveBuffer
    */CWaveBuffer::CWaveBuffer()
    {
    }BOOL CWaveBuffer::Init(HWAVEOUT hWave, int Size)
    {
        m_hWave  = hWave;
        m_nBytes = 0;    /*  Allocate a buffer and initialize the header */
        m_Hdr.lpData = (LPSTR)LocalAlloc(LMEM_FIXED, Size);
        if (m_Hdr.lpData == NULL) {
            return FALSE;
        }
        m_Hdr.dwBufferLength  = Size;
        m_Hdr.dwBytesRecorded = 0;
        m_Hdr.dwUser = 0;
        m_Hdr.dwFlags = 0;
        m_Hdr.dwLoops = 0;
        m_Hdr.lpNext = 0;
        m_Hdr.reserved = 0;    /*  Prepare it */
        waveOutPrepareHeader(hWave, &m_Hdr, sizeof(WAVEHDR));    return TRUE;
    }CWaveBuffer::~CWaveBuffer() {
        if (m_Hdr.lpData) {
            waveOutUnprepareHeader(m_hWave, &m_Hdr, sizeof(WAVEHDR));
            LocalFree(m_Hdr.lpData);
        }
    }void CWaveBuffer::Flush()
    {
        //ASSERT(m_nBytes != 0);
        m_nBytes = 0;
        waveOutWrite(m_hWave, &m_Hdr, sizeof(WAVEHDR));
    }BOOL CWaveBuffer::Write(PBYTE pData, int nBytes, int& BytesWritten)
    {
        //ASSERT((DWORD)m_nBytes != m_Hdr.dwBufferLength);
        BytesWritten = min((int)m_Hdr.dwBufferLength - m_nBytes, nBytes);
        CopyMemory((PVOID)(m_Hdr.lpData + m_nBytes), (PVOID)pData, BytesWritten);
        m_nBytes += BytesWritten;
        if (m_nBytes == (int)m_Hdr.dwBufferLength) {
            /*  Write it! */
            m_nBytes = 0;
            waveOutWrite(m_hWave, &m_Hdr, sizeof(WAVEHDR));
            return TRUE;
        }
        return FALSE;
    }void CALLBACK WaveCallback(HWAVEOUT hWave, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
    {
        if (uMsg == WOM_DONE) {
            ReleaseSemaphore((HANDLE)dwUser, 1, NULL);
        }
    }/*
        CWaveOut
    */CWaveOut::CWaveOut(LPCWAVEFORMATEX Format, int nBuffers, int BufferSize) :
        m_nBuffers(nBuffers),
        m_CurrentBuffer(0),
        m_NoBuffer(TRUE),
        m_hSem(CreateSemaphore(NULL, nBuffers, nBuffers, NULL)),
        m_Hdrs(new CWaveBuffer[nBuffers]),
        m_hWave(NULL)
    {
        /*  Create wave device */
        waveOutOpen(&m_hWave,
                    WAVE_MAPPER,
                    Format,
                    (DWORD)WaveCallback,
                    (DWORD)m_hSem,
                    CALLBACK_FUNCTION);    /*  Initialize the wave buffers */
        for (int i = 0; i < nBuffers; i++) {
            m_Hdrs[i].Init(m_hWave, BufferSize);
        }
    }CWaveOut::~CWaveOut()
    {
        /*  First get our buffers back */
        waveOutReset(m_hWave);    /*  Free the buffers */
        delete [] m_Hdrs;    /*  Close the wave device */
        waveOutClose(m_hWave);    /*  Free our semaphore */
        CloseHandle(m_hSem);
    }void CWaveOut::Flush()
    {
        if (!m_NoBuffer) {
            m_Hdrs[m_CurrentBuffer].Flush();
            m_NoBuffer = TRUE;
            m_CurrentBuffer = (m_CurrentBuffer + 1) % m_nBuffers;
        }
    }void CWaveOut::Reset()
    {
        waveOutReset(m_hWave);
    }
    void CWaveOut::Write(PBYTE pData, int nBytes)
    {
        while (nBytes != 0) {
            /*  Get a buffer if necessary */
            if (m_NoBuffer) {
                WaitForSingleObject(m_hSem, INFINITE);
                m_NoBuffer = FALSE;
            }        /*  Write into a buffer */
            int nWritten;
            if (m_Hdrs[m_CurrentBuffer].Write(pData, nBytes, nWritten)) {
                m_NoBuffer = TRUE;
                m_CurrentBuffer = (m_CurrentBuffer + 1) % m_nBuffers;
                nBytes -= nWritten;
                pData += nWritten;
            } else {
                //ASSERT(nWritten == nBytes);
                break;
            }
        }
    }void CWaveOut::Wait()
    {
        /*  Send any remaining buffers */
        Flush();    /*  Wait for our buffers back */
        for (int i = 0; i < m_nBuffers; i++) {
            WaitForSingleObject(m_hSem, INFINITE);
        }
        LONG lPrevCount;
        ReleaseSemaphore(m_hSem, m_nBuffers, &lPrevCount);
    }/**************************************************************************  End of wave player stuff **************************************************************************/
    HRESULT RenderStreamToDevice(IMultiMediaStream *pMMStream)
    {
        WAVEFORMATEX wfx;
        #define DATA_SIZE 5000
        PBYTE pBuffer = (PBYTE)LocalAlloc(LMEM_FIXED, DATA_SIZE);    IMediaStream *pStream;
        IAudioStreamSample *pSample;
        IAudioMediaStream *pAudioStream;
        IAudioData *pAudioData;    pMMStream->GetMediaStream(MSPID_PrimaryAudio, &pStream);
        pStream->QueryInterface(IID_IAudioMediaStream, (void **)&pAudioStream);
        pAudioStream->GetFormat(&wfx);
        CoCreateInstance(CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER,
                                        IID_IAudioData, (void **)&pAudioData);
        pAudioData->SetBuffer(DATA_SIZE, pBuffer, 0);
        pAudioData->SetFormat(&wfx);
        pAudioStream->CreateSample(pAudioData, 0, &pSample);
        HANDLE hEvent = CreateEvent(FALSE, NULL, NULL, FALSE);
        CWaveOut WaveOut(&wfx, 4, 2048);
        int iTimes;
        for (iTimes = 0; iTimes < 3; iTimes++) {
            DWORD dwStart = timeGetTime();
            for (; ; ) {
                HRESULT hr = pSample->Update(0, hEvent, NULL, 0);
                if (FAILED(hr) || MS_S_ENDOFSTREAM == hr) {
                    break;
                }
                WaitForSingleObject(hEvent, INFINITE);
                DWORD dwTimeDiff = timeGetTime() - dwStart;
                //  We'll get bored after about 10 seconds
                if (dwTimeDiff > 10000) {
                    break;
                }
                DWORD dwLength;
                pAudioData->GetInfo(NULL, NULL, &dwLength);
                WaveOut.Write(pBuffer, dwLength);
            }
            pMMStream->Seek(0);
        }    pAudioData->Release();
        pSample->Release();
        pStream->Release();
        pAudioStream->Release();
        LocalFree((HLOCAL)pBuffer);    return S_OK;
    }HRESULT RenderFileToMMStream(WCHAR * pszFileName, IMultiMediaStream **ppMMStream)
    {
        IAMMultiMediaStream *pAMStream;
        CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER,
           IID_IAMMultiMediaStream, (void **)&pAMStream);
        pAMStream->Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL);
        pAMStream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, NULL);
        pAMStream->OpenFile(pszFileName, AMMSF_RUN);
        *ppMMStream = pAMStream;
        return S_OK;
    }int _CRTAPI1 main(int argc, char *argv[])
    {
        IMultiMediaStream *pMMStream;
        CoInitialize(NULL);
        WCHAR wszName[1000];
        MultiByteToWideChar(CP_ACP, 0, argv[1], -1, wszName,
                            sizeof(wszName) / sizeof(wszName[0]));
        RenderFileToMMStream(wszName, &pMMStream);
        RenderStreamToDevice(pMMStream);
        pMMStream->Release();
        CoUninitialize();
        return 0;
    }
      

  15.   

    前天晚上我想了一晚上,昨天整了一天,将原来一个线程解码,一个线程播放的结构更改成为都在一个线程里面处理,每一帧解码完成以后,都new出一个WAVEHDR(当然,也要new一个char数组给lpData),然后直接给new出来的WAVEHDR填充数据,prepare,write,然后再将这个WAVEHDR的指针压入一个list(push_front)中,在callback函数中将当前播放的WAVHDR delete(pop_back())掉。因为解码速度,远远大于播放速度,这样,到最后的结果就是如果歌曲比较小,OK,但是如果歌曲比较长就会是内存消耗完,然后new分配不到空间,然后报错,甚至死机,所以,我在添加一个成员的时候,先判断list的长度是否大于MAX_WAVEHDR_NUMBER,如果是的话,就等待片刻(我用的是while(i_lsize>MAX_WAVEHDR_NUMBER){Sleep(0);continue;}),这样就没有问题了!哈哈!不过还是很感谢zaodt的帮助。解帖啦!
      

  16.   

    楼主
    能不能把这个代码发给我参考学习下
    十分感谢
    [email protected]
      

  17.   

    楼主能否发我一份,谢谢。
    [email protected]
      

  18.   

    楼主能否吧代码发给我参考一下 谢了!
    [email protected]
      

  19.   

    我也碰到这个问题了,已经快一个星期了,楼主如果看到的话就给我一份源码参考下,谢谢啦
    [email protected]