more audio data and voice restructuring
							parent
							
								
									771dc6e7b3
								
							
						
					
					
						commit
						0500d94930
					
				| 
						 | 
					@ -0,0 +1,56 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace MoonWorks.Audio
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public class AudioBuffer : AudioResource
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							IntPtr BufferDataPtr;
 | 
				
			||||||
 | 
							uint BufferDataLength;
 | 
				
			||||||
 | 
							private bool OwnsBufferData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public Format Format { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public AudioBuffer(
 | 
				
			||||||
 | 
								AudioDevice device,
 | 
				
			||||||
 | 
								Format format,
 | 
				
			||||||
 | 
								IntPtr bufferPtr,
 | 
				
			||||||
 | 
								uint bufferLengthInBytes,
 | 
				
			||||||
 | 
								bool ownsBufferData) : base(device)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Format = format;
 | 
				
			||||||
 | 
								BufferDataPtr = bufferPtr;
 | 
				
			||||||
 | 
								BufferDataLength = bufferLengthInBytes;
 | 
				
			||||||
 | 
								OwnsBufferData = ownsBufferData;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public AudioBuffer CreateSubBuffer(int offset, uint length)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return new AudioBuffer(Device, Format, BufferDataPtr + offset, length, false);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public FAudio.FAudioBuffer ToFAudioBuffer(bool loop = false)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return new FAudio.FAudioBuffer
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Flags = FAudio.FAUDIO_END_OF_STREAM,
 | 
				
			||||||
 | 
									pContext = IntPtr.Zero,
 | 
				
			||||||
 | 
									pAudioData = BufferDataPtr,
 | 
				
			||||||
 | 
									AudioBytes = BufferDataLength,
 | 
				
			||||||
 | 
									PlayBegin = 0,
 | 
				
			||||||
 | 
									PlayLength = 0,
 | 
				
			||||||
 | 
									LoopBegin = 0,
 | 
				
			||||||
 | 
									LoopLength = 0,
 | 
				
			||||||
 | 
									LoopCount = loop ? FAudio.FAUDIO_LOOP_INFINITE : 0
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							protected override unsafe void Destroy()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (OwnsBufferData)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									NativeMemory.Free((void*) BufferDataPtr);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MoonWorks.Audio
 | 
					namespace MoonWorks.Audio
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	public class AudioDataOgg : AudioData
 | 
						public class AudioDataOgg : AudioDataStreamable
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private IntPtr FileDataPtr = IntPtr.Zero;
 | 
							private IntPtr FileDataPtr = IntPtr.Zero;
 | 
				
			||||||
		private IntPtr VorbisHandle = IntPtr.Zero;
 | 
							private IntPtr VorbisHandle = IntPtr.Zero;
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ namespace MoonWorks.Audio
 | 
				
			||||||
		public override bool Loaded => VorbisHandle != IntPtr.Zero;
 | 
							public override bool Loaded => VorbisHandle != IntPtr.Zero;
 | 
				
			||||||
		public override uint DecodeBufferSize => 32768;
 | 
							public override uint DecodeBufferSize => 32768;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public AudioDataOgg(string filePath)
 | 
							public AudioDataOgg(AudioDevice device, string filePath) : base(device)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			FilePath = filePath;
 | 
								FilePath = filePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,5 +92,44 @@ namespace MoonWorks.Audio
 | 
				
			||||||
				FileDataPtr = IntPtr.Zero;
 | 
									FileDataPtr = IntPtr.Zero;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public unsafe static AudioBuffer CreateBuffer(AudioDevice device, string filePath)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								var filePointer = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (error != 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									throw new AudioLoadException("Error loading file!");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								var info = FAudio.stb_vorbis_get_info(filePointer);
 | 
				
			||||||
 | 
								var lengthInFloats =
 | 
				
			||||||
 | 
									FAudio.stb_vorbis_stream_length_in_samples(filePointer) * info.channels;
 | 
				
			||||||
 | 
								var lengthInBytes = lengthInFloats * Marshal.SizeOf<float>();
 | 
				
			||||||
 | 
								var buffer = NativeMemory.Alloc((nuint) lengthInBytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								FAudio.stb_vorbis_get_samples_float_interleaved(
 | 
				
			||||||
 | 
									filePointer,
 | 
				
			||||||
 | 
									info.channels,
 | 
				
			||||||
 | 
									(nint) buffer,
 | 
				
			||||||
 | 
									(int) lengthInFloats
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								FAudio.stb_vorbis_close(filePointer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var format = new Format
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Tag = FormatTag.IEEE_FLOAT,
 | 
				
			||||||
 | 
									BitsPerSample = 32,
 | 
				
			||||||
 | 
									Channels = (ushort) info.channels,
 | 
				
			||||||
 | 
									SampleRate = info.sample_rate
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return new AudioBuffer(
 | 
				
			||||||
 | 
									device,
 | 
				
			||||||
 | 
									format,
 | 
				
			||||||
 | 
									(nint) buffer,
 | 
				
			||||||
 | 
									(uint) lengthInBytes,
 | 
				
			||||||
 | 
									true);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace MoonWorks.Audio
 | 
					namespace MoonWorks.Audio
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	public class AudioDataQoa : AudioData
 | 
						public class AudioDataQoa : AudioDataStreamable
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private IntPtr QoaHandle = IntPtr.Zero;
 | 
							private IntPtr QoaHandle = IntPtr.Zero;
 | 
				
			||||||
		private IntPtr FileDataPtr = IntPtr.Zero;
 | 
							private IntPtr FileDataPtr = IntPtr.Zero;
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ namespace MoonWorks.Audio
 | 
				
			||||||
		private uint decodeBufferSize;
 | 
							private uint decodeBufferSize;
 | 
				
			||||||
		public override uint DecodeBufferSize => decodeBufferSize;
 | 
							public override uint DecodeBufferSize => decodeBufferSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public AudioDataQoa(string filePath)
 | 
							public AudioDataQoa(AudioDevice device, string filePath) : base(device)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			FilePath = filePath;
 | 
								FilePath = filePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,6 +102,42 @@ namespace MoonWorks.Audio
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public unsafe static AudioBuffer CreateBuffer(AudioDevice device, string filePath)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
 | 
				
			||||||
 | 
								var fileDataPtr = NativeMemory.Alloc((nuint) fileStream.Length);
 | 
				
			||||||
 | 
								var fileDataSpan = new Span<byte>(fileDataPtr, (int) fileStream.Length);
 | 
				
			||||||
 | 
								fileStream.ReadExactly(fileDataSpan);
 | 
				
			||||||
 | 
								fileStream.Close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var qoaHandle = FAudio.qoa_open_from_memory((char*) fileDataPtr, (uint) fileDataSpan.Length, 0);
 | 
				
			||||||
 | 
								if (qoaHandle == 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									NativeMemory.Free(fileDataPtr);
 | 
				
			||||||
 | 
									Logger.LogError("Error opening QOA file!");
 | 
				
			||||||
 | 
									throw new AudioLoadException("Error opening QOA file!");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								FAudio.qoa_attributes(qoaHandle, out var channels, out var samplerate, out var samples_per_channel_per_frame, out var total_samples_per_channel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var bufferLengthInBytes = total_samples_per_channel * channels * sizeof(short);
 | 
				
			||||||
 | 
								var buffer = NativeMemory.Alloc(bufferLengthInBytes);
 | 
				
			||||||
 | 
								FAudio.qoa_decode_entire(qoaHandle, (short*) buffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								FAudio.qoa_close(qoaHandle);
 | 
				
			||||||
 | 
								NativeMemory.Free(fileDataPtr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var format = new Format
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Tag = FormatTag.PCM,
 | 
				
			||||||
 | 
									BitsPerSample = 16,
 | 
				
			||||||
 | 
									Channels = (ushort) channels,
 | 
				
			||||||
 | 
									SampleRate = samplerate
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return new AudioBuffer(device, format, (nint) buffer, bufferLengthInBytes, true);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private static unsafe UInt64 ReverseEndianness(UInt64 value)
 | 
							private static unsafe UInt64 ReverseEndianness(UInt64 value)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			byte* bytes = (byte*) &value;
 | 
								byte* bytes = (byte*) &value;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,25 @@
 | 
				
			||||||
using System;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace MoonWorks.Audio
 | 
					namespace MoonWorks.Audio
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	public abstract class AudioData
 | 
						public abstract class AudioDataStreamable : AudioResource
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		public Format Format { get; protected set; }
 | 
							public Format Format { get; protected set; }
 | 
				
			||||||
 | 
							public abstract bool Loaded { get; }
 | 
				
			||||||
		public abstract uint DecodeBufferSize { get; }
 | 
							public abstract uint DecodeBufferSize { get; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public abstract bool Loaded { get; }
 | 
							protected AudioDataStreamable(AudioDevice device) : base(device)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Loads the raw audio data into memory.
 | 
							/// Loads the raw audio data into memory to prepare it for stream decoding.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public abstract void Load();
 | 
							public abstract void Load();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Unloads the raw audio data from memory.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public abstract void Unload();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Seeks to the given sample frame.
 | 
							/// Seeks to the given sample frame.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
| 
						 | 
					@ -28,9 +34,9 @@ namespace MoonWorks.Audio
 | 
				
			||||||
		/// <param name="reachedEnd">Whether the end of the data was reached on this decode.</param>
 | 
							/// <param name="reachedEnd">Whether the end of the data was reached on this decode.</param>
 | 
				
			||||||
		public abstract unsafe void Decode(void* buffer, int bufferLengthInBytes, out int filledLengthInBytes, out bool reachedEnd);
 | 
							public abstract unsafe void Decode(void* buffer, int bufferLengthInBytes, out int filledLengthInBytes, out bool reachedEnd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
							protected override void Destroy()
 | 
				
			||||||
		/// Unloads the raw audio data from memory.
 | 
							{
 | 
				
			||||||
		/// </summary>
 | 
								Unload();
 | 
				
			||||||
		public abstract void Unload();
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,95 @@
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.IO;
 | 
				
			||||||
 | 
					using System.Runtime.InteropServices;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace MoonWorks.Audio
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public static class AudioDataWav
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							// mostly borrowed from https://github.com/FNA-XNA/FNA/blob/b71b4a35ae59970ff0070dea6f8620856d8d4fec/src/Audio/SoundEffect.cs#L385
 | 
				
			||||||
 | 
							public unsafe static AudioBuffer CreateBuffer(AudioDevice device, string filePath)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								// WaveFormatEx data
 | 
				
			||||||
 | 
								ushort wFormatTag;
 | 
				
			||||||
 | 
								ushort nChannels;
 | 
				
			||||||
 | 
								uint nSamplesPerSec;
 | 
				
			||||||
 | 
								uint nAvgBytesPerSec;
 | 
				
			||||||
 | 
								ushort nBlockAlign;
 | 
				
			||||||
 | 
								ushort wBitsPerSample;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
 | 
				
			||||||
 | 
								using var reader = new BinaryReader(stream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// RIFF Signature
 | 
				
			||||||
 | 
								string signature = new string(reader.ReadChars(4));
 | 
				
			||||||
 | 
								if (signature != "RIFF")
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									throw new NotSupportedException("Specified stream is not a wave file.");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								reader.ReadUInt32(); // Riff Chunk Size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								string wformat = new string(reader.ReadChars(4));
 | 
				
			||||||
 | 
								if (wformat != "WAVE")
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									throw new NotSupportedException("Specified stream is not a wave file.");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// WAVE Header
 | 
				
			||||||
 | 
								string format_signature = new string(reader.ReadChars(4));
 | 
				
			||||||
 | 
								while (format_signature != "fmt ")
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									reader.ReadBytes(reader.ReadInt32());
 | 
				
			||||||
 | 
									format_signature = new string(reader.ReadChars(4));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								int format_chunk_size = reader.ReadInt32();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								wFormatTag = reader.ReadUInt16();
 | 
				
			||||||
 | 
								nChannels = reader.ReadUInt16();
 | 
				
			||||||
 | 
								nSamplesPerSec = reader.ReadUInt32();
 | 
				
			||||||
 | 
								nAvgBytesPerSec = reader.ReadUInt32();
 | 
				
			||||||
 | 
								nBlockAlign = reader.ReadUInt16();
 | 
				
			||||||
 | 
								wBitsPerSample = reader.ReadUInt16();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Reads residual bytes
 | 
				
			||||||
 | 
								if (format_chunk_size > 16)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									reader.ReadBytes(format_chunk_size - 16);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// data Signature
 | 
				
			||||||
 | 
								string data_signature = new string(reader.ReadChars(4));
 | 
				
			||||||
 | 
								while (data_signature.ToLowerInvariant() != "data")
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									reader.ReadBytes(reader.ReadInt32());
 | 
				
			||||||
 | 
									data_signature = new string(reader.ReadChars(4));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (data_signature != "data")
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									throw new NotSupportedException("Specified wave file is not supported.");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								int waveDataLength = reader.ReadInt32();
 | 
				
			||||||
 | 
								var waveDataBuffer = NativeMemory.Alloc((nuint) waveDataLength);
 | 
				
			||||||
 | 
								var waveDataSpan = new Span<byte>(waveDataBuffer, waveDataLength);
 | 
				
			||||||
 | 
								stream.ReadExactly(waveDataSpan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var format = new Format
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Tag = (FormatTag) wFormatTag,
 | 
				
			||||||
 | 
									BitsPerSample = wBitsPerSample,
 | 
				
			||||||
 | 
									Channels = nChannels,
 | 
				
			||||||
 | 
									SampleRate = nSamplesPerSec
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return new AudioBuffer(
 | 
				
			||||||
 | 
									device,
 | 
				
			||||||
 | 
									format,
 | 
				
			||||||
 | 
									(nint) waveDataBuffer,
 | 
				
			||||||
 | 
									(uint) waveDataLength,
 | 
				
			||||||
 | 
									true
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					namespace MoonWorks.Audio
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public class PersistentVoice : SourceVoice, IPoolable<PersistentVoice>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							public PersistentVoice(AudioDevice device, Format format) : base(device, format)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public static PersistentVoice Create(AudioDevice device, Format format)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return new PersistentVoice(device, format);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Adds an AudioBuffer to the voice queue.
 | 
				
			||||||
 | 
							/// The voice processes and plays back the buffers in its queue in the order that they were submitted.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="buffer">The buffer to submit to the voice.</param>
 | 
				
			||||||
 | 
							/// <param name="loop">Whether the voice should loop this buffer.</param>
 | 
				
			||||||
 | 
							public void Submit(AudioBuffer buffer, bool loop = false)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Submit(buffer.ToFAudioBuffer(loop));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ namespace MoonWorks.Audio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public SoundSequence(AudioDevice device, StaticSound templateSound) : base(device, templateSound.Format)
 | 
							public SoundSequence(AudioDevice device, AudioBuffer templateSound) : base(device, templateSound.Format)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,6 @@ namespace MoonWorks.Audio
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			lock (StateLock)
 | 
								lock (StateLock)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				if (IsDisposed) { return; }
 | 
					 | 
				
			||||||
				if (State != SoundState.Playing) { return; }
 | 
									if (State != SoundState.Playing) { return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (NeedSoundThreshold > 0)
 | 
									if (NeedSoundThreshold > 0)
 | 
				
			||||||
| 
						 | 
					@ -37,18 +36,18 @@ namespace MoonWorks.Audio
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public void EnqueueSound(StaticSound sound)
 | 
							public void EnqueueSound(AudioBuffer buffer)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
#if DEBUG
 | 
					#if DEBUG
 | 
				
			||||||
			if (!(sound.Format == Format))
 | 
								if (!(buffer.Format == Format))
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				Logger.LogWarn("Playlist audio format mismatch!");
 | 
									Logger.LogWarn("Sound sequence audio format mismatch!");
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			lock (StateLock)
 | 
								lock (StateLock)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				Submit(sound.Buffer);
 | 
									Submit(buffer.ToFAudioBuffer());
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,13 +5,15 @@ namespace MoonWorks.Audio
 | 
				
			||||||
	/// <summary>
 | 
						/// <summary>
 | 
				
			||||||
	/// Emits audio from submitted audio buffers.
 | 
						/// Emits audio from submitted audio buffers.
 | 
				
			||||||
	/// </summary>
 | 
						/// </summary>
 | 
				
			||||||
	public class SourceVoice : Voice
 | 
						public abstract class SourceVoice : Voice
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		private Format format;
 | 
							private Format format;
 | 
				
			||||||
		public Format Format => format;
 | 
							public Format Format => format;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		protected object StateLock = new object();
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The number of buffers queued in the voice.
 | 
				
			||||||
 | 
							/// This includes the currently playing voice!
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
		public uint BuffersQueued
 | 
							public uint BuffersQueued
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			get
 | 
								get
 | 
				
			||||||
| 
						 | 
					@ -45,6 +47,8 @@ namespace MoonWorks.Audio
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							protected object StateLock = new object();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public SourceVoice(
 | 
							public SourceVoice(
 | 
				
			||||||
			AudioDevice device,
 | 
								AudioDevice device,
 | 
				
			||||||
			Format format
 | 
								Format format
 | 
				
			||||||
| 
						 | 
					@ -124,20 +128,13 @@ namespace MoonWorks.Audio
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Adds an FAudio buffer to the voice queue.
 | 
							/// Adds an AudioBuffer to the voice queue.
 | 
				
			||||||
		/// The voice processes and plays back the buffers in its queue in the order that they were submitted.
 | 
							/// The voice processes and plays back the buffers in its queue in the order that they were submitted.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="buffer">The buffer to submit to the voice.</param>
 | 
							/// <param name="buffer">The buffer to submit to the voice.</param>
 | 
				
			||||||
		public void Submit(FAudio.FAudioBuffer buffer)
 | 
							public void Submit(AudioBuffer buffer)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			lock (StateLock)
 | 
								Submit(buffer.ToFAudioBuffer());
 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				FAudio.FAudioSourceVoice_SubmitSourceBuffer(
 | 
					 | 
				
			||||||
					Handle,
 | 
					 | 
				
			||||||
					ref buffer,
 | 
					 | 
				
			||||||
					IntPtr.Zero
 | 
					 | 
				
			||||||
				);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
| 
						 | 
					@ -152,9 +149,27 @@ namespace MoonWorks.Audio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Called automatically by AudioDevice in the audio thread.
 | 
							/// Called automatically by AudioDevice in the audio thread.
 | 
				
			||||||
 | 
							/// Don't call this yourself! You might regret it!
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public virtual void Update() { }
 | 
							public virtual void Update() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Adds an FAudio buffer to the voice queue.
 | 
				
			||||||
 | 
							/// The voice processes and plays back the buffers in its queue in the order that they were submitted.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="buffer">The buffer to submit to the voice.</param>
 | 
				
			||||||
 | 
							protected void Submit(FAudio.FAudioBuffer buffer)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								lock (StateLock)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									FAudio.FAudioSourceVoice_SubmitSourceBuffer(
 | 
				
			||||||
 | 
										Handle,
 | 
				
			||||||
 | 
										ref buffer,
 | 
				
			||||||
 | 
										IntPtr.Zero
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		protected override unsafe void Destroy()
 | 
							protected override unsafe void Destroy()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			Stop();
 | 
								Stop();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,272 +0,0 @@
 | 
				
			||||||
using System;
 | 
					 | 
				
			||||||
using System.IO;
 | 
					 | 
				
			||||||
using System.Runtime.InteropServices;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace MoonWorks.Audio
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	public class StaticSound : AudioResource
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		internal FAudio.FAudioBuffer Buffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		private Format format;
 | 
					 | 
				
			||||||
		public Format Format => format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		private bool OwnsBuffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		public static unsafe StaticSound LoadOgg(AudioDevice device, string filePath)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			var filePointer = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (error != 0)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				throw new AudioLoadException("Error loading file!");
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			var info = FAudio.stb_vorbis_get_info(filePointer);
 | 
					 | 
				
			||||||
			var lengthInFloats =
 | 
					 | 
				
			||||||
				FAudio.stb_vorbis_stream_length_in_samples(filePointer) * info.channels;
 | 
					 | 
				
			||||||
			var lengthInBytes = lengthInFloats * Marshal.SizeOf<float>();
 | 
					 | 
				
			||||||
			var buffer = NativeMemory.Alloc((nuint) lengthInBytes);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			FAudio.stb_vorbis_get_samples_float_interleaved(
 | 
					 | 
				
			||||||
				filePointer,
 | 
					 | 
				
			||||||
				info.channels,
 | 
					 | 
				
			||||||
				(nint) buffer,
 | 
					 | 
				
			||||||
				(int) lengthInFloats
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			FAudio.stb_vorbis_close(filePointer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var format = new Format
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				Tag = FormatTag.IEEE_FLOAT,
 | 
					 | 
				
			||||||
				BitsPerSample = 32,
 | 
					 | 
				
			||||||
				Channels = (ushort) info.channels,
 | 
					 | 
				
			||||||
				SampleRate = info.sample_rate
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return new StaticSound(
 | 
					 | 
				
			||||||
				device,
 | 
					 | 
				
			||||||
				format,
 | 
					 | 
				
			||||||
				(nint) buffer,
 | 
					 | 
				
			||||||
				(uint) lengthInBytes,
 | 
					 | 
				
			||||||
				true);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// mostly borrowed from https://github.com/FNA-XNA/FNA/blob/b71b4a35ae59970ff0070dea6f8620856d8d4fec/src/Audio/SoundEffect.cs#L385
 | 
					 | 
				
			||||||
		public static unsafe StaticSound LoadWav(AudioDevice device, string filePath)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			// WaveFormatEx data
 | 
					 | 
				
			||||||
			ushort wFormatTag;
 | 
					 | 
				
			||||||
			ushort nChannels;
 | 
					 | 
				
			||||||
			uint nSamplesPerSec;
 | 
					 | 
				
			||||||
			uint nAvgBytesPerSec;
 | 
					 | 
				
			||||||
			ushort nBlockAlign;
 | 
					 | 
				
			||||||
			ushort wBitsPerSample;
 | 
					 | 
				
			||||||
			int samplerLoopStart = 0;
 | 
					 | 
				
			||||||
			int samplerLoopEnd = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
 | 
					 | 
				
			||||||
			using var reader = new BinaryReader(stream);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// RIFF Signature
 | 
					 | 
				
			||||||
			string signature = new string(reader.ReadChars(4));
 | 
					 | 
				
			||||||
			if (signature != "RIFF")
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				throw new NotSupportedException("Specified stream is not a wave file.");
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			reader.ReadUInt32(); // Riff Chunk Size
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			string wformat = new string(reader.ReadChars(4));
 | 
					 | 
				
			||||||
			if (wformat != "WAVE")
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				throw new NotSupportedException("Specified stream is not a wave file.");
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// WAVE Header
 | 
					 | 
				
			||||||
			string format_signature = new string(reader.ReadChars(4));
 | 
					 | 
				
			||||||
			while (format_signature != "fmt ")
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				reader.ReadBytes(reader.ReadInt32());
 | 
					 | 
				
			||||||
				format_signature = new string(reader.ReadChars(4));
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			int format_chunk_size = reader.ReadInt32();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			wFormatTag = reader.ReadUInt16();
 | 
					 | 
				
			||||||
			nChannels = reader.ReadUInt16();
 | 
					 | 
				
			||||||
			nSamplesPerSec = reader.ReadUInt32();
 | 
					 | 
				
			||||||
			nAvgBytesPerSec = reader.ReadUInt32();
 | 
					 | 
				
			||||||
			nBlockAlign = reader.ReadUInt16();
 | 
					 | 
				
			||||||
			wBitsPerSample = reader.ReadUInt16();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Reads residual bytes
 | 
					 | 
				
			||||||
			if (format_chunk_size > 16)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				reader.ReadBytes(format_chunk_size - 16);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// data Signature
 | 
					 | 
				
			||||||
			string data_signature = new string(reader.ReadChars(4));
 | 
					 | 
				
			||||||
			while (data_signature.ToLowerInvariant() != "data")
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				reader.ReadBytes(reader.ReadInt32());
 | 
					 | 
				
			||||||
				data_signature = new string(reader.ReadChars(4));
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			if (data_signature != "data")
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				throw new NotSupportedException("Specified wave file is not supported.");
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			int waveDataLength = reader.ReadInt32();
 | 
					 | 
				
			||||||
			var waveDataBuffer = NativeMemory.Alloc((nuint) waveDataLength);
 | 
					 | 
				
			||||||
			var waveDataSpan = new Span<byte>(waveDataBuffer, waveDataLength);
 | 
					 | 
				
			||||||
			stream.ReadExactly(waveDataSpan);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// Scan for other chunks
 | 
					 | 
				
			||||||
			while (reader.PeekChar() != -1)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				char[] chunkIDChars = reader.ReadChars(4);
 | 
					 | 
				
			||||||
				if (chunkIDChars.Length < 4)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					break; // EOL!
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				byte[] chunkSizeBytes = reader.ReadBytes(4);
 | 
					 | 
				
			||||||
				if (chunkSizeBytes.Length < 4)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					break; // EOL!
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				string chunk_signature = new string(chunkIDChars);
 | 
					 | 
				
			||||||
				int chunkDataSize = BitConverter.ToInt32(chunkSizeBytes, 0);
 | 
					 | 
				
			||||||
				if (chunk_signature == "smpl") // "smpl", Sampler Chunk Found
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					reader.ReadUInt32(); // Manufacturer
 | 
					 | 
				
			||||||
					reader.ReadUInt32(); // Product
 | 
					 | 
				
			||||||
					reader.ReadUInt32(); // Sample Period
 | 
					 | 
				
			||||||
					reader.ReadUInt32(); // MIDI Unity Note
 | 
					 | 
				
			||||||
					reader.ReadUInt32(); // MIDI Pitch Fraction
 | 
					 | 
				
			||||||
					reader.ReadUInt32(); // SMPTE Format
 | 
					 | 
				
			||||||
					reader.ReadUInt32(); // SMPTE Offset
 | 
					 | 
				
			||||||
					uint numSampleLoops = reader.ReadUInt32();
 | 
					 | 
				
			||||||
					int samplerData = reader.ReadInt32();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					for (int i = 0; i < numSampleLoops; i += 1)
 | 
					 | 
				
			||||||
					{
 | 
					 | 
				
			||||||
						reader.ReadUInt32(); // Cue Point ID
 | 
					 | 
				
			||||||
						reader.ReadUInt32(); // Type
 | 
					 | 
				
			||||||
						int start = reader.ReadInt32();
 | 
					 | 
				
			||||||
						int end = reader.ReadInt32();
 | 
					 | 
				
			||||||
						reader.ReadUInt32(); // Fraction
 | 
					 | 
				
			||||||
						reader.ReadUInt32(); // Play Count
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						if (i == 0) // Grab loopStart and loopEnd from first sample loop
 | 
					 | 
				
			||||||
						{
 | 
					 | 
				
			||||||
							samplerLoopStart = start;
 | 
					 | 
				
			||||||
							samplerLoopEnd = end;
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					if (samplerData != 0) // Read Sampler Data if it exists
 | 
					 | 
				
			||||||
					{
 | 
					 | 
				
			||||||
						reader.ReadBytes(samplerData);
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				else // Read unwanted chunk data and try again
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					reader.ReadBytes(chunkDataSize);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// End scan
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var format = new Format
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				Tag = (FormatTag) wFormatTag,
 | 
					 | 
				
			||||||
				BitsPerSample = wBitsPerSample,
 | 
					 | 
				
			||||||
				Channels = nChannels,
 | 
					 | 
				
			||||||
				SampleRate = nSamplesPerSec
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var sound = new StaticSound(
 | 
					 | 
				
			||||||
				device,
 | 
					 | 
				
			||||||
				format,
 | 
					 | 
				
			||||||
				(nint) waveDataBuffer,
 | 
					 | 
				
			||||||
				(uint) waveDataLength,
 | 
					 | 
				
			||||||
				true
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return sound;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		public static unsafe StaticSound FromQOA(AudioDevice device, string path)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
 | 
					 | 
				
			||||||
			var fileDataPtr = NativeMemory.Alloc((nuint) fileStream.Length);
 | 
					 | 
				
			||||||
			var fileDataSpan = new Span<byte>(fileDataPtr, (int) fileStream.Length);
 | 
					 | 
				
			||||||
			fileStream.ReadExactly(fileDataSpan);
 | 
					 | 
				
			||||||
			fileStream.Close();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var qoaHandle = FAudio.qoa_open_from_memory((char*) fileDataPtr, (uint) fileDataSpan.Length, 0);
 | 
					 | 
				
			||||||
			if (qoaHandle == 0)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				NativeMemory.Free(fileDataPtr);
 | 
					 | 
				
			||||||
				Logger.LogError("Error opening QOA file!");
 | 
					 | 
				
			||||||
				throw new AudioLoadException("Error opening QOA file!");
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			FAudio.qoa_attributes(qoaHandle, out var channels, out var samplerate, out var samples_per_channel_per_frame, out var total_samples_per_channel);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var bufferLengthInBytes = total_samples_per_channel * channels * sizeof(short);
 | 
					 | 
				
			||||||
			var buffer = NativeMemory.Alloc(bufferLengthInBytes);
 | 
					 | 
				
			||||||
			FAudio.qoa_decode_entire(qoaHandle, (short*) buffer);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			FAudio.qoa_close(qoaHandle);
 | 
					 | 
				
			||||||
			NativeMemory.Free(fileDataPtr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			var format = new Format
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				Tag = FormatTag.PCM,
 | 
					 | 
				
			||||||
				BitsPerSample = 16,
 | 
					 | 
				
			||||||
				Channels = (ushort) channels,
 | 
					 | 
				
			||||||
				SampleRate = samplerate
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			return new StaticSound(
 | 
					 | 
				
			||||||
				device,
 | 
					 | 
				
			||||||
				format,
 | 
					 | 
				
			||||||
				(nint) buffer,
 | 
					 | 
				
			||||||
				bufferLengthInBytes,
 | 
					 | 
				
			||||||
				true
 | 
					 | 
				
			||||||
			);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		public StaticSound(
 | 
					 | 
				
			||||||
			AudioDevice device,
 | 
					 | 
				
			||||||
			Format format,
 | 
					 | 
				
			||||||
			IntPtr bufferPtr,
 | 
					 | 
				
			||||||
			uint bufferLengthInBytes,
 | 
					 | 
				
			||||||
			bool ownsBuffer) : base(device)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			this.format = format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			Buffer = new FAudio.FAudioBuffer
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				Flags = FAudio.FAUDIO_END_OF_STREAM,
 | 
					 | 
				
			||||||
				pContext = IntPtr.Zero,
 | 
					 | 
				
			||||||
				pAudioData = bufferPtr,
 | 
					 | 
				
			||||||
				AudioBytes = bufferLengthInBytes,
 | 
					 | 
				
			||||||
				PlayBegin = 0,
 | 
					 | 
				
			||||||
				PlayLength = 0
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			OwnsBuffer = ownsBuffer;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		protected override unsafe void Destroy()
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if (OwnsBuffer)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				NativeMemory.Free((void*) Buffer.pAudioData);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,54 +0,0 @@
 | 
				
			||||||
namespace MoonWorks.Audio
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	public class StaticVoice : SourceVoice, IPoolable<StaticVoice>
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		/// <summary>
 | 
					 | 
				
			||||||
		/// Indicates if the voice should return to the voice pool when the voice is idle.
 | 
					 | 
				
			||||||
		/// If you set this and then hold on to the voice reference there will be problems!
 | 
					 | 
				
			||||||
		/// </summary>
 | 
					 | 
				
			||||||
		public bool DeactivateWhenIdle { get; set; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		public static StaticVoice Create(AudioDevice device, Format format)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			return new StaticVoice(device, format);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		public StaticVoice(AudioDevice device, Format format) : base(device, format)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		public override void Update()
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			lock (StateLock)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if (DeactivateWhenIdle)
 | 
					 | 
				
			||||||
				{
 | 
					 | 
				
			||||||
					if (BuffersQueued == 0)
 | 
					 | 
				
			||||||
					{
 | 
					 | 
				
			||||||
						Return();
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/// <summary>
 | 
					 | 
				
			||||||
		/// Adds a static sound to the voice queue.
 | 
					 | 
				
			||||||
		/// The voice processes and plays back the buffers in its queue in the order that they were submitted.
 | 
					 | 
				
			||||||
		/// </summary>
 | 
					 | 
				
			||||||
		/// <param name="sound">The sound to submit to the voice.</param>
 | 
					 | 
				
			||||||
		/// <param name="loop">Designates that the voice will loop the submitted buffer.</param>
 | 
					 | 
				
			||||||
		public void Submit(StaticSound sound, bool loop = false)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			if (loop)
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				sound.Buffer.LoopCount = FAudio.FAUDIO_LOOP_INFINITE;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				sound.Buffer.LoopCount = 0;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			Submit(sound.Buffer);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -12,19 +12,19 @@ namespace MoonWorks.Audio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public bool Loop { get; set; }
 | 
							public bool Loop { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public AudioData AudioData { get; protected set; }
 | 
							public AudioDataStreamable AudioData { get; protected set; }
 | 
				
			||||||
 | 
					 | 
				
			||||||
		public static StreamingVoice Create(AudioDevice device, Format format)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			return new StreamingVoice(device, format);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public unsafe StreamingVoice(AudioDevice device, Format format) : base(device, format)
 | 
							public unsafe StreamingVoice(AudioDevice device, Format format) : base(device, format)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			buffers = new IntPtr[BUFFER_COUNT];
 | 
								buffers = new IntPtr[BUFFER_COUNT];
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public void Load(AudioData data)
 | 
							public static StreamingVoice Create(AudioDevice device, Format format)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return new StreamingVoice(device, format);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void Load(AudioDataStreamable data)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			lock (StateLock)
 | 
								lock (StateLock)
 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
| 
						 | 
					@ -57,8 +57,6 @@ namespace MoonWorks.Audio
 | 
				
			||||||
		public override void Update()
 | 
							public override void Update()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			lock (StateLock)
 | 
								lock (StateLock)
 | 
				
			||||||
			{
 | 
					 | 
				
			||||||
				if (!IsDisposed)
 | 
					 | 
				
			||||||
			{
 | 
								{
 | 
				
			||||||
				if (AudioData == null || State != SoundState.Playing)
 | 
									if (AudioData == null || State != SoundState.Playing)
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
| 
						 | 
					@ -68,7 +66,6 @@ namespace MoonWorks.Audio
 | 
				
			||||||
				QueueBuffers();
 | 
									QueueBuffers();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		protected void QueueBuffers()
 | 
							protected void QueueBuffers()
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					namespace MoonWorks.Audio
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// These voices are intended for playing one-off sound effects that don't have a long term reference.
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public class TransientVoice : SourceVoice, IPoolable<TransientVoice>
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							static TransientVoice IPoolable<TransientVoice>.Create(AudioDevice device, Format format)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return new TransientVoice(device, format);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public TransientVoice(AudioDevice device, Format format) : base(device, format)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public override void Update()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								lock (StateLock)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									if (BuffersQueued == 0)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Return();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue