Воспроизведение аудио с помощью триггеров

81

До сих пор переход от класса MediaPlayer к MediaElement не дал никаких преимуществ (помимо поддержки видео). Однако при использовании MediaElement также появляется возможность управлять аудио декларативно, через XAML-разметку, а не код. Это делается с помощью триггеров и раскадровок. Единственный новый ингредиент — MediaTimeline, который управляет временной шкалой аудио- или видеофайла и работает совместно с MediaElement для координации воспроизведения. Класс MediaTimeline унаследован от Timeline и добавляет свойство Source, идентифицирующее аудиофайл, который предназначен для воспроизведения.

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

<StackPanel Margin="5">
                <StackPanel.Resources>
                    <Style TargetType="Button">
                        <Setter Property="Margin" Value="5,5,0,5"></Setter>
                        <Setter Property="Padding" Value="5"></Setter>
                    </Style>
                </StackPanel.Resources>
                <MediaElement UnloadedBehavior="Close" LoadedBehavior="Manual" Name="my_media"></MediaElement>
                <StackPanel Orientation="Horizontal">
                    <StackPanel.Triggers>
                        <EventTrigger RoutedEvent="Button.Click" SourceName="cmd_play">
                            <EventTrigger.Actions>
                                <BeginStoryboard Name="MediaStoryboard">
                                    <Storyboard>
                                        <MediaTimeline Storyboard.TargetName="my_media"
                                                       Source="music1.mp3"></MediaTimeline>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger.Actions>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="Button.Click" SourceName="cmd_stop">
                            <EventTrigger.Actions>
                                <StopStoryboard BeginStoryboardName="MediaStoryboard"></StopStoryboard>
                            </EventTrigger.Actions>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="Button.Click" SourceName="cmd_pause">
                            <EventTrigger.Actions>
                                <PauseStoryboard BeginStoryboardName="MediaStoryboard"></PauseStoryboard>
                            </EventTrigger.Actions>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="Button.Click" SourceName="cmd_resume">
                            <EventTrigger.Actions>
                                <ResumeStoryboard BeginStoryboardName="MediaStoryboard"></ResumeStoryboard>
                            </EventTrigger.Actions>
                        </EventTrigger>
                    </StackPanel.Triggers>
                    <Button Name="cmd_play">Play</Button>
                    <Button Name="cmd_stop">Stop</Button>
                    <Button Name="cmd_pause">Pause</Button>
                    <Button Name="cmd_resume">Resume</Button>
                </StackPanel>
                
            </StackPanel>

Поскольку этот пример воспроизводит аудио, позиция MediaElement несущественна. В данном примере он размещается внутри Grid, перед Button. (Порядок не важен, поскольку во время выполнения программы MediaElement не будет иметь никакого визуального представления.) Когда осуществляется щелчок на кнопке, создается Storyboard с MediaTimeline. Обратите внимание, что источник в свойстве MediaElement.Source не указывается. Вместо этого источник передается через свойство MediaTimeline.Source.

Когда вы используете MediaElement в качестве цели для MediaTimeline, уже не имеет значения, установлены ли свойства LoadedBehavior и UnloadedBehavior. После применения MediaTimeline аудио или видео будет управляться таймером анимации WPF (конкретно — экземпляром класса MediaClock, который представлен в свойстве MediaElement.Clock).

Для управления воспроизведением в MediaElement можно использовать единственный экземпляр Storyboard — другими словами, можно не только останавливать, но также временно приостанавливать и возобновлять воспроизведение:

Окно управления воспроизведением

Единственная сложность состоит в том, что нужно не забыть определить все триггеры для управления раскадровкой в одной коллекции. Затем их можно присоединить к соответствующим элементам управления, используя свойство EventTrigger.SourceName. В данном примере все триггеры объявлены внутри панели StасkPanel, содержащей кнопки.

Воспроизведение множества звуков

Хотя в предыдущем примере демонстрировалось воспроизведение единственного медиафайла, нет никаких причин, которые помешали бы расширить его, добавив возможность одновременного воспроизведения нескольких аудиофайлов. В показанном ниже примере добавлены две кнопки, каждая из которых запускает воспроизведение собственного звука. Когда выполняется щелчок на кнопке, создается новый объект Storyboard, с новой MediaTimeline, которая используется для воспроизведения отдельного аудиофайла через один и тот же MediaElement:

<EventTrigger RoutedEvent="Button.Click" SourceName="cmd_newSound">
             <EventTrigger.Actions>
                      <BeginStoryboard>
                                <Storyboard>
                                        <MediaTimeline Storyboard.TargetName="my_media"
                                                       Source="music1.mp3"></MediaTimeline>
                                </Storyboard>
                      </BeginStoryboard>
              </EventTrigger.Actions>
      </EventTrigger>
...
   <Button Name="cmd_newSound">Add Sound</Button>

В этом примере, если быстро щелкнуть на обеих кнопках подряд, обнаружится, что второй звук прервет воспроизведение первого. Это следствие применения одного и того же MediaElement для обеих временных шкал. Более гибкий (но и более ресурсоемкий) подход предусматривает использование отдельного MediaElement для каждой кнопки и установке каждой MediaTimeline на соответствующий MediaElement. (В этом случае можно задать свойство Source непосредственно в дескрипторе MediaElement, поскольку оно не изменяется.) Если теперь быстро щелкнуть подряд на двух кнопках, оба звука будут воспроизводиться одновременно.

То же самое касается класса MediaPlayer. Для воспроизведения нескольких аудиофайлов понадобится несколько объектов MediaPlayer. Если решено использовать в коде MediaPlayer или MediaElement, то появляется шанс провести более разумную оптимизацию, которая, например, позволит воспроизводить одновременно только два звука, но не больше.

Базовый прием заключается в определении двух объектов MediaPlayer с переключением между ними всякий раз, когда требуется запустить воспроизведение нового звука. (Отслеживать, какой объект использовался последний раз, можно с помощью переменной булевского типа.) Чтобы облегчить применение этого приема, можно поместить имена аудиофайлов в свойство Tag соответствующего элемента, так что коду обработки событий понадобится только найти нужный MediaPlayer, установить свойство Source и вызвать метод Play().

Пройди тесты
Лучший чат для C# программистов