花了2天的时间研究了一下windows下播放pcm的waveout接口,http://blog.csdn.net/nokianasty/article/details/8558151这个帖子对windows音频播放说的比较详细,我自己另外重新写了一个循环缓冲的播放demo,在main主线程中开了一个decode线程,decode线程负责解码和送数据到waveout,(目前是pcm数据所以解码部分可以忽略),decode线程一直填数据到waveout直到循环缓冲中没有空闲块就挂起等待空闲块,waveoutproc函数负责通知哪个数据块播放完成,他们之前的通信和同步我采用了queue+semaphore。对windows进程通信方式不熟所以先暂时用这个了,只要能实现有空闲数据块唤醒decode线程,同时不阻塞waveout音乐播放进程就可以了。期间碰到fread读文件采用“r”方式时,读取不完的问题,这个windows下有这个问题,当读取到0x1a时会认为文件结束了,当遇到0x0d0a时读取出来的是0x0a ,linux下r/rb是一样的。代码如下:
#include<windows.h>
#include<mmsystem.h>
#include<stdio.h>
#include<queue>
using std::queue;
#pragma comment(lib,"winmm.lib")
/*
* some good values for block size and count
*/
#define BLOCK_SIZE 4096
#define BLOCK_COUNT 3
/*
*waveoutprocdecodethread share vars
*/
static CRITICAL_SECTION waveCriticalSection;
static HANDLE freeBufferSema;
static queue<WAVEHDR*> qWHDR;
typedef struct decodeThreadParam
{
char *filename;
HWAVEOUT *device;
}decThreadParmT;
static DWORD decodeThreadProc(LPVOID lpdwThreadParam );
static void CALLBACK waveOutProc(HWAVEOUT hwo,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2);
int main()
{
HWAVEOUT hWaveOut;
MMRESULT ret;
WAVEFORMATEX wfx;
DWORD decodeThreadId;
HANDLE hDecodeThread;
char filename[]="../test.pcm";
decThreadParmT decThreadData;
int i;
freeBufferSema = CreateSemaphore(0,
0,
BLOCK_COUNT,
"bufferSema"
);
if(freeBufferSema==NULL)
{
printf("Create Buffer Sema ERROR!\n");
return 1;
}
InitializeCriticalSection(&waveCriticalSection);
wfx.nSamplesPerSec = 48000;
wfx.wBitsPerSample = 16;
wfx.nChannels = 2;
wfx.cbSize = 0;
wfx.wFormatTag =WAVE_FORMAT_PCM;
wfx.nBlockAlign =(wfx.wBitsPerSample*wfx.nChannels)>>3;
wfx.nAvgBytesPerSec =wfx.nSamplesPerSec*wfx.nBlockAlign;
ret = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (DWORD_PTR)waveOutProc, 0, CALLBACK_FUNCTION);
if(ret!=MMSYSERR_NOERROR)
{
printf("waveoutopen ERR!\n");
return 1;
}
printf("waveout open successfully!\n");
decThreadData.device = &hWaveOut;
decThreadData.filename = filename;
hDecodeThread=CreateThread(NULL, //Choose default security
0, //Default stack size
(LPTHREAD_START_ROUTINE)&decodeThreadProc,
//Routine to execute
(LPVOID) &decThreadData, //Thread parameter
0, //Immediately run the thread
&decodeThreadId //Thread Id
);
if(hDecodeThread==NULL)
{
printf("Error Creating Decode Thread\n");
return 1;
}
WaitForSingleObject(hDecodeThread, INFINITE);
Sleep(1000);
waveOutClose(hWaveOut);
CloseHandle(freeBufferSema);
CloseHandle(hDecodeThread);
DeleteCriticalSection(&waveCriticalSection);
}
static DWORD decodeThreadProc(LPVOID lpdwThreadParam )
{
char *filename=((decThreadParmT*)lpdwThreadParam)->filename;
HWAVEOUT *hWaveOut=((decThreadParmT*)lpdwThreadParam)->device;
WAVEHDR* current=NULL;
WAVEHDR* waveBlocks=NULL;
char *audioBuf=NULL;
FILE *fp=NULL;
int i;
audioBuf = (char*)malloc(BLOCK_SIZE*BLOCK_COUNT);
memset(audioBuf, 0, BLOCK_SIZE*BLOCK_COUNT);
waveBlocks = (WAVEHDR*)malloc(BLOCK_COUNT*sizeof(WAVEHDR));
memset(waveBlocks, 0, BLOCK_COUNT*sizeof(WAVEHDR));
for(i=0;i<BLOCK_COUNT;i++)
{
waveBlocks[i].lpData = (audioBuf+i*BLOCK_SIZE);
waveBlocks[i].dwBufferLength = BLOCK_SIZE;
}
fp= fopen(filename, "rb");
if(fp==NULL)
{
printf("open file :%s error\n",filename);
return -1;
}
//preread BLOCK_COUNT data to buffer
for(i=0; i<BLOCK_COUNT; i++)
{
current = &waveBlocks[i];
fread(current->lpData, 1, BLOCK_SIZE, fp);
waveOutPrepareHeader(*hWaveOut, current, sizeof(WAVEHDR));
}
for(i=0; i<BLOCK_COUNT; i++)
{
current = &waveBlocks[i];
printf("i:%x\n",current);
waveOutWrite(*hWaveOut, current, sizeof(WAVEHDR));
}
while(1)
{
int size=0;
//wait a free pcm buf
WaitForSingleObject(
freeBufferSema, // event handle
INFINITE); // indefinite wait
//read data to pcm buf,then write to waveout
//
EnterCriticalSection(&waveCriticalSection);
current = qWHDR.front();
qWHDR.pop();
LeaveCriticalSection(&waveCriticalSection);
printf("f:%x\n",current);
waveOutUnprepareHeader(*hWaveOut, current, sizeof(WAVEHDR));
size=fread(current->lpData, 1,BLOCK_SIZE, fp);
//printf("size:%d\n",size);
if(size>0)
{
waveOutPrepareHeader(*hWaveOut, current, sizeof(WAVEHDR));
waveOutWrite(*hWaveOut, current, sizeof(WAVEHDR));
}
else
{
printf("play over!%d\n",size);
break;
}
}
printf("decode finished!\n");
fclose(fp);
free(audioBuf);
free(waveBlocks);
}
static void CALLBACK waveOutProc(HWAVEOUT hwo,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
{
WAVEHDR *freeWaveHdr=NULL;
switch(uMsg)
{
case WOM_DONE:
EnterCriticalSection(&waveCriticalSection);
freeWaveHdr = (WAVEHDR*)dwParam1;
printf("o:%x\n",freeWaveHdr);
qWHDR.push(freeWaveHdr);
LeaveCriticalSection(&waveCriticalSection);
ReleaseSemaphore(freeBufferSema, 1, NULL);
break;
case WOM_OPEN:
printf("open waveout\n");
break;
case WOM_CLOSE:
printf("close waveout\n");
break;
default:
printf("ERROR MSG!\n");
}
}