2012-05-09 1 views
2

Я использую класс SoundPlayer для воспроизведения WAV-файлов в моем приложении WPF. Файлы короткие и являются частью приложения и воспроизводятся в ответ на события, которые происходят в программе. Пользователь не контролирует, какой звук воспроизводится, и не может решить самостоятельно воспроизвести звук. Я также пытался использовать элемент управления WPF MediaPlayer, но у меня были проблемы с этим элементом управления. SoundPlayer отлично работает, но есть проблема.Альтернативы SoundPlayer

По существу, мне нужно держать очередь звуков и воспроизводить звуки один за другим, когда они стоят в очереди. В некоторых случаях я должен остановить звук, который звучит, и вместо этого воспроизвести другой звук. Таким образом, у меня есть два требования, которые оказываются взаимоисключающими с контролем SoundPlayer:

  1. Мой код должен знать, когда звук закончил играть.
  2. звуки должны играть в фоновом режиме

Чтобы реализовать это, я создал класс под названием SoundController. Это имеет фоновый поток, и он использует ThreadDispatcher для вызова вызовов метода, который я использую для воспроизведения звуков, используя BeginInvoke. Метод вызывает событие в событии SoundController, прежде чем он называет SoundPlayer.PlaySync для воспроизведения звука, а затем он вызывает другое событие после возврата PlaySync.

В моей теме пользовательского интерфейса, в точке, где мне нужно остановить звук, я вызываю метод SoundController класса Stop. Это вызывает метод SoundPlayerStop, чтобы остановить воспроизведение звука. И вот здесь проблема. Оказывается, что SoundPlayer.Stop НЕ останавливает звук, но он ждет, пока звук закончится, прежде чем вернуться.

Это останавливает мой графический интерфейс. Хотя я мог бы назвать это асинхронно, мой графический интерфейс не остановится, но звук также не остановится.

Как я уже сказал, WPF MediaPlayer не работал для нас, и это было слишком сложно. Есть ли другая альтернатива для воспроизведения звуков, которая даст мне возможность поднимать события, воспроизводить звук в фоновом потоке и останавливать воспроизведение?

Редактировать 06/26/2012:

Это было здесь в течение месяца, и я не получили никаких ответов. Поэтому я думаю, что нет никаких других альтернатив для управления SoundPlayer.

Я собираюсь опубликовать новый вопрос, относящийся к проблеме под другим углом.

ответ

1

На этот вопрос не было ответов, и я перешел за рамки этого. Таким образом, для тех, кто задается вопросом, альтернативами является управление WPF MediaElement и вызов WIN32 API.

Какой вариант я использовал? Ни.

Оказалось, что для меня мне пришлось добавить вторую нить в мой класс SoundController. Второй поток не запускает Dispatcher; вместо этого он запускает код, который я написал в цикле:

  1. Он изначально ждет неопределенно на AutoResetEvent.
  2. Когда поднят AutoResetEvent, он начинает цикл while, который проверяет, должен ли воспроизводиться звук.
  3. Если звук воспроизводится, он делает это.
  4. Это петли вокруг, и если другой звук был «поставлен в очередь» во время воспроизведения последнего звука, он воспроизводит этот звук.
  5. Это повторяется до тех пор, пока нет звуков, стоящих в очереди для воспроизведения.

Есть немного больше, но это не имеет значения. В любом случае, похоже, это работает нормально. Поскольку звуки воспроизводятся на отдельном Thread, текущий воспроизводимый звук может быть прерван с другого Thread, вызвав метод SoundPlayer.Stop.

EDIT:

Вот усеченную версию моего кода. Я удалил некоторые вещи, которые были особенно важны для требований моей программы, которые связаны со специальным звуком. Это показывает, как работает основной процесс.

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 и начинаю тему для вас.

+0

Я просто столкнулся с этой точной проблемой с приложением winforms, а не с WPF, но я сомневаюсь, что в этом дело. Я немного не понимаю, почему/как добавление второго рабочего потока может иметь значение здесь (или .STOP() 'работает поперечный поток, или это не правда? Почему *, который * thread это имеет значение?) Я собираюсь вернуться к этому завтра или в понедельник, чтобы попытаться обернуть вокруг себя голову, но если у вас есть время/мотивация, чтобы подробно остановиться на этом и/или положить его в скелетный код, я был бы очень благодарен :-) – DaveRandom

+0

Проблема в том, что поток, который вызывает метод 'Play', не может прервать себя, поскольку он занят воспроизведением звука. Он не может выполнить вызов «Стоп» до тех пор, пока он не начнет воспроизводить звук, и к тому времени уже слишком поздно. Единственный способ остановить звук, который находится в середине игры, - это вызвать «Stop» из другого потока. Вот почему я добавил отдельный поток, посвященный тому, чтобы ничего не делать, кроме воспроизведения звуков. Я отредактирую свой ответ и добавлю код скелета. –

+0

Большое спасибо за это обновление, я собираюсь поиграть с ним по утрам – DaveRandom