Изменение громкости, баланса, скорости и позиции воспроизведения

94

Для управления громкостью, балансом, скоростью и текущей позицией медиафайла MediaElement предлагает те же свойства, что и MediaPlayer. Ниже показано простое окно, расширяющее пример аудиопроигрывателя из предыдущей статьи, с дополнительными элементами для управления этими параметрами.

Ползунки громкости и баланса привязать проще всего. Поскольку Volume и Balance — свойства зависимости, их элементы управления можно подключить к MediaElement с помощью выражения двунаправленной привязки.

Хотя выражения двунаправленной привязки требуют некоторых дополнительных накладных расходов, они обеспечивают обратную связь: если свойства MediaElement будут изменены каким-то другим способом, эти ползунки останутся синхронизированными с текущими значениями свойств. Свойство SpeedRatio может быть подключено аналогичным образом:

<Grid>
          <Grid.RowDefinitions>
                        <RowDefinition Height="auto"></RowDefinition>
                        <RowDefinition Height="auto"></RowDefinition>
                        <RowDefinition Height="auto"></RowDefinition>
                        <RowDefinition Height="auto"></RowDefinition>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="auto"></ColumnDefinition>
                        <ColumnDefinition></ColumnDefinition>
          </Grid.ColumnDefinitions>
          <TextBlock Margin="5">Громкость:</TextBlock>
          <Slider Margin="5" Grid.Column="1" Minimum="0" Maximum="1" 
                            Value="{Binding ElementName=my_media,Path=Volume,Mode=TwoWay}"></Slider>
          <TextBlock Grid.Row="1" Margin="5">Баланс:</TextBlock>
          <Slider Margin="5" Grid.Column="1" Grid.Row="1" Minimum="-1" Maximum="1"
                            Value="{Binding ElementName=my_media,Path=Balance,Mode=TwoWay}"></Slider>
          <TextBlock Grid.Row="2">Скорость:</TextBlock>
          <Slider Margin="5" Grid.Column="1" Grid.Row="2" Minimum="0" Maximum="3"
                            Value="{Binding ElementName=my_media,Path=SpeedRatio}"></Slider>
</Grid>

Однако здесь есть несколько нюансов. Во-первых, SpeedRatio не задействовано в управляемом таймером аудио (где применяется MediaTimeline). Чтобы использовать его, придется установить свойство LoadedBehavior из SpeedRatio в Manual и принять управление воспроизведением на себя через соответствующие методы.

В случае использования MediaTimeline от действия SetStoryboardSpeedRatio получается тот же эффект, что и от установки свойства MediaElement.SpeedRatio. Во-вторых, SpeedRatio не является свойством зависимости, и WPF не принимает уведомлений о его изменении. Это значит, что если свойство SpeedRatio модифицируется в коде, то ползунок соответствующим образом не обновляется. (Одним из обходных путей может быть изменение самого ползунка в коде вместо прямой модификации MediaElement.)

Изменение скорости воспроизведения аудио может исказить звук и вызвать появление эффектов вроде эха. И последняя деталь — текущая позиция, которая представлена свойством Position.

Опять-таки, для установки свойства Position элемент MediaElement должен находиться в режиме Manual, что означает невозможность применения MediaTimeline. (При использовании TimeLine подумайте о применении действия BeginStoryboard вместе с Offset для установки требуемой позиции.)

Чтобы заставить это работать, не применяйте никаких привязок данных в ползунке:

<Slider Minimum="0" Name="sliderPosition"
ValueChanged="sliderPosition_ValueChanged"></Slider>

Для установки позиции ползунка при открытии медиафайла можно использовать код, подобный следующему:

private void media_MediaOpened(object sender, RoutedEventArgs e)
{
   sliderPosition.Maximum = media.NaturalDuration.TimeSpan.TotalSeconds;
}

Затем при перемещении ползунка можно перепрыгнуть в определенную позицию:

private void sliderPosition_ValueChanged(object sender, RoutedEventArgs e)
{
   // Приостановка воспроизведения перед переходом в другую позицию
   // исключит "заикания" при слишком быстрых движениях ползунка,
   media.Pause();
   media.Position = TimeSpan.FromSeconds(sliderPosition.Value);
   media.Play();
}

Недостаток такого решения состоит в том, что ползунок не будет обновляться по мере воспроизведения. Если же это необходимо, придется прибегнуть к обходному маневру (вроде применения элемента DispatcherTimer, который будет выполнять периодическую проверку текущей позиции в процессе воспроизведения и соответственно обновлять положение ползунка). То же самое справедливо и при использовании MediaTimeline. По разным причинам привязаться напрямую к информации MediaElement.Clock невозможно. Вместо этого придется обрабатывать событие Storyboard.CurrentTimeInvalidated.

Настройка громкости и баланса аудио

Синхронизация анимации с аудио

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

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

Один прием, позволяющий связать воспроизведение с другими действиями, состоит в применении анимации ключевого кадра. Эту анимацию можно поместить вместе с MediaTimeline в одну раскадровку. Подобным образом можно применить определенные смещения времени в анимации, которые будут соответствовать определенным моментам времени в аудиофайле. Фактически, есть возможность даже воспользоваться программой от независимых разработчиков, которая может аннотировать аудио и экспортировать список важных моментов времени.

Затем эта информация применяется для установки времени каждого ключевого кадра. Используя анимацию ключевого кадра, важно установить свойство Storyboard.SlipBehavior в Slip. Это укажет, что анимация ключевого кадра не должна обгонять MediaTimeline, если происходит задержка воспроизведения. Это важно потому, что MediaTimeline может тормозить из-за буферизации (когда зависит от потока с сервера) или же, что бывает чаще, по причине задержки при загрузке.

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

<Window.Resources>
        <Storyboard x:Key="Board" SlipBehavior="Slip">
            <MediaTimeline Source="music1.mp3" Storyboard.TargetName="my_media"></MediaTimeline>
            <StringAnimationUsingKeyFrames Storyboard.TargetName="lbl"
                                           Storyboard.TargetProperty="(Label.Content)"
                                           FillBehavior="HoldEnd">
                <DiscreteStringKeyFrame Value="First note..." KeyTime="0:0:3.4"></DiscreteStringKeyFrame>
                <DiscreteStringKeyFrame Value="Introducing the main theme" KeyTime="0:0:5.8"></DiscreteStringKeyFrame>
                <DiscreteStringKeyFrame Value="Irritating bass begins..." KeyTime="0:0:28.7"></DiscreteStringKeyFrame>
                <DiscreteStringKeyFrame Value="Modulation!" KeyTime="0:0:53.2"></DiscreteStringKeyFrame>
                <DiscreteStringKeyFrame Value="Back to original theme" KeyTime="0:1:8"></DiscreteStringKeyFrame>
            </StringAnimationUsingKeyFrames>
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="ellipse"
                                           Storyboard.TargetProperty="Opacity"
                                           BeginTime="0:0:29.36"
                                           RepeatBehavior="30x">
                <LinearDoubleKeyFrame Value="1" KeyTime="0:0:0"></LinearDoubleKeyFrame>
                <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0.64"></LinearDoubleKeyFrame>
            </DoubleAnimationUsingKeyFrames>
        </Storyboard>
    </Window.Resources>
Пройди тесты
Лучший чат для C# программистов