2012-05-09 49 views
2

我正在使用SoundPlayer类在我的WPF应用程序中播放WAV文件。这些文件很短并且是应用程序的一部分,并且响应程序中发生的事件而播放。用户无法控制播放什么声音,也不能决定自己播放声音。我也尝试使用WPF MediaPlayer控件,但是我遇到了该控件的问题。 SoundPlayer工作得很好,但有一个问题。SoundPlayer的替代品

本质上,我需要保持一个声音的队列,并在他们排队时一个接一个地播放声音。在某些情况下,我必须停止正在播放的声音,然后播放另一个声音。所以我有两个要求,这与SoundPlayer控件相互排斥:

  1. 我的代码需要知道声音何时播放完毕。
  2. 的声音在后台

为了实现这个玩,我创建了一个名为SoundController类。这有一个后台线程,它使用ThreadDispatcher将呼叫排队到我用来播放声音的方法,使用BeginInvoke。该方法在SoundController事件中引发一个事件,然后它将SoundPlayer.PlaySync播放声音,然后在PlaySync返回后引发另一个事件。

在我的UI线程中,在需要停止声音的位置,我调用SoundController类的Stop方法。这叫SoundPlayerStop方法停止播放声音。这就是问题发生的地方。事实证明,SoundPlayer.Stop不会停止声音,但它会在返回之前等待声音完成。

这会阻止我的GUI死机。虽然我可以异步调用它,但我的GUI不会停止,但声音也不会停止。

正如我所说,WPF MediaPlayer没有为我们工作,无论如何它是矫枉过正。有没有其他的替代方法可以播放声音,让我能够引发事件,在后台线程中播放声音,并停止播放?

编辑06/26/2012:

这一直在这里进行了一个多月,我已经变得没有任何反应。所以我猜想SoundPlayer控制没有其他替代方案。

我打算从另一个角度发布一个新问题。

回答

1

没有回应这个问题,我已经超越了这个。因此,对于任何想知道的人来说,替代方案都是WPF控件和WPF控件。

那我使用哪种方法?都不是。

原来,对我来说,我不得不添加第二个线程到我的SoundController类。第二个线程不运行Dispatcher;相反,它运行的代码,我在一个循环中写道:

  1. 它最初无限期等待上AutoResetEvent
  2. AutoResetEvent升高时,它开始一个while循环来检查,看是否有声音播放。
  3. 如果要播放声音,则会播放声音。
  4. 它循环播放,如果另一个声音在最后一个声音播放时“排队”,它会播放该声音。
  5. 重复此操作,直到没有声音排队等待播放。

还有更多一点点,但也没关系。无论如何,这似乎工作正常。由于声音在单独的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方法。

如果您想要,您可以用Queue替换NextSound属性。该PlaySound方法将不得不排队声音的关键播放和SoundController线程将不得不离队从线程每个键在while循环,退出时没有什么留在Queue

最后,当您的程序停止时,您需要将Stopping标志设置为true &设置PlayASoundFlag,以便代码从循环中出来。这将导致SoundController线程停止。

我离开初始化Dictionary和启动线程你。

+0

我正好碰上了WinForms应用程序,而不是WPF这种精确的问题,但我怀疑在这种情况下的事项。我有点不清楚为什么/如何添加第二个工作线程会在这里有所作为(或者'.stop()'可以跨线程工作,或者它不确定?为什么*哪个*线程是重要的?)我将在明天或周一回来试图围绕它回顾一下,但如果你有时间/动机来详细说明这个问题并且/或者把它放到骨架代码中,我会非常感激:-) – DaveRandom

+0

问题是调用'Play'方法的线程不能自行中断,因为它正忙于播放声音。在完成播放声音之前,它不可能执行到“停止”的呼叫,此时已经太晚了。阻止正在播放中的声音的唯一方法是从另一个线程调用“Stop”。这就是为什么我添加了一个单独的线程,致力于除了播放声音外什么也不做。我将编辑我的答案并添加一些骨架代码。 –

+0

非常感谢本次更新,我要去有一个玩它在早晨 – DaveRandom