Windows Phone 8.1 Media Elements - c#

I have currently 2 buttons, Audi TT RS and Audi R8
when the user clicks on button TT RS it plays a mp3, when user clicks on button R8 it opens a different mp3 file.
This works, however... if the user clicks on one of them, and then clicks on the other. both mp3 files will open and it will sound terrible.
those are my functions :
async void Audir8_Click(object sender, RoutedEventArgs e)
{
MediaElement mediaplayer = new MediaElement();
if (mediaplayer.CurrentState == MediaElementState.Playing)
{
mediaplayer.Stop();
}
// get folder app is installed to
var installFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
// get folders mp3 files are installed to.
var resourcesFolder = await installFolder.GetFolderAsync("Resources");
var mp3FilesFolder = await resourcesFolder.GetFolderAsync("mp3Files");
// open the mp3 file async
var audioFile = await mp3FilesFolder.GetFileAsync("audir8.mp3");
// var stream = await audioFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
using (var stream = await audioFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
// play dat funky music
// MediaElement mediaplayer = new MediaElement();
mediaplayer.SetSource(stream, audioFile.ContentType);
var tcs = new TaskCompletionSource<bool>();
mediaplayer.CurrentStateChanged += (_, __) =>
{
if (mediaplayer.CurrentState != MediaElementState.Opening &&
mediaplayer.CurrentState != MediaElementState.Playing &&
mediaplayer.CurrentState != MediaElementState.Buffering)
// mediaplayer.CurrentState != MediaElementState.AcquiringLicense)
{
// Any other state should mean we're done playing
tcs.TrySetResult(true);
}
};
mediaplayer.Play();
await tcs.Task; // Asynchronously wait for media to finish
}
}
and the other one, actually the same..
async void Audittrs_Click(object sender, RoutedEventArgs e)
{
MediaElement mediaplayer = new MediaElement();
if (mediaplayer.CurrentState == MediaElementState.Playing)
{
mediaplayer.Stop();
}
// get folder app is installed to
var installFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
// get folders mp3 files are installed to.
var resourcesFolder = await installFolder.GetFolderAsync("Resources");
var mp3FilesFolder = await resourcesFolder.GetFolderAsync("mp3Files");
// open the mp3 file async
var audioFile = await mp3FilesFolder.GetFileAsync("ttrs.mp3");
// var stream = await audioFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
using (var stream = await audioFile.OpenAsync(Windows.Storage.FileAccessMode.Read))
{
// play dat funky music
// MediaElement mediaplayer = new MediaElement();
mediaplayer.SetSource(stream, audioFile.ContentType);
var tcs = new TaskCompletionSource<bool>();
mediaplayer.CurrentStateChanged += (_, __) =>
{
if (mediaplayer.CurrentState != MediaElementState.Opening &&
mediaplayer.CurrentState != MediaElementState.Playing &&
mediaplayer.CurrentState != MediaElementState.Buffering)
// mediaplayer.CurrentState != MediaElementState.AcquiringLicense)
{
// Any other state should mean we're done playing
tcs.TrySetResult(true);
}
};
mediaplayer.Play();
await tcs.Task; // Asynchronously wait for media to finish
}
}
I thought this should not happen since I am using
MediaElement mediaplayer = new MediaElement();
if (mediaplayer.CurrentState == MediaElementState.Playing)
{
mediaplayer.Stop();
}

When you set up your mediaplayer local variable, you're creating a new mediaplayer, not re-using the one you've previously created. If you re-use the previously-created one (say by making it a field plus property in your class), then the .Stop() call should stop the previous sound.
If you do this, you'll have to change the first line of each of your ..._Click methods to use the persistent MediaElement.
For example, in your class you can do something like this:
private MediaElement _mediaplayer;
public MediaElement Mediaplayer
{
get{
if( _mediaplayer == null )
{
_mediaplayer = new MediaElement();
}
return _mediaplayer;
}
}
Then in your ..._Click methods, swap out this:
MediaElement mediaplayer = new MediaElement();
... for this:
MediaElement mediaplayer = this.Mediaplayer;
The getter should do the rest, making sure that you're always using the same MediaElement for the various sounds you play, allowing you to stop previously-playing sounds as you desire.

Related

MediaStreamSource is not applying Bitrate Assigned to Media Encoding Profile

I'm using media composition to preview videos and to generate streams. I pass a MediaEncoding Profile in which the video container I am assigning the bitrate has to apply when previewing. But it's not working. Here's the code:
MediaEncodingProfile profile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.HD720p);
profile.Video.Bitrate = 1000000; // Any Bitrate
var stream = VideoComposition.GenerateMediaStreamSource(profile);
videoPlayer.SetMediaStreamSource(stream);
By testing, there is no effect in the MediaStreamSource when we assign MediaEncodingProfile.Video.Bitrate in your scenario.
You could use Windows.Media.Transcoding APIs to transcode video files from one format to another to show the effect of assigning bitrate.
Please check the following code as a sample:
private MediaComposition VideoComposition;
private MediaStreamSource mediaStreamSource;
private StorageFile destination;
……
private async void button_Click(object sender, RoutedEventArgs e)
{
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
picker.FileTypeFilter.Add(".mp4");
Windows.Storage.StorageFile pickedFile = await picker.PickSingleFileAsync();
if (pickedFile == null)
{
return;
}
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
destination = await localFolder.CreateFileAsync("destination.mp4",CreationCollisionOption.ReplaceExisting);
MediaEncodingProfile profile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.HD720p);
MediaTranscoder transcoder = new MediaTranscoder();
profile.Video.Bitrate = 50000; // Any Bitrate
PrepareTranscodeResult prepareOp = await
transcoder.PrepareFileTranscodeAsync(pickedFile, destination, profile);
if (prepareOp.CanTranscode)
{
var transcodeOp = prepareOp.TranscodeAsync();
transcodeOp.Completed +=
new AsyncActionWithProgressCompletedHandler<double>(TranscodeComplete);
}
else
{
switch (prepareOp.FailureReason)
{
case TranscodeFailureReason.CodecNotFound:
System.Diagnostics.Debug.WriteLine("Codec not found.");
break;
case TranscodeFailureReason.InvalidProfile:
System.Diagnostics.Debug.WriteLine("Invalid profile.");
break;
default:
System.Diagnostics.Debug.WriteLine("Unknown failure.");
break;
}
}
}
private async void TranscodeComplete(IAsyncActionWithProgress<double> asyncInfo, AsyncStatus asyncStatus)
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => {
var stream = await destination.OpenAsync(Windows.Storage.FileAccessMode.Read);
videoPlayer.SetSource(stream, destination.ContentType);
});
}
You could delete the destination file if you do not want to save the file.

Direct3D11CaptureFramePool frame is null in async method

I am trying to use the Windows.Graphics.Capture namespace to capture screenshot from a comaptible application.
I based my code on the official examples here: MS sample screenshot code
The code in question is the following:
private async void btnCapture_Click(object sender, EventArgs e)
{
process = Process.GetProcesses().Where(p => p.ProcessName == "xxxxxxxxxxx").Single();
hwnd = process.MainWindowHandle;
GraphicsCaptureItem item = CaptureHelper.CreateItemForWindow(hwnd);
device = Direct3D11Helper.CreateDevice();
capture = new BasicCapture(device, item);
framePool = Direct3D11CaptureFramePool.Create(device, DirectXPixelFormat.B8G8R8A8UIntNormalized, 1, item.Size);
session = framePool.CreateCaptureSession(item);
session.StartCapture();
using (frame = framePool.TryGetNextFrame())
{
var bmp = await SoftwareBitmap.CreateCopyFromSurfaceAsync(frame.Surface);
StorageFolder pictureFolder = KnownFolders.CameraRoll;
StorageFile file = await pictureFolder.CreateFileAsync("test.png", CreationCollisionOption.ReplaceExisting);
using (var fileStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, fileStream);
encoder.SetSoftwareBitmap(bmp);
await encoder.FlushAsync();
}
}
}
If i put a breakpoint in line using (frame = framePool.TryGetNextFrame()) the frame is populated and the file is saved normally by the code that follows.
If i just run the app then frame is null and an error occurs. I cant use await on the framePool.TryGetNextFrame()
I feel that this is a Frankenstein code as i am learning the new capturing paradigm. Any help how to make this code work?
Eventually i will need this code to work with a timer taking screenshots at a regular interval.
Direct3D11CaptureFramePool has a FrameArrived event, which you could listen for before trying to get the frame:
var cts = new TaskCompletionSource<object>();
framePool.FrameArrived += (s, e) => cts.SetResult(null);
session.StartCapture();
await cts.Task;
// Frame should now be available
using (frame = framePool.TryGetNextFrame())

Want to Open multiple audio files and play them in UWP

I want to open multiple audio files in UWP using the FileOpenPicker but I am getting an error that I cant Cannot Convert. How can I fix this?
And if this is fixed, will all the audio files play in order or all at the same time?
public MainPage()
{
this.InitializeComponent();
}
MediaSource media_source;
MediaPlayer media_player;
public async System.Threading.Tasks.Task OpenfileAsync()
{
var filePicker = new Windows.Storage.Pickers.FileOpenPicker();
filePicker.FileTypeFilter.Add(".mp3");
filePicker.FileTypeFilter.Add(".mp4");
filePicker.FileTypeFilter.Add(".ogg");
filePicker.FileTypeFilter.Add(".wav");
filePicker.FileTypeFilter.Add(".wma");
filePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.MusicLibrary;
StorageFile file = await filePicker.PickSingleFileAsync();
if (file != null)
{
media_source = MediaSource.CreateFromStorageFile(file);
media_player = new MediaPlayer();
media_player.Source = media_source;
mediaPlayerElement.SetMediaPlayer(media_player);
media_player.Play();
}
}
private async void Select_track_Click(object sender, RoutedEventArgs e)
{
await OpenfileAsync();
}
public async System.Threading.Tasks.Task OpenMultipleAsync()
{
var filePicker = new Windows.Storage.Pickers.FileOpenPicker();
filePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.MusicLibrary;
filePicker.FileTypeFilter.Add(".mp3");
filePicker.FileTypeFilter.Add(".mp4");
filePicker.FileTypeFilter.Add(".ogg");
filePicker.FileTypeFilter.Add(".wav");
filePicker.FileTypeFilter.Add(".wma");
filePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.MusicLibrary;
StorageFile file = await filePicker.PickMultipleFilesAsync();
if (file != null)
{
media_source = MediaSource.CreateFromStorageFile(file);
media_player = new MediaPlayer();
media_player.Source = media_source;
mediaPlayerElement.SetMediaPlayer(media_player);
media_player.Play();
}
}
private async void playlist_Click(object sender, RoutedEventArgs e)
{
await OpenMultipleAsync();
}
I am getting the error at StorageFile file = await filepicker.PickmultiplefilesAsync();
The FileOpenPicker.PickMultipleFilesAsync method has the following signature:
IAsyncOperation<IReadOnlyList<StorageFile>> PickMultipleFilesAsync()
In contrast to PickFileAsync it returns a IReadOnlyList<StorageFile>, so you will actually get a list of multiple files the user selected. You should update the code like this:
var files = await filePicker.PickMultipleFilesAsync();
foreach (var file in files)
{
if (file != null)
{
media_source = MediaSource.CreateFromStorageFile(file);
media_player = new MediaPlayer();
media_player.Source = media_source;
mediaPlayerElement.SetMediaPlayer(media_player);
media_player.Play();
}
}
This solution will play all the sounds at once. For one by one playback you can use #touseefbsb solution :-) .
for playing a list of files its best that you use MediaPlaybackList
Also you only need to set the SuggestedStartLocation once, and when you use PickMultipleFilesAsync() you get a List of files returned so you need that iterate through that list to get all files and add them to your MediaPlaybackList
Modify your OpenMultipleAsync method like this :
public async System.Threading.Tasks.Task OpenMultipleAsync()
{
var filePicker = new Windows.Storage.Pickers.FileOpenPicker();
filePicker.FileTypeFilter.Add(".mp3");
filePicker.FileTypeFilter.Add(".mp4");
filePicker.FileTypeFilter.Add(".ogg");
filePicker.FileTypeFilter.Add(".wav");
filePicker.FileTypeFilter.Add(".wma");
filePicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.MusicLibrary;
_mediaPlaybackList = new MediaPlaybackList();
var files = await filePicker.PickMultipleFilesAsync();
foreach (var file in files)
{
var mediaPlaybackItem = new MediaPlaybackItem(MediaSource.CreateFromStorageFile(file));
_mediaPlaybackList.Items.Add(mediaPlaybackItem);
}
_mediaPlayer = new MediaPlayer();
_mediaPlayer.Source = _mediaPlaybackList;
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
}
More details about MediaPlaybackItem can be seen here
and to answer 'will these media files play together at the same time or one after the other' : they will play one after the other in a row, that is the purpose if MediaPlaybackList, it supports gapless playback for playlists.

Xamarin Forms Play a sound Asynchronously

I can successfully play sounds using Xamarin forms (Android and iOS) however I also need to achieve the following:
I need to await so that if multiple sounds are 'played', one will complete before the next.
I need to return a boolean to indicate whether operation was a success.
Here is my current simplified code (for the iOS platform):
public Task<bool> PlayAudioTask(string fileName)
{
var tcs = new TaskCompletionSource<bool>();
string filePath = NSBundle.MainBundle.PathForResource(
Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));
var url = NSUrl.FromString(filePath);
var _player = AVAudioPlayer.FromUrl(url);
_player.FinishedPlaying += (object sender, AVStatusEventArgs e) =>
{
_player = null;
tcs.SetResult(true);
};
_player.Play();
return tcs.Task;
}
To test the method, I have tried calling it like so:
var res1 = await _audioService.PlayAudioTask("file1");
var res2 = await _audioService.PlayAudioTask("file2");
var res3 = await _audioService.PlayAudioTask("file3");
I had hoped to hear the audio for file1, then file2, then file3. However I only hear file 1 and the code doesn't seem to reach the second await.
Thankyou
I think your issue here is that the AVAudioPlayer _player was being cleared out before it was finished. If you were to add debugging to your FinsihedPlaying, you'll notice that you never hit that point.
Try these changes out, I made a private AVAudioPlayer to sit outside of the Task
(I used the following guide as a reference https://developer.xamarin.com/recipes/ios/media/sound/avaudioplayer/)
public async void play()
{
System.Diagnostics.Debug.WriteLine("Play 1");
await PlayAudioTask("wave2.wav");
System.Diagnostics.Debug.WriteLine("Play 2");
await PlayAudioTask("wave2.wav");
System.Diagnostics.Debug.WriteLine("Play 3");
await PlayAudioTask("wave2.wav");
}
private AVAudioPlayer player; // Leave the player outside the Task
public Task<bool> PlayAudioTask(string fileName)
{
var tcs = new TaskCompletionSource<bool>();
// Any existing sound playing?
if (player != null)
{
//Stop and dispose of any sound
player.Stop();
player.Dispose();
}
string filePath = NSBundle.MainBundle.PathForResource(
Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));
var url = NSUrl.FromString(filePath);
player = AVAudioPlayer.FromUrl(url);
player.FinishedPlaying += (object sender, AVStatusEventArgs e) =>
{
System.Diagnostics.Debug.WriteLine("DONE PLAYING");
player = null;
tcs.SetResult(true);
};
player.NumberOfLoops = 0;
System.Diagnostics.Debug.WriteLine("Start Playing");
player.Play();
return tcs.Task;
}

How to combine asynchronous method with synchronous method in C#?

I'm calling from the main method:
public MainPage()
{
Text_to_Speech.changetospeech("Welcome to Nepal!", newmedia).Wait();
mytxtblck.Text="Hello from Nepal!"
}
What I really want to do is Wait until "Welcome to Nepal" is being spoken and then write "Hello" in mytextblck.
I've gone to several threads and worked, but nothing could make it work.
public async static Task changetospeech(string text, MediaElement mediaa)
{
var synth = new SpeechSynthesizer();
var voices = SpeechSynthesizer.AllVoices;
synth.Voice = voices.First(x => x.Gender == VoiceGender.Female );
SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);
MediaElement media = mediaa;
media.SetSource(stream,stream.ContentType);
media.Play();
}
It sounds like what you really want is to trigger the text change when the MediaEnded event is fired.
You could do that within your ChangeToSpeech method, although it'll be a little ugly:
public async static Task ChangeToSpeech(string text, MediaElement media)
{
var synth = new SpeechSynthesizer();
var voices = SpeechSynthesizer.AllVoices;
synth.Voice = voices.First(x => x.Gender == VoiceGender.Female);
SpeechSynthesisStream stream = await synth.SynthesizeTextToStreamAsync(text);
var tcs = new TaskCompletionSource<int>();
RoutedEventHandler handler = delegate { tcs.TrySetResult(10); };
media.MediaEnded += handler;
try
{
media.SetSource(stream, stream.ContentType);
media.Play();
// Asynchronously wait for the event to fire
await tcs.Task;
}
finally
{
media.MediaEnded -= handler;
}
}

Categories