2

我正在使用SoundPlayer该类在我的 WPF 应用程序中播放 WAV 文件。这些文件很短,是应用程序的一部分,并根据程序中发生的事件进行播放。用户无法控制播放什么声音,也无法自行决定播放声音。我也尝试过使用 WPFMediaPlayer控件,但我遇到了该控件的问题。效果很好,SoundPlayer但是有问题。

本质上,我需要保留一系列声音,并在它们排队时一个接一个地播放声音。在某些情况下,我必须停止播放任何声音并播放另一种声音。SoundPlayer所以我有两个要求与控件互斥:

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

为了实现这一点,我创建了一个名为SoundController. 这有一个后台线程,它使用Thread's Dispatcher对我用来播放声音的方法的调用进行排队,使用BeginInvoke. 该方法在SoundController调用播放声音之前在事件中引发一个事件SoundPlayer.PlaySync,然后在PlaySync返回后引发另一个事件。

在我的 UI 线程中,在我需要停止声音的地方,我调用了SoundController类的Stop方法。这会调用SoundPlayer'Stop方法来停止播放声音。这就是问题发生的地方。事实证明,SoundPlayer.Stop它不会停止声音,而是等待声音结束后再返回。

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

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

2012 年 6 月 26 日编辑:

这个问题已经存在一个多月了,我还没有收到任何回复。所以我想没有其他替代SoundPlayer控制。

我将从不同的角度发布一个新问题。

4

1 回答 1

2

没有人回答这个问题,我已经超越了这个。因此,对于任何想知道的人来说,替代方案是MediaElementWPF 控件和进行 WIN32 API 调用。

那么我使用了哪种替代方法?两者都不。

事实证明,对我来说,我不得不在我的SoundController班级中添加第二个线程。第二个线程不运行Dispatcher; 相反,它运行我在循环中编写的代码:

  1. 它最初无限期地等待AutoResetEvent.
  2. AutoResetEvent引发时,它会启动一个 while 循环,检查是否要播放声音。
  3. 如果要播放声音,它会这样做。
  4. 它循环播放,如果在播放最后一个声音时“排队”了另一个声音,它就会播放那个声音。
  5. 重复此过程,直到没有声音排队播放。

还有一点点,但没关系。无论如何,这似乎工作正常。因为声音是在一个单独的 上播放的Thread,所以当前播放的声音可以Thread通过调用该SoundPlayer.Stop方法从一个不同的地方中止。

编辑:

这是我的代码的精简版本。我删除了一些特定于我的程序要求的东西,这些东西与特殊的声音有关。这显示了基本过程是如何工作的。

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和启动线程留给你。

于 2012-07-20T13:55:27.920 回答