Play audio from Assets folder in W10 universal app - c#

I have several .wav and .mp3 files in my app under Assets/Audio, and I'm trying to play them when something is tapped in the UI. I've written the following code (with the file name hardcoded for testing purposes), but when the function is triggered, no sound plays. If I replace attempting to play from a file to playing an audio stream created by Windows.Media.SpeechSynthesis.SpeechSynthesizer, everything works fine.
private async void SoundItem_Tapped(object sender, TappedRoutedEventArgs e)
{
ListViewItem soundItem = sender as ListViewItem;
if (soundItem.IsSelected)
{
Uri sourceUri = new Uri(String.Format("ms-appx:///Assets/Audio/151Cry.wav", UriKind.Absolute));
await PlayAudio(sourceUri, soundItem);
}
else if (inUsePlayers.ContainsKey(soundItem))
{
MediaElement player = inUsePlayers[soundItem];
player.Stop();
inUsePlayers.Remove(soundItem);
players.Enqueue(player);
}
else
{
}
}
private async Task PlayAudio(Uri sourceUri, ListViewItem soundItem)
{
MediaElement player = RequestPlayer(soundItem);
player.Source = sourceUri;
player.IsLooping = true;
await player.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
player.Stop();
player.Play();
});
}
Here's the location of the file within the tree:
Aside from dragging the sound files into the project, I've haven't done anything. Maybe they need to be added to a resource file or copied to the output folder as well? I expected that something would throw an exception if the item pointed to by the URI didn't exist, but nothing's being thrown, even when I give it a bogus filename.

You need to do following things to play you media element.
Your resource files should be set to "Copy to Output directory"
value to "Copy if newer" or "Copy always". to do this right
click your resource file go to Properties and set Copy to Output directory value to Copy always and Build action to
content
Your player(MediaElement) should be added somewhere to View xaml
tree. I do not know your RequestPlayer method is adding
mediaEelement to view xaml or not. e.g
layoutRoot.Children.Add(player)
You need to register Player_MediaOpened event to play your
audio file. If you call 'play' before player is opened media will
not play the sound... and if you want that if any thing is happend
to your player not playing than register Player_MediaFailed it
will give you the reason why it is filed to play.
here is the code.
private async Task PlayAudio(Uri sourceUri, ListViewItem soundItem)
{
MediaElement player = RequestPlayer(soundItem);
player.IsLooping = true;
player.AutoPlay = false;
player.MediaOpened += Player_MediaOpened;
player.MediaFailed += Player_MediaFailed;
player.Source = sourceUri;
player.IsLooping = true;
//Add media element to xaml tree if not added by your RequestPlayer Method..
this.LayoutRoot.Children.Add(player);
}
private void Player_MediaFailed(object sender, ExceptionRoutedEventArgs e)
{
}
private async void Player_MediaOpened(object sender, RoutedEventArgs e)
{
await player.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
player.Play();
});
}
Hope this helps...

Related

No audio with WAV and MediaPlayer in WPF

I'm trying to add background music to my WPF program. I also want additional sounds to happen "over" the background music. I tried using a SoundPlayer however, this could only play one piece of audio at once.
I am now trying to use MediaPlayer but I cannot get any audio to play. Here is my code:
In my ShellViewModel I start the background music:
Sounds.StartBackgroundMusic()
In my sounds class I have the following:
private static MediaPlayer _backgroundMusic = new MediaPlayer();
public static void StartBackgroundMusic()
{
_backgroundMusic.Open(new Uri("pack://application:,,,/Assets/Sounds/backgroundmusic.wav"));
_backgroundMusic.MediaEnded += new EventHandler (BackgroundMusic_Ended);
_backgroundMusic.Play();
}
private static void BackgroundMusic_Ended(object sender, EventArgs e)
{
_backgroundMusic.Position = TimeSpan.Zero;
_backgroundMusic.Play();
}
Since I want the background music to loop continuously, I used the answer in this question to add the BackgroundMusic_Ended event. Can someone please help shed some light on why my audio isn't playing?
I tried to reproduce the problem but it worked as expected.
I created a new WPF application in Visual Studio and used the following code in MainWindow.xaml.cs:
using System;
using System.Windows;
using System.Windows.Media;
namespace WpfApp1
{
public partial class MainWindow : Window
{
private static MediaPlayer _backgroundMusic = new MediaPlayer();
public MainWindow()
{
InitializeComponent();
StartBackgroundMusic();
}
public static void StartBackgroundMusic()
{
_backgroundMusic.Open(new Uri(#"C:\<path-to-sound-file>\music.wav"));
_backgroundMusic.MediaEnded += new EventHandler(BackgroundMusic_Ended);
_backgroundMusic.Play();
}
private static void BackgroundMusic_Ended(object sender, EventArgs e)
{
_backgroundMusic.Position = TimeSpan.Zero;
_backgroundMusic.Play();
}
}
}
Did you try to use a "regular" file path to load a sound file instead of "pack://..."? When I used the wrong path I didn't get any sound and there was no error message or exception.
Since I couldn't use MediaPlayer to play embedded resources and SoundPlayer can only play one sound at a time, I used a combination of them and saved the embedded background music resource to disk so that MediaPlayer could play it. I make a blog about it here
Heres what I did:
I set up my SoundPlayer as this was the simplest one of the two. I created a new SoundPlayer object using the embedded resource
private static readonly SoundPlayer _soundOne = new SoundPlayer(WPF.Properties.Resources.soundOne);
Now the MediaPlayer. I make sure that my audio file is set as an Embedded Resource under the Build Actions in the file’s properties in Visual Studio. Now that we have done this, we can create the method for saving the embedded WAV file to the %temp% location on disk:
public static void SaveMusicToDisk(){
//This sets up a new temporary file in the %temp% location called "backgroundmusic.wav"
using (FileStream fileStream = File.Create(Path.GetTempPath() + "backgroundmusic.wav")){
//This them looks into the assembly and finds the embedded resource
//inside the WPF project, under the assets folder
//under the sounds folder called backgroundmusic.wav
//PLEASE NOTE: this will be different to you
Assembly.GetExecutingAssembly().GetManifestResourceStream("WPF.Assets.Sounds.backgroundmusic.wav").CopyTo(fileStream);
}
}
We play this by creating a new MediaPlayer object and using the temp file location to play the audio:
//Create a new MediaPlayer object
private static readonly MediaPlayer _backgroundMusic = new MediaPlayer();
public static void StartBackgroundMusic(){
//Open the temp WAV file saved in the temp location and called "backgroundmusic.wav"
_backgroundMusic.Open(new Uri(Path.Combine(Path.GetTempPath(), "backgroundmusic.wav")));
//Add an event handler for when the media has ended, this way
//the music can be played on a loop
_backgroundMusic.MediaEnded += new EventHandler(BackgroundMusic_Ended);
//Start the music playing
_backgroundMusic.Play();
}
My BackgroundMusic_Ended method looks like this and just makes sure that the music is always restarted once it has finished:
private static void BackgroundMusic_Ended(object sender, EventArgs e){
//Set the music back to the beginning
_backgroundMusic.Position = TimeSpan.Zro;
//Play the music
_backgroundMusic.Play();
}
Then I just had to worry about disposing of the objects and cleaning up the temp file when the program is closing.

Video is not rendered in accord videoSourcePlayer control

I have an accord videoSourcePlayer control in my form.Why is it that it is not rendering the video that I select? Please see my code below:
// Open video file using DirectShow
private void openVideoFileusingDirectShowToolStripMenuItem_Click(object sender, EventArgs e)
{
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
// create video source
FileVideoSource = new FileVideoSource(openFileDialog.FileName);
// open it
sourceInitialiization = true;
OpenVideoSource(FileVideoSource);
}
}
// Open video source
private void OpenVideoSource(IVideoSource source)
{
// set busy cursor
this.Cursor = Cursors.WaitCursor;
// close previous video source
CloseVideoSource();
// start new video source
videoSourcePlayer.VideoSource = new AsyncVideoSource(source);
videoSourcePlayer.Start();
// reset statistics
statIndex = statReady = 0;
// start timers
timer.Start();
alarmTimer.Start();
//alarmTimer1.Start();
videoSource = source;
this.Cursor = Cursors.Default;
}
In my laptop where I initially coded this program, this works perfectly, but if I transfer it to another machine, say a desktop or another laptop, the code doesn't work anymore. It runs but it doesn't render the video and there is not error detected in the debugger too.
I tried downloading a sample video project from accord framework but I still couldn't get it to play a video on the desktop except on my laptop. What am I missing? Thank you.
Have you subscribed to the NewFrameReceived event?

MediaElement is refusing to loop

So I was trying to loop Background music in my UWP App, I have a class called soundControl that handles music and sounds like this:
public class soundControl
{
private static MediaElement loop = new MediaElement();
public static async void stopLoop()
{
loop.Stop();
}
public static async void loadLoopTimeBG()
{
Windows.Storage.StorageFolder folder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync(#"Assets\Sounds");
Windows.Storage.StorageFile file = await folder.GetFileAsync("battle.wav");
var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
loop.AutoPlay = false;
loop.SetSource(stream, file.ContentType);
loop.IsLooping = true;
}
public static void loopTimeBG()
{
loop.Play();
}
And whenever I want to play this music I call :
soundControl.loadLoopTimeBG();
soundControl.loopTimeBG();
the problem is the it plays just one time and stops and I have no Idea why
I tried another approach like:
loop.MediaEnded += mediaEnded;
and the event handler like this:
private static void mediaEnded(object sender, RoutedEventArgs e)
{
loop.Position = TimeSpan.Zero;
loop.Play();
}
it also didn't work and when debugging it doesn't even triger the mediaEnded event when music is complete.
Any help here would be most appreciated.
Thanks
MediaPlayer
Windows.Media.Playback.MediaPlayer is the recommended player for UWP that does not require to be in the XAML visual tree.
Its API is very similar to MediaElement:
private static MediaPlayer _mediaPlayer = new MediaPlayer();
public static async Task PlayUsingMediaPlayerAsync()
{
Windows.Storage.StorageFolder folder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync(#"Assets");
Windows.Storage.StorageFile file = await folder.GetFileAsync("Click.wav");
_mediaPlayer.AutoPlay = false;
_mediaPlayer.Source = MediaSource.CreateFromStorageFile(file);
_mediaPlayer.MediaOpened += _mediaPlayer_MediaOpened;
_mediaPlayer.IsLoopingEnabled = true;
}
private static void _mediaPlayer_MediaOpened(MediaPlayer sender, object args)
{
sender.Play();
}
You can even display the visuals of a MediaPlayer in XAML using MediaPlayerElement.
MediaPlayer allows for even more advanced playback scenarios using the MediaPlaybackList with support for looping, shuffle and gapless playback.
mediaElement.SetPlaybackSource(mediaPlaybackList);
MediaElement
After some digging around it seems that there are two issues.
MediaElement is XAML based control (in the Windows.UI.Xaml.Controls namespace), and it seems that it does not work properly until it is actually attached to a visual tree. Once you put the MediaElement on the page, it works as expected.
Secondly, loading source media does not happen immediately. Once you set the source, the control needs some time to actually load the media. For this purpose, you can use the MediaOpened event, that will notify you once it is really loaded.
So the code could look somewhat like this:
public static async Task LoadAndPlayAsync()
{
Windows.Storage.StorageFolder folder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync(#"Assets");
Windows.Storage.StorageFile file = await folder.GetFileAsync("Click.wav");
var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
loop.AutoPlay = false;
loop.SetSource(stream, file.ContentType);
//or simpler -
//loop.Source = new Uri("ms-appx:///Assets/Click.wav", UriKind.Absolute);
loop.MediaOpened += Loop_MediaOpened;
loop.IsLooping = true;
}
private static void Loop_MediaOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
//play once the media is actually ready
loop.Play();
}
And before you call the LoadAndPlayAsync method, you have to attach the control somewhere (for example in a Grid):
GridContainer.Children.Add(SoundController.loop);
await SoundController.LoadAndPlayAsync();
I have created a sample project for my tests on my GitHub, you can check it out to see how I implemented it. The first button in the app attaches the control and the second loads and plays the sound. You can see that if you click only the second one, the sound does not play.

MediaElement doesn't play mp3

Trying to make some mediaplayer app. I put the mediaelement on wpf, wrote code to open media. But when i try to play it nothing is happening...
public void ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs args)
{
if (args.Files.Count > 0)
{
foreach (StorageFile file in args.Files)
{
if (playlist.Contains(file.Path)) return;
playlist.Add(file.Path);
}
}
}
private void PlayButton_OnClick(object sender, RoutedEventArgs e)
{
MyMedia.Source = new Uri(playlist[0], UriKind.RelativeOrAbsolute);
MyMedia.Play();
}
Checked that mediaelement source isn't empty, it has a right value of path.
Try to rebuild like that, still doesnt work
private async void PlayButton_OnClick(object sender, RoutedEventArgs e)
{
var stream = await Playlist[0].OpenAsync(FileAccessMode.Read);
MyMedia.SetSource(stream, Playlist[0].ContentType);
MyMedia.Play();
}
Sorry it was my mistake.
Into my XAML, I forgot that the AutoPlay property of the MediaElement control was set to false.
This solve my problem.
Couple of things you need to watch out.
foreach (StorageFile file in args.Files)
{
if(playlist.Contains(file.Path))
{
return;// Dont return use continue. You will probably skip rest of the files.
}
playlist.Add(file.Path);
}
Add a Watch/Quickwatch to see if the new Uri(playlist[0], UriKind.RelativeOrAbsolute) points to a right location.
Setup source from storage file:
var storageFile = await KnownFolders.MusicLibrary.GetFileAsync("line.mp3");
var stream = await storageFile.OpenAsync(FileAccessMode.Read);
mediaElement.SetSource(stream, storageFile.ContentType);
mediaElement.Play();
I got it from this answer: How to play file from Library by MediaElement?

MediaPlayer class listening for NaturalDuration.HasTimeSpan change

I am writing a custom audio player in c# using the MediaPlayer class. I have implemented a scroll bar so the user can seek through a track and this is where I am having the problem.
WHen the user selects an audio track (loaded from an xml playlist) the app calculates the length in seconds of the track and sets this as the max value for the scroll bar. This all works fine except the NaturalDuration.TimeSpan property sometimes returns 0 rather than the amount. I have proved this by adding a loop that exits when NaturalDuration.HasTimeSpan is true then returns the NaturalDuration.TimeSpan value.
My question is how can I just get the NaturalDuration.TimeSpan when the NaturalDuration.HasTimeSpan is changed to true?
The correct way to do this is to handle the MediaPlayer.MediaOpened event:
mediaPlayer = new MediaPlayer();
mediaPlayer.MediaOpened += MediaPlayer_MediaOpened;
mediaPlayer.Open(new Uri(mediaFilePath, UriKind.Absolute));
...
private void MediaPlayer_MediaOpened(object sender, EventArgs e)
{
if (mediaPlayer.NaturalDuration.HasTimeSpan)
{
SliderMaximum = mediaPlayer.NaturalDuration.TimeSpan.TotalSeconds;
mediaPlayer.Play();
}
}

Categories

Resources