From ec28d17a755e267bafd4354d615c3f6ddb1f0cc2 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Tue, 19 Jan 2021 21:33:25 -0800 Subject: [PATCH] fix static sound loading + window title --- src/Audio/AudioDevice.cs | 36 ++++++++++++++++++--- src/Audio/DynamicSound.cs | 29 ++++++++++++++--- src/Audio/DynamicSoundInstance.cs | 8 +++-- src/Audio/Sound.cs | 23 +++----------- src/Audio/StaticSound.cs | 53 +++++++++++++++++++++++-------- src/Audio/StaticSoundInstance.cs | 1 + src/Game.cs | 1 + src/Window.cs | 2 +- src/WindowCreateInfo.cs | 1 + 9 files changed, 110 insertions(+), 44 deletions(-) diff --git a/src/Audio/AudioDevice.cs b/src/Audio/AudioDevice.cs index 35b7d74..bfc0702 100644 --- a/src/Audio/AudioDevice.cs +++ b/src/Audio/AudioDevice.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.InteropServices; namespace MoonWorks.Audio @@ -17,6 +18,8 @@ namespace MoonWorks.Audio private FAudio.FAudioVoiceSends reverbSends; + private readonly List> dynamicSoundInstances = new List>(); + public unsafe AudioDevice() { FAudio.FAudioCreate(out var handle, 0, 0); @@ -177,16 +180,39 @@ namespace MoonWorks.Audio /* Init reverb sends */ - reverbSends = new FAudio.FAudioVoiceSends(); - reverbSends.SendCount = 2; - reverbSends.pSends = Marshal.AllocHGlobal( - 2 * Marshal.SizeOf() - ); + reverbSends = new FAudio.FAudioVoiceSends + { + SendCount = 2, + pSends = Marshal.AllocHGlobal( + 2 * Marshal.SizeOf() + ) + }; FAudio.FAudioSendDescriptor* sendDesc = (FAudio.FAudioSendDescriptor*) reverbSends.pSends; sendDesc[0].Flags = 0; sendDesc[0].pOutputVoice = MasteringVoice; sendDesc[1].Flags = 0; sendDesc[1].pOutputVoice = ReverbVoice; } + + public void Update() + { + for (var i = dynamicSoundInstances.Count - 1; i >= 0; i--) + { + var weakReference = dynamicSoundInstances[i]; + if (weakReference.TryGetTarget(out var dynamicSoundInstance)) + { + dynamicSoundInstance.Update(); + } + else + { + dynamicSoundInstances.RemoveAt(i); + } + } + } + + internal void AddDynamicSoundInstance(DynamicSoundInstance instance) + { + dynamicSoundInstances.Add(new WeakReference(instance)); + } } } diff --git a/src/Audio/DynamicSound.cs b/src/Audio/DynamicSound.cs index 636c4c4..0485d03 100644 --- a/src/Audio/DynamicSound.cs +++ b/src/Audio/DynamicSound.cs @@ -4,10 +4,13 @@ using System.IO; namespace MoonWorks.Audio { /// - /// For streaming long playback. + /// For streaming long playback. Reads an OGG file. /// 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; } @@ -15,9 +18,7 @@ namespace MoonWorks.Audio private bool IsDisposed; - // FIXME: what should this value be? - - public DynamicSound(FileInfo fileInfo, ushort channels, uint samplesPerSecond) : base(channels, samplesPerSecond) + public DynamicSound(AudioDevice device, FileInfo fileInfo) : base(device) { FileHandle = FAudio.stb_vorbis_open_filename(fileInfo.FullName, out var error, IntPtr.Zero); @@ -28,6 +29,26 @@ namespace MoonWorks.Audio } 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() + { + var instance = new DynamicSoundInstance(Device, this, false); + Device.AddDynamicSoundInstance(instance); + return instance; } protected virtual void Dispose(bool disposing) diff --git a/src/Audio/DynamicSoundInstance.cs b/src/Audio/DynamicSoundInstance.cs index a36824b..5ef7c7a 100644 --- a/src/Audio/DynamicSoundInstance.cs +++ b/src/Audio/DynamicSoundInstance.cs @@ -16,7 +16,7 @@ namespace MoonWorks.Audio public override SoundState State { get; protected set; } - public DynamicSoundInstance( + internal DynamicSoundInstance( AudioDevice device, DynamicSound parent, bool is3D @@ -26,6 +26,8 @@ namespace MoonWorks.Audio queuedSizes = new List(); buffer = new float[DynamicSound.BUFFER_SIZE]; + + State = SoundState.Stopped; } public void Play() @@ -60,7 +62,7 @@ namespace MoonWorks.Audio ClearBuffers(); } - private void Update() + internal void Update() { if (State != SoundState.Playing) { @@ -117,7 +119,7 @@ namespace MoonWorks.Audio buffer.Length ); - IntPtr next = Marshal.AllocHGlobal(buffer.Length); + IntPtr next = Marshal.AllocHGlobal(buffer.Length * sizeof(float)); Marshal.Copy(buffer, 0, next, buffer.Length); lock (queuedBuffers) diff --git a/src/Audio/Sound.cs b/src/Audio/Sound.cs index 714824b..d890fc3 100644 --- a/src/Audio/Sound.cs +++ b/src/Audio/Sound.cs @@ -2,25 +2,12 @@ namespace MoonWorks.Audio { public abstract class Sound { - internal FAudio.FAudioWaveFormatEx Format { get; } + internal AudioDevice Device { get; } + internal abstract FAudio.FAudioWaveFormatEx Format { get; } - /* NOTE: we only support float decoding! WAV sucks! */ - public Sound( - ushort channels, - uint samplesPerSecond - ) { - var blockAlign = (ushort) (4 * channels); - - Format = new FAudio.FAudioWaveFormatEx - { - wFormatTag = 3, - wBitsPerSample = 32, - nChannels = channels, - nBlockAlign = blockAlign, - nSamplesPerSec = samplesPerSecond, - nAvgBytesPerSec = blockAlign * samplesPerSecond, - cbSize = 0 - }; + public Sound(AudioDevice device) + { + Device = device; } } } diff --git a/src/Audio/StaticSound.cs b/src/Audio/StaticSound.cs index f812a70..f9c3ac6 100644 --- a/src/Audio/StaticSound.cs +++ b/src/Audio/StaticSound.cs @@ -6,13 +6,15 @@ namespace MoonWorks.Audio { public class StaticSound : Sound, IDisposable { + internal override FAudio.FAudioWaveFormatEx Format { get; } internal FAudio.FAudioBuffer Handle; - private bool IsDisposed; public uint LoopStart { get; set; } = 0; public uint LoopLength { get; set; } = 0; - public static StaticSound FromOgg(FileInfo fileInfo) + private bool IsDisposed; + + public static StaticSound LoadOgg(AudioDevice device, FileInfo fileInfo) { var filePointer = FAudio.stb_vorbis_open_filename(fileInfo.FullName, out var error, IntPtr.Zero); @@ -21,12 +23,20 @@ namespace MoonWorks.Audio throw new AudioLoadException("Error loading file!"); } var info = FAudio.stb_vorbis_get_info(filePointer); - var bufferSize = (uint)(info.sample_rate * info.channels); + var bufferSize = FAudio.stb_vorbis_stream_length_in_samples(filePointer); var buffer = new float[bufferSize]; + FAudio.stb_vorbis_get_samples_float_interleaved( + filePointer, + info.channels, + buffer, + (int) bufferSize + ); + FAudio.stb_vorbis_close(filePointer); return new StaticSound( + device, buffer, 0, (ushort) info.channels, @@ -35,25 +45,42 @@ namespace MoonWorks.Audio } public StaticSound( + AudioDevice device, float[] buffer, uint bufferOffset, ushort channels, uint samplesPerSecond - ) : base(channels, samplesPerSecond) { - var bufferLength = 4 * buffer.Length; + ) : base(device) { + var blockAlign = (ushort)(4 * channels); + + Format = new FAudio.FAudioWaveFormatEx + { + wFormatTag = 3, + wBitsPerSample = 32, + nChannels = channels, + nBlockAlign = blockAlign, + nSamplesPerSec = samplesPerSecond, + nAvgBytesPerSec = blockAlign * samplesPerSecond + }; + + var bufferLengthInBytes = sizeof(float) * buffer.Length; Handle = new FAudio.FAudioBuffer(); Handle.Flags = FAudio.FAUDIO_END_OF_STREAM; Handle.pContext = IntPtr.Zero; - Handle.AudioBytes = (uint) bufferLength; - Handle.pAudioData = Marshal.AllocHGlobal((int) bufferLength); - Marshal.Copy(buffer, (int) bufferOffset, Handle.pAudioData, (int) bufferLength); + Handle.AudioBytes = (uint) bufferLengthInBytes; + Handle.pAudioData = Marshal.AllocHGlobal(bufferLengthInBytes); + Marshal.Copy(buffer, (int) bufferOffset, Handle.pAudioData, buffer.Length); Handle.PlayBegin = 0; - Handle.PlayLength = ( - Handle.AudioBytes / - (uint) Format.nChannels / - (uint) (Format.wBitsPerSample / 8) - ); + Handle.PlayLength = 0; + + LoopStart = 0; + LoopLength = 0; + } + + public StaticSoundInstance CreateInstance() + { + return new StaticSoundInstance(Device, this, false, true); } protected virtual void Dispose(bool disposing) diff --git a/src/Audio/StaticSoundInstance.cs b/src/Audio/StaticSoundInstance.cs index 5f39f21..2a5e669 100644 --- a/src/Audio/StaticSoundInstance.cs +++ b/src/Audio/StaticSoundInstance.cs @@ -38,6 +38,7 @@ namespace MoonWorks.Audio ) : base(device, parent, is3D) { Loop = loop; + State = SoundState.Stopped; } public void Play() diff --git a/src/Game.cs b/src/Game.cs index 76d11a4..8c74485 100644 --- a/src/Game.cs +++ b/src/Game.cs @@ -82,6 +82,7 @@ namespace MoonWorks HandleSDLEvents(); Input.Update(); + AudioDevice.Update(); Update(timestep); diff --git a/src/Window.cs b/src/Window.cs index cc23be1..19bcdcd 100644 --- a/src/Window.cs +++ b/src/Window.cs @@ -24,7 +24,7 @@ namespace MoonWorks ScreenMode = windowCreateInfo.ScreenMode; Handle = SDL.SDL_CreateWindow( - "MoonWorks.GraphicsTest", + windowCreateInfo.WindowTitle, SDL.SDL_WINDOWPOS_UNDEFINED, SDL.SDL_WINDOWPOS_UNDEFINED, (int)windowCreateInfo.WindowWidth, diff --git a/src/WindowCreateInfo.cs b/src/WindowCreateInfo.cs index fa34a66..30b27b4 100644 --- a/src/WindowCreateInfo.cs +++ b/src/WindowCreateInfo.cs @@ -2,6 +2,7 @@ namespace MoonWorks { public struct WindowCreateInfo { + public string WindowTitle; public uint WindowWidth; public uint WindowHeight; public ScreenMode ScreenMode;