使用WaveOut接口播放WAV文件

我需要在插件中实现一边接收数据,一边展示媒体文件的功能,因此从最简单的音频文件来开始研究,绝大多数API如:playSound等都是根据完整文件来播放音频的,即便是内存模式也是需要将整个文件全部读入一片内容,然后将内存指针传递给playSound。最后逐渐寻找到WaveOut接口。

要一边接收数据一边播放,首先需要得到头信息,然后使用多个缓存在WaveOutWrite中交替为音频设备填充数据。

这里有几个我觉得不错的参考资料:

http://apps.hi.baidu.com/share/detail/59300070

http://blog.csdn.net/jinlking/article/details/3864259

我在做的过程中,遇到的比较棘手的问题是:1:在WaveOutOpen函数中的回调函数的处理。2、多个缓存交替播放时声音一卡一卡的。

第一个问题:因为我使用了一个WavePlayer类来实现我的功能,在这个类的构造函数中做了一些准备工作,根据文件头数据音乐信息。然后,最后play函数中指定回调函数,准备最开始的数据填充接受三个参数:最后每当播放完毕就会调用回调函数,在回调函数中填充当前数据。

回调函数必须是全局函数,在类中只能以static函数存在,但是静态成员函数无法访问类的非静态成员变量,因此这里很久才搞定,最后解决方案是在WaveOutOpen中奖第五个参数设置为this。利用this指针来将对象传递到静态回调函数中,在回调函数中将这个指针强制转换成wavePlayer*。这样就可以访问该对象的非静态成员了。

bool WavePlayer::wavePlay(HPSTR wavData,long dataSize,int numOfBuf/* =1 */)
{
        //多缓存交替播放
	DWORD rs=0;
	//检查音频设备,返回音频输出设备的性能
	if (MMSYSERR_NOERROR != (rs = waveOutGetDevCaps(WAVE_MAPPER, &pwoc, sizeof(WAVEOUTCAPS))))
	{
		ErrHandler(_T("waveOutGetDevCaps()"),rs);
		return false;
	}

	if (MMSYSERR_NOERROR != (rs =waveOutOpen(&hWaveOut, WAVE_MAPPER, &wvformat, (DWORD)waveOutCallBack, (DWORD_PTR)this, CALLBACK_FUNCTION)))
	{
		ErrHandler(_T("waveOutOpen()"),rs);
		return false;
	}
	HPSTR dataBlocks=new char[dataSize*numOfBuf];
	memcpy(dataBlocks,wavData,dataSize*numOfBuf);
	lpWavhdr = new WAVEHDR[numOfBuf];
	for (int i=0;i<numOfBuf;++i)
	{
		//准备播放数据
		lpWavhdr[i].lpData = (HPSTR)(dataBlocks+i*dataSize);
		lpWavhdr[i].dwBufferLength = dataSize;
		lpWavhdr[i].dwFlags = 0;
		if (MMSYSERR_NOERROR != (rs =waveOutPrepareHeader(hWaveOut, (LPWAVEHDR)(&lpWavhdr[i]), sizeof(WAVEHDR))))
		{
			ErrHandler(_T("waveOutPrepareHeader()"),rs);
			return false;
		}
		//将数据写入设备并开始播放
		if (MMSYSERR_NOERROR != (rs =waveOutWrite(hWaveOut,(LPWAVEHDR)(&lpWavhdr[i]) , sizeof(WAVEHDR))))
		{
			ErrHandler(_T("waveOutWrite()"),rs);
			return false;
		}
	}
	*pIsLoop=true;
	*pIsBufNull=true;
	lpData=new char[dataSize];
	return true;
}
回调函数:

void CALLBACK WavePlayer::waveOutCallBack(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
	WavePlayer* pPlayer=(WavePlayer*)dwInstance;
	
	if(uMsg == WOM_DONE)
	{
		if (!*(pPlayer->pIsLoop))
		{
			return;//不在交替播放循环,直接返回
		}

		if (*(pPlayer->pIsBufNull)&&!*(pPlayer->pIsEod))
		{
			return;//缓存为空,但不是文件尾部,返回
		}
		
		LPWAVEHDR pWaveHeader = (LPWAVEHDR)dwParam1;//系统自动识别是哪一个WAVEHDR播放完毕
		pWaveHeader->dwFlags=0;
		waveOutUnprepareHeader( hwo, pWaveHeader, sizeof(WAVEHDR) );//播放完后须调用此函数

		if (*(pPlayer->pIsEod)&&*(pPlayer->pIsBufNull))
		{
			return;//数据已经读取完毕,且缓存为空。Unprepare之后再返回
		}
		//此处填充WAVEHDR的lpdate缓冲
		pWaveHeader->lpData=pPlayer->lpData;
		pWaveHeader->dwBufferLength=pPlayer->mBufSize;
		pWaveHeader->dwFlags=0;

		waveOutPrepareHeader( hwo, pWaveHeader, sizeof(WAVEHDR));
		waveOutWrite( hwo, pWaveHeader, sizeof(WAVEHDR) );
		*(pPlayer->pIsBufNull)=true;
	}
	return;
}
实际接收数据是在main中进行的。实现得比较别扭,用了一个while(1):

	/************************************************************************/
	/* 双缓存交替播放                                                        */
	/************************************************************************/
	long bufSize=8*1024;
	int bufNum=2;
	long curPos=44+bufSize*bufNum;
	HPSTR lpPreData=new char[bufSize*bufNum];
	SetFilePointer(hFile,44,NULL,FILE_BEGIN);
	ReadFile(hFile,lpPreData,bufSize*bufNum,&dwRead,NULL);
	*wavPlayer.pIsBufNull=false;
	wavPlayer.mBufSize=bufSize;

	while(1)
 	{
		if (!*(wavPlayer.pIsLoop))
		{
			wavPlayer.wavePlay(lpPreData,bufSize,bufNum);
		}
		if (*wavPlayer.pIsLoop)
		{
			if (lpPreData!=NULL)
			{
				delete[] lpPreData;
				lpPreData=NULL;
			}
		}

		if (*wavPlayer.pIsBufNull && !(*wavPlayer.pIsEod))
		{
			SetFilePointer(hFile,curPos,NULL,FILE_BEGIN);
			ReadFile(hFile,wavPlayer.lpData,bufSize,&dwRead,NULL);
			if (0==dwRead||ERROR_HANDLE_EOF==GetLastError())
			{
				*wavPlayer.pIsEod=true;
				*wavPlayer.pIsBufNull=false;
				break;
			}
			curPos+=bufSize;
			*wavPlayer.pIsBufNull=false;
		}
		Sleep(bufSize/0xb110);
 	}
获取数据这里就是readfile来实现的,最终将会使用OnStreamDataArrived接收数据。

第二个问题比较小:就是声音卡顿的现象。但是找了很久才找到;就是在回调函数中填充数据的时候处理得不合理:

pWaveHeader->lpData=pPlayer->lpData;
上面这句改成

memcpy(pWaveHeader->lpData,pPlayer->lpData,pPlayer->mBufSize);
就不卡了。但是还需要在while中的sleep时间做一下调整,否则可能在不同的电脑上会出现不同的效果。

关于实现双缓冲连续播放大文件的最好参考资料是前面给出的第一个链接。不过我是在作完这些之后才看到。否则我的程序应该会更漂亮一点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
waveOut 是 Windows 操作系统的一个音频输出接口。它是一个音频设备驱动程序,在 C 语言中使用 waveOut 接口可以实现音频的播放和控制。 wav 是一种无损的音频文件格式,它采用 PCM(脉冲编码调制)数据进行编码,并且以 RIFF(资源交换文件格式)结构组织。waveOut 在 C 语言中可以使用播放 wav 文件。 在使用 waveOut 播放 wav 文件之前,首先需要初始化 waveOut 接口,然后打开音频设备,并设置音频参数,如采样率、位深度、声道数等。接下来,可以使用 waveOut 接口的函数来加载、播放、暂停、停止、调节音量等操作。 具体而言,在 C 语言中使用 waveOut 接口播放 wav 文件的流程如下: 1. 引入 waveOut 相关的头文件。 2. 定义 waveOut 相关的数据结构,如 WAVEFORMATEX 结构体来存储音频参数。 3. 初始化 waveOut 接口,通过 waveOutOpen 函数打开音频设备。 4. 加载并准备要播放wav 文件,通过 waveOutPrepareHeader 函数准备音频缓冲区。 5. 将准备好的音频缓冲区送到音频设备中,通过 waveOutWrite 函数进行播放。 6. 可以通过 waveOutPause、waveOutRestart、waveOutVolume 等函数来控制音频的暂停、继续播放、调节音量等操作。 7. 最后,通过 waveOutReset 函数停止播放并释放资源,关闭音频设备。 以上就是使用 waveOut wav c 在 C 语言中实现音频播放的基本流程。通过 waveOut 接口,可以方便地控制音频的播放和调节,实现了对音频的灵活控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值