WaveOut播放音乐

上个月用WaveOut写了一个播放音乐的SoundRender,当时是参考一个mp3播放器的源代码,这里对WaveOut做一下小节。

播放文件其实就是多个缓冲循环使用,一个缓冲播放完了,会产生一个消息,它马上跳到下一个缓冲,然后马上把新的数据给播放过的缓冲,这样就可以连续不断的播放。

 

wave的播放方法很多,可以用dsound,dshow,甚至是sndPlay,sendMciString等等方法都可以实现。但是这都是对于 windows平台下的音频播放,如果换到windows mobile平台,就未必支持以上几种方式,这时就必须用到低级的wave系列的API了。

这里有一个很重要的数据类型WAVEHDR,我们的声卡就是根据这个结构体来播放音乐的,首先这个结构体中要指明播放的数据,(LPTSTR)_pWaveData + 44,因为考虑到文件头,然后这段数据的长度,格式,dwBytesRecorded

When the header is used in input, this member specifies how much data is in the buffer. When the header is used in output, this member specifies the number of bytes played from the buffer.

在输出的时候表示已经播放了多少。

这里我们的缓冲区的大小是PLAY_BUFFE_SIZE,从0开始播放,然后就是第二个缓冲的大小,这里的缓冲区地址有两种方法。

首先,一次性的把文件读入内存,每次只要修改缓冲区的其实地址就可以更新数据,这样避免了频繁的文件操作,但是损失内存;其次,指定固定大小和固定地址的缓冲区,每次读取文件的对应部分到这里就可以了,这样比较节省内存,但是有频繁的文件读取操作。

 

步骤1:
申请二个或二个以上的WAVEHDR结构,m_WaveHdr1,m_WaveHdr2
填充WAVEHDR结构, 如下:
m_WaveHdr1.lpData           = (LPTSTR)_pWaveData + 44;
m_WaveHdr1.dwBufferLength  = PLAY_BUFFE_SIZE;
m_WaveHdr1.dwBytesRecorded = 0;
m_WaveHdr1.dwUser          = 0;
m_WaveHdr1.dwFlags         = WHDR_BEGINLOOP | WHDR_ENDLOOP;
m_WaveHdr1.dwLoops         = 1;
m_WaveHdr1.lpNext          = NULL;
m_WaveHdr1.reserved        = 0;
// 第二个的数据起止位置一定要大于第一个数据的播放缓冲大小,否则可能会声音不正常。
m_WaveHdr2.lpData          = (LPTSTR)_pWaveData + 44 + PLAY_BUFFE_SIZE;
m_WaveHdr2.dwBufferLength  = PLAY_BUFFE_SIZE;
m_WaveHdr2.dwBytesRecorded = 0;
m_WaveHdr2.dwUser          = 0;
m_WaveHdr2.dwFlags         = WHDR_BEGINLOOP | WHDR_ENDLOOP;
m_WaveHdr2.dwLoops         = 1;
m_WaveHdr2.lpNext          = NULL;
m_WaveHdr2.reserved        = 0;

 

然后在回调函数里面做下面的操作

在回调函数、回调线程或回调窗口的 WOM_DONE 响应里,作以下操作,
LPWAVEHDR pWaveHeader  = (LPWAVEHDR)dwParam1;   // 系统自动识别是哪一个WAVEHDR播放完毕,首先获得已经播放完的数据快的WAVEHDR,

waveOutUnprepareHeader(hwo, pWaveHeader, sizeof(WAVEHDR)); // 清空设备缓存

然后就是,把弹夹下下来,准备往里面塞子弹。

 

下面就是上子弹的过程:

pWaveHeader->lpData     += (5 * PLAY_BUFFE_SIZE);
   pWaveHeader->dwBufferLength = pThis->m_lLeaveSize < PLAY_BUFFE_SIZE ? pThis->m_lLeaveSize : PLAY_BUFFE_SIZE;   // 得到正确的缓冲大小.
   waveOutPrepareHeader(hwo, pWaveHeader, sizeof(WAVEHDR));
   waveOutWrite(hwo, pWaveHeader, sizeof(WAVEHDR));

 

这样几个缓冲区不断的循环播放文件。

 

 

一段用 waveOutWrite 输出音频的代码

大致流程:
1. 首先用 waveOutOpen 获得 Windows 混音器的句柄。
2. 再初始化一个 Wave Header, 并用 waveOutPrepareHeader 将它“准备好”。
3. 接着用 waveOutWrite 输出这个 Wave Header。
4. 等待播放完成后用 waveOutUnprepareHeader 将 Wave Header “反准备”。
5. 最后调用 waveOutClose 来关闭最开始获得的句柄。
红色的是 API 的名称。

注意事项:
1. 每一步都要判断错误, 使用 waveOutGetErrorTextW 这个 API 可以获得错误代码对应的文本。为了方便, 我写了如下函数直接返回 vb 所支持的 String 类型:

 


2. waveOutPrepareHeader 和 waveOutUnprepareHeader 的意思有点像“锁定”。就是只要 Prepare 了, 你就不能改动了, 特别地, 你不能释放已锁定 Wave Header 的内存, 否则会引起程序崩溃。内存指 Wave Header 的 lpData 指向的一块内存区域, 这块内存区域在播放完成之前不能释放。我们在这个程序里简单地通过 waveOutUnprepareHeader 的返回值来判断是否播放完成。在较为复杂的程序中, 我们可以通过回调函数来判断何时播放完毕。回调函数可以在 waveOutOpen 的 dwCallback 参数中设置。

本程序简单地播放了如下 buffer:


效果是“嘟”一秒钟。

3. 句柄用完了记得关掉

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值