audio interpolation support + audio threading
parent
040bf1c184
commit
f70caa1a42
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using EasingFunction = System.Func<float, float>;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
|
@ -29,10 +30,16 @@ 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<WeakReference<StreamingSound>> streamingSounds = new List<WeakReference<StreamingSound>>();
|
||||||
|
|
||||||
|
private AudioTweenPool AudioTweenPool = new AudioTweenPool();
|
||||||
|
private readonly List<AudioTween> AudioTweens = new List<AudioTween>();
|
||||||
|
|
||||||
private const int Step = 200;
|
private const int Step = 200;
|
||||||
private TimeSpan UpdateInterval;
|
private TimeSpan UpdateInterval;
|
||||||
|
private System.Diagnostics.Stopwatch TickStopwatch = new System.Diagnostics.Stopwatch();
|
||||||
|
private long previousTickTime;
|
||||||
private Thread Thread;
|
private Thread Thread;
|
||||||
private AutoResetEvent WakeSignal;
|
private AutoResetEvent WakeSignal;
|
||||||
|
private readonly object StateLock = new object();
|
||||||
|
|
||||||
private bool IsDisposed;
|
private bool IsDisposed;
|
||||||
|
|
||||||
|
@ -119,13 +126,19 @@ namespace MoonWorks.Audio
|
||||||
Thread = new Thread(ThreadMain);
|
Thread = new Thread(ThreadMain);
|
||||||
Thread.IsBackground = true;
|
Thread.IsBackground = true;
|
||||||
Thread.Start();
|
Thread.Start();
|
||||||
|
|
||||||
|
TickStopwatch.Start();
|
||||||
|
previousTickTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ThreadMain()
|
private void ThreadMain()
|
||||||
{
|
{
|
||||||
while (!IsDisposed)
|
while (!IsDisposed)
|
||||||
|
{
|
||||||
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
ThreadMainTick();
|
ThreadMainTick();
|
||||||
|
}
|
||||||
|
|
||||||
WakeSignal.WaitOne(UpdateInterval);
|
WakeSignal.WaitOne(UpdateInterval);
|
||||||
}
|
}
|
||||||
|
@ -133,6 +146,10 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
private void ThreadMainTick()
|
private void ThreadMainTick()
|
||||||
{
|
{
|
||||||
|
long tickDelta = TickStopwatch.Elapsed.Ticks - previousTickTime;
|
||||||
|
previousTickTime = TickStopwatch.Elapsed.Ticks;
|
||||||
|
float elapsedSeconds = (float) tickDelta / System.TimeSpan.TicksPerSecond;
|
||||||
|
|
||||||
for (var i = streamingSounds.Count - 1; i >= 0; i--)
|
for (var i = streamingSounds.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var weakReference = streamingSounds[i];
|
var weakReference = streamingSounds[i];
|
||||||
|
@ -145,11 +162,26 @@ namespace MoonWorks.Audio
|
||||||
streamingSounds.RemoveAt(i);
|
streamingSounds.RemoveAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock (AudioTweens)
|
||||||
|
{
|
||||||
|
for (var i = AudioTweens.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
bool finished = true;
|
||||||
|
var audioTween = AudioTweens[i];
|
||||||
|
|
||||||
|
if (audioTween.SoundInstanceReference.TryGetTarget(out var soundInstance))
|
||||||
|
{
|
||||||
|
finished = UpdateAudioTween(audioTween, soundInstance, elapsedSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void WakeThread()
|
if (finished)
|
||||||
{
|
{
|
||||||
WakeSignal.Set();
|
AudioTweenPool.Free(audioTween);
|
||||||
|
AudioTweens.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SyncPlay()
|
public void SyncPlay()
|
||||||
|
@ -157,14 +189,91 @@ namespace MoonWorks.Audio
|
||||||
FAudio.FAudio_CommitChanges(Handle, 1);
|
FAudio.FAudio_CommitChanges(Handle, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void CreateTween(
|
||||||
|
SoundInstance soundInstance,
|
||||||
|
AudioTweenProperty property,
|
||||||
|
EasingFunction easingFunction,
|
||||||
|
float start,
|
||||||
|
float end,
|
||||||
|
float duration
|
||||||
|
) {
|
||||||
|
var tween = AudioTweenPool.Obtain();
|
||||||
|
tween.SoundInstanceReference = new WeakReference<SoundInstance>(soundInstance);
|
||||||
|
tween.Property = property;
|
||||||
|
tween.EasingFunction = easingFunction;
|
||||||
|
tween.Start = start;
|
||||||
|
tween.End = end;
|
||||||
|
tween.Duration = duration;
|
||||||
|
tween.Time = 0;
|
||||||
|
|
||||||
|
lock (AudioTweens)
|
||||||
|
{
|
||||||
|
AudioTweens.Add(tween);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool UpdateAudioTween(AudioTween audioTween, SoundInstance soundInstance, float delta)
|
||||||
|
{
|
||||||
|
float value;
|
||||||
|
audioTween.Time += delta;
|
||||||
|
|
||||||
|
if (audioTween.Time > audioTween.Duration)
|
||||||
|
{
|
||||||
|
value = audioTween.End;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = MoonWorks.Math.Easing.Interp(
|
||||||
|
audioTween.Start,
|
||||||
|
audioTween.End,
|
||||||
|
audioTween.Time,
|
||||||
|
audioTween.Duration,
|
||||||
|
audioTween.EasingFunction
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (audioTween.Property)
|
||||||
|
{
|
||||||
|
case AudioTweenProperty.Pan:
|
||||||
|
soundInstance.Pan = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AudioTweenProperty.Pitch:
|
||||||
|
soundInstance.Pitch = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AudioTweenProperty.Volume:
|
||||||
|
soundInstance.Volume = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AudioTweenProperty.FilterFrequency:
|
||||||
|
soundInstance.FilterFrequency = value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AudioTweenProperty.Reverb:
|
||||||
|
soundInstance.Reverb = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioTween.Time > audioTween.Duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void WakeThread()
|
||||||
|
{
|
||||||
|
WakeSignal.Set();
|
||||||
|
}
|
||||||
|
|
||||||
internal void AddDynamicSoundInstance(StreamingSound instance)
|
internal void AddDynamicSoundInstance(StreamingSound instance)
|
||||||
|
{
|
||||||
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
streamingSounds.Add(new WeakReference<StreamingSound>(instance));
|
streamingSounds.Add(new WeakReference<StreamingSound>(instance));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal void AddResourceReference(WeakReference<AudioResource> resourceReference)
|
internal void AddResourceReference(WeakReference<AudioResource> resourceReference)
|
||||||
{
|
{
|
||||||
lock (resources)
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
resources.Add(resourceReference);
|
resources.Add(resourceReference);
|
||||||
}
|
}
|
||||||
|
@ -172,7 +281,7 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
internal void RemoveResourceReference(WeakReference<AudioResource> resourceReference)
|
internal void RemoveResourceReference(WeakReference<AudioResource> resourceReference)
|
||||||
{
|
{
|
||||||
lock (resources)
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
resources.Remove(resourceReference);
|
resources.Remove(resourceReference);
|
||||||
}
|
}
|
||||||
|
@ -181,6 +290,8 @@ namespace MoonWorks.Audio
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!IsDisposed)
|
if (!IsDisposed)
|
||||||
|
{
|
||||||
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
|
@ -202,6 +313,7 @@ namespace MoonWorks.Audio
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
// TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
||||||
~AudioDevice()
|
~AudioDevice()
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using EasingFunction = System.Func<float, float>;
|
||||||
|
|
||||||
|
namespace MoonWorks.Audio
|
||||||
|
{
|
||||||
|
internal enum AudioTweenProperty
|
||||||
|
{
|
||||||
|
Pan,
|
||||||
|
Pitch,
|
||||||
|
Volume,
|
||||||
|
FilterFrequency,
|
||||||
|
Reverb
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AudioTween
|
||||||
|
{
|
||||||
|
public System.WeakReference<SoundInstance> SoundInstanceReference;
|
||||||
|
public AudioTweenProperty Property;
|
||||||
|
public EasingFunction EasingFunction;
|
||||||
|
public float Time;
|
||||||
|
public float Start;
|
||||||
|
public float End;
|
||||||
|
public float Duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class AudioTweenPool
|
||||||
|
{
|
||||||
|
private Queue<AudioTween> Tweens = new Queue<AudioTween>();
|
||||||
|
|
||||||
|
public AudioTween Obtain()
|
||||||
|
{
|
||||||
|
if (Tweens.Count > 0)
|
||||||
|
{
|
||||||
|
var tween = Tweens.Dequeue();
|
||||||
|
return tween;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new AudioTween();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Free(AudioTween tween)
|
||||||
|
{
|
||||||
|
Tweens.Enqueue(tween);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using EasingFunction = System.Func<float, float>;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
|
@ -21,7 +22,7 @@ namespace MoonWorks.Audio
|
||||||
public float Pan
|
public float Pan
|
||||||
{
|
{
|
||||||
get => pan;
|
get => pan;
|
||||||
set
|
internal set
|
||||||
{
|
{
|
||||||
pan = value;
|
pan = value;
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ namespace MoonWorks.Audio
|
||||||
public float Pitch
|
public float Pitch
|
||||||
{
|
{
|
||||||
get => pitch;
|
get => pitch;
|
||||||
set
|
internal set
|
||||||
{
|
{
|
||||||
pitch = Math.MathHelper.Clamp(value, -1f, 1f);
|
pitch = Math.MathHelper.Clamp(value, -1f, 1f);
|
||||||
UpdatePitch();
|
UpdatePitch();
|
||||||
|
@ -63,7 +64,7 @@ namespace MoonWorks.Audio
|
||||||
public float Volume
|
public float Volume
|
||||||
{
|
{
|
||||||
get => volume;
|
get => volume;
|
||||||
set
|
internal set
|
||||||
{
|
{
|
||||||
volume = value;
|
volume = value;
|
||||||
FAudio.FAudioVoice_SetVolume(Voice, volume, 0);
|
FAudio.FAudioVoice_SetVolume(Voice, volume, 0);
|
||||||
|
@ -80,10 +81,10 @@ namespace MoonWorks.Audio
|
||||||
OneOverQ = 1f
|
OneOverQ = 1f
|
||||||
};
|
};
|
||||||
|
|
||||||
private float FilterFrequency
|
public float FilterFrequency
|
||||||
{
|
{
|
||||||
get => filterParameters.Frequency;
|
get => filterParameters.Frequency;
|
||||||
set
|
internal set
|
||||||
{
|
{
|
||||||
value = System.Math.Clamp(value, 0.01f, MAX_FILTER_FREQUENCY);
|
value = System.Math.Clamp(value, 0.01f, MAX_FILTER_FREQUENCY);
|
||||||
filterParameters.Frequency = value;
|
filterParameters.Frequency = value;
|
||||||
|
@ -156,7 +157,7 @@ namespace MoonWorks.Audio
|
||||||
public unsafe float Reverb
|
public unsafe float Reverb
|
||||||
{
|
{
|
||||||
get => reverb;
|
get => reverb;
|
||||||
set
|
internal set
|
||||||
{
|
{
|
||||||
if (ReverbEffect != null)
|
if (ReverbEffect != null)
|
||||||
{
|
{
|
||||||
|
@ -188,7 +189,7 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoundInstance(
|
public unsafe SoundInstance(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
ushort formatTag,
|
ushort formatTag,
|
||||||
ushort bitsPerSample,
|
ushort bitsPerSample,
|
||||||
|
@ -277,6 +278,58 @@ namespace MoonWorks.Audio
|
||||||
ReverbEffect = reverbEffect;
|
ReverbEffect = reverbEffect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: since we're using setters now, could just get rid of the property setters
|
||||||
|
|
||||||
|
public void SetPan(float targetValue)
|
||||||
|
{
|
||||||
|
Pan = targetValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPan(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Pan, easingFunction, Pan, targetValue, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPitch(float targetValue)
|
||||||
|
{
|
||||||
|
Pitch = targetValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPitch(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Pitch, easingFunction, Pan, targetValue, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float targetValue)
|
||||||
|
{
|
||||||
|
Volume = targetValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Volume, easingFunction, Volume, targetValue, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFilterFrequency(float targetValue)
|
||||||
|
{
|
||||||
|
FilterFrequency = targetValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFilterFrequency(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.FilterFrequency, easingFunction, FilterFrequency, targetValue, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetReverb(float targetValue)
|
||||||
|
{
|
||||||
|
Reverb = targetValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetReverb(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Reverb, easingFunction, Volume, targetValue, duration);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void Play();
|
public abstract void Play();
|
||||||
public abstract void QueueSyncPlay();
|
public abstract void QueueSyncPlay();
|
||||||
public abstract void Pause();
|
public abstract void Pause();
|
||||||
|
@ -297,14 +350,12 @@ namespace MoonWorks.Audio
|
||||||
);
|
);
|
||||||
|
|
||||||
dspSettings.pMatrixCoefficients = (nint) NativeMemory.Alloc(memsize);
|
dspSettings.pMatrixCoefficients = (nint) NativeMemory.Alloc(memsize);
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
byte* memPtr = (byte*) dspSettings.pMatrixCoefficients;
|
byte* memPtr = (byte*) dspSettings.pMatrixCoefficients;
|
||||||
for (uint i = 0; i < memsize; i += 1)
|
for (uint i = 0; i < memsize; i += 1)
|
||||||
{
|
{
|
||||||
memPtr[i] = 0;
|
memPtr[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
SetPanMatrixCoefficients();
|
SetPanMatrixCoefficients();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue