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() | ||||
|         { | ||||
|  | @ -87,18 +81,19 @@ namespace MoonWorks.Audio | |||
|             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