AVAudioPlayer : waiting for a sound to finish playing - c#

I'm trying to play a sound file and wait for it to finish before exiting the function that calls AVAudioPlayer.Play().
I understand that there is a FinishedPlaying EventHandler that could be initialized and will be called accordingly.
But the method must wait without bloking the main thread...
How could this be achived?
EDIT 1:
I've tried using a ManualResetEvent but the delegate FinishedPlaying on the AVAudioPlayer instance is never called at the end of the playback...
NSError err;
ManualResetEvent playSoundFinished = new ManualResetEvent(false);
var currentPlayer = new AVAudioPlayer(soundResource, "mp3", out err);
currentPlayer.FinishedPlaying += delegate { playSoundFinished.Set();};
currentPlayer.Play();
// This call should block until the event is set
DispatchQueue.DefaultGlobalQueue.DispatchSync(() => playSoundFinished.WaitOne(-1));

The delegate is never called because the call to DispatchQueue.DefaultGlobalQueue.DispatchSync blocks the main thread.
The solution is to use a CancellationToken (or ManualResetEvent for that matter
) that is canceled in the delegate and waited outside the method that plays the sound.
Pseudo Code:
Playing the sound
NSError err;
this.currentPlayer = new AVAudioPlayer(soundResource, "mp3", out err);
this.waitFinishedPlaying = new CancellationTokenSource();
this.currentPlayer.FinishedPlaying = delegate {waitFinishedPlaying.Cancel();};
this.currentPlayer.NumberOfLoops = looping ? -1 : 0;
this.currentPlayer.PrepareToPlay();
if (delay != 0)
{
this.currentPlayer.PlayAtTime(this.currentPlayer.DeviceCurrentTime + delay);
}
else
{
this.currentPlayer.Play();
}
Outside the method that contains the code above:
try
{
// The TimeSpan duration must be greater than the duration of the longuest sound to be played.
await Task.Delay(TimeSpan.FromSeconds(20), this.audioService.GetFinishedPlayingToken()).ConfigureAwait(true);
}
catch (OperationCanceledException)
{
}
GetFinishedToken() returns the waitFinishedPlaying instance initiated in the first block of code above.
The only limitation to this solution is that the caller method must be async to await the Task.

ManualResetEvent maintains a boolean variable in memory. When the boolean variable is false then it blocks all threads and when the boolean variable is true it unblocks all threads. You can control the initial state of a ManualResetEvent by passing a Boolean value to the constructor: true if the initial state is signaled, and false otherwise.
You can solve the problem by changing it as below:
ManualResetEvent playSoundFinished = new ManualResetEvent(true);

Related

How to make a controlled infinite loop async in c#?

I found this
Run async method regularly with specified interval
which does half of what I want, but at the same time I want to be able to stop the loop whenever I want and then resume it as well. However while it's stopped, I don't want the infinite loop to keep running where the body gets skipped through a flag.
Basically I don't want this
while (true) {
if (!paused) {
// run work
}
// task delay
}
because then the while loop still runs.
How can I set it so that while its paused, nothing executes?
How can I set it so that while its paused, nothing executes?
That's hard to answer: if you define "pause" as: the object state remains valid while the loop doesn't use any resources then you'll have to stop and restart it (the loop).
All other timers, including Thread.Sleep, Task.Delays etc. will put your thread in idle/suspended mode.
If that's not sufficient for your needs, you'll need to actually stop the "infinite" loop.
It will free up thread related resources as well.
More info about sleep:
Thread.Sleep
More about sleep
You could use System.Threading.Timer and dispose of it while it is not in use and re-create it when you are ready to "resume". These timers are light weight so creating and destroying them on demand is not a problem.
private System.Threading.Timer _timer;
public void StartResumeTimer()
{
if(_timer == null)
_timer = new System.Threading.Timer(async (e) => await DoWorkAsync(e), null, 0, 5000);
}
public void StopPauseTimer()
{
_timer?.Dispose();
_timer = null;
}
public async Task DoWorkAsync(object state)
{
await Task.Delay(500); // do some work here, Task.Delay is just something to make the code compile
}
If you are really adverse to timers and want it to look like a while loop, then you can use TaskCompletionSource<T>:
private TaskCompletionSource<bool> _paused = null;
public async Task DoWork()
{
while (true)
{
if (_paused != null)
{
await _paused.Task;
_paused = null;
}
//run work
await Task.Delay(100);
}
}
public void Pause()
{
_paused = _paused ?? new TaskCompletionSource<bool>();
}
public void UnPause()
{
_paused?.SetResult(true);
}

How do I ensure that my AutoResetEvent methods are called in the right order?

I have some code that needs to run a Timer. The Timer checks for a condition and, based on the result, signals to the caller that it can continue. Here's my pseudocode:
class MyClass
{
private AutoResetEvent _reset;
private System.Threading.Timer _timer;
public void Run()
{
this._reset = new AutoResetEvent(false);
this._timer = new System.Threading.Timer(this.TimerStep, null, 0, 1000);
this._reset.WaitOne(); //wait for condition() to be true
this._reset.Dispose();
this._timer.Dispose();
}
private void TimerStep(object arg)
{
if(condition())
{
this._reset.Set(); //should happen after the _reset.WaitOne() call
}
}
}
My concern has to do with how I'm instantiating the Timer. If I start it with a 0 dueTime, the comments say that the timer will start immediately. What happens if the calling thread gets preempted by the timer and the this._reset.Set() call happens before the calling thread has a chance to call this._reset.WaitOne()? Is this something I have to worry about? So far in my testing the code works like I'd expect.
Note that I set up the code in this way because I want to block the Run() function until condition() is true, but I only want to check condition() every second or so.

Notify when thread is complete, without locking calling thread

I am working on a legacy application that is built on top of NET 3.5. This is a constraint that I can't change.
I need to execute a second thread to run a long running task without locking the UI. When the thread is complete, somehow I need to execute a Callback.
Right now I tried this pseudo-code:
Thread _thread = new Thread(myLongRunningTask) { IsBackground = True };
_tread.Start();
// wait until it's done
_thread.Join();
// execute finalizer
The second option, which does not lock the UI, is the following:
Thread _thread = new Thread(myLongRunningTask) { IsBackground = True };
_tread.Start();
// wait until it's done
while(_thread.IsAlive)
{
Application.DoEvents();
Thread.Sleep(100);
}
// execute finalizer
Of course the second solution is not good cause it overcharge the UI.
What is the correct way to execute a callback when a _thread is complete? Also, how do I know if the thread was cancelled or aborted?
*Note: * I can't use the BackgroundWorker and I can't use the Async library, I need to work with the native thread class.
There are two slightly different kinds of requirement here:
Execute a callback once the long-running task has completed
Execute a callback once the thread in which the long-running task was running has completed.
If you're happy with the first of these, the simplest approach is to create a compound task of "the original long-running task, and the callback", basically. You can even do this just using the way that multicast delegates work:
ThreadStart starter = myLongRunningTask;
starter += () => {
// Do what you want in the callback
};
Thread thread = new Thread(starter) { IsBackground = true };
thread.Start();
That's very vanilla, and the callback won't be fired if the thread is aborted or throws an exception. You could wrap it up in a class with either multiple callbacks, or a callback which specifies the status (aborted, threw an exception etc) and handles that by wrapping the original delegate, calling it in a method with a try/catch block and executing the callback appropriately.
Unless you take any special action, the callback will be executed in the background thread, so you'll need to use Control.BeginInvoke (or whatever) to marshal back to the UI thread.
I absolutely understand your requirements, but you've missed one crucial thing: do you really need to wait for the end of that thread synchronously? Or maybe you just need to execute the "finalizer" after thread's end is detected?
In the latter case, simply wrap the call to myLongRunningTask into another method:
void surrogateThreadRoutine() {
// try{ ..
mytask();
// finally { ..
..all 'finalization'.. or i.e. raising some Event that you'll handle elsewhere
}
and use it as the thread's routine. That way, you'll know that the finalization will occur at the thread's and, just after the end of the actual job.
However, of course, if you're with some UI or other schedulers, the "finalization" will now run on yours thread, not on the "normal threads" of your UI or comms framework. You will need to ensure that all resources are external to your thread-task are properly guarded or synchronized, or else you'll probably clash with other application threads.
For instance, in WinForms, before you touch any UI things from the finalizer, you will need the Control.InvokeRequired (surely=true) and Control.BeginInvoke/Invoke to bounce the context back to the UI thread.
For instance, in WPF, before you touch any UI things from the finalizer, you will need the Dispatcher.BeginInvoke..
Or, if the clash could occur with any threads you control, simple proper lock() could be enough. etc.
You can use a combination of custom event and the use of BeginInvoke:
public event EventHandler MyLongRunningTaskEvent;
private void StartMyLongRunningTask() {
MyLongRunningTaskEvent += myLongRunningTaskIsDone;
Thread _thread = new Thread(myLongRunningTask) { IsBackground = true };
_thread.Start();
label.Text = "Running...";
}
private void myLongRunningTaskIsDone(object sender, EventArgs arg)
{
label.Text = "Done!";
}
private void myLongRunningTask()
{
try
{
// Do my long task...
}
finally
{
this.BeginInvoke(Foo, this, EventArgs.Empty);
}
}
I checked, it's work under .NET 3.5
You could use the Observer Pattern, take a look here:
http://www.dofactory.com/Patterns/PatternObserver.aspx
The observer pattern will allow you, to notify other objects which were previously defined as observer.
A very simple thread of execution with completion callback
This does not need to run in a mono behavior and is simply used for convenience
using System;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class ThreadTest : MonoBehaviour
{
private List<int> numbers = null;
private void Start()
{
Debug.Log("1. Call thread task");
StartMyLongRunningTask();
Debug.Log("2. Do something else");
}
private void StartMyLongRunningTask()
{
numbers = new List<int>();
ThreadStart starter = myLongRunningTask;
starter += () =>
{
myLongRunningTaskDone();
};
Thread _thread = new Thread(starter) { IsBackground = true };
_thread.Start();
}
private void myLongRunningTaskDone()
{
Debug.Log("3. Task callback result");
foreach (int num in numbers)
Debug.Log(num);
}
private void myLongRunningTask()
{
for (int i = 0; i < 10; i++)
{
numbers.Add(i);
Thread.Sleep(1000);
}
}
}
Try to use ManualRestEvent to signal of thread complete.
Maybe using conditional variables and mutex, or some functions like wait(), signal(), maybe timed wait() to not block main thread infinitely.
In C# this will be:
void Notify()
{
lock (syncPrimitive)
{
Monitor.Pulse(syncPrimitive);
}
}
void RunLoop()
{
for (;;)
{
// do work here...
lock (syncPrimitive)
{
Monitor.Wait(syncPrimitive);
}
}
}
more on that here:
Condition Variables C#/.NET
It is the concept of Monitor object in C#, you also have version that enables to set timeout
public static bool Wait(
object obj,
TimeSpan timeout
)
more on that here:
https://msdn.microsoft.com/en-us/library/system.threading.monitor_methods(v=vs.110).aspx

Event gets triggered after timeout is over

I have to wait for an event to be triggered. My initial solution was to use AutoResetEvent and WaitOne(), but the event was always triggered just after the waiting timeout was over. So I went back to the approach below, but I still have the same problem. 2 or 3 seconds after the timeout is over the event gets triggered no matter what the timeout was.
_wait = true;
_delayedResponse = null;
var thread = new Thread(delegate
{
while (_wait)
{
Thread.Sleep(500);
if (_delayedResponse != null)
return;
}
});
thread.Start();
var received = thread.Join(_responseTimeout);
_wait = false;
if (!received)
throw new TimeoutException(
"Timeout for waiting for response reached.");
return _delayedResponse;
Here is the event handler code:
private void OnResponseArrived(object sender, ResponseEventArgs args)
{
_delayedResponse = args.VerificationResponse;
}
The event itself is triggered from another functions that calls the function above.
Basically it looks like this:
var result = DoStuff(); // Library function that is responsible for the event
if (result.Status == Status.Wait)
Wait(); // Function above
Does anyone have an idea what causes this problem and how I can solve it?
EDIT: No longer relevant. Forwarded the OnResponseArrived event, because I found no other solution in time.
Thread.Join is a blocking call - it'll stop the thread you're calling from doing any other work. My guess is that you're waiting for the event on a background thread, but the code that will raise your event is running on the same thread as the code you posted runs in.
By calling thread.Join you're blocking the thread that should be doing your processing. So, you wait for your timeout to expire... then whichever method your posted code is in completes... then your processing actually happens and the ResponseArrived event is raised.
It would be useful if you'd post the rest of your code, but the gist of the solution will be to run the actual work (whatever code raises the ResponseArrived event) in a background thread - and remove the extra threading from the code you posted.
EDIT in response to comment...
In order to synchronise your two pieces of code, you can use an AutoResetEvent. Instead of using Thread.Sleep and your other code, try something like this:
// create an un-signalled AutoResetEvent
AutoResetEvent _waitForResponse = new AutoResetEvent(false);
void YourNewWorkerMethod()
{
_delayedResponse = null;
var result = DoStuff();
// this causes the current thread to wait for the AutoResetEvent to be signalled
// ... the parameter is a timeout value in milliseconds
if (!_waitForResponse.WaitOne(5000))
throw new TimeOutException();
return _delayedResponse;
}
private void OnResponseArrived(object sender, ResponseEventArgs args)
{
_delayedResponse = args.VerificationResponse;
_waitForResponse.Set(); // this signals the waiting thread to continue...
}
Note that you'll need to dispose of the AutoResetEvent when you're done with it.
Well, the first thing you need to do is make sure that DoStuff actually works in a background thread.
If that is correct, the way your code is written right now, you don't event need to spawn a second thread, just to join it one line below, something like this would simply work (as a test):
// handler needs to be attached before starting
library.ResponseReceived += OnResponseReceived;
// call the method
var result = library.DoStuff();
// poll and sleep, but 10 times max (5s)
int watchdog = 10;
while (_delayedResponse == null && watchdog-- > 0)
Thread.Sleep(500);
// detach handler - always clean up after yourself
library.ResponseReceived -= OnResponseReceived;
Console.WriteLine(_delayedResponse != null);
If this works, and you are programming a WinForms app, then you should consider doing the entire thing in a background thread, and then notifying the UI when it's finished. Of course, you will need to provide more details if you need help with that.

Execute a statement after thread gets over

I am having a function where I have used a thread in c#.net.
I am having a another function on the next line of that thread. But this function has to be called only after the thread gets executed.
How can i do it ?
Example..
Somefunction()
{
// thread //(thread started)
add() (another function but need to be executed only tha above thread gets over)
}
Use a BackgroundWorker and include the function call in the worker completeted event handler.
var worker = new BackgroundWorker();
_worker.DoWork += delegate { DoStuff(); };
_worker.RunWorkerCompleted += worker_RunWorkerCompleted;
_worker.RunWorkerAsync();
[...]
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
/// Do post-thread stuff
}
Use Thread.Join to block the current thread until the specified thread has finished execution.
Why start a separate thread if you want execution to be single threaded?
by "after the thread gets executed", do you mean it must have started? or it must have finished?
If you mean finished, then you would typically Join() the thread - but there is no point Join()ing a thread you have stared in the line before (just execute the code directly). The other approach is to use a "callback" at the end of the threaded method.
If you mean started, then you can do things like:
object lockObj = new object();
lock(lockObj) {
ThreadPool.QueueUserWorkItem(delegate {
lock(lockObj) {
Monitor.Pulse(lockObj);
}
// do things (we're on the second thread...)
});
Monitor.Wait(lockObj);
}
// thread has definitely started here
You can use , for instance, a ManualResetEvent.
When you start the processing on the other thread, you call the reset method.
When processing on the other thread has finished, you call set.
Then, the method that must be executed on the 'main thread', needs to wait until the ManualResetEvent has been set before it can execute.
For more info, you can have a look at the ManualResetEvent at MSDN.
If add() is thread safe, just call it at the end of the function you pass to create thread.
You could try the following, it may not work for your scenario: I can't tell given the amount of detail you provided:
First create a delegate:
public delegate int MyThreadSignature(Something arg);
Then use the Begin/End Invoke pattern:
var thread = new MyThreadSignature(WorkerMethod);
thread.BeginInvoke(theArg,
MyThreadEnded, /*Method to call when done*/,
thread /*AsyncState*/);
Create the MyThreadEnded method:
void MyThreadEnded(IAsyncResult result)
{
var thread = (MyThreadSignature)result.AsyncState;
var result = thread.EndInvoke(result);
// Call your next worker here.
}
The method to call MUST have the signature in the example: Name(IAsyncResult result).

Categories