I am new to IDisposable and trying to figure out what I am doing wrong in my MAUI app. I have the following code where I initialize an AVAudioPlayer, and methods to Play and Dispose (irrelevant methods are disregarded below).
IVoicePlayer
inherits IDisposable
public class VoicePlayer : IVoicePlayer
{
AVAudioPlayer player;
private bool isDisposed;
public VoicePlayer(byte[] audioBytes)
{
var data = NSData.FromArray(audioBytes);
player = AVAudioPlayer.FromData(data)
?? throw new Exception("Unable to create AVAudioPlayer from data.");
//player.EnableRate = true;
player.PrepareToPlay();
}
public void Play()
{
isFinishedPlaying = false;
player.Play();
}
protected virtual void Dispose(bool disposing)
{
if (isDisposed)
return;
if (disposing)
{
// Stop the player and release the resources
// if (player != null)
// {
// if (player.Playing)
// {
// player.Stop();
// }
// player.Dispose();
// player = null;
// }
player.Stop();
player.Dispose();
}
isDisposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Destructor to catch cases where Dispose was not called
~VoicePlayer()
{
Dispose(false);
}
}
My issue is that when I use VoicePlayer as follows in my ViewModel, the audio never plays - when I have breakpoints in the Dispose method, it plays the audio while I'm waiting to step in, this makes me think that Dispose is getting called before the audio even gets a chance to play out of Debug mode.
using (var voicePlayer = new VoicePlayer(record.Bytes))
{
voicePlayer.Play();
}
But if I execute without the using statement it plays fine, but it seems the destructor is never called so the player is never released.
var voicePlayer = new VoicePlayer(record.Bytes);
voicePlayer.Play();
What I am trying to figure out is a way to Dispose the player once playback finishes.
There's a FinishedPlaying
event you can try like so...
Add a method to handle the event...
void HandleFinishedPlaying(object sender, AVStatusEventArgs e)
{
DisposeAudioPlayer();
}
Straight after the player
is initialised, wire up the handler to the event...
player.FinishedPlaying += HandleFinishedPlaying;
Define a method to dispose the player on the main thread...
void DisposeAudioPlayer()
{
if (player == null)
return;
MainThread.BeginInvokeOnMainThread(() => {
player.Stop();
player.FinishedPlaying -= HandleFinishedPlaying;
player.Dispose();
player = null;
});
}