我正在使用 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文件。)
答案 0 :(得分:4)
首先,您似乎错误地使用了MediaPlayer
类。它继承DispatcherObject
并且也没有阻塞,所以它应该真正用在UI线程上。
现在主题。
我无法找到重复播放此媒体文件的直接选项
嗯,实际上它支持你需要的一切,除了总的播放持续时间。但你是对的 - 它不是那么直接(就像大多数WPF的东西) - 重复是通过使用MediaTimeline
到RepeatBehavior
属性来实现的。您可以在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
属性和Pause
,Stop
和MediaPlayer
方法,但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
}
然后通过在任何地方给出参数来调用方法。
希望有所帮助,