fix audio thread deadlocks
parent
52c4fa26c7
commit
8aa9ce550d
|
@ -28,7 +28,7 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<WeakReference<AudioResource>> resources = new List<WeakReference<AudioResource>>();
|
private readonly List<WeakReference<AudioResource>> resources = new List<WeakReference<AudioResource>>();
|
||||||
private readonly List<WeakReference<StreamingSound>> streamingSounds = new List<WeakReference<StreamingSound>>();
|
private readonly List<StreamingSound> streamingSounds = new List<StreamingSound>();
|
||||||
|
|
||||||
private AudioTweenPool AudioTweenPool = new AudioTweenPool();
|
private AudioTweenPool AudioTweenPool = new AudioTweenPool();
|
||||||
private readonly List<AudioTween> AudioTweens = new List<AudioTween>();
|
private readonly List<AudioTween> AudioTweens = new List<AudioTween>();
|
||||||
|
@ -39,7 +39,7 @@ namespace MoonWorks.Audio
|
||||||
private long previousTickTime;
|
private long previousTickTime;
|
||||||
private Thread Thread;
|
private Thread Thread;
|
||||||
private AutoResetEvent WakeSignal;
|
private AutoResetEvent WakeSignal;
|
||||||
private readonly object StateLock = new object();
|
internal readonly object StateLock = new object();
|
||||||
|
|
||||||
private bool IsDisposed;
|
private bool IsDisposed;
|
||||||
|
|
||||||
|
@ -152,15 +152,7 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
for (var i = streamingSounds.Count - 1; i >= 0; i--)
|
for (var i = streamingSounds.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var weakReference = streamingSounds[i];
|
streamingSounds[i].Update();
|
||||||
if (weakReference.TryGetTarget(out var streamingSound))
|
|
||||||
{
|
|
||||||
streamingSound.Update();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
streamingSounds.RemoveAt(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = AudioTweens.Count - 1; i >= 0; i--)
|
for (var i = AudioTweens.Count - 1; i >= 0; i--)
|
||||||
|
@ -262,7 +254,12 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
private void AddDynamicSoundInstance(StreamingSound instance)
|
private void AddDynamicSoundInstance(StreamingSound instance)
|
||||||
{
|
{
|
||||||
streamingSounds.Add(new WeakReference<StreamingSound>(instance));
|
streamingSounds.Add(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveDynamicSoundInstance(StreamingSound reference)
|
||||||
|
{
|
||||||
|
streamingSounds.Remove(reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddResourceReference(AudioResource resource, WeakReference<AudioResource> resourceReference)
|
internal void AddResourceReference(AudioResource resource, WeakReference<AudioResource> resourceReference)
|
||||||
|
@ -278,11 +275,16 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveResourceReference(WeakReference<AudioResource> resourceReference)
|
internal void RemoveResourceReference(AudioResource resource, WeakReference<AudioResource> resourceReference)
|
||||||
{
|
{
|
||||||
lock (StateLock)
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
resources.Remove(resourceReference);
|
resources.Remove(resourceReference);
|
||||||
|
|
||||||
|
if (resource is StreamingSound streamingSound)
|
||||||
|
{
|
||||||
|
RemoveDynamicSoundInstance(streamingSound);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +316,6 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
|
||||||
~AudioDevice()
|
~AudioDevice()
|
||||||
{
|
{
|
||||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
if (selfReference != null)
|
if (selfReference != null)
|
||||||
{
|
{
|
||||||
Device.RemoveResourceReference(selfReference);
|
Device.RemoveResourceReference(this, selfReference);
|
||||||
selfReference = null;
|
selfReference = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,8 @@ namespace MoonWorks.Audio
|
||||||
internal unsafe void Update()
|
internal unsafe void Update()
|
||||||
{
|
{
|
||||||
lock (StateLock)
|
lock (StateLock)
|
||||||
|
{
|
||||||
|
if (!IsDisposed)
|
||||||
{
|
{
|
||||||
if (State != SoundState.Playing)
|
if (State != SoundState.Playing)
|
||||||
{
|
{
|
||||||
|
@ -100,6 +102,7 @@ namespace MoonWorks.Audio
|
||||||
QueueBuffers();
|
QueueBuffers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void QueueBuffers()
|
protected void QueueBuffers()
|
||||||
{
|
{
|
||||||
|
@ -181,6 +184,8 @@ namespace MoonWorks.Audio
|
||||||
protected unsafe override void Destroy()
|
protected unsafe override void Destroy()
|
||||||
{
|
{
|
||||||
lock (StateLock)
|
lock (StateLock)
|
||||||
|
{
|
||||||
|
if (!IsDisposed)
|
||||||
{
|
{
|
||||||
StopImmediate();
|
StopImmediate();
|
||||||
|
|
||||||
|
@ -191,4 +196,5 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,9 +88,14 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
|
|
||||||
protected unsafe override void Destroy()
|
protected unsafe override void Destroy()
|
||||||
|
{
|
||||||
|
base.Destroy();
|
||||||
|
|
||||||
|
if (!IsDisposed)
|
||||||
{
|
{
|
||||||
FAudio.stb_vorbis_close(VorbisHandle);
|
FAudio.stb_vorbis_close(VorbisHandle);
|
||||||
NativeMemory.Free((void*) FileDataPtr);
|
NativeMemory.Free((void*) FileDataPtr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,11 @@ namespace MoonWorks.Video
|
||||||
) {
|
) {
|
||||||
var lengthInFloats = bufferLengthInBytes / sizeof(float);
|
var lengthInFloats = bufferLengthInBytes / sizeof(float);
|
||||||
|
|
||||||
|
// FIXME: this gets gnarly with theorafile being not thread safe
|
||||||
|
// is there some way we could just manually update in VideoPlayer
|
||||||
|
// instead of going through AudioDevice?
|
||||||
|
lock (Device.StateLock)
|
||||||
|
{
|
||||||
int samples = Theorafile.tf_readaudio(
|
int samples = Theorafile.tf_readaudio(
|
||||||
VideoHandle,
|
VideoHandle,
|
||||||
(IntPtr) buffer,
|
(IntPtr) buffer,
|
||||||
|
@ -41,6 +46,7 @@ namespace MoonWorks.Video
|
||||||
filledLengthInBytes = samples * sizeof(float);
|
filledLengthInBytes = samples * sizeof(float);
|
||||||
reachedEnd = Theorafile.tf_eos(VideoHandle) == 1;
|
reachedEnd = Theorafile.tf_eos(VideoHandle) == 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnReachedEnd() { }
|
protected override void OnReachedEnd() { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace MoonWorks.Video
|
||||||
private int yWidth;
|
private int yWidth;
|
||||||
private int yHeight;
|
private int yHeight;
|
||||||
|
|
||||||
private bool disposed;
|
private bool IsDisposed;
|
||||||
|
|
||||||
public Video(string filename)
|
public Video(string filename)
|
||||||
{
|
{
|
||||||
|
@ -89,7 +89,7 @@ namespace MoonWorks.Video
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!disposed)
|
if (!IsDisposed)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
|
@ -100,7 +100,7 @@ namespace MoonWorks.Video
|
||||||
Theorafile.tf_close(ref Handle);
|
Theorafile.tf_close(ref Handle);
|
||||||
NativeMemory.Free(videoData);
|
NativeMemory.Free(videoData);
|
||||||
|
|
||||||
disposed = true;
|
IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -181,18 +181,22 @@ namespace MoonWorks.Video
|
||||||
lastTimestamp = 0;
|
lastTimestamp = 0;
|
||||||
timeElapsed = 0;
|
timeElapsed = 0;
|
||||||
|
|
||||||
if (audioStream != null)
|
lock (AudioDevice.StateLock)
|
||||||
{
|
{
|
||||||
audioStream.StopImmediate();
|
DestroyAudioStream();
|
||||||
audioStream.Dispose();
|
|
||||||
audioStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Theorafile.tf_reset(Video.Handle);
|
Theorafile.tf_reset(Video.Handle);
|
||||||
|
}
|
||||||
|
|
||||||
State = VideoState.Stopped;
|
State = VideoState.Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Unload()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
Video = null;
|
||||||
|
}
|
||||||
|
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
if (Video == null || State == VideoState.Stopped)
|
if (Video == null || State == VideoState.Stopped)
|
||||||
|
@ -200,6 +204,9 @@ namespace MoonWorks.Video
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Theorafile is not thread safe so we have to do this. Fun!
|
||||||
|
lock (AudioDevice.StateLock)
|
||||||
|
{
|
||||||
timeElapsed += (timer.Elapsed.TotalMilliseconds - lastTimestamp) * PlaybackSpeed;
|
timeElapsed += (timer.Elapsed.TotalMilliseconds - lastTimestamp) * PlaybackSpeed;
|
||||||
lastTimestamp = timer.Elapsed.TotalMilliseconds;
|
lastTimestamp = timer.Elapsed.TotalMilliseconds;
|
||||||
|
|
||||||
|
@ -210,7 +217,8 @@ namespace MoonWorks.Video
|
||||||
Video.Handle,
|
Video.Handle,
|
||||||
(IntPtr) yuvData,
|
(IntPtr) yuvData,
|
||||||
thisFrame - currentFrame
|
thisFrame - currentFrame
|
||||||
) == 1 || currentFrame == -1) {
|
) == 1 || currentFrame == -1)
|
||||||
|
{
|
||||||
UpdateRenderTexture();
|
UpdateRenderTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,12 +231,7 @@ namespace MoonWorks.Video
|
||||||
timer.Stop();
|
timer.Stop();
|
||||||
timer.Reset();
|
timer.Reset();
|
||||||
|
|
||||||
if (audioStream != null)
|
DestroyAudioStream();
|
||||||
{
|
|
||||||
audioStream.Stop();
|
|
||||||
audioStream.Dispose();
|
|
||||||
audioStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Theorafile.tf_reset(Video.Handle);
|
Theorafile.tf_reset(Video.Handle);
|
||||||
|
|
||||||
|
@ -245,6 +248,7 @@ namespace MoonWorks.Video
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateRenderTexture()
|
private void UpdateRenderTexture()
|
||||||
{
|
{
|
||||||
|
@ -306,14 +310,27 @@ namespace MoonWorks.Video
|
||||||
// Grab the first bit of audio. We're trying to start the decoding ASAP.
|
// Grab the first bit of audio. We're trying to start the decoding ASAP.
|
||||||
if (AudioDevice != null && Theorafile.tf_hasaudio(Video.Handle) == 1)
|
if (AudioDevice != null && Theorafile.tf_hasaudio(Video.Handle) == 1)
|
||||||
{
|
{
|
||||||
|
DestroyAudioStream();
|
||||||
|
|
||||||
int channels, sampleRate;
|
int channels, sampleRate;
|
||||||
Theorafile.tf_audioinfo(Video.Handle, out channels, out sampleRate);
|
Theorafile.tf_audioinfo(Video.Handle, out channels, out sampleRate);
|
||||||
|
|
||||||
audioStream = new StreamingSoundTheora(AudioDevice, Video.Handle, channels, (uint) sampleRate);
|
audioStream = new StreamingSoundTheora(AudioDevice, Video.Handle, channels, (uint) sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentFrame = -1;
|
currentFrame = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DestroyAudioStream()
|
||||||
|
{
|
||||||
|
if (audioStream != null)
|
||||||
|
{
|
||||||
|
audioStream.StopImmediate();
|
||||||
|
audioStream.Dispose();
|
||||||
|
audioStream = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!disposed)
|
if (!disposed)
|
||||||
|
|
Loading…
Reference in New Issue