I have some Windows Phone 7 code that starts playing a sound using SoundEffect.FromStream. I am using this instead of a normal media object as I need multiple audio clips to be on the one page.
However, based on certain external events I want to stop a particular sound playing. As sounds played via Open Stream are "play and forget" I cannot work out how to reference this playing sound and stop it.
public void PlaySoundCircus(object sender, RoutedEventArgs e)
{
if (audioPlaying == false)
{
using (var stream = TitleContainer.OpenStream("circus.wav"))
{
var effect = SoundEffect.FromStream(stream);
FrameworkDispatcher.Update();
effect.Play();
}
audioPlaying = true;
}
}
You need to create a SoundEffectInstance which will store a reference to your SoundEffect. The SoundEffectInstance has a Stop method which can be called.
SoundEffectInstance seiCircus;
using (var stream = TitleContainer.OpenStream("circus.wav"))
{
var effect = SoundEffect.FromStream(stream);
//create the instance
seiCircus = effect.CreateInstance();
FrameworkDispatcher.Update();
//play sound via the instance
seiCircus.Play();
}
//some event called to stop sound
seiCircus.Stop();
Related
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.
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.
I am making a UWP App where I run Background Audio in the MainPage on a Button Click event. When I move to another page, there's also a different Media to play in Background Audio Task there.
How can I stop the currently playing Task to run the other? Should I define something globally? Any help regarding this issue?
Edit
I am using this sample: https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BackgroundAudio While the backgroundAudio of the first Page is running, I go to the second page and on a click event I set a new List with the following code:
// First update the persisted start track
ApplicationSettingsHelper.SaveSettingsValue(ApplicationSettingsConstants.TrackId, RadioFacade.mySongs[0].MediaUri.ToString()); //here
ApplicationSettingsHelper.SaveSettingsValue(ApplicationSettingsConstants.Position, new TimeSpan().ToString());
// Start task
StartBackgroundAudioTask();
But the new song takes more than the estimated time to run and enter the else of this method:
private void StartBackgroundAudioTask()
{
AddMediaPlayerEventHandlers();
var startResult = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
bool result = backgroundAudioTaskStarted.WaitOne(10000);
//Send message to initiate playback
if (result == true)
{
MessageService.SendMessageToBackground(new UpdatePlaylistMessage(RadioFacade.mySongs));
MessageService.SendMessageToBackground(new StartPlaybackMessage());
}
else
{
throw new Exception("Background Audio Task didn't start in expected time");
}
});
startResult.Completed = new AsyncActionCompletedHandler(BackgroundTaskInitializationCompleted);
}
and the old (first playing) song keeps playing.
I tried to Stop the current BackgroundMediaPlayer using BackgroundMediaPLayer.Shutdown() but it didn't work.
Any idea how to let the old song stop and the current song play?
You can control the background media player by sending messages to it from the foreground. For example,
From the foreground app:
BackgroundMediaPlayer.SendMessageToBackground(new ValueSet
{
{"playnew", "some value"}
});
In your background task:
public sealed class AudioPlayer : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundMediaPlayer.MessageReceivedFromForeground += BackgroundMediaPlayer_MessageReceivedFromForeground;
...
...
}
private async void BackgroundMediaPlayer_MessageReceivedFromForeground(object sender, MediaPlayerDataReceivedEventArgs e)
{
object value;
if (e.Data.TryGetValue("playnew", out value) && value != null)
{
// value will be whatever you pass from the foreground.
BackgroundMediaPlayer.Current.Pause();
BackgroundMediaPlayer.Current.Source = stream source;
BackgroundMediaPlayer.Current.Play();
}
}
...
}
"playnew" can be a global constant in your application. You can use the ValueSet to pass the live stream url to the background task as the value.
i need to know how to save a sound stream in a file on the fly after record
this is my code, that i need to save an external file wav or smth.
so at that point here there are record button play button and play objects
private void recordButton_Click(object sender, EventArgs e)
{
// Get audio data in 1/2 second chunks
microphone.BufferDuration = TimeSpan.FromMilliseconds(500);
// Allocate memory to hold the audio data
buffer = new byte[microphone.GetSampleSizeInBytes(microphone.BufferDuration)];
// Set the stream back to zero in case there is already something in it
stream.SetLength(0);
// Start recording
microphone.Start();
SetButtonStates(false, false, true);
UserHelp.Text = "recording";
// StatusImage.Source = microphoneImage;
}
private void playButton_Click(object sender, EventArgs e)
{
if (stream.Length > 0)
{
// Update the UI to reflect that
// sound is playing
SetButtonStates(false, false, true);
UserHelp.Text = "play";
// Play the audio in a new thread so the UI can update.
Thread soundThread = new Thread(new ThreadStart(playSound));
soundThread.Start();
}
}
private void playSound()
{
// Play audio using SoundEffectInstance so we can monitor it's State
// and update the UI in the dt_Tick handler when it is done playing.
SoundEffect sound = new SoundEffect(stream.ToArray(), microphone.SampleRate, AudioChannels.Mono);
soundInstance = sound.CreateInstance();
soundIsPlaying = true;
soundInstance.Play();
}
You need to build the .wav header properly and then write the audio stream.
Check this example
In my WP7 gaming app , I want two music files to run. One is the background music and another follows the user action , say, user kills the enemy. I am using MediaElement to do this. I am facing two issues.
1) How to loop background music ?
2) As soon as second music starts, the first music stops and does not start back when the second music stops. I do not want background music to stop, they should overlap. How to do this ?
I am using silverlight.
XAML
<MediaElement x:Name="stroke" AutoPlay="False" />
<MediaElement x:Name="bmusic" AutoPlay="True" />
C#
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var settings = IsolatedStorageSettings.ApplicationSettings;
if (settings.Contains("bm"))
{
string hy=(string)settings["bm"];
//check if user has disabled music play
if (hy == "1")
{
bmplay = 1;
// play background music
bmusic.Source = new Uri("bmusic.mp3", UriKind.Relative);
bmusic.Play();
}
else
{
bmplay = 0;
}
}
else
{
bmplay = 1;
// play b music
bmusic.Source = new Uri("bmusic.mp3", UriKind.Relative);
bmusic.Play();
}
if (NavigationContext.QueryString.TryGetValue("msg", out msg))
{
// textBox1.Text +=" "+ msg;
find_move();
}
}
For repeating music you can use this:
Song s = Song.FromUri("song", new Uri(path));
FrameworkDispatcher.Update();
MediaPlayer.IsRepeating = repeat;
MediaPlayer.Play(s);
And try to use SoundEffect for sounds, they can be played simultaneously with background music.
Sound effect in windows phone 7