Как использовать 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 для воспроизведения файла. Немного неуклюже, но просто, и у вас не будет проблемы с утилизацией ресурсов.