audio interpolation support + audio threading
							parent
							
								
									040bf1c184
								
							
						
					
					
						commit
						f70caa1a42
					
				| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using EasingFunction = System.Func<float, float>;
 | 
			
		||||
 | 
			
		||||
namespace MoonWorks.Audio
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -29,10 +30,16 @@ namespace MoonWorks.Audio
 | 
			
		|||
		private readonly List<WeakReference<AudioResource>> resources = new List<WeakReference<AudioResource>>();
 | 
			
		||||
		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 TimeSpan UpdateInterval;
 | 
			
		||||
		private System.Diagnostics.Stopwatch TickStopwatch = new System.Diagnostics.Stopwatch();
 | 
			
		||||
		private long previousTickTime;
 | 
			
		||||
		private Thread Thread;
 | 
			
		||||
		private AutoResetEvent WakeSignal;
 | 
			
		||||
		private readonly object StateLock = new object();
 | 
			
		||||
 | 
			
		||||
		private bool IsDisposed;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -119,13 +126,19 @@ namespace MoonWorks.Audio
 | 
			
		|||
			Thread = new Thread(ThreadMain);
 | 
			
		||||
			Thread.IsBackground = true;
 | 
			
		||||
			Thread.Start();
 | 
			
		||||
 | 
			
		||||
			TickStopwatch.Start();
 | 
			
		||||
			previousTickTime = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void ThreadMain()
 | 
			
		||||
		{
 | 
			
		||||
			while (!IsDisposed)
 | 
			
		||||
			{
 | 
			
		||||
				lock (StateLock)
 | 
			
		||||
				{
 | 
			
		||||
					ThreadMainTick();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				WakeSignal.WaitOne(UpdateInterval);
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -133,6 +146,10 @@ namespace MoonWorks.Audio
 | 
			
		|||
 | 
			
		||||
		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--)
 | 
			
		||||
			{
 | 
			
		||||
				var weakReference = streamingSounds[i];
 | 
			
		||||
| 
						 | 
				
			
			@ -145,11 +162,26 @@ namespace MoonWorks.Audio
 | 
			
		|||
					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()
 | 
			
		||||
| 
						 | 
				
			
			@ -157,14 +189,91 @@ namespace MoonWorks.Audio
 | 
			
		|||
			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)
 | 
			
		||||
		{
 | 
			
		||||
			lock (StateLock)
 | 
			
		||||
			{
 | 
			
		||||
				streamingSounds.Add(new WeakReference<StreamingSound>(instance));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		internal void AddResourceReference(WeakReference<AudioResource> resourceReference)
 | 
			
		||||
		{
 | 
			
		||||
			lock (resources)
 | 
			
		||||
			lock (StateLock)
 | 
			
		||||
			{
 | 
			
		||||
				resources.Add(resourceReference);
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +281,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
 | 
			
		||||
		internal void RemoveResourceReference(WeakReference<AudioResource> resourceReference)
 | 
			
		||||
		{
 | 
			
		||||
			lock (resources)
 | 
			
		||||
			lock (StateLock)
 | 
			
		||||
			{
 | 
			
		||||
				resources.Remove(resourceReference);
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -181,6 +290,8 @@ namespace MoonWorks.Audio
 | 
			
		|||
		protected virtual void Dispose(bool disposing)
 | 
			
		||||
		{
 | 
			
		||||
			if (!IsDisposed)
 | 
			
		||||
			{
 | 
			
		||||
				lock (StateLock)
 | 
			
		||||
				{
 | 
			
		||||
					if (disposing)
 | 
			
		||||
					{
 | 
			
		||||
| 
						 | 
				
			
			@ -202,6 +313,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
					IsDisposed = true;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
 | 
			
		||||
		~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.Runtime.InteropServices;
 | 
			
		||||
using EasingFunction = System.Func<float, float>;
 | 
			
		||||
 | 
			
		||||
namespace MoonWorks.Audio
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +22,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
		public float Pan
 | 
			
		||||
		{
 | 
			
		||||
			get => pan;
 | 
			
		||||
			set
 | 
			
		||||
			internal set
 | 
			
		||||
			{
 | 
			
		||||
				pan = value;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +53,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
		public float Pitch
 | 
			
		||||
		{
 | 
			
		||||
			get => pitch;
 | 
			
		||||
			set
 | 
			
		||||
			internal set
 | 
			
		||||
			{
 | 
			
		||||
				pitch = Math.MathHelper.Clamp(value, -1f, 1f);
 | 
			
		||||
				UpdatePitch();
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +64,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
		public float Volume
 | 
			
		||||
		{
 | 
			
		||||
			get => volume;
 | 
			
		||||
			set
 | 
			
		||||
			internal set
 | 
			
		||||
			{
 | 
			
		||||
				volume = value;
 | 
			
		||||
				FAudio.FAudioVoice_SetVolume(Voice, volume, 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -80,10 +81,10 @@ namespace MoonWorks.Audio
 | 
			
		|||
			OneOverQ = 1f
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		private float FilterFrequency
 | 
			
		||||
		public float FilterFrequency
 | 
			
		||||
		{
 | 
			
		||||
			get => filterParameters.Frequency;
 | 
			
		||||
			set
 | 
			
		||||
			internal set
 | 
			
		||||
			{
 | 
			
		||||
				value = System.Math.Clamp(value, 0.01f, MAX_FILTER_FREQUENCY);
 | 
			
		||||
				filterParameters.Frequency = value;
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +157,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
		public unsafe float Reverb
 | 
			
		||||
		{
 | 
			
		||||
			get => reverb;
 | 
			
		||||
			set
 | 
			
		||||
			internal set
 | 
			
		||||
			{
 | 
			
		||||
				if (ReverbEffect != null)
 | 
			
		||||
				{
 | 
			
		||||
| 
						 | 
				
			
			@ -188,7 +189,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public SoundInstance(
 | 
			
		||||
		public unsafe SoundInstance(
 | 
			
		||||
			AudioDevice device,
 | 
			
		||||
			ushort formatTag,
 | 
			
		||||
			ushort bitsPerSample,
 | 
			
		||||
| 
						 | 
				
			
			@ -277,6 +278,58 @@ namespace MoonWorks.Audio
 | 
			
		|||
			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 QueueSyncPlay();
 | 
			
		||||
		public abstract void Pause();
 | 
			
		||||
| 
						 | 
				
			
			@ -297,14 +350,12 @@ namespace MoonWorks.Audio
 | 
			
		|||
			);
 | 
			
		||||
 | 
			
		||||
			dspSettings.pMatrixCoefficients = (nint) NativeMemory.Alloc(memsize);
 | 
			
		||||
			unsafe
 | 
			
		||||
			{
 | 
			
		||||
			byte* memPtr = (byte*) dspSettings.pMatrixCoefficients;
 | 
			
		||||
			for (uint i = 0; i < memsize; i += 1)
 | 
			
		||||
			{
 | 
			
		||||
				memPtr[i] = 0;
 | 
			
		||||
			}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			SetPanMatrixCoefficients();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue