winCE下使用一组buffer实现音频回环

来源:互联网 发布:红色经典知多少手抄报 编辑:程序博客网 时间:2024/06/11 04:24

 

 

最近有做项目,在WinCE下从一个音频设备获取数据,通过另一个音频设备输出。要求是尽量小的延迟,声音不要有停顿,也不能有丢失,在高的采样率的情况下,尽量少的占用cpu。东西很简单,但要做好也挺麻烦,特别是有了上面的要求。

先简单介绍一下思路:

使用wavein系列API,从一个音频驱动中读数据,然后使用waveout系列的API,把读到的数据写到另外一个音频驱动中。

 

如何实现比较好的性能。

1、Buffer尽量小,以减小延迟。为了保持声音连续和稳定,可以多开几个buffer

2、尽量不要搬动数据,当采样率高了以后,数据量其实是挺大的,做一次复制操作就会消耗一定的CPU。经过N次的实验,终于成功让两个驱动使用了一组buffer,避免了数据的复制。

3、读和写一定要使用两个线程,中间使用消息等方式沟通。避免声音的停顿和丢失。

 

几个重要的变量和空间:

初始化:

先分配buffer空间,打开输入输出设备。Preparehead。顺序如下。需注意的是写数据时,需要使用线程回调,这样就可以和读数据线程互不影响。

 

 

 

 

 

WIM_DATA消息的处理:

这里先讲一下读与写怎么同步。有两个变量,dwValidBlocksdwValidBlockStart。初始都为0。这两个变量表示了从dwValidBlockStart开始的dwValidBlocks块数据,是新的,有效地。

dwValidBlocks有两个线程同时维护,当读线程读一个block了以后,dwValidBlocks++,写线程写完一个blockdwValidBlocks――。

dwValidBlockStart平时由读线程维护,每读掉一个数据dwValidBlockStart++。但是如果写线程没有及时写掉数据,造成所有block都满了,则由读线程更新掉最远一次的数据,即dwValidBlockStart++。

 

这里需要注意的是WIM_DATA的响应中,没有必要使用waveInUnprepare,waveInPrepare。因为没有什么参数发生改变,直接扔回去就可以了。

 

 

 

写线程回调处理:

写线程做的事情就是不停的把buffer里面的数据写到输出的音频驱动中。这里需要提的是,写线程不是一有数据就开始写,而是等到有WAV_OUT_BUFFER_COUNTblock后,再开始写。一下子把这几个buffer一起扔下去,这样做的好处是保证一直有buffer在被驱动处理,而不会出现由于数据来不及扔下去造成声音停顿或当中有pop音出现。其实WAV_OUT_BUFFER_COUNT只要=2,应该就可以了。

对于MM_WOM_DONE的处理,需要waveOutUnprepareHeader,修改完那个pWaveOutHdr的结构以后,再扔回去即可。

 

 

 

小结

 

 

 

在写这个程序的过程中,花时间最多的有两个问题。一个是如何直接利用一组buffer,在应用层不搬动任何数据,实现音频回环。使用wav API的时候,很容易造成deadlock。另外一个,就是如何使声音不间断的播放。没有使用线程,经常会卡。写数据时缓存没有连续给到驱动,也会小卡,最后看了很多人的blog后,终于解决了问题。其实无需两个设备,只要一个codec,配置好waveinwaveout的设备ID,就可以用speaker听到mic的呻吟了。

程序小改一下就可以变译运行,但是还有问题,其实线程那部分还很挫,退不出来,而且还有很多其他小问题。没时间修改这个demo,也不能把现在用得代码贴出来,请谅解。

 

最后感谢不下雨(Norains)大虾,代码是在他的CSoundBase实现录音与播放上修改过来的。希望不要介意。

 

参考:

http://blog.csdn.net/norains/archive/2007/01/10/1479648.aspx CSoundBase实现录音与播放

http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=4422&lngWId=3  Playing Audio in Windows using waveOut Interface

 

原创粉丝点击