我正在使用SoundPlayer
类在我的WPF应用程序中播放WAV文件。 这些文件很短,是应用程序的一部分,并响应程序中发生的事件而播放。 用户无法控制播放的声音,也无法自行决定播放声音。 我也尝试过使用 WPF MediaPlayer
控件,但该控件出现问题。 SoundPlayer
运行良好,但存在问题。
从本质上讲,我需要保留一个声音队列,并在它们排队时一个接一个地播放声音。 在某些情况下,我必须停止播放任何声音并播放另一种声音。 因此,我有两个要求,结果证明它们与SoundPlayer
控件相互排斥:
- 我的代码需要知道声音何时完成播放。
- 声音必须在后台播放
为了实现这一点,我创建了一个名为 SoundController
的类。 它有一个后台线程,它使用 Thread
的Dispatcher
将调用排队到我用来播放声音的方法,使用 BeginInvoke
。 该方法在调用SoundPlayer.PlaySync
播放声音之前引发SoundController
事件中的一个事件,然后在PlaySync
返回后引发另一个事件。
在我的 UI 线程中,在我需要停止声音的地方,我调用 SoundController
类的 Stop
方法。 这调用SoundPlayer
的Stop
方法来停止播放声音。 这就是问题发生的地方。 事实证明,SoundPlayer.Stop
不会停止声音,而是等待声音结束再返回。
这会阻止我的 GUI 死机。虽然我可以异步调用它,但我的 GUI 不会停止,但声音也不会停止。
正如我所说,WPF MediaPlayer对我们不起作用,无论如何,这是矫枉过正。 是否有任何其他播放声音的替代方法,使我能够引发事件、在后台线程中播放声音以及停止播放?
编辑06/26/2012:
这已经在这里一个多月了,我没有得到任何回应。 所以我想除了SoundPlayer
控制之外,没有其他选择。
我将发布一个新问题,从不同的角度提出问题。
没有回答,我已经超越了这一点。 因此,对于任何想知道的人来说,替代方案是MediaElement
WPF 控件和进行 WIN32 API 调用。
那么我使用了哪种替代方案? 也不。
事实证明,对我来说,我必须在SoundController
课上添加第二个线程。 第二个线程不运行Dispatcher
;相反,它运行我在循环中编写的代码:
- 它最初无限期地等待
AutoResetEvent
。 - 当
AutoResetEvent
被抬起时,它会启动一个 while 循环,检查是否要播放声音。 - 如果要播放声音,它会这样做。
- 它循环播放,如果在播放最后一个声音时"排队"另一个声音,它会播放该声音。
- 重复此操作,直到没有排队等待播放的声音。
还有一点,但没关系。 无论如何,这似乎工作正常。 由于声音在单独的Thread
上播放,因此可以通过调用SoundPlayer.Stop
方法从不同的Thread
中止当前播放的声音。
编辑:
这是我代码的精简版本。 我删除了一些与特殊声音有关的程序要求的特定内容。这显示了基本过程的工作原理。
private string NextSound { get; set; }
public AutoResetEvent PlayASoundFlag = new AutoResetEvent( false );
private Dictionary<string, SoundPlayer> Sounds { get; set; }
private object soundLocker = new object();
public string SoundPlaying { get; private set; }
public bool Stopping { get; set; }
public void PlaySound( string key, bool stopCurrentSound ) {
if ( !Sounds.ContainsKey( key ) )
throw new ArgumentException( string.Format( "Sound "{0}" does not exist", key ), "key" );
lock ( soundLocker ) {
NextSound = key;
if ( SoundPlaying != null && stopCurrentSound )
Sounds[ SoundPlaying ].Stop();
PlayASoundFlag.Set();
}
}
private void SoundController() {
do {
PlayASoundFlag.WaitOne();
while ( !Stopping && NextSound != null ) {
lock ( soundLocker ) {
SoundPlaying = NextSound;
NextSound = null;
}
Sounds[ SoundPlaying ].PlaySync();
lock ( soundLocker )
SoundPlaying = null;
}
} while ( !Stopping );
}
当程序启动时,该类会将键和声音文件加载到Sounds Dictionary
中。这样,用户所要做的就是将他们想要播放的声音的键传递给PlaySound
方法。
如果需要,可以将NextSound
属性替换为 Queue
。PlaySound
方法必须将要播放的声音的键排队,并且SoundController
线程必须将每个键与while
循环中的线程取消排队,当Queue
中没有剩余内容时退出。
最后,当程序停止时,您需要将Stopping
标志设置为 true 并设置 PlayASoundFlag,以便代码脱离循环。这将导致SoundController
线程停止。
我将初始化Dictionary
和启动线程留给您。