refactor sound API to support generic decoding
							parent
							
								
									2d5d70106f
								
							
						
					
					
						commit
						41636e923c
					
				| 
						 | 
				
			
			@ -18,7 +18,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
 | 
			
		||||
        internal FAudio.FAudioVoiceSends ReverbSends;
 | 
			
		||||
 | 
			
		||||
        private readonly List<WeakReference<DynamicSoundInstance>> dynamicSoundInstances = new List<WeakReference<DynamicSoundInstance>>();
 | 
			
		||||
        private readonly List<WeakReference<StreamingSound>> streamingSounds = new List<WeakReference<StreamingSound>>();
 | 
			
		||||
 | 
			
		||||
        public unsafe AudioDevice()
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -196,23 +196,23 @@ namespace MoonWorks.Audio
 | 
			
		|||
 | 
			
		||||
        public void Update()
 | 
			
		||||
        {
 | 
			
		||||
            for (var i = dynamicSoundInstances.Count - 1; i >= 0; i--)
 | 
			
		||||
            for (var i = streamingSounds.Count - 1; i >= 0; i--)
 | 
			
		||||
            {
 | 
			
		||||
                var weakReference = dynamicSoundInstances[i];
 | 
			
		||||
                if (weakReference.TryGetTarget(out var dynamicSoundInstance))
 | 
			
		||||
                var weakReference = streamingSounds[i];
 | 
			
		||||
                if (weakReference.TryGetTarget(out var streamingSound))
 | 
			
		||||
                {
 | 
			
		||||
                    dynamicSoundInstance.Update();
 | 
			
		||||
                    streamingSound.Update();
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    dynamicSoundInstances.RemoveAt(i);
 | 
			
		||||
                    streamingSounds.RemoveAt(i);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal void AddDynamicSoundInstance(DynamicSoundInstance instance)
 | 
			
		||||
        internal void AddDynamicSoundInstance(StreamingSound instance)
 | 
			
		||||
        {
 | 
			
		||||
            dynamicSoundInstances.Add(new WeakReference<DynamicSoundInstance>(instance));
 | 
			
		||||
            streamingSounds.Add(new WeakReference<StreamingSound>(instance));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,82 +0,0 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
 | 
			
		||||
namespace MoonWorks.Audio
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// For streaming long playback. Reads an OGG file.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class DynamicSound : Sound, IDisposable
 | 
			
		||||
    {
 | 
			
		||||
        internal override FAudio.FAudioWaveFormatEx Format { get; }
 | 
			
		||||
 | 
			
		||||
        // FIXME: what should this value be?
 | 
			
		||||
        public const int BUFFER_SIZE = 1024 * 128;
 | 
			
		||||
 | 
			
		||||
        internal IntPtr FileHandle { get; }
 | 
			
		||||
        internal FAudio.stb_vorbis_info Info { get; }
 | 
			
		||||
 | 
			
		||||
        private bool IsDisposed;
 | 
			
		||||
 | 
			
		||||
        public DynamicSound(AudioDevice device, FileInfo fileInfo) : base(device)
 | 
			
		||||
        {
 | 
			
		||||
            FileHandle = FAudio.stb_vorbis_open_filename(fileInfo.FullName, out var error, IntPtr.Zero);
 | 
			
		||||
 | 
			
		||||
            if (error != 0)
 | 
			
		||||
            {
 | 
			
		||||
                Logger.LogError("Error opening OGG file!");
 | 
			
		||||
                throw new AudioLoadException("Error opening OGG file!");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Info = FAudio.stb_vorbis_get_info(FileHandle);
 | 
			
		||||
 | 
			
		||||
            var blockAlign = (ushort)(4 * Info.channels);
 | 
			
		||||
 | 
			
		||||
            Format = new FAudio.FAudioWaveFormatEx
 | 
			
		||||
            {
 | 
			
		||||
                wFormatTag = 3,
 | 
			
		||||
                wBitsPerSample = 32,
 | 
			
		||||
                nChannels = (ushort) Info.channels,
 | 
			
		||||
                nBlockAlign = blockAlign,
 | 
			
		||||
                nSamplesPerSec = Info.sample_rate,
 | 
			
		||||
                nAvgBytesPerSec = blockAlign * Info.sample_rate,
 | 
			
		||||
                cbSize = 0
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public DynamicSoundInstance CreateInstance(bool loop = false)
 | 
			
		||||
        {
 | 
			
		||||
            var instance = new DynamicSoundInstance(Device, this, false, loop);
 | 
			
		||||
            Device.AddDynamicSoundInstance(instance);
 | 
			
		||||
            return instance;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected virtual void Dispose(bool disposing)
 | 
			
		||||
        {
 | 
			
		||||
            if (!IsDisposed)
 | 
			
		||||
            {
 | 
			
		||||
                if (disposing)
 | 
			
		||||
                {
 | 
			
		||||
                    // dispose managed state (managed objects)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                FAudio.stb_vorbis_close(FileHandle);
 | 
			
		||||
                IsDisposed = true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
 | 
			
		||||
        ~DynamicSound()
 | 
			
		||||
        {
 | 
			
		||||
            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 | 
			
		||||
            Dispose(disposing: false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
 | 
			
		||||
            Dispose(disposing: true);
 | 
			
		||||
            GC.SuppressFinalize(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								Sound.cs
								
								
								
								
							
							
						
						
									
										13
									
								
								Sound.cs
								
								
								
								
							| 
						 | 
				
			
			@ -1,13 +0,0 @@
 | 
			
		|||
namespace MoonWorks.Audio
 | 
			
		||||
{
 | 
			
		||||
    public abstract class Sound
 | 
			
		||||
    {
 | 
			
		||||
        internal AudioDevice Device { get; }
 | 
			
		||||
        internal abstract FAudio.FAudioWaveFormatEx Format { get; }
 | 
			
		||||
 | 
			
		||||
        public Sound(AudioDevice device)
 | 
			
		||||
        {
 | 
			
		||||
            Device = device;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
    {
 | 
			
		||||
        protected AudioDevice Device { get; }
 | 
			
		||||
        internal IntPtr Handle { get; }
 | 
			
		||||
        public Sound Parent { get; }
 | 
			
		||||
        internal FAudio.FAudioWaveFormatEx Format { get; }
 | 
			
		||||
        public bool Loop { get; }
 | 
			
		||||
 | 
			
		||||
        protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
 | 
			
		||||
| 
						 | 
				
			
			@ -118,10 +118,12 @@ namespace MoonWorks.Audio
 | 
			
		|||
            {
 | 
			
		||||
                _lowPassFilter = value;
 | 
			
		||||
 | 
			
		||||
                FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters();
 | 
			
		||||
                p.Type = FAudio.FAudioFilterType.FAudioLowPassFilter;
 | 
			
		||||
                p.Frequency = _lowPassFilter;
 | 
			
		||||
                p.OneOverQ = 1f;
 | 
			
		||||
                FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters
 | 
			
		||||
                {
 | 
			
		||||
                    Type = FAudio.FAudioFilterType.FAudioLowPassFilter,
 | 
			
		||||
                    Frequency = _lowPassFilter,
 | 
			
		||||
                    OneOverQ = 1f
 | 
			
		||||
                };
 | 
			
		||||
                FAudio.FAudioVoice_SetFilterParameters(
 | 
			
		||||
                    Handle,
 | 
			
		||||
                    ref p,
 | 
			
		||||
| 
						 | 
				
			
			@ -138,10 +140,12 @@ namespace MoonWorks.Audio
 | 
			
		|||
            {
 | 
			
		||||
                _highPassFilter = value;
 | 
			
		||||
 | 
			
		||||
                FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters();
 | 
			
		||||
                p.Type = FAudio.FAudioFilterType.FAudioHighPassFilter;
 | 
			
		||||
                p.Frequency = _highPassFilter;
 | 
			
		||||
                p.OneOverQ = 1f;
 | 
			
		||||
                FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters
 | 
			
		||||
                {
 | 
			
		||||
                    Type = FAudio.FAudioFilterType.FAudioHighPassFilter,
 | 
			
		||||
                    Frequency = _highPassFilter,
 | 
			
		||||
                    OneOverQ = 1f
 | 
			
		||||
                };
 | 
			
		||||
                FAudio.FAudioVoice_SetFilterParameters(
 | 
			
		||||
                    Handle,
 | 
			
		||||
                    ref p,
 | 
			
		||||
| 
						 | 
				
			
			@ -158,10 +162,12 @@ namespace MoonWorks.Audio
 | 
			
		|||
            {
 | 
			
		||||
                _bandPassFilter = value;
 | 
			
		||||
 | 
			
		||||
                FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters();
 | 
			
		||||
                p.Type = FAudio.FAudioFilterType.FAudioBandPassFilter;
 | 
			
		||||
                p.Frequency = _bandPassFilter;
 | 
			
		||||
                p.OneOverQ = 1f;
 | 
			
		||||
                FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters
 | 
			
		||||
                {
 | 
			
		||||
                    Type = FAudio.FAudioFilterType.FAudioBandPassFilter,
 | 
			
		||||
                    Frequency = _bandPassFilter,
 | 
			
		||||
                    OneOverQ = 1f
 | 
			
		||||
                };
 | 
			
		||||
                FAudio.FAudioVoice_SetFilterParameters(
 | 
			
		||||
                    Handle,
 | 
			
		||||
                    ref p,
 | 
			
		||||
| 
						 | 
				
			
			@ -172,14 +178,25 @@ namespace MoonWorks.Audio
 | 
			
		|||
 | 
			
		||||
        public SoundInstance(
 | 
			
		||||
            AudioDevice device,
 | 
			
		||||
            Sound parent,
 | 
			
		||||
            ushort channels,
 | 
			
		||||
            uint samplesPerSecond,
 | 
			
		||||
            bool is3D,
 | 
			
		||||
            bool loop
 | 
			
		||||
        ) {
 | 
			
		||||
            Device = device;
 | 
			
		||||
            Parent = parent;
 | 
			
		||||
 | 
			
		||||
            FAudio.FAudioWaveFormatEx format = Parent.Format;
 | 
			
		||||
            var blockAlign = (ushort)(4 * channels);
 | 
			
		||||
            var format = new FAudio.FAudioWaveFormatEx
 | 
			
		||||
            {
 | 
			
		||||
                wFormatTag = 3,
 | 
			
		||||
                wBitsPerSample = 32,
 | 
			
		||||
                nChannels = channels,
 | 
			
		||||
                nBlockAlign = blockAlign,
 | 
			
		||||
                nSamplesPerSec = samplesPerSecond,
 | 
			
		||||
                nAvgBytesPerSec = blockAlign * samplesPerSecond
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            Format = format;
 | 
			
		||||
 | 
			
		||||
            FAudio.FAudio_CreateSourceVoice(
 | 
			
		||||
                Device.Handle,
 | 
			
		||||
| 
						 | 
				
			
			@ -200,7 +217,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
 | 
			
		||||
            Handle = handle;
 | 
			
		||||
            this.is3D = is3D;
 | 
			
		||||
            InitDSPSettings(Parent.Format.nChannels);
 | 
			
		||||
            InitDSPSettings(Format.nChannels);
 | 
			
		||||
 | 
			
		||||
            FAudio.FAudioVoice_SetOutputVoices(
 | 
			
		||||
                handle,
 | 
			
		||||
| 
						 | 
				
			
			@ -208,6 +225,7 @@ namespace MoonWorks.Audio
 | 
			
		|||
            );
 | 
			
		||||
 | 
			
		||||
            Loop = loop;
 | 
			
		||||
            State = SoundState.Stopped;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void InitDSPSettings(uint srcChannels)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,10 +4,12 @@ using System.Runtime.InteropServices;
 | 
			
		|||
 | 
			
		||||
namespace MoonWorks.Audio
 | 
			
		||||
{
 | 
			
		||||
    public class StaticSound : Sound, IDisposable
 | 
			
		||||
    public class StaticSound : IDisposable
 | 
			
		||||
    {
 | 
			
		||||
        internal override FAudio.FAudioWaveFormatEx Format { get; }
 | 
			
		||||
        internal AudioDevice Device { get; }
 | 
			
		||||
        internal FAudio.FAudioBuffer Handle;
 | 
			
		||||
        public ushort Channels { get; }
 | 
			
		||||
        public uint SamplesPerSecond { get; }
 | 
			
		||||
 | 
			
		||||
        public uint LoopStart { get; set; } = 0;
 | 
			
		||||
        public uint LoopLength { get; set; } = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -37,33 +39,26 @@ namespace MoonWorks.Audio
 | 
			
		|||
 | 
			
		||||
            return new StaticSound(
 | 
			
		||||
                device,
 | 
			
		||||
                (ushort) info.channels,
 | 
			
		||||
                info.sample_rate,
 | 
			
		||||
                buffer,
 | 
			
		||||
                0,
 | 
			
		||||
                (uint) buffer.Length,
 | 
			
		||||
                (ushort) info.channels,
 | 
			
		||||
                info.sample_rate
 | 
			
		||||
                (uint) buffer.Length
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public StaticSound(
 | 
			
		||||
            AudioDevice device,
 | 
			
		||||
            ushort channels,
 | 
			
		||||
            uint samplesPerSecond,
 | 
			
		||||
            float[] buffer,
 | 
			
		||||
            uint bufferOffset, /* in floats */
 | 
			
		||||
            uint bufferLength, /* in floats */
 | 
			
		||||
            ushort channels,
 | 
			
		||||
            uint samplesPerSecond
 | 
			
		||||
        ) : base(device) {
 | 
			
		||||
            var blockAlign = (ushort)(4 * channels);
 | 
			
		||||
            uint bufferLength  /* in floats */
 | 
			
		||||
        ) {
 | 
			
		||||
            Device = device;
 | 
			
		||||
 | 
			
		||||
            Format = new FAudio.FAudioWaveFormatEx
 | 
			
		||||
            {
 | 
			
		||||
                wFormatTag = 3,
 | 
			
		||||
                wBitsPerSample = 32,
 | 
			
		||||
                nChannels = channels,
 | 
			
		||||
                nBlockAlign = blockAlign,
 | 
			
		||||
                nSamplesPerSec = samplesPerSecond,
 | 
			
		||||
                nAvgBytesPerSec = blockAlign * samplesPerSecond
 | 
			
		||||
            };
 | 
			
		||||
            Channels = channels;
 | 
			
		||||
            SamplesPerSecond = samplesPerSecond;
 | 
			
		||||
 | 
			
		||||
            var bufferLengthInBytes = (int) (bufferLength * sizeof(float));
 | 
			
		||||
            Handle = new FAudio.FAudioBuffer();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,8 @@ namespace MoonWorks.Audio
 | 
			
		|||
{
 | 
			
		||||
    public class StaticSoundInstance : SoundInstance
 | 
			
		||||
    {
 | 
			
		||||
        public StaticSound Parent { get; }
 | 
			
		||||
 | 
			
		||||
        private SoundState _state = SoundState.Stopped;
 | 
			
		||||
        public override SoundState State
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -33,15 +35,13 @@ namespace MoonWorks.Audio
 | 
			
		|||
            StaticSound parent,
 | 
			
		||||
            bool is3D,
 | 
			
		||||
            bool loop
 | 
			
		||||
        ) : base(device, parent, is3D, loop)
 | 
			
		||||
        ) : base(device, parent.Channels, parent.SamplesPerSecond, is3D, loop)
 | 
			
		||||
        {
 | 
			
		||||
            State = SoundState.Stopped;
 | 
			
		||||
            Parent = parent;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Play()
 | 
			
		||||
        {
 | 
			
		||||
            var parent = (StaticSound) Parent;
 | 
			
		||||
 | 
			
		||||
            if (State == SoundState.Playing)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
| 
						 | 
				
			
			@ -49,20 +49,20 @@ namespace MoonWorks.Audio
 | 
			
		|||
 | 
			
		||||
            if (Loop)
 | 
			
		||||
            {
 | 
			
		||||
                parent.Handle.LoopCount = 255;
 | 
			
		||||
                parent.Handle.LoopBegin = parent.LoopStart;
 | 
			
		||||
                parent.Handle.LoopLength = parent.LoopLength;
 | 
			
		||||
                Parent.Handle.LoopCount = 255;
 | 
			
		||||
                Parent.Handle.LoopBegin = Parent.LoopStart;
 | 
			
		||||
                Parent.Handle.LoopLength = Parent.LoopLength;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                parent.Handle.LoopCount = 0;
 | 
			
		||||
                parent.Handle.LoopBegin = 0;
 | 
			
		||||
                parent.Handle.LoopLength = 0;
 | 
			
		||||
                Parent.Handle.LoopCount = 0;
 | 
			
		||||
                Parent.Handle.LoopBegin = 0;
 | 
			
		||||
                Parent.Handle.LoopLength = 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            FAudio.FAudioSourceVoice_SubmitSourceBuffer(
 | 
			
		||||
                Handle,
 | 
			
		||||
                ref parent.Handle,
 | 
			
		||||
                ref Parent.Handle,
 | 
			
		||||
                IntPtr.Zero
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,32 +4,26 @@ using System.Runtime.InteropServices;
 | 
			
		|||
 | 
			
		||||
namespace MoonWorks.Audio
 | 
			
		||||
{
 | 
			
		||||
    public class DynamicSoundInstance : SoundInstance
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// For streaming long playback. Reads an OGG file.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public abstract class StreamingSound : SoundInstance
 | 
			
		||||
    {
 | 
			
		||||
        private List<IntPtr> queuedBuffers;
 | 
			
		||||
        private List<uint> queuedSizes;
 | 
			
		||||
        private readonly List<IntPtr> queuedBuffers = new List<IntPtr>();
 | 
			
		||||
        private readonly List<uint> queuedSizes = new List<uint>();
 | 
			
		||||
        private const int MINIMUM_BUFFER_CHECK = 3;
 | 
			
		||||
 | 
			
		||||
        public int PendingBufferCount => queuedBuffers.Count;
 | 
			
		||||
 | 
			
		||||
        private readonly float[] buffer;
 | 
			
		||||
        private bool IsDisposed;
 | 
			
		||||
 | 
			
		||||
        public override SoundState State { get; protected set; }
 | 
			
		||||
 | 
			
		||||
        internal DynamicSoundInstance(
 | 
			
		||||
        public StreamingSound(
 | 
			
		||||
            AudioDevice device,
 | 
			
		||||
            DynamicSound parent,
 | 
			
		||||
            ushort channels,
 | 
			
		||||
            uint samplesPerSecond,
 | 
			
		||||
            bool is3D,
 | 
			
		||||
            bool loop
 | 
			
		||||
        ) : base(device, parent, is3D, loop)
 | 
			
		||||
        {
 | 
			
		||||
            queuedBuffers = new List<IntPtr>();
 | 
			
		||||
            queuedSizes = new List<uint>();
 | 
			
		||||
 | 
			
		||||
            buffer = new float[DynamicSound.BUFFER_SIZE];
 | 
			
		||||
 | 
			
		||||
            State = SoundState.Stopped;
 | 
			
		||||
        }
 | 
			
		||||
        ) : base(device, channels, samplesPerSecond, is3D, loop) { }
 | 
			
		||||
 | 
			
		||||
        public void Play()
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -78,27 +72,28 @@ namespace MoonWorks.Audio
 | 
			
		|||
            );
 | 
			
		||||
 | 
			
		||||
            while (PendingBufferCount > state.BuffersQueued)
 | 
			
		||||
            lock (queuedBuffers)
 | 
			
		||||
            {
 | 
			
		||||
                Marshal.FreeHGlobal(queuedBuffers[0]);
 | 
			
		||||
                queuedBuffers.RemoveAt(0);
 | 
			
		||||
            }
 | 
			
		||||
                lock (queuedBuffers)
 | 
			
		||||
                {
 | 
			
		||||
                    Marshal.FreeHGlobal(queuedBuffers[0]);
 | 
			
		||||
                    queuedBuffers.RemoveAt(0);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            QueueBuffers();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void QueueBuffers()
 | 
			
		||||
        protected void QueueBuffers()
 | 
			
		||||
        {
 | 
			
		||||
            for (
 | 
			
		||||
                int i = MINIMUM_BUFFER_CHECK - PendingBufferCount;
 | 
			
		||||
                i > 0;
 | 
			
		||||
                i -= 1
 | 
			
		||||
            ) {
 | 
			
		||||
            )
 | 
			
		||||
            {
 | 
			
		||||
                AddBuffer();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ClearBuffers()
 | 
			
		||||
        protected void ClearBuffers()
 | 
			
		||||
        {
 | 
			
		||||
            lock (queuedBuffers)
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			@ -111,23 +106,19 @@ namespace MoonWorks.Audio
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void AddBuffer()
 | 
			
		||||
        protected void AddBuffer()
 | 
			
		||||
        {
 | 
			
		||||
            var parent = (DynamicSound) Parent;
 | 
			
		||||
 | 
			
		||||
            /* NOTE: this function returns samples per channel, not total samples */
 | 
			
		||||
            var samples = FAudio.stb_vorbis_get_samples_float_interleaved(
 | 
			
		||||
                parent.FileHandle,
 | 
			
		||||
                parent.Info.channels,
 | 
			
		||||
                buffer,
 | 
			
		||||
                buffer.Length
 | 
			
		||||
            AddBuffer(
 | 
			
		||||
                out var buffer, 
 | 
			
		||||
                out var bufferOffset, 
 | 
			
		||||
                out var bufferLength,
 | 
			
		||||
                out var reachedEnd
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            var sampleCount = samples * parent.Info.channels;
 | 
			
		||||
            var lengthInBytes = (uint) sampleCount * sizeof(float);
 | 
			
		||||
            var lengthInBytes = bufferLength * sizeof(float);
 | 
			
		||||
 | 
			
		||||
            IntPtr next = Marshal.AllocHGlobal((int) lengthInBytes);
 | 
			
		||||
            Marshal.Copy(buffer, 0, next, sampleCount);
 | 
			
		||||
            Marshal.Copy(buffer, (int) bufferOffset, next, (int) bufferLength);
 | 
			
		||||
 | 
			
		||||
            lock (queuedBuffers)
 | 
			
		||||
            {
 | 
			
		||||
| 
						 | 
				
			
			@ -140,8 +131,8 @@ namespace MoonWorks.Audio
 | 
			
		|||
                        pAudioData = next,
 | 
			
		||||
                        PlayLength = (
 | 
			
		||||
                            lengthInBytes /
 | 
			
		||||
                            (uint) parent.Info.channels /
 | 
			
		||||
                            (uint) (parent.Format.wBitsPerSample / 8)
 | 
			
		||||
                            Format.nChannels /
 | 
			
		||||
                            (uint)(Format.wBitsPerSample / 8)
 | 
			
		||||
                        )
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -158,11 +149,11 @@ namespace MoonWorks.Audio
 | 
			
		|||
            }
 | 
			
		||||
 | 
			
		||||
            /* We have reached the end of the file, what do we do? */
 | 
			
		||||
            if (sampleCount < buffer.Length)
 | 
			
		||||
            if (reachedEnd)
 | 
			
		||||
            {
 | 
			
		||||
                if (Loop)
 | 
			
		||||
                {
 | 
			
		||||
                    FAudio.stb_vorbis_seek_start(parent.FileHandle);
 | 
			
		||||
                    SeekStart();
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
| 
						 | 
				
			
			@ -170,5 +161,31 @@ namespace MoonWorks.Audio
 | 
			
		|||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected abstract void AddBuffer(
 | 
			
		||||
            out float[] buffer,
 | 
			
		||||
            out uint bufferOffset, /* in floats */
 | 
			
		||||
            out uint bufferLength, /* in floats */
 | 
			
		||||
            out bool reachedEnd
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        protected abstract void SeekStart();
 | 
			
		||||
 | 
			
		||||
        protected override void Dispose(bool disposing)
 | 
			
		||||
        {
 | 
			
		||||
            if (!IsDisposed)
 | 
			
		||||
            {
 | 
			
		||||
                if (disposing)
 | 
			
		||||
                {
 | 
			
		||||
                    // dispose managed state (managed objects)
 | 
			
		||||
                    Stop(true);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // dispose unmanaged state
 | 
			
		||||
                IsDisposed = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            base.Dispose(disposing);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
 | 
			
		||||
namespace MoonWorks.Audio
 | 
			
		||||
{
 | 
			
		||||
    public class StreamingSoundOgg : StreamingSound
 | 
			
		||||
    {
 | 
			
		||||
        // FIXME: what should this value be?
 | 
			
		||||
        public const int BUFFER_SIZE = 1024 * 128;
 | 
			
		||||
 | 
			
		||||
        internal IntPtr FileHandle { get; }
 | 
			
		||||
        internal FAudio.stb_vorbis_info Info { get; }
 | 
			
		||||
 | 
			
		||||
        private readonly float[] buffer;
 | 
			
		||||
 | 
			
		||||
        public override SoundState State { get; protected set; }
 | 
			
		||||
 | 
			
		||||
        private bool IsDisposed;
 | 
			
		||||
 | 
			
		||||
        public static StreamingSoundOgg Load(
 | 
			
		||||
            AudioDevice device, 
 | 
			
		||||
            FileInfo fileInfo,
 | 
			
		||||
            bool is3D = false,
 | 
			
		||||
            bool loop = false
 | 
			
		||||
        ) {
 | 
			
		||||
            var fileHandle = FAudio.stb_vorbis_open_filename(fileInfo.FullName, out var error, IntPtr.Zero);
 | 
			
		||||
            if (error != 0)
 | 
			
		||||
            {
 | 
			
		||||
                Logger.LogError("Error opening OGG file!");
 | 
			
		||||
                throw new AudioLoadException("Error opening OGG file!");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var info = FAudio.stb_vorbis_get_info(fileHandle);
 | 
			
		||||
 | 
			
		||||
            return new StreamingSoundOgg(
 | 
			
		||||
                device,
 | 
			
		||||
                fileHandle,
 | 
			
		||||
                info,
 | 
			
		||||
                is3D,
 | 
			
		||||
                loop
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        internal StreamingSoundOgg(
 | 
			
		||||
            AudioDevice device,
 | 
			
		||||
            IntPtr fileHandle,
 | 
			
		||||
            FAudio.stb_vorbis_info info,
 | 
			
		||||
            bool is3D,
 | 
			
		||||
            bool loop
 | 
			
		||||
        ) : base(device, (ushort) info.channels, info.sample_rate, is3D, loop)
 | 
			
		||||
        {
 | 
			
		||||
            FileHandle = fileHandle;
 | 
			
		||||
            Info = info;
 | 
			
		||||
            buffer = new float[BUFFER_SIZE];
 | 
			
		||||
 | 
			
		||||
            device.AddDynamicSoundInstance(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void AddBuffer(
 | 
			
		||||
            out float[] buffer,
 | 
			
		||||
            out uint bufferOffset,
 | 
			
		||||
            out uint bufferLength,
 | 
			
		||||
            out bool reachedEnd
 | 
			
		||||
        ) {
 | 
			
		||||
            buffer = this.buffer;
 | 
			
		||||
 | 
			
		||||
            /* NOTE: this function returns samples per channel, not total samples */
 | 
			
		||||
            var samples = FAudio.stb_vorbis_get_samples_float_interleaved(
 | 
			
		||||
                FileHandle,
 | 
			
		||||
                Info.channels,
 | 
			
		||||
                buffer,
 | 
			
		||||
                buffer.Length
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            var sampleCount = samples * Info.channels;
 | 
			
		||||
            bufferOffset = 0;
 | 
			
		||||
            bufferLength = (uint) sampleCount;
 | 
			
		||||
            reachedEnd = sampleCount < buffer.Length;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void SeekStart()
 | 
			
		||||
        {
 | 
			
		||||
            FAudio.stb_vorbis_seek_start(FileHandle);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Dispose(bool disposing)
 | 
			
		||||
        {
 | 
			
		||||
            if (!IsDisposed)
 | 
			
		||||
            {
 | 
			
		||||
                if (disposing)
 | 
			
		||||
                {
 | 
			
		||||
                    // dispose managed state (managed objects)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // dispose unmanaged state
 | 
			
		||||
                FAudio.stb_vorbis_close(FileHandle);
 | 
			
		||||
 | 
			
		||||
                IsDisposed = true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            base.Dispose(disposing);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue