我用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 ¤t_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(¤t_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);就会出现很严重的变调(也是短歌曲中才会出现),请各位帮忙解决,谢谢*/
创建一个新的线程用来播放解码后的数据,播放线程和解码线程分开
*/
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 ¤t_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(¤t_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);就会出现很严重的变调(也是短歌曲中才会出现),请各位帮忙解决,谢谢*/
然而不管加大成几,都会出现变调的情况
{
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;
}/*这样的代码我也试过了,压根就没有声音了*/
回调函数中,当接收到【WOM_DONE】消息时,参数【dwParam1】为【PWAVEHDR】;可以这样用:
1、分配数据
CopyMemory( xxx, ((PWAVEHDR)dwParam1)->lpData , size );2、播放
waveOutWrite(hwo, (PWAVEHDR)dwParam1, sizeof(WAVEHDR));
红字部分,是双缓冲播放的精华!
1、每当一个给定缓冲区的内容被播放完毕后,会收到【WOM_DONE】消息;2、这时参数【dwParam1】为已经播放完毕的【WAVEHDR】;3、需要把其中的内容【((PWAVEHDR)dwParam1)->lpData】更新为新的音频内容;4、然后调用【waveOutWrite】函数把这个【WAVEHDR】放入播放队列中。=============================================如果只用一个缓冲区,这时就会卡一下;因为当播放完这个缓冲区时,需要更新音频内容,这需要一点时间;如果改用两个缓冲区,当第一个缓冲区播放完毕后,开始播放第二个缓冲区;这时,第一个缓冲区正好更新内容,所以不会卡一下。
可以分两步来:1、先保证你直接播放波形文件【*.wav】没有问题;2、对【MP3】文件进行解码,然后保存成【*.wav】波形文件;3、播放保存后的波形文件,如果没有问题说明解码成功;4、把解码和播放连起来,形成实时解码后播放。
#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;
}
能不能把这个代码发给我参考学习下
十分感谢
[email protected]
[email protected]
[email protected]
[email protected]