How to make a smooth moving Audio-Slider in Xamarin.Forms - c#

How can one make a smooth moving audio player in Xamarin.Forms? Thats what i tried:
Using an MediaPlayer to play Audio-Files. Media-Player is used within
a Service called "AudioPlayer".
The property "AudioPosition" is bound to the value of the slider.
There is a cancellation token used to cancel the playing.
Code looks like:
await Task.Run(
() =>
{
while (!ct.IsCancellationRequested)
{
this.AudioPosition = this.audioPlayer.CurrentPosition;
}
},
ct);
This works great if i play an audio file once. However, if i try to replay the file, a few times, the UI gets blocked more and more. Seems like the Task.Run() always creates a new task and doesn't "dispose" the older ones..?
I also tried to update the values in the Device.InvokeOnMainThread() if if i use that, the Slider won't get updated at all.
How can i update the audio slider while the file is playing in a smooth way?

Have you tried using a Timer instead of a Task with a while loop?
I assume your app is the only one controlling playback - so you could handle the timer in your playback pause / stop event handlers / commands.
The amount of code you are providing isn't enough to give more advanced suggestions.

Related

Controlling Sounds in audio in multiple forms

I want to play a playlist in the resources folder for a windows forms project I'm working on (wav or mp3). The application will be completely offline. (actually a game project)
What I want to do is this:
I need to be able to stop or convert a music that I created in one form to another sound file, depending on the situation.
In this case, I couldn't figure out how to write a code block. There are many examples that describe how we can pause and start the music on single form, but I have not come across an example that covers the whole project and can be intervened from different forms.
I'm not sure if I explained exactly what I wanted to ask, but I can give an example as follows. Let's say I created 3 forms.
1st form and 2nd form game menus.
In these menus, the same music should continue to play.
But when I reach the 3rd form, this music should be cut and replaced with in-game music.
What I want to ask is: How can I control the music I created in form 1, in form 2 or form 3?
The reason I don't use the soundplayer class is because I have to play multiple sounds at once.
i.e., somehow I need to make the music player "static" so that I can control it from all forms, but I couldn't find the way around it.
var importer = new RawSourceWaveStream(Properties.Resources.sound, new WaveFormat());
var soundFx = new WaveOut();
soundFx.Init(importer);
soundFx.Play();
For example, in this way, I start playing the music in the form1. I need to give the command to change this music in the form3, but somehow I cannot reach the "soundfx" object that I defined there because I cannot make this process static.
I think that your intuition is valid. It is probably not the best way, but I inffer that it is a way that you would manage to do.
Have a public static class, that holds a private (or public) instance of you sound player. Have public static methods of the different things that you want to do with the audio player.
Control that class from all forms.
Since this is your intuition. I am curious of what have you so far in this way?
Anyhow, I have a few questions.
When you say "convert" the music, what do you mean?
What do you mean that you couldnt find a way around it?
Can you add some code of how you are controlling the sounds? With some code, people will be able to better support you.
It is recommended that you use MediaPlayer. From your description, MediaPlayer can better meet your needs. Compared with SoundPlayer, MediaPlayer has the following characteristics:
Multiple sounds can be played at the same time (create multiple MediaPlayer objects);
You can adjust the volume (Volume property);
You can use Play, Pause, Stop and other methods to control;
You can set the IsMuted property to True to achieve mute;
You can use the Balance property to adjust the balance of the left and right speakers;
The speed of audio playback can be controlled through the SpeedRatio property;
The length of the audio can be obtained through the NaturalDuration property, and the current playback progress can be obtained through the Position property;
Seek can be performed through the Position property;
Use MediaPlayer to play audio files as follows:
MediaPlayer player = new MediaPlayer();
player.Open(new Uri("BLOW.WAV", UriKind.Relative));
player. Play();
A MediaPlayer object can only play one file at a time. And the file is played asynchronously, you can also call Close to release the file.
For details, please refer to https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.mediaplayer?view=windowsdesktop-7.0
When using it in VS, you need to perform the following steps first:
This way you can find MediaPlayer in the toolbar.
If you still have any questions please speak up.

Delay between on screen display and parallel port trigger

I am working on a research project which needs precise timing/synchronize between the on screen display and a trigger from parallel port in Unity.
What I am trying to do is to flash the screen to white while sending a trigger to the parellel port at the same time (desired difference is within 10ms).
I mesured the screen flash with a photodiode to determine the exact time it turns white, and synchronize it with the trigger from parellel port. I always observed a delay of 40 - 70ms between the trigger and the flash (the flash arrived slower) which is my main problem.
What I have tried so far:
- Update the flash and send the trigger in the same frame (bigger delay)
- Update the flash -> WaitForEndOfFrame() -> send trigger (lower delay but still big). Below is a sample code:
IEnumerator UpdateParallelPort()
{
while (true)
{
yield return new WaitForEndOfFrame();
if (flashed == FlashState.ToWhite)
{
parallelPort.SendTrigger();
}
}
}
I also tested if the flash take multiple frames to be rendered by using ReadPixels to determine at what frame the screen turn white, but it was not the case, it was in the same frame when I issued the command. So I guess the delay comes from the time the buffer being sent to screen ? If that is the case, is there anyway to determine/synchronize the exact timing, or to minimize it ?
This is my first post in Stackoverflow, hope that I explained it clear enough. Thank you in advance for your help!
which needs precise timing
I have bad news, completely forget it.
Unity is a game engine through-and-through.
The whole entire raison d'etre, the most fundamental aspects of it, is that it lets you render mesh of dinosaurs etc, with "total compromise" of granular time, and reasonable overall perceptive time.
Unfortunately, you literally could not choose a worse milieu for the project! Sorry! :O

I want to force a render, but only draw as fast as possible (InvalidateVisual / CompositionTarget.Rendering)

I'm working on a real-time WPF/Silverlight (and soon WP7) visualization component and I'm looking for the best solution to force a redraw of the entire component in a Game-loop style. Redraw should be on-demand, but I don't want to back up the message pump with re-draw calls. Most of the drawing in my component is done using non-WPF primitives (e.g. Bitmap Interop, Direct2D) so my code does not use InvalidateVisual, and as a result, currently looks like this
// Pseudocode, doesnt compile, just to convey the meaning
public void InvalidateElement()
{
if (CurrentlyDrawing)
return;
Dispatcher.BeginInvoke(() =>
{
CurrentlyDrawing = true;
DoDrawInternal();
CurrentlyDrawing = false;
}
}
Ok so this is great. If I call InvalidateElement lots of times I get good responsiveness. However, what I want to do is ensure I can push data to my visualization component as fast as possible but only draw when the component is able to draw, and not keep drawing to catch up with the data once the input stream completes.
No I can't override OnRender, I'm using non-WPF drawing inside WPF ;-)
Basically what I want is something like the old Invalidate() / OnPaint in WindowsForms, or better yet, a game loop in DirectX.
At the moment I get the situation where if I have an external thread that pushes data to the visualization component at a high rate then if I Stop pushing data I get another 20 seconds worth of refreshes to get through before the component stops drawing. I want to stop drawing as soon as data has gone in.
Another idea I had was to handle CompositionTarget.Rendering in the visualization component then implement some sort of rudimentary Queue to push data to and the Rendering event consumes this data as fast as it can.
In Summary
Given a WPF visualization component, V, and a datasource which pushes it data every 1ms, D, how can I ensure that no matter the datarate of D, V draws data at 30FPS (or whatever it can do) and updates itself in chunks, sort of how a game render loop does in DirectX?
When the data stops, V should redraw everything it has up to now in one go. When the data is too fast, V draws larger chunks at a time to compensate.
If you need more information I'd be happy to share it. Right now I've just posted a synopsis to gauge if there are any quick fixes but a fuller Q with code examples can be provided on request.
Best regards,
You might want to consider rendering on the CompositionTarget.Rendering event and throttling on the invalidated state.
Silverlight game loop example (F#):
/// Run game
let runGame () =
let state = gameState.GetEnumerator()
let rate = TimeSpan.FromSeconds(1.0/50.0)
let lastUpdate = ref DateTime.Now
let residual = ref (TimeSpan())
CompositionTarget.Rendering.Add (fun x ->
let now = DateTime.Now
residual := !residual + (now - !lastUpdate)
while !residual > rate do
state.MoveNext() |> ignore
residual := !residual - rate
lastUpdate := now
)
Play the game: http://trelford.com/blog/post/LightCycles.aspx
Read the source: https://bitbucket.org/ptrelford/lightcycles
You can listen to the CompositionTarget.Rendering event, which is triggered right before WPF renders the UI, and do your drawing in there.
Another tidbit.. InvalidateVisuals() is nothing like Form.Invalidate(), as it also causes re-layout which is expensive. If you want something like Form.Invalidate(), then create a DrawingGroup (or bitmap image) "backingStore", place it in the DrawingContext during OnRender(), and then update it whenever you want. WPF will automatically update and repaint the UI.
Have you thought of using a dispatch timer running at 30FPS, then take a snapshot of the current data and rendering it at each timer tick? If you want to avoid redrawing if nothing has changed, you can simply keep timestamps for LastChanged and LastRendered, only performing an actual redraw if LastChanged > LastRendered. Basically updating the data and rendering the data are decoupled from one-another; the main trick is making sure you can somehow get a coherent snapshot of the data when the rendering thread wants to render it (i.e. you'll need some sort of locking.)
I was recently working with a project that required a game loop like style. Although my example is purely in F#, you can figure it out how you can do that way in C# too, may be use some interop code to initialize the timer and hooking up events as given in this below link,
http://dl.dropbox.com/u/23500975/Demos/loopstate.zip
The sample doesn't show how to redraw, it just updates the underlying stock data for every 500ms, It should pretty much work for any kind of drawing mechanisms with WPF. The core idea is to use composable events, in F# an event is a first-class citizen + an IObservable (reactive extensions for C#), so we can easily compose functions that in-turn return a set of events or a single event. There is a function Observable.await, which takes in an Observable and also has a state to return.
eventSource
|> Observable.await(fun (t:State.t) e ->
// return the modified state back on every check or in the end
match e with
// start button click
| Choice1Of3(_) ->
{t with start=true}
// stop button click
| Choice2Of3(_) ->
{t with start=false}
// timer tick event,
| Choice3Of3(_) ->
if t.start = true then
handleStockUpdate(t)
else t
) (state)
I just used some of FP terms here, but it should work just fine with normal C# (OO) way of doing things here.
Hope this helps!
-Fahad
I'm not sure why you would use WPF for your front-end if you're drawing using non-WPF elements and require the Invalidate() method that was provided by WinForms? Can't you just switch the UI to use WinForms?

Wait for animation, render to complete - XAML and C#

I have a situation where I am animating part of my XAML application, and I need to wait for the animation AND rendering to complete before I can move on in my code. So far the tail end of my function looks like:
ProcExpandCollapse.Begin();
while (ProcExpandCollapse.GetCurrentState() != ClockState.Stopped) { }
}
Which, in theory, will wait until the animation is finished. But it will not wait until the rendering is finished - the thread drawing the application might still not have re-drawn the animation.
The animation is expanding a UIElement, and then the next part of my code uses it's rendered size to do some things. My question then is, how do I wait until my UI Element is re-rendered before moving on?
I finally found an answer (although I had to pay for consulting services)! Essentially what I ended up doing was putting the bit of code which uses the rendered controls on the control dispatcher's queue with very low priority, so that it naturally renders before handling that task. For example:
mycontrol.Dispatcher.BeginInvoke((Action)delegate{textbox1.Text = "Grazie";});
mycontrol.Dispatcher.Invoke((Action)delegate{GetScreenshot();}, DispatcherPriority.Background);
The code will then procede after GetScreenshot is called and finished, which will be after the rendering is completed (because rendering has higher priority than background).
The Storyboard object has a 'Completed' event you can subscribe to. It will be called when the animation is done. This what you are looking for?
Basically I think you want "ProcExpandCollapse.Completed +=" (then hit tab a view times, VS will generate a snippet for you)

Draw image on a form from a separate thread

I am currently working on a Windows.Forms application. It's basically a simple motion detection problem.
I have a button on a form, that when pressed launches a background worker that does the following :
Fetch an image from disk
Create a new bitmap, to be used as the buffer.
Perform Motion Detection
From the results of Motion Detection, update the buffer (using the buffer's drawing surface)
Fire the Progress Changed Event with an argument consisting of a clone of the buffer, basically (sender as BackgroundWorker).ReportProgress((Bitmap)buffer.Clone())
In the Progress Changed Event, I then draw the buffer to screen.
if (!PnlImage.IsDisposed)
PnlImage.CreateGraphics().DrawImageUnscaled(buffer, 0, 0);
I can't help wondering if this is the best way to draw the updated image on the screen. Can anyone suggest any improvements I can make?
Thanks.
EDIT :
I have since updated the program to use the .NET Framework 4, and we're no longer using a BackgroundWorker. Instead, we are now using the System.Threading.Tasks namespaces, and using Invoke to update the background image from within the task.
Thanks to all replies.
I believe the root of any problems you may be experiencing is the fact that any GUI updates must be done on the UI thread. You cannot safely update the UI from another thread. So, basically, you need to do something like the following (I'm just changing the background color as an example, but you can do whatever you like):
private void SomethingCalledFromBackgroundThread()
{
panel1.Invoke(new DoUpdatePanel(UpdatePanel), Color.Blue);
}
private delegate void DoUpdatePanel(Color aColor);
private void UpdatePanel(Color aColor)
{
panel1.BackColor = aColor;
}
============ Update =======>
#Ash you have mischaracterized my answer. I did not say to call Invoke from within ProgressChanged. #Jean keep in mind that ReportProgress/ProgressChanged is being run asynchronously--which is why you find yourself making a clone of your image. This would not be necessary if you use Invoke from within your background thread, rather than ReportProgress.
I'm not sure if this is strictly true but I'm sure you can't cross thread GUI/Control operations on a separate thread as it is handled by default on a dedicated GUI thread.
I tried to do something similar to this before and in the end i decided on an entirely different approach to it as setting a property to false was the worst way to get it to work.
The ProgressChanged and RunWorkerCompleted Events allow you to update the UI directly. It is only the DoWork event handler that you must not access the from. See MSDN:
You must be careful not to manipulate
any user-interface objects in your
DoWork event handler. Instead,
communicate to the user interface
through the ProgressChanged and
RunWorkerCompleted events.
This is one of the major benefits in using the BackgroundWorker over creating your own thread. So TheObjectGuy is not correct, you do not need to use BeginInvoke/Invoke in ProgressChanged.
As long as your image is not too large, cloning it should not cause any serious performance issues. Run some performance tests with bigger images if you have concerns.
Otherwise, to avoid tricky synchronization issues such as using lock, I think making a clone of the image is a good way to keep things simple.
Using the ProgressChanged event is fine. What is not fine is drawing directly to the screen. Your image will disappear when you minimize and restore the form. The workaround is simple:
PnlImage.BackgroundImage = buffer;

Categories

Resources