Воспроизведение WAV-аудио
87WPF --- Элементы управления WPF --- Воспроизведение WAV-аудио
Платформа .NET Framework имеет небогатую историю поддержки звука. Версии 1.0 и 1.1 не предлагали никакого управляемого способа воспроизведения аудио, а когда долгожданная поддержка, наконец, появилась в .NET 2.0, она была представлена в форме не приводящего в восторг класса SoundPlayer (который можно найти в "малонаселенном" пространстве имен System.Media). Класс SoundPlayer довольно ограничен: он может воспроизводить только файлы в формате WAV, не поддерживает воспроизведения одновременно более одного звука и совсем не предоставляет возможностей управления никакими аспектами воспроизведения аудио (например, громкостью и балансом).
Чтобы получить эти возможности, разработчики, использующие Windows Forms, вынуждены были работать с библиотекой неуправляемого кода quartz.dll. Библиотека quartz.dll — ключевая часть DirectX, и она присутствует в проигрывателе Windows Media и операционной системе Windows. (Тот же компонент известен под названием DirectShow, а предыдущие версии назывались ActiveMovie.)
Класс SoundPlayer поддерживается в приложениях WPF. Если смириться с его существенными ограничениями, то можно сказать, что он предлагает наиболее простой и легкий способ добавления работы с аудио в приложения. Класс SoundPlayer также упаковывается в класс SoundPlayerAction. который позволяет воспроизводить звук через декларативный триггер (вместо написания нескольких строк кода C# в обработчике событий). В следующих разделах будет представлен краткий обзор обоих классов, а затем уже описания более мощных WPF-классов MediaPlayer и MediaElement.
Класс SoundPlayer
Чтобы воспроизвести звук с помощью класса SoundPlayer, понадобится выполнить перечисленные ниже шаги:
Создать экземпляр SoundPlayer.
Указать звуковое содержимое, установив либо свойство Stream, либо свойство SoundLocation. Если есть объект Stream, содержащий звук в формате WAV, используйте свойство Stream. Если же есть путь к файлу или URL, указывающий на файл WAV, применяйте свойство SoundLocation.
Если аудио-содержимое хранится в виде двоичного ресурса и встроено в приложение, то потребуется доступ к нему в виде потока и использование свойства SoundPlayer.Stream. Причина в том, что SoundPlayer не поддерживает синтаксис упакованных URL в WPF.
Установив свойство Stream или SoundLocation, можно заставить SoundPlayer в действительности загрузить аудиоданные, вызвав метод Load() или LoadAsync(). Метод Load() наиболее прост — он останавливает выполнение кода до тех пор, пока весь звуковой фрагмент не будет загружен в память. LoadAsync() выполняет свою работу в другом потоке и по завершении инициирует событие LoadCompleted.
Формально использовать Load() или LoadAsync() не обязательно. Экземпляр SoundPlayer загружает аудиоданные по мере необходимости, когда вызывается метод Play() или PlaySync(). Однако явно загрузить аудио-фрагмент — хорошая идея; это не только позволит снизить накладные расходы при многократном воспроизведении, но также упростит обработку исключений, связанных с файловыми проблемами, отдельно от исключений, вызванных причинами, относящимися к процессу воспроизведения.
После этого можно вызвать PlaySync(), который приостановит код на время воспроизведения аудио-фрагмента, или же применить Play() для воспроизведения в другом потоке, обеспечивая интерфейсу приложения способность реагировать на действия пользователя. Единственный другой доступный вариант — это метод PlayLooping(), воспроизводящий аудио-фрагмент асинхронно в бесконечном цикле (что идеально для саундтреков). Чтобы остановить текущее воспроизведение в любой момент, необходимо вызвать метод Stop().
В следующем фрагменте кода демонстрируется простейший подход к загрузке и асинхронному воспроизведению аудиофайла:
SoundPlayer sp = new SoundPlayer();
sp.SoundLocation = "tada.wav";
sp.Load();
sp.PlayLooping()
До сих пор в коде предполагалось, что аудиофайл присутствует в том же каталоге, что и скомпилированное приложение. Однако загружать SoundPlayer-аудио из файла не обязательно. Для коротких звуков, которые воспроизводятся в нескольких местах приложения, возможно, разумнее встроить звуковые файлы непосредственно в скомпилированную сборку в виде двоичных ресурсов (не путайте их с декларативными ресурсами, определяемыми в коде разметки XAML). Эта техника работает со звуковыми файлами так же хорошо, как и с графическими изображениями.
Например, если добавить файл ding.wav как ресурс по имени Ding (просто перейдите к узлу Properties --> Resources (Свойства --> ресурсы) в окне Solution Explorer и воспользуйтесь поддержкой визуального конструктора), то можно будет применить следующий код для его воспроизведения:
SoundPlayer player = new SoundPlayer();
player.Stream = Properties.Resources.Ding;
player.Play();
Класс SoundPlayer не слишком хорошо работает с большими аудиофайлами, поскольку он должен загрузить в память весь файл целиком. Может показаться, что данную проблему можно разрешить, разбив большой аудиофайл на куски, однако класс SoundPlayer не предназначен для этого. Не существует простого способа такой синхронизации SoundPlayer, чтобы он мог воспроизвести множество аудиофрагментов друг за другом, поскольку он не обеспечивает никаких средств для организации очередей. Всякий раз, когда вызывается метод PlaySound() или Play(), текущее воспроизведение останавливается. Обходные пути возможны, но намного лучше вместо этого воспользоваться классом MediaElement.
Класс SoundPlayerAction
Класс SoundPlayerAction позволяет более удобно использовать класс SoundPlayer. Класс SoundPlayerAction унаследован от TriggerAction, который позволяет использовать его в ответ на любое событие.
Ниже приведена разметка кнопки, применяющей SoundPlayerAction для подключения события Click к звуку. Триггер организован так, что его можно применить к множеству кнопок (если перенести его в коллекцию Resources):
<Button Name="soundplayer" Content="Go">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<SoundPlayerAction Source="tada.wav"></SoundPlayerAction>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
При использовании SoundPlayerAction звук всегда воспроизводится асинхронно.
Системные звуки
Одной из особенностей операционной системы Windows является ее способность отображать аудиофайлы на определенные системные события. Наряду с SoundPlayer в WPF также предоставлен класс SystemSounds, позволяющий получить доступ к наиболее часто используемым из этих звуков и задействовать их в собственных приложениях. Эта техника работает лучше, если все, что требуется — это простые короткие звуки, предназначенные для того, чтобы уведомить о завершении какой-то длительной операции или подать сигнал предупреждения.
К сожалению, класс SystemSounds основан на функции MessageBeep из API-интерфейса Win32, в результате чего он обеспечивает доступ только к следующим общим системным звукам:
Asterisk (Вопросительный знак)
Веер (Уведомление о получении почты)
Exclamation (Восклицание)
Hand (Критическая ошибка)
Question (Вопрос)
Класс SystemSounds предоставляет свойство для каждого из этих звуков, возвращающее объект SystemSound, который можно использовать для воспроизведения звука с помощью метода Play(). Например, для воспроизведения звука Веер в коде служит следующая строка:
SystemSounds.Веер.Play();
Чтобы указать системе, какие файлы WAV следует применять для каждого системного звука, войдите в панель управления и выберите значок Звуки и аудиоустройства (в Windows ХР) либо Звук (в Windows Vista или Windows 7).