I am developing a Windows Phone 8 application. I need to stream a .mp3 sound file from a remote server in my application.
I have tried to use MediaElement:
private MediaElement media;
// Constructor of class
media = new MediaElement();
media.Source = new Uri(string.Format("{0}b10en_US.mp3", mp3HostName), UriKind.Absolute);
media.MediaFailed += media_MediaFailed;
media.MediaEnded += media_MediaEnded;
media.MediaOpened += media_MediaOpened;
media.Loaded += media_Loaded;
media.BufferingProgressChanged += media_BufferingProgressChanged;
// In a method I call the following
media.play();
However no sound gets played whatsoever. I added breakpoints to the mediaelement's events, but none gets fired.
I have double checked, the URI to the mp3 file is correct.
What am I doing wrong?
You can play mp3 after media opened event is raised. I think your method(in which you are calling play is called before media opened event is raised.
you can implement some hack in mediaOpened event and your method(play). like
private bool isMediaLoaded = false;
private bool isPlayCalled = false;
private void PlayMP3()
{
if(isMediaLoaded)
media.Play();
else
isPlayCalled = true;
}
void MediaElement1_MediaOpened(object sender, RoutedEventArgs e)
{
isMediaLoaded = true;
if(isPlayCalled)
MediaElement1.Play();
}
Try this :
string url = "http://traffic.libsyn.com/slashfilmcast/Davidmichod.mp3";//your url link
// Constructor
public MainPage()
{
InitializeComponent();
Microsoft.Xna.Framework.FrameworkDispatcher.Update();
Song track = Song.FromUri("Sample Song", new Uri(url));
MediaPlayer.Play(track);
}
using Microsoft.Xna.Framework.Media; //for getting MediaPlayer
Related
I'm trying to play OGG file stream with NVorbis and NAudio, as described in the documention, I'm trying to run this code when on a Button click, but I get an exception:
System.ArgumentException: 'Could not initialize container!'
I'm targeting .Net Framework 4.5
Here is my code:
private void button1_Click(object sender, EventArgs e)
{
using (var vorbisStream = new NAudio.Vorbis.VorbisWaveReader(#"OGG file path"))
using (var waveOut = new NAudio.Wave.WaveOutEvent())
{
waveOut.Init(vorbisStream);
waveOut.Play();
// wait here until playback stops or should stop
}
}
Two issues here:
The first is that you cannot declare with using statements either the WaveOutReader or WaveOut object, since the latter is playing the sound asynchronously and it needs the WaveOutReader's Stream open
You were targeting .Net Framework 4.5, which is not suitable to handle the current version of both NAudio and NVorbis
I assume you have already successfully installed the NAudio 2.1.0 and NAudio.Vorbis 1.5.0 packages in a new .Net 6+ Project.
You just need to add references to NAudio.Vorbis and NAudio.Wave
Targeting .Net 6+, nullable enabled.
Add two Button Controls to a Form, one named btnPlayAudio and the other btnStopPlayback, initially disabled; subscribe to their Click event using the event handlers shown here.
The WaveOutReader and the WaveOut object that acts as player are declared as instance Fields, initially set to null.
When you press the Play Audio Button, the audio Stream and the Player are initialized, calling the WaveOutInit() method, which also subscribes to the WaveOut object's PlaybackStopped event.
When you call the WaveOut object's Stop() method or the OGG playback terminates, this event is raised. Here, the Stop() method is called in the btnStopPlayback click handler.
When this happens, another method, WaveOutReset(), is called; this method disposes of the WaveOut object and the Stream of the WaveOutReader. The event handler is also removed.
using NAudio.Vorbis;
using NAudio.Wave;
public partial class MainForm : Form {
private VorbisWaveReader? vorbis = null;
private WaveOut? oggPlayer = null;
private void btnPlayAudio_Click(object sender, EventArgs e)
{
btnPlayAudio.Enabled = false;
btnStopPlayback.Enabled = true;
vorbis = new VorbisWaveReader("OGG File path"));
oggPlayer = WaveOutInit(vorbis);
oggPlayer.Play();
}
private void btnStopPlayback_Click(object sender, EventArgs e)
{
oggPlayer?.Stop();
btnStopPlayback.Enabled = false;
}
private void WaveOut_PlaybackStopped(object? sender, StoppedEventArgs e)
{
WaveOutReset(oggPlayer, vorbis);
btnPlayAudio.Enabled = true;
}
private WaveOut WaveOutInit(IWaveProvider reader)
{
var waveOut = new WaveOut();
waveOut.PlaybackStopped += WaveOut_PlaybackStopped;
waveOut.Init(reader);
return waveOut;
}
private void WaveOutReset(WaveOut? player, VorbisWaveReader? reader)
{
if (player != null) {
player.PlaybackStopped -= WaveOut_PlaybackStopped;
player.Dispose();
}
reader?.Dispose();
}
}
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,
In my App you can open a Site where you can switch on and off the Flashlight.
The first time it works, but if I try to switch the flashlight on a second time the App crashes.
I think this is a Problem with AudioVideoCaptureDevice.OpenAsync. If I call it a second time the App crashes with a System.Reflection.TargetInvocationException WinRT-Informationen: Unable to acquire the camera. You can only use this class while in the foreground.
Someone know this Problem?
protected AudioVideoCaptureDevice Device { get; set; }
public Page10()
{
InitializeComponent();
}
async void tglSwitch_Checked(object sender, RoutedEventArgs e)
{
var sensorLocation = CameraSensorLocation.Back;
if (this.Device == null)
{
// get the AudioVideoCaptureDevice
this.Device = await AudioVideoCaptureDevice.OpenAsync(sensorLocation,
AudioVideoCaptureDevice.GetAvailableCaptureResolutions(sensorLocation).First());
}
var supportedCameraModes = AudioVideoCaptureDevice
.GetSupportedPropertyValues(sensorLocation, KnownCameraAudioVideoProperties.VideoTorchMode);
if (supportedCameraModes.ToList().Contains((UInt32)VideoTorchMode.On))
{
this.Device.SetProperty(KnownCameraAudioVideoProperties.VideoTorchMode, VideoTorchMode.On);
// set flash power to maxinum
this.Device.SetProperty(KnownCameraAudioVideoProperties.VideoTorchPower,
AudioVideoCaptureDevice.GetSupportedPropertyRange(sensorLocation, KnownCameraAudioVideoProperties.VideoTorchPower).Max);
this.tglSwitch.Content = "Light on";
this.tglSwitch.SwitchForeground = new SolidColorBrush(Colors.Green);
}
}
void tglSwitch_Unchecked(object sender, RoutedEventArgs e)
{
var sensorLocation = CameraSensorLocation.Back;
sensorLocation = CameraSensorLocation.Back;
var supportedCameraModes = AudioVideoCaptureDevice
.GetSupportedPropertyValues(sensorLocation, KnownCameraAudioVideoProperties.VideoTorchMode);
if (this.Device != null && supportedCameraModes.ToList().Contains((UInt32)VideoTorchMode.Off))
{
this.Device.SetProperty(KnownCameraAudioVideoProperties.VideoTorchMode, VideoTorchMode.Off);
this.tglSwitch.Content = "Light off";
}
}
I would recommend to initialize the camera with OpenAsync ONE TIME in page lifecycle, for example in OnNavigatedTo event. And only makeSetProperty() methods calls code in your checkbox events to control light. It is also very important to dispose camera correctly then leaving the page, for example in OnNavigatedFrom event, by calling device.Dispose(). This option also make your flashlight to work faster.
Keep in mind that Windows Phone 8.1 now has dedicated API for torch, which works great and the code is more beautiful. You can use in Silverlight project as well, but you have to migrate your project. Here is more about this http://developer.nokia.com/community/wiki/Using_the_camera_light_in_Windows_Phone_7,_8_and_8.1 and https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.devices.torchcontrol.
I've developing a Windows 8 app in Visual Studio 2012 ultimate but i'm having an issue with the MediaElement where the MediaOpened and MediaEnded events are not being fired.
Below is my code:
public static async void PlaySound(string AudioFile, RoutedEventHandler PlayerEnded)
{
Uri audioUri = new Uri("ms-appx:///Assets/" + AudioFile);
StorageFile audioStorage = await StorageFile.GetFileFromApplicationUriAsync(audioUri);
if (audioStorage != null)
{
var stream = await audioStorage.OpenAsync(Windows.Storage.FileAccessMode.Read);
MediaElement player = new MediaElement();
player.AudioCategory = AudioCategory.GameEffects;
player.AutoPlay = false;
player.MediaEnded += PlayerEnded;
player.MediaOpened += new RoutedEventHandler(delegate(Object sender, RoutedEventArgs e)
{
player.Play();
});
player.SetSource(stream, audioStorage.ContentType);
}
}
Basically this will be passed a button click sound file which after finish playing will call the this.Frame.Navigate method to switch to another page. But it doesn't work since none of the events are being fired.
If I set AutoPlay = true then it plays fine, but then events still don't get fired.
Can anyone see what is wrong?
Thanks for your help
I think your MediaElement needs to be in the visual tree to fire.
See http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/f5b9cdba-5521-467d-b838-8420afc68e7f/
I want to play two sounds one after the other in reaction to a button click. When the first sound finishes, the second sound should start playing.
My problem is that every time the button is clicked these two sounds are different and I don't know their lengths in order to use Thread.Sleep. But I don't want these sounds to play on top of each other.
Sounds like you're after the PlaySync method of SoundPlayer class.. first add this on top:
using System.Media;
Then have such code:
SoundPlayer player = new SoundPlayer(#"path to first media file");
player.PlaySync();
player = new SoundPlayer(#"path to second media file");
player.PlaySync();
This class is available since .NET 2.0 so you should have it.
The MediaPlayer has MediaEnded event. In the event handler, just start the new media and they should play back to back.
protected System.Windows.Media.MediaPlayer pl = new MediaPlayer();
public void StartPlayback(){
pl.Open(new Uri(#"/Path/to/media/file.wav"));
pl.MediaEnded += PlayNext;
pl.Play();
}
private void PlayNext(object sender, EventArgs e){
pl.Open(new Uri(#"/Path/to/media/file.wav"));
pl.Play();
}
In Shadow Wizards example, it's not necessary to make a new soundplayer each time.
This also works:
player.SoundLocation = "path/to/media";
player.PlaySync();
player.SoundLocation = "path/to/media2";
player.PlaySync();
Example Using NAudio
private List<string> wavlist = new List<string>();
wavlist.Add("c:\\1.wav");
wavlist.Add("c:\\2.wav");
foreach(string file in wavlist)
{
AudioFileReader audio = new AudioFileReader(file);
audio.Volume = 1;
IWavePlayer player = new WaveOut(WaveCallbackInfo.FunctionCallback());
player.Init(audio);
player.Play();
System.Threading.Thread.Sleep(audio.TotalTime);
player.Stop();
player.Dispose();
audio.Dispose();
player = null;
audio = null;
}