Bob*_*nny 22 .net c# directx audio directsound
我有一个C#项目,为RSS提要播放摩尔斯电码.我使用Managed DirectX编写它,却发现Managed DirectX已经过时并且已弃用.我的任务是播放纯正的正弦波突发,其间穿插着静音期(代码),它们的持续时间精确定时.我需要能够调用一个播放纯音多少毫秒的函数,然后Thread.Sleep()然后播放另一个等等.最快,音调和空格可以短至40ms.
它在Managed DirectX中运行良好.为了获得精确定时的音调,我创造了1秒.将正弦波放入辅助缓冲器,然后播放一定持续时间的音调,我在缓冲结束的x毫秒内向前寻找然后播放.
我试过System.Media.SoundPlayer.这是一个失败者[编辑 - 请参阅下面的答案]因为你必须播放(),睡眠(),然后停止()任意音长.结果是音调太长,因CPU负载而异.实际停止音调需要不确定的时间.
然后,我踏上了漫长的尝试使用n音讯1.3.我最终得到了一个提供音调数据的内存驻留流,并再次向前搜索,留下流中剩余的所需音调长度,然后播放.这在DirectSoundOut类上工作了一段时间(见下文),但WaveOut类很快就死了,内部断言说尽管PlayerStopped = true,缓冲区仍在队列中.这是奇怪的,因为我玩到最后然后在音调结束和下一个开始之间等待相同的持续时间.您认为在开始播放40毫秒音调后80毫秒,队列中没有缓冲区.
DirectSoundOut运行良好一段时间,但它的问题是,对于每个音调突发Play()它旋转一个单独的线程.最终(5分钟左右)它就停止工作了.在VS2008 IDE中运行项目时,可以在"输出"窗口中退出线程后查看线程之后的线程.我不会在播放期间创建新对象,我只是Seek()音色流然后反复调用Play(),所以我认为这不是孤立缓冲区的问题/无论是什么堆积直到它被阻塞.
我对这一个没有耐心,所以我希望这里有人遇到类似的要求,并且可以引导我朝着一个可能的解决方案的方向前进.
我不敢相信......我回到System.Media.SoundPlayer并让它做我想要的...没有巨大的依赖库,95%未使用的代码和/或等待被发现的怪癖:-) .此外,它在Mono(2.6)下的MacOSX上运行! [错 - 没有声音,会问单独的问题]
我使用MemoryStream和BinaryWriter来伪装WAV文件,完成RIFF标题和分块.不需要"事实"块,这是44100Hz的16位采样.所以现在我有一个带有1000ms样本的MemoryStream,并由BinaryReader包装.
在RIFF文件中有两个4字节/ 32位长度,"整体"长度是流中的4个字节(在ASCII中的"RIFF"之后),以及在采样数据字节之前的"数据"长度.我的策略是在流中寻找并使用BinaryWriter来改变两个长度,以欺骗SoundPlayer认为音频流只是我想要的长度/持续时间,然后Play()它.下一次,持续时间是不同的,所以再次使用BinaryWriter覆盖MemoryStream中的长度,Flush()它再次调用Play().
当我尝试这个时,即使我设置了Stream属性,我也无法让SoundPlayer看到对流的更改.我被迫创建一个新的SoundPlayer ...每40毫秒??? 没有.
好吧,我今天想回到那个代码并开始关注SoundPlayer成员.我看到了"SoundLocation"并阅读了它.在那里它说,设置SoundLocation的副作用是使Stream属性为空,反之亦然为Stream.所以我添加了一行代码来将SOundLocation属性设置为虚假的"x",然后将Stream属性设置为my(刚修改的)MemoryStream.该死的,如果它没有选择那个,并且只要我要求的话就准确地发出一个音调.似乎没有任何疯狂的副作用,如死后时间或增加记忆,或??? 这需要1-2毫秒来调整WAV流,然后加载/启动播放器,但它非常小,而且价格合适!
我还实现了一个频率属性,它重新生成样本,并使用Seek/BinaryWriter技巧覆盖RIFF/WAV MemoryStream中的旧数据,但样本数相同,但频率不同,并且同样对于振幅属性.
这个项目在SourceForge上.您可以从SVN浏览器的此页面获取SPTones.CS中此hack的C#代码.感谢所有提供此信息的人,包括@arke,他的想法与我的关系很接近.我很感激.