refactor sound API to support generic decoding
							parent
							
								
									2d5d70106f
								
							
						
					
					
						commit
						41636e923c
					
				|  | @ -18,7 +18,7 @@ namespace MoonWorks.Audio | ||||||
| 
 | 
 | ||||||
|         internal FAudio.FAudioVoiceSends ReverbSends; |         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() |         public unsafe AudioDevice() | ||||||
|         { |         { | ||||||
|  | @ -196,23 +196,23 @@ namespace MoonWorks.Audio | ||||||
| 
 | 
 | ||||||
|         public void Update() |         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]; |                 var weakReference = streamingSounds[i]; | ||||||
|                 if (weakReference.TryGetTarget(out var dynamicSoundInstance)) |                 if (weakReference.TryGetTarget(out var streamingSound)) | ||||||
|                 { |                 { | ||||||
|                     dynamicSoundInstance.Update(); |                     streamingSound.Update(); | ||||||
|                 } |                 } | ||||||
|                 else |                 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; } |         protected AudioDevice Device { get; } | ||||||
|         internal IntPtr Handle { get; } |         internal IntPtr Handle { get; } | ||||||
|         public Sound Parent { get; } |         internal FAudio.FAudioWaveFormatEx Format { get; } | ||||||
|         public bool Loop { get; } |         public bool Loop { get; } | ||||||
| 
 | 
 | ||||||
|         protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings; |         protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings; | ||||||
|  | @ -118,10 +118,12 @@ namespace MoonWorks.Audio | ||||||
|             { |             { | ||||||
|                 _lowPassFilter = value; |                 _lowPassFilter = value; | ||||||
| 
 | 
 | ||||||
|                 FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters(); |                 FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters | ||||||
|                 p.Type = FAudio.FAudioFilterType.FAudioLowPassFilter; |                 { | ||||||
|                 p.Frequency = _lowPassFilter; |                     Type = FAudio.FAudioFilterType.FAudioLowPassFilter, | ||||||
|                 p.OneOverQ = 1f; |                     Frequency = _lowPassFilter, | ||||||
|  |                     OneOverQ = 1f | ||||||
|  |                 }; | ||||||
|                 FAudio.FAudioVoice_SetFilterParameters( |                 FAudio.FAudioVoice_SetFilterParameters( | ||||||
|                     Handle, |                     Handle, | ||||||
|                     ref p, |                     ref p, | ||||||
|  | @ -138,10 +140,12 @@ namespace MoonWorks.Audio | ||||||
|             { |             { | ||||||
|                 _highPassFilter = value; |                 _highPassFilter = value; | ||||||
| 
 | 
 | ||||||
|                 FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters(); |                 FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters | ||||||
|                 p.Type = FAudio.FAudioFilterType.FAudioHighPassFilter; |                 { | ||||||
|                 p.Frequency = _highPassFilter; |                     Type = FAudio.FAudioFilterType.FAudioHighPassFilter, | ||||||
|                 p.OneOverQ = 1f; |                     Frequency = _highPassFilter, | ||||||
|  |                     OneOverQ = 1f | ||||||
|  |                 }; | ||||||
|                 FAudio.FAudioVoice_SetFilterParameters( |                 FAudio.FAudioVoice_SetFilterParameters( | ||||||
|                     Handle, |                     Handle, | ||||||
|                     ref p, |                     ref p, | ||||||
|  | @ -158,10 +162,12 @@ namespace MoonWorks.Audio | ||||||
|             { |             { | ||||||
|                 _bandPassFilter = value; |                 _bandPassFilter = value; | ||||||
| 
 | 
 | ||||||
|                 FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters(); |                 FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters | ||||||
|                 p.Type = FAudio.FAudioFilterType.FAudioBandPassFilter; |                 { | ||||||
|                 p.Frequency = _bandPassFilter; |                     Type = FAudio.FAudioFilterType.FAudioBandPassFilter, | ||||||
|                 p.OneOverQ = 1f; |                     Frequency = _bandPassFilter, | ||||||
|  |                     OneOverQ = 1f | ||||||
|  |                 }; | ||||||
|                 FAudio.FAudioVoice_SetFilterParameters( |                 FAudio.FAudioVoice_SetFilterParameters( | ||||||
|                     Handle, |                     Handle, | ||||||
|                     ref p, |                     ref p, | ||||||
|  | @ -172,14 +178,25 @@ namespace MoonWorks.Audio | ||||||
| 
 | 
 | ||||||
|         public SoundInstance( |         public SoundInstance( | ||||||
|             AudioDevice device, |             AudioDevice device, | ||||||
|             Sound parent, |             ushort channels, | ||||||
|  |             uint samplesPerSecond, | ||||||
|             bool is3D, |             bool is3D, | ||||||
|             bool loop |             bool loop | ||||||
|         ) { |         ) { | ||||||
|             Device = device; |             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( |             FAudio.FAudio_CreateSourceVoice( | ||||||
|                 Device.Handle, |                 Device.Handle, | ||||||
|  | @ -200,7 +217,7 @@ namespace MoonWorks.Audio | ||||||
| 
 | 
 | ||||||
|             Handle = handle; |             Handle = handle; | ||||||
|             this.is3D = is3D; |             this.is3D = is3D; | ||||||
|             InitDSPSettings(Parent.Format.nChannels); |             InitDSPSettings(Format.nChannels); | ||||||
| 
 | 
 | ||||||
|             FAudio.FAudioVoice_SetOutputVoices( |             FAudio.FAudioVoice_SetOutputVoices( | ||||||
|                 handle, |                 handle, | ||||||
|  | @ -208,6 +225,7 @@ namespace MoonWorks.Audio | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|             Loop = loop; |             Loop = loop; | ||||||
|  |             State = SoundState.Stopped; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void InitDSPSettings(uint srcChannels) |         private void InitDSPSettings(uint srcChannels) | ||||||
|  |  | ||||||
|  | @ -4,10 +4,12 @@ using System.Runtime.InteropServices; | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Audio | 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; |         internal FAudio.FAudioBuffer Handle; | ||||||
|  |         public ushort Channels { get; } | ||||||
|  |         public uint SamplesPerSecond { get; } | ||||||
| 
 | 
 | ||||||
|         public uint LoopStart { get; set; } = 0; |         public uint LoopStart { get; set; } = 0; | ||||||
|         public uint LoopLength { get; set; } = 0; |         public uint LoopLength { get; set; } = 0; | ||||||
|  | @ -37,33 +39,26 @@ namespace MoonWorks.Audio | ||||||
| 
 | 
 | ||||||
|             return new StaticSound( |             return new StaticSound( | ||||||
|                 device, |                 device, | ||||||
|  |                 (ushort) info.channels, | ||||||
|  |                 info.sample_rate, | ||||||
|                 buffer, |                 buffer, | ||||||
|                 0, |                 0, | ||||||
|                 (uint) buffer.Length, |                 (uint) buffer.Length | ||||||
|                 (ushort) info.channels, |  | ||||||
|                 info.sample_rate |  | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public StaticSound( |         public StaticSound( | ||||||
|             AudioDevice device, |             AudioDevice device, | ||||||
|  |             ushort channels, | ||||||
|  |             uint samplesPerSecond, | ||||||
|             float[] buffer, |             float[] buffer, | ||||||
|             uint bufferOffset, /* in floats */ |             uint bufferOffset, /* in floats */ | ||||||
|             uint bufferLength, /* in floats */ |             uint bufferLength  /* in floats */ | ||||||
|             ushort channels, |         ) { | ||||||
|             uint samplesPerSecond |             Device = device; | ||||||
|         ) : base(device) { |  | ||||||
|             var blockAlign = (ushort)(4 * channels); |  | ||||||
| 
 | 
 | ||||||
|             Format = new FAudio.FAudioWaveFormatEx |             Channels = channels; | ||||||
|             { |             SamplesPerSecond = samplesPerSecond; | ||||||
|                 wFormatTag = 3, |  | ||||||
|                 wBitsPerSample = 32, |  | ||||||
|                 nChannels = channels, |  | ||||||
|                 nBlockAlign = blockAlign, |  | ||||||
|                 nSamplesPerSec = samplesPerSecond, |  | ||||||
|                 nAvgBytesPerSec = blockAlign * samplesPerSecond |  | ||||||
|             }; |  | ||||||
| 
 | 
 | ||||||
|             var bufferLengthInBytes = (int) (bufferLength * sizeof(float)); |             var bufferLengthInBytes = (int) (bufferLength * sizeof(float)); | ||||||
|             Handle = new FAudio.FAudioBuffer(); |             Handle = new FAudio.FAudioBuffer(); | ||||||
|  |  | ||||||
|  | @ -4,6 +4,8 @@ namespace MoonWorks.Audio | ||||||
| { | { | ||||||
|     public class StaticSoundInstance : SoundInstance |     public class StaticSoundInstance : SoundInstance | ||||||
|     { |     { | ||||||
|  |         public StaticSound Parent { get; } | ||||||
|  | 
 | ||||||
|         private SoundState _state = SoundState.Stopped; |         private SoundState _state = SoundState.Stopped; | ||||||
|         public override SoundState State |         public override SoundState State | ||||||
|         { |         { | ||||||
|  | @ -33,15 +35,13 @@ namespace MoonWorks.Audio | ||||||
|             StaticSound parent, |             StaticSound parent, | ||||||
|             bool is3D, |             bool is3D, | ||||||
|             bool loop |             bool loop | ||||||
|         ) : base(device, parent, is3D, loop) |         ) : base(device, parent.Channels, parent.SamplesPerSecond, is3D, loop) | ||||||
|         { |         { | ||||||
|             State = SoundState.Stopped; |             Parent = parent; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public void Play() |         public void Play() | ||||||
|         { |         { | ||||||
|             var parent = (StaticSound) Parent; |  | ||||||
| 
 |  | ||||||
|             if (State == SoundState.Playing) |             if (State == SoundState.Playing) | ||||||
|             { |             { | ||||||
|                 return; |                 return; | ||||||
|  | @ -49,20 +49,20 @@ namespace MoonWorks.Audio | ||||||
| 
 | 
 | ||||||
|             if (Loop) |             if (Loop) | ||||||
|             { |             { | ||||||
|                 parent.Handle.LoopCount = 255; |                 Parent.Handle.LoopCount = 255; | ||||||
|                 parent.Handle.LoopBegin = parent.LoopStart; |                 Parent.Handle.LoopBegin = Parent.LoopStart; | ||||||
|                 parent.Handle.LoopLength = parent.LoopLength; |                 Parent.Handle.LoopLength = Parent.LoopLength; | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|                 parent.Handle.LoopCount = 0; |                 Parent.Handle.LoopCount = 0; | ||||||
|                 parent.Handle.LoopBegin = 0; |                 Parent.Handle.LoopBegin = 0; | ||||||
|                 parent.Handle.LoopLength = 0; |                 Parent.Handle.LoopLength = 0; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             FAudio.FAudioSourceVoice_SubmitSourceBuffer( |             FAudio.FAudioSourceVoice_SubmitSourceBuffer( | ||||||
|                 Handle, |                 Handle, | ||||||
|                 ref parent.Handle, |                 ref Parent.Handle, | ||||||
|                 IntPtr.Zero |                 IntPtr.Zero | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -4,32 +4,26 @@ using System.Runtime.InteropServices; | ||||||
| 
 | 
 | ||||||
| namespace MoonWorks.Audio | 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 readonly List<IntPtr> queuedBuffers = new List<IntPtr>(); | ||||||
|         private List<uint> queuedSizes; |         private readonly List<uint> queuedSizes = new List<uint>(); | ||||||
|         private const int MINIMUM_BUFFER_CHECK = 3; |         private const int MINIMUM_BUFFER_CHECK = 3; | ||||||
| 
 | 
 | ||||||
|         public int PendingBufferCount => queuedBuffers.Count; |         public int PendingBufferCount => queuedBuffers.Count; | ||||||
| 
 | 
 | ||||||
|         private readonly float[] buffer; |         private bool IsDisposed; | ||||||
| 
 | 
 | ||||||
|         public override SoundState State { get; protected set; } |         public StreamingSound( | ||||||
| 
 |  | ||||||
|         internal DynamicSoundInstance( |  | ||||||
|             AudioDevice device, |             AudioDevice device, | ||||||
|             DynamicSound parent, |             ushort channels, | ||||||
|  |             uint samplesPerSecond, | ||||||
|             bool is3D, |             bool is3D, | ||||||
|             bool loop |             bool loop | ||||||
|         ) : base(device, parent, is3D, loop) |         ) : base(device, channels, samplesPerSecond, is3D, loop) { } | ||||||
|         { |  | ||||||
|             queuedBuffers = new List<IntPtr>(); |  | ||||||
|             queuedSizes = new List<uint>(); |  | ||||||
| 
 |  | ||||||
|             buffer = new float[DynamicSound.BUFFER_SIZE]; |  | ||||||
| 
 |  | ||||||
|             State = SoundState.Stopped; |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         public void Play() |         public void Play() | ||||||
|         { |         { | ||||||
|  | @ -78,27 +72,28 @@ namespace MoonWorks.Audio | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|             while (PendingBufferCount > state.BuffersQueued) |             while (PendingBufferCount > state.BuffersQueued) | ||||||
|             lock (queuedBuffers) |                 lock (queuedBuffers) | ||||||
|             { |                 { | ||||||
|                 Marshal.FreeHGlobal(queuedBuffers[0]); |                     Marshal.FreeHGlobal(queuedBuffers[0]); | ||||||
|                 queuedBuffers.RemoveAt(0); |                     queuedBuffers.RemoveAt(0); | ||||||
|             } |                 } | ||||||
| 
 | 
 | ||||||
|             QueueBuffers(); |             QueueBuffers(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void QueueBuffers() |         protected void QueueBuffers() | ||||||
|         { |         { | ||||||
|             for ( |             for ( | ||||||
|                 int i = MINIMUM_BUFFER_CHECK - PendingBufferCount; |                 int i = MINIMUM_BUFFER_CHECK - PendingBufferCount; | ||||||
|                 i > 0; |                 i > 0; | ||||||
|                 i -= 1 |                 i -= 1 | ||||||
|             ) { |             ) | ||||||
|  |             { | ||||||
|                 AddBuffer(); |                 AddBuffer(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void ClearBuffers() |         protected void ClearBuffers() | ||||||
|         { |         { | ||||||
|             lock (queuedBuffers) |             lock (queuedBuffers) | ||||||
|             { |             { | ||||||
|  | @ -111,23 +106,19 @@ namespace MoonWorks.Audio | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private void AddBuffer() |         protected void AddBuffer() | ||||||
|         { |         { | ||||||
|             var parent = (DynamicSound) Parent; |             AddBuffer( | ||||||
| 
 |                 out var buffer,  | ||||||
|             /* NOTE: this function returns samples per channel, not total samples */ |                 out var bufferOffset,  | ||||||
|             var samples = FAudio.stb_vorbis_get_samples_float_interleaved( |                 out var bufferLength, | ||||||
|                 parent.FileHandle, |                 out var reachedEnd | ||||||
|                 parent.Info.channels, |  | ||||||
|                 buffer, |  | ||||||
|                 buffer.Length |  | ||||||
|             ); |             ); | ||||||
| 
 | 
 | ||||||
|             var sampleCount = samples * parent.Info.channels; |             var lengthInBytes = bufferLength * sizeof(float); | ||||||
|             var lengthInBytes = (uint) sampleCount * sizeof(float); |  | ||||||
| 
 | 
 | ||||||
|             IntPtr next = Marshal.AllocHGlobal((int) lengthInBytes); |             IntPtr next = Marshal.AllocHGlobal((int) lengthInBytes); | ||||||
|             Marshal.Copy(buffer, 0, next, sampleCount); |             Marshal.Copy(buffer, (int) bufferOffset, next, (int) bufferLength); | ||||||
| 
 | 
 | ||||||
|             lock (queuedBuffers) |             lock (queuedBuffers) | ||||||
|             { |             { | ||||||
|  | @ -140,8 +131,8 @@ namespace MoonWorks.Audio | ||||||
|                         pAudioData = next, |                         pAudioData = next, | ||||||
|                         PlayLength = ( |                         PlayLength = ( | ||||||
|                             lengthInBytes / |                             lengthInBytes / | ||||||
|                             (uint) parent.Info.channels / |                             Format.nChannels / | ||||||
|                             (uint) (parent.Format.wBitsPerSample / 8) |                             (uint)(Format.wBitsPerSample / 8) | ||||||
|                         ) |                         ) | ||||||
|                     }; |                     }; | ||||||
| 
 | 
 | ||||||
|  | @ -158,11 +149,11 @@ namespace MoonWorks.Audio | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             /* We have reached the end of the file, what do we do? */ |             /* We have reached the end of the file, what do we do? */ | ||||||
|             if (sampleCount < buffer.Length) |             if (reachedEnd) | ||||||
|             { |             { | ||||||
|                 if (Loop) |                 if (Loop) | ||||||
|                 { |                 { | ||||||
|                     FAudio.stb_vorbis_seek_start(parent.FileHandle); |                     SeekStart(); | ||||||
|                 } |                 } | ||||||
|                 else |                 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