Как использовать System.Media.SoundPlayer для асинхронного воспроизведения звукового файла?

Здесь обманчиво простой вопрос:

Каков правильный способ асинхронного воспроизведения встроенного файла ресурсов .wav в Windows Forms?

Попытка # 1:

var player = new SoundPlayer();
player.Stream = Resources.ResourceManager.GetStream("mySound");
player.Play(); // Note that Play is asynchronous
  • Хорошо: не блокирует поток пользовательского интерфейса.
  • Плохо: SoundPlayer и встроенный поток ресурсов не сразу расположены.

Попытка № 2:

using (var audioMemory = Resources.ResourceManager.GetStream("mySound"))
{
    using (var player = new SoundPlayer(audioMemory))
    {
        player.Play();
    }
}
  • Хорошо: поток пользовательского интерфейса не заблокирован, звук SoundPlayer и поток звуковой памяти немедленно удаляются.
  • Плохо: состояние гонки! Play() - это асинхронный режим, и если звуковая память удаляется до того, как воспроизведение будет выполнено... бум! Исключено исключение времени выполнения.

Попытка № 3:

using (var audioMemory = Resources.ResourceManager.GetStream("mySound"))
{
    using (var player = new SoundPlayer(audioMemory))
    {
        player.PlaySync();
    }
}
  • Хорошо: проигрыватель и аудиопоток немедленно удаляются.
  • Плохо: PlaySync блокирует поток пользовательского интерфейса.

Попытка # 4:

ThreadPool.QueueUserWorkItem(ignoredState =>
  {
    using (var audioMemory = Resources.ResourceManager.GetStream("mySound"))
    {
        using (var player = new SoundPlayer(audioMemory))
        {
            player.PlaySync();
        }
    }
  });
  • Хорошо: пользовательский интерфейс не замерзает, проигрыватель и поток памяти немедленно удаляются.
  • Плохо: потому что это часто срабатывает, у нас могут закончиться потоки пула потоков! См. Larry Osterman что не так с этой частью кода 26.

Кажется, что SoundPlayer должен иметь событие PlayAsyncCompleted. К сожалению, такого события нет. Я что-то упускаю? Каков правильный способ асинхронного воспроизведения встроенного ресурса .wav в Windows Forms?

Ответ 1

У меня недостаточно репутации для комментариев, поэтому я просто отвечу.

Если ваши требования к воспроизведению звука "обманчиво просты" (вы просто хотите воспроизвести случайный звук, когда один пользователь winform что-то делает), я бы использовал попытку № 4 выше.

Ларри Остерман "что не так с этой частью кода 26" имеет свою "систему", которая отбрасывает новый поток нити (чтобы воспроизводить звук) с каждым нажатием клавиши. Он указывает на то, что он ударил по нему, насытив размер пула потоков по умолчанию 500 за 15 секунд после ввода, но это было также с приложением клиент/сервер с использованием async RPC, которые также использовали threadpool. На самом деле это не "обманчиво простое" приложение.

Если вы пытаетесь поочередно помещать звуковые байты каждую секунду (или быстрее) в течение 10 или 100 секунд в секунду, то это действительно не "простое приложение" и подсистема подкачки/приоритета в очереди, вероятно, будет в порядке.

Ответ 2

Я все еще использую функции good ol 'waveOut____ от API win32. Здесь хороший пример кода:

http://www.codeproject.com/KB/audio-video/cswavplay.aspx

Изменить: гораздо проще решить вашу проблему - извлечь встроенный ресурс, сохранить его как реальный файл где-нибудь, а затем использовать SoundPlayer для воспроизведения файла. Немного неуклюже, но просто, и у вас не будет проблемы с утилизацией ресурсов.