CrossMediaManager MediaItemFinished gives playing status instead of stop in Xamarin Forms - c#

I am playing audio files from device library using CrossMediaManager plugin. It plays files correctly, however, in MediaitemFinisedinstead of stopped status I am getting playing status. What I am trying to achieve is to play another file or stop playing based on some settings. e.g If user setting is configured to play next, I am picking another file and playing next item, but I need to do that after playing the first item is fully finished.
void AddCrossMediaHandlers()
{
CrossMediaManager.Current.StateChanged += Current_StateChanged;
CrossMediaManager.Current.MediaItemFinished += Current_MediaItemFinished;
CrossMediaManager.Current.MediaPlayer.BeforePlaying += MediaPlayer_BeforePlaying;
}
void RemoveCrossMediaHandlers()
{
CrossMediaManager.Current.StateChanged -= Current_StateChanged;
CrossMediaManager.Current.MediaItemFinished -= Current_MediaItemFinished;
CrossMediaManager.Current.MediaPlayer.BeforePlaying -= MediaPlayer_BeforePlaying;
}
private void Button_Clicked(object sender, EventArgs e)
{
StartPlaying(someFilePath);
}
async void StartPlaying(filePath)
{
var player = CrossMediaManager.Current;
if (player.State != MediaManager.Player.MediaPlayerState.Playing)
{
if (!string.IsNullOrEmpty(filePath))
await player.Play(new FileInfo(filePath));
}
}
private void Current_MediaItemFinished(object sender, MediaManager.Media.MediaItemEventArgs e)
{
Debug.WriteLine("item Finished event: " + CrossMediaManager.Current.State); //this return playing status therefore it doesen't play the next file.
//When media is finished look for user setting and play next or stop playing.
if (playNext) //I also checked !CrossMediaManager.Current.IsPlaying(), but didn't help.
StartPlaying(NextFilePath); //This fire, but because the status is playing, it doesn't play next.
}

Solved it. In case someone else may need it. I added Task.Delay(milliseconds) to wait for the player to stop.
private void Current_MediaItemFinished(object sender, MediaManager.Media.MediaItemEventArgs e)
{
Debug.WriteLine("item Finished event: " + CrossMediaManager.Current.State); //this return playing status therefore it doesen't play the next file.
var player = CrossMediaManager.Current.MediaPlayer;
await player.Stop(); //stop playing explicitly
await Task.Delay(TimeSpan.FromSeconds(1)); // add a delay (you can add milliseconds)
}

Related

MediaPlayer is not looping/repeating the audio provided

I am using System.Windows.Media.MediaPlayer for playing .wma audio file of 5 seconds length .
I was not able to find any direct option for repeatedly playing this media file so I have implemented looping logic as follows. But it seems to be not working.
For the following code, MediaEnded event is not getting fired even after playback is ended. What am I doing wrong here?
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();
}
Looping logic in above code is not working.
If above approach is not possible, please recommend me another audio player which supports Volume adjustments and Repeat media option. (I tried System.Media.SoundPlayer but it does not support Volume adjustments and .wma files are also not supported in it.)
First, you seem to be using the MediaPlayer class incorrectly. It inherits DispatcherObject and also is not blocking, so it should really be used on the UI thread.
Now on the main subject.
I was not able to find any direct option for repeatedly playing this media file
Well, actually it supports everything you need except the total play duration time. But you are right - it's not so direct (as most of the WPF stuff) - the repeating is achieved by using MediaTimeline through RepeatBehavior property. You can find sample usage in How to: Play Media using a VideoDrawing. So the basic playing code with repeating is like this:
var timeLine = new MediaTimeline(new Uri(audioFilePath));
timeLine.RepeatBehavior = RepeatBehavior.Forever;
var mediaPlayer = new MediaPlayer();
mediaPlayer.Clock = timeLine.CreateClock();
mediaPlayer.Clock.Controller.Begin();
You create MediaTimeline object, set properties (use RepeatBehavior.Forever to get indefinite repeating, but you can also use the constructor and specify concrete count), then create MediaClock from it and assign it to the MediaPlayer.Clock property. Make sure to read the documentation, because in this mode you should not use Position property and Play, Pause and Stop methods of the MediaPlayer class, but the Clock.Controller methods.
The MediaTimeline also has a property Duration, but it allows you (along with the BeginTime property) to select a portion of the audio file to be played, hence cannot be used to set up the total play duration. So the play time duration problem should be solved in a separate way.
The easiest way I see to support what you need, along with stop function, is to use async method:
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 will give you the desired play duration, and CancellationToken - the stop functionality.
Jus make sure to call it from UI thread. Here is a sample usage:
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>
Code behind:
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();
}
MediaPlayers "Play" is not thread locking. the thread ends execution as soon as the sound starts playing. I've setup a local class testing this out and I get the event to fire (On a background thread) like this (I've changed it to be OOP, not a statically used class, you have to call Stop from somewhere else):
public class MediaStuff
{
private bool _closing = false;
public void PlayAudio(string audioFilePath)
{
var thread = new Thread(() => { PlayAudioThreadProc(audioFilePath); });
thread.Start();
}
private void PlayAudioThreadProc(string audioFilePath)
{
MediaPlayer mediaPlayer = CreateMediaPlayer(audioFilePath);
mediaPlayer.Play();
while (!_closing)
{
System.Threading.Thread.Sleep(10);
Dispatcher.Run();
}
mediaPlayer.Stop();
mediaPlayer.MediaEnded -= MediaPlayer_MediaEnded;
mediaPlayer.Close();
}
private MediaPlayer CreateMediaPlayer(string audioFilePath)
{
var mediaPlayer = new MediaPlayer();
mediaPlayer.MediaEnded += MediaPlayer_MediaEnded; //This event is not getting fired.
mediaPlayer.Open(new Uri(Path.GetFullPath(audioFilePath)));
return mediaPlayer;
}
private 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();
}
public void Stop()
{
_closing = true;
}
}
I too had your problem when testing your code. After changing and testing my code, the event now fires, and the sound loops.
Alternatively, you can use mciSendString(). Tried to make an example and it succeed, try this;
winmm.dll import,
[DllImport("winmm.dll")]
private static extern long mciSendString( string command, string returnValue,
int returnLength, IntPtr winHandle);
Need to catch operations done by mciSendString(), so we need 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);
}
Method that executes, media file by using mciSendString() that we imported at the beginning
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
}
Then call the method by giving parameter anywhere.
Hope helps,

Do something after a music ends with SoundPlayer

I need to do something after my Async Loaded music file finishes. Let's say I want the program to exit or something. Now how do I make it do is after the music finishes?
private void button1_Click(object sender, EventArgs e)
{
player.SoundLocation = #"Music\" + FileList[0];
player.LoadAsync();
}
private void Player_LoadCompleted(object sender, AsyncCompletedEventArgs e)
{
if (player.IsLoadCompleted)
{
player.PlaySync();
}
}
Since the PlaySync method is synchronous then it will not return until the file has been played to the end. So, you can simply do it like this:
if (player.IsLoadCompleted)
{
player.PlaySync();
DoSomethingAfterMusicIsDone();
}
UPDATE:
LoadAsync seems to run synchronously if the SoundLocation points to a file on the file system. This means that you should invoke LoadAsync on another thread if you don't want to freeze the UI thread. Here is an example:
Task.Run(() => player.LoadAsync());
Use MediaPlayer instead:
mediaPlayer = new MediaPlayer();
mediaPlayer.MediaEnded += delegate { MessageBox.Show("Media Ended"); };
mediaPlayer.Open(new Uri(#"C:\myfile.mp3"));
mediaPlayer.Play();
Sounds good in theory. However, in truth, I couldn't get the MediaEnded Event to fire. Thus, I had to poll for the end of MediaEvent as follows:
while(true)
{
System.Threading.Thread.Sleep(1000);
string pos = "unknown";
string dur = "unknown";
try
{
pos = mediaplayer1.Position.Ticks.ToString();
dur = mediaplayer1.NaturalDurataion.TimeSpan.Ticks.ToString()
if (pos == dur)
{
// MediaEnded!!!!!
pos = "0";
dur = "0";
}
catch {}
}
}
On the positive side, you can update your Audio player Slider by polling...
On the downside, it makes the pause button a little bit unresponsive if you notice 1000 milliseconds lag...
If you use this workaround, I would recommend placing the Mediaplayer in a background thread so that the polling loop doesn't lock up the UI thread.

Custom .wav file in c# windows form application every second

I am having some trouble playing a 4second long wave... What I am currently doing is running a timer....
So the timer is set to a second intervals... So every second, I run off and check something... If this check fails.. I play a wav file saying "Get back to work!"...
Currently, it pauses the timer.... So I hear "Get back to work" but while it is playing, I have lost 4 seconds of count time, because it is still finishing playing the sound.... Here is my call and my function...
playSimpleSound();
private void playSimpleSound()
{
SoundPlayer simpleSound = new SoundPlayer(#"c:\Windows\Media\shortwav.wav");
simpleSound.PlaySync();
}
If I switch them out, so that it actually plays everytime it hits.... I only hear the beginning of the wav file....
playSimpleSound();
private void playSimpleSound()
{
SoundPlayer simpleSound = new SoundPlayer(#"c:\Windows\Media\shortwav.wav");
simpleSound.Play();
}
So my question is...
How can I continue counting, and play the whole wav file?
Should I figure out how long the wav file is and then go ahead and do some kind of count with a mod on it?
So that I basically only play the file every x amount of seconds or something?
So basically just call the playsound function everytime, but inside that function count how many times it has been visited and only play it on the 4th second?
You could do something like this...play the sound on a different thread and toggle a flag:
public partial class Form1 : Form
{
private SoundPlayer simpleSound;
private bool SoundPlaying = false;
public Form1()
{
InitializeComponent();
this.Load += Form1_Load;
}
void Form1_Load(object sender, EventArgs e)
{
simpleSound = new SoundPlayer(#"c:\Windows\Media\shortwav.wav");
simpleSound.LoadAsync();
}
private void timer1_Tick(object sender, EventArgs e)
{
Console.WriteLine("Tick");
if (true) // check your condition
{
this.PlaySound();
}
}
private void PlaySound()
{
if (!this.SoundPlaying)
{
Console.WriteLine("Starting Sound");
this.SoundPlaying = true;
Task.Factory.StartNew(() => {
simpleSound.PlaySync();
this.SoundPlaying = false;
});
}
}
}

How can I play a sound with defined start- and end-time in .Net

In my C# project I want to play a sound with a specified start- and end time.
I used the System.Media.SoundPlayer with the .Play() function. But with this function I can only play the whole sound file or can abort it after I have counted the runtime.
In fact I want to say, that the given sound file should start for example at time 1m:25s:30ms and should end at time 1m:50s:00ms or after a duration of 10s or so.
Does anybody know a simple solution for his problem?
Thanks 4 help.
Not really an answer, but this question got me wondering if you can do something like this:
long startPositionInBytes = 512;
long endPositionInBytes = 2048;
using ( var audioStream = File.OpenRead(#"audio.wav") )
{
audioStream.Position = startPositionInBytes;
using ( var player = new SoundPlayer(audioStream) )
{
player.Play();
do
{
Thread.Sleep(1);
} while ( audioStream.Position <= endPositionInBytes );
player.Stop();
}
}
In case you want to play a Background Media Player (for e.g., an audio file) which would start from a specific time and would work for a certain duration, then this code snippet could also be useful:
StorageFile mediaFile = await KnownFolders.VideosLibrary.GetFileAsync(fileName);//the path
BackgroundMediaPlayer.Current.SetFileSource(mediaFile);
BackgroundMediaPlayer.Current.Position = TimeSpan.FromMilliseconds(3000/*enter the start time here*/);
BackgroundMediaPlayer.Current.Play();
await Task.Delay(TimeSpan.FromMilliseconds(10000/*for how long you want to play*/));
BackgroundMediaPlayer.Shutdown();//stops the player
**But this would only work for Windows 8.1 and above.
For more information, click here...
In the End I use the NAudio library. That can handle this - not in a perfect way but ok.
see https://stackoverflow.com/a/13372540/2936206
I got it to work by using Windows Media Player in a C sharp program with a timer control and an open dialog.
I followed a sample that used buttons on the form and made the media player invisible.
After opening the file with an Open button that used the open dialog to open the mp3 file, on the Play button I put this code [the end effect is that it started the file in position 28.00 and ended it in position 32.50]:
private void button2_Click(object sender, EventArgs e)
{
axWindowsMediaPlayer1.URL = openFileDialog1.FileName;
timer1.Start();
axWindowsMediaPlayer1.Ctlcontrols.currentPosition = 28.00;
axWindowsMediaPlayer1.Ctlcontrols.play();
}
Then in the timer tick event:
private void timer1_Tick(object sender, EventArgs e)
{
if (axWindowsMediaPlayer1.Ctlcontrols.currentPosition >= 32.50)
{ axWindowsMediaPlayer1.Ctlcontrols.pause(); }
label1.Text = String.Format("{0:0.00}",
axWindowsMediaPlayer1.Ctlcontrols.currentPosition);
}

C# Wav File Playing freezes GUI

Problem: The wav files plays, but GUI does not respond at all. The lbl_PhonoString.Text is not updated every loop. User cannot pause or cancel until all the files are played completely.
I know I have to use a seaparate thread to play the Wav file. I am not at all familiar with threading. Can someone suggest what I should do here?
My Code:
//====================================================
bool bPause = false, bCancel = false;
private void btn_DicStart_Click(object sender,EventArgs e)
{
PlayWave("StartProgram.WAV");
for(int i=0;i<10;i++)
{
lbl_PhonoString.Text=i.ToString();
PlayWave("NextSound.WAV");
PlayWave("Clue"+i.ToString()+".WAV");
//Check if user pressed pause/cancel
while(bPause);
if(bCancel)
break;
}
}
private void btn_Pause_Click(object sender,EventArgs e)
{
bPause=!bPause;
if(!bPause)
btn_Pause.Text ="PAUSE";
else
btn_Pause.Text ="CONTINUE";
}
private void btn_Cancel_Click(object sender,EventArgs e)
{
bCancel=true;
}
//Play the Wave File
private void PlayWave(string WaveFile)
{
System.Media.SoundPlayer myPlayer=new System.Media.SoundPlayer();
myPlayer.SoundLocation=WaveFile;
myPlayer.PlaySync();
}
//====================================================
Look here essentially the same question and the accepted answer is your answer. In a nutshell, yes, you should be doing all long running processes in a seperate thread or they will block the UI, or use BackgroundWorker of course.

Categories