MediaPlayer没有循环/重复提供的音频

时间:2017-03-07 10:36:07

标签: c# .net

我正在使用 System.Windows.Media.MediaPlayer 播放长度为5秒的.wma音频文件。

我无法找到重复播放此媒体文件的任何直接选项,因此我已实现如下循环逻辑。但似乎没有用。

对于以下代码,即使在播放结束后, MediaEnded事件也不会被解雇。我在这里做错了什么?

public void PlayAudio(string audioFilePath, TimeSpan duration)
{
    var thread = new Thread(() => { PlayAudioThreadProc(audioFilePath, duration); });
    thread.Start();
}
private void PlayAudioThreadProc(string audioFilePath, TimeSpan duration)
{
    MediaPlayer mediaPlayer = CreateMediaPlayer(audioFilePath);
    mediaPlayer.Play();

    _stopSignal.WaitOne(duration);

    Stop(mediaPlayer);
}
private static MediaPlayer CreateMediaPlayer(string audioFilePath)
{
    var mediaPlayer = new MediaPlayer();
    mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;     //This event is not getting fired.
    mediaPlayer.Open(new Uri(audioFilePath));
    return mediaPlayer;
}
private static void MediaPlayer_MediaEnded(object sender, EventArgs e)
{
    //This part of code is supposed to start the media again after it is ended playing.
    var player = (MediaPlayer)sender;
    player.Position = TimeSpan.Zero;
    player.Play();
}
private static void Stop(MediaPlayer mediaPlayer)
{
    mediaPlayer.Stop();
    mediaPlayer.MediaEnded -= MediaPlayer_MediaEnded;
    mediaPlayer.Close();
}

上述代码中的循环逻辑无效。

如果无法采用上述方法,请向我推荐另一款支持音量调节和重复媒体选项的音频播放器。 (我尝试System.Media.SoundPlayer但它不支持音量调整,也不支持.wma文件。)

3 个答案:

答案 0 :(得分:4)

首先,您似乎错误地使用了MediaPlayer类。它继承DispatcherObject并且也没有阻塞,所以它应该真正用在UI线程上。

现在主题。

  

我无法找到重复播放此媒体文件的直接选项

嗯,实际上它支持你需要的一切,除了总的播放持续时间。但你是对的 - 它不是那么直接(就像大多数WPF的东西) - 重复是通过使用MediaTimelineRepeatBehavior属性来实现的。您可以在How to: Play Media using a VideoDrawing中找到示例用法。所以重复的基本播放代码是这样的:

var timeLine = new MediaTimeline(new Uri(audioFilePath));
timeLine.RepeatBehavior = RepeatBehavior.Forever;
var mediaPlayer = new MediaPlayer();
mediaPlayer.Clock = timeLine.CreateClock();
mediaPlayer.Clock.Controller.Begin();

您创建MediaTimeline对象,设置属性(使用RepeatBehavior.Forever获得无限重复,但您也可以使用构造函数并指定具体计数),然后从中创建MediaClock并分配它到MediaPlayer.Clock属性。请务必阅读文档,因为在此模式下,您不应使用Position类的Play属性和PauseStopMediaPlayer方法,但Clock.Controller方法。

MediaTimeline还有一个属性Duration,但它允许您(以及BeginTime属性)选择要播放的音频文件的一部分,因此无法使用设置总播放持续时间。所以播放持续时间问题应该以单独的方式解决。

我看到支持您需要的最简单的方法,以及停止功能,是使用async方法:

public async Task PlayAudioAsync(string audioFilePath, TimeSpan duration, CancellationToken cancellationToken)
{
    var timeLine = new MediaTimeline(new Uri(audioFilePath));
    timeLine.RepeatBehavior = RepeatBehavior.Forever;
    var mediaPlayer = new MediaPlayer();
    mediaPlayer.Clock = timeLine.CreateClock();
    mediaPlayer.Clock.Controller.Begin();
    try
    {
        await Task.Delay(duration, cancellationToken);
    }
    finally
    {
        mediaPlayer.Clock.Controller.Stop();
    }
}

Task.Delay会为您提供所需的播放时间,以及CancellationToken - 停止功能。

Jus确保从UI线程调用它。以下是一个示例用法:

XAML:

<Canvas>
    <Button x:Name="playButton" Content="Play" Width="75" RenderTransformOrigin="1.2,5.24" Canvas.Left="98" Canvas.Top="135" Click="playButton_Click"/>
    <Button x:Name="stopButton" Content="Stop" IsEnabled="False" Width="75" Canvas.Left="208" Canvas.Top="135" Click="stopButton_Click"/>
</Canvas>

代码背后:

private CancellationTokenSource ctsPlay;

private async void playButton_Click(object sender, RoutedEventArgs e)
{
    string audioFile = ...;
    TimeSpan duration = ...;

    ctsPlay = new CancellationTokenSource();
    playButton.IsEnabled = false;
    stopButton.IsEnabled = true;
    try
    {
        await PlayAudioAsync(audioFile, duration, ctsPlay.Token);
    }
    catch
    {
    }
    ctsPlay.Dispose();
    ctsPlay = null;
    stopButton.IsEnabled = false;
    playButton.IsEnabled = true;
}

private void stopButton_Click(object sender, RoutedEventArgs e)
{
    ctsPlay.Cancel();
}

答案 1 :(得分:1)

MediaPlayers“Play”不是线程锁定。一旦声音开始播放,线程就会结束执行。我已经设置了一个本地类来测试它,我得到了这样的事件(在后台线程上)(我已将其更改为OOP,而不是静态使用的类,您必须从其他地方调用Stop) :

<p:column headerText="Name"
          sortBy="#{item.name}"
          filterBy="#{item.name}"
          filterMatchMode="contains"
          filterValue="#{yourBean.filterValues['name']}">
  <h:outputText value="#{item.name}" />
</p:column>

在测试代码时我也遇到了问题。在更改和测试我的代码之后,事件现在激活,声音循环。

答案 2 :(得分:1)

或者,您可以使用mciSendString()。试图做一个例子,它成功了,试试这个;

winmm.dll导入,

[DllImport("winmm.dll")]
private static extern long mciSendString( string command, string returnValue,
int returnLength, IntPtr winHandle);

需要抓住mciSendString()完成的操作,因此我们需要WndProc;

private const int MM_MCINOTIFY = 0x03b9; // notify mci completed operation
private const int MCI_NOTIFY_SUCCESS = 0x01; // mci successfully executed command

protected override void WndProc(ref Message m)
{
     if (m.Msg == MM_MCINOTIFY)
     {
         switch (m.WParam.ToInt32())
         {
             case MCI_NOTIFY_SUCCESS: // if successfull
                 if (IsLoop) // check if we gave parameter true
                     PlayMediaFile(IsLoop); // so it should run forever, call again function
                 break;
             default:
                 break;
         }
     }
     base.WndProc(ref m);
 }

使用我们在开头导入的mciSendString()执行媒体文件的方法

private bool IsLoop = false; // need this to check inside WndProc
private void PlayMediaFile(bool RepeatForever)
{
     IsLoop = RepeatForever; 
     mciSendString("close voice1", null, 0, this.Handle); // first close, after first run, the previous opened file should be terminated
     mciSendString("stop voice1", null, 0, this.Handle); // close file
     string playCommand = "open  " + "yourFilePath" + " type waveaudio alias voice1"; // open command string
     mciSendString(playCommand, null, 0, this.Handle); // open file
     mciSendString("play voice1 notify", null, 0, this.Handle); // play file
}

然后通过在任何地方给出参数来调用方法。

希望有所帮助,