diff --git a/src/Audio/StaticSound.cs b/src/Audio/StaticSound.cs index b780c650..ca7188ad 100644 --- a/src/Audio/StaticSound.cs +++ b/src/Audio/StaticSound.cs @@ -53,11 +53,8 @@ namespace MoonWorks.Audio } // mostly borrowed from https://github.com/FNA-XNA/FNA/blob/b71b4a35ae59970ff0070dea6f8620856d8d4fec/src/Audio/SoundEffect.cs#L385 - public static StaticSound LoadWav(AudioDevice device, string filePath) + public static unsafe StaticSound LoadWav(AudioDevice device, string filePath) { - // Sample data - byte[] data; - // WaveFormatEx data ushort wFormatTag; ushort nChannels; @@ -68,141 +65,144 @@ namespace MoonWorks.Audio int samplerLoopStart = 0; int samplerLoopEnd = 0; - using (BinaryReader reader = new BinaryReader(File.OpenRead(filePath))) + 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") { - // 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(); - data = reader.ReadBytes(waveDataLength); - - // 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 + throw new NotSupportedException("Specified stream is not a wave file."); } - return new StaticSound( + 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(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 sound = new StaticSound( device, wFormatTag, wBitsPerSample, nBlockAlign, nChannels, nSamplesPerSec, - data, - 0, - (uint) data.Length + (nint) waveDataBuffer, + (uint) waveDataLength, + true ); + + return sound; } - public unsafe StaticSound( + public StaticSound( AudioDevice device, ushort formatTag, ushort bitsPerSample, ushort blockAlign, ushort channels, uint samplesPerSecond, - byte[] buffer, - uint bufferOffset, /* number of bytes */ - uint bufferLength /* number of bytes */ - ) : base(device) + IntPtr bufferPtr, + uint bufferLengthInBytes, + bool ownsBuffer) : base(device) { FormatTag = formatTag; BitsPerSample = bitsPerSample; @@ -210,19 +210,17 @@ namespace MoonWorks.Audio Channels = channels; SamplesPerSecond = samplesPerSecond; - Handle = new FAudio.FAudioBuffer(); - Handle.Flags = FAudio.FAUDIO_END_OF_STREAM; - Handle.pContext = IntPtr.Zero; - Handle.AudioBytes = bufferLength; - Handle.pAudioData = (nint) NativeMemory.Alloc(bufferLength); - Marshal.Copy(buffer, (int) bufferOffset, Handle.pAudioData, (int) bufferLength); - Handle.PlayBegin = 0; - Handle.PlayLength = 0; + Handle = new FAudio.FAudioBuffer + { + Flags = FAudio.FAUDIO_END_OF_STREAM, + pContext = IntPtr.Zero, + pAudioData = bufferPtr, + AudioBytes = bufferLengthInBytes, + PlayBegin = 0, + PlayLength = 0 + }; - LoopStart = 0; - LoopLength = 0; - - OwnsBuffer = true; + OwnsBuffer = ownsBuffer; } public unsafe StaticSound( @@ -256,35 +254,6 @@ namespace MoonWorks.Audio OwnsBuffer = true; } - public StaticSound( - AudioDevice device, - ushort formatTag, - ushort bitsPerSample, - ushort blockAlign, - ushort channels, - uint samplesPerSecond, - IntPtr bufferPtr, - uint bufferLengthInBytes) : base(device) - { - FormatTag = formatTag; - BitsPerSample = bitsPerSample; - BlockAlign = blockAlign; - Channels = channels; - SamplesPerSecond = samplesPerSecond; - - Handle = new FAudio.FAudioBuffer - { - Flags = FAudio.FAUDIO_END_OF_STREAM, - pContext = IntPtr.Zero, - pAudioData = bufferPtr, - AudioBytes = bufferLengthInBytes, - PlayBegin = 0, - PlayLength = 0 - }; - - OwnsBuffer = false; - } - /// /// Gets a sound instance from the pool. /// NOTE: If you lose track of instances, you will create garbage collection pressure! diff --git a/src/Audio/StreamingSoundOgg.cs b/src/Audio/StreamingSoundOgg.cs index 882d00ec..027faea6 100644 --- a/src/Audio/StreamingSoundOgg.cs +++ b/src/Audio/StreamingSoundOgg.cs @@ -15,10 +15,13 @@ namespace MoonWorks.Audio public unsafe static StreamingSoundOgg Load(AudioDevice device, string filePath) { - var fileData = File.ReadAllBytes(filePath); - var fileDataPtr = NativeMemory.Alloc((nuint) fileData.Length); - Marshal.Copy(fileData, 0, (IntPtr) fileDataPtr, fileData.Length); - var vorbisHandle = FAudio.stb_vorbis_open_memory((IntPtr) fileDataPtr, fileData.Length, out int error, IntPtr.Zero); + var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + var fileDataPtr = NativeMemory.Alloc((nuint) fileStream.Length); + var fileDataSpan = new Span(fileDataPtr, (int) fileStream.Length); + fileStream.ReadExactly(fileDataSpan); + fileStream.Close(); + + var vorbisHandle = FAudio.stb_vorbis_open_memory((IntPtr) fileDataPtr, fileDataSpan.Length, out int error, IntPtr.Zero); if (error != 0) { NativeMemory.Free(fileDataPtr); diff --git a/src/Graphics/Font/Font.cs b/src/Graphics/Font/Font.cs index 95e8780e..cf4982e1 100644 --- a/src/Graphics/Font/Font.cs +++ b/src/Graphics/Font/Font.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices; using WellspringCS; namespace MoonWorks.Graphics.Font @@ -12,11 +13,15 @@ namespace MoonWorks.Graphics.Font public unsafe Font(string path) { - var bytes = File.ReadAllBytes(path); - fixed (byte* pByte = &bytes[0]) - { - Handle = Wellspring.Wellspring_CreateFont((IntPtr) pByte, (uint) bytes.Length); - } + var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read); + var fileByteBuffer = NativeMemory.Alloc((nuint) fileStream.Length); + var fileByteSpan = new Span(fileByteBuffer, (int) fileStream.Length); + fileStream.ReadExactly(fileByteSpan); + fileStream.Close(); + + Handle = Wellspring.Wellspring_CreateFont((IntPtr) fileByteBuffer, (uint) fileByteSpan.Length); + + NativeMemory.Free(fileByteBuffer); } protected virtual void Dispose(bool disposing) diff --git a/src/Graphics/Resources/ShaderModule.cs b/src/Graphics/Resources/ShaderModule.cs index 01b86220..61f87793 100644 --- a/src/Graphics/Resources/ShaderModule.cs +++ b/src/Graphics/Resources/ShaderModule.cs @@ -1,6 +1,7 @@ using RefreshCS; using System; using System.IO; +using System.Runtime.InteropServices; namespace MoonWorks.Graphics { @@ -13,10 +14,8 @@ namespace MoonWorks.Graphics public unsafe ShaderModule(GraphicsDevice device, string filePath) : base(device) { - using (FileStream stream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - Handle = CreateFromStream(device, stream); - } + using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); + Handle = CreateFromStream(device, stream); } public unsafe ShaderModule(GraphicsDevice device, Stream stream) : base(device) @@ -24,19 +23,20 @@ namespace MoonWorks.Graphics Handle = CreateFromStream(device, stream); } - private unsafe static IntPtr CreateFromStream(GraphicsDevice device, Stream stream) + private static unsafe IntPtr CreateFromStream(GraphicsDevice device, Stream stream) { - var bytecode = new byte[stream.Length]; - stream.Read(bytecode, 0, (int) stream.Length); + var bytecodeBuffer = NativeMemory.Alloc((nuint) stream.Length); + var bytecodeSpan = new Span(bytecodeBuffer, (int) stream.Length); + stream.ReadExactly(bytecodeSpan); - fixed (byte* ptr = bytecode) - { - Refresh.ShaderModuleCreateInfo shaderModuleCreateInfo; - shaderModuleCreateInfo.codeSize = (UIntPtr) bytecode.Length; - shaderModuleCreateInfo.byteCode = (IntPtr) ptr; + Refresh.ShaderModuleCreateInfo shaderModuleCreateInfo; + shaderModuleCreateInfo.codeSize = (nuint) stream.Length; + shaderModuleCreateInfo.byteCode = (nint) bytecodeBuffer; - return Refresh.Refresh_CreateShaderModule(device.Handle, shaderModuleCreateInfo); - } + var shaderModule = Refresh.Refresh_CreateShaderModule(device.Handle, shaderModuleCreateInfo); + + NativeMemory.Free(bytecodeBuffer); + return shaderModule; } } } diff --git a/src/Graphics/Resources/Texture.cs b/src/Graphics/Resources/Texture.cs index 008b7617..63f52617 100644 --- a/src/Graphics/Resources/Texture.cs +++ b/src/Graphics/Resources/Texture.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices; using RefreshCS; namespace MoonWorks.Graphics @@ -217,47 +218,44 @@ namespace MoonWorks.Graphics return texture; } - public static Texture LoadDDS(GraphicsDevice graphicsDevice, CommandBuffer commandBuffer, System.IO.Stream stream) + public unsafe static Texture LoadDDS(GraphicsDevice graphicsDevice, CommandBuffer commandBuffer, System.IO.Stream stream) { - using (var reader = new BinaryReader(stream)) + using var reader = new BinaryReader(stream); + Texture texture; + int faces; + ParseDDS(reader, out var format, out var width, out var height, out var levels, out var isCube); + + if (isCube) { - Texture texture; - int faces; - ParseDDS(reader, out var format, out var width, out var height, out var levels, out var isCube); - - if (isCube) - { - texture = CreateTextureCube(graphicsDevice, (uint) width, format, TextureUsageFlags.Sampler, (uint) levels); - faces = 6; - } - else - { - texture = CreateTexture2D(graphicsDevice, (uint) width, (uint) height, format, TextureUsageFlags.Sampler, (uint) levels); - faces = 1; - } - - for (int i = 0; i < faces; i += 1) - { - for (int j = 0; j < levels; j += 1) - { - var levelWidth = width >> j; - var levelHeight = height >> j; - - var pixels = reader.ReadBytes( - Texture.CalculateDDSLevelSize( - levelWidth, - levelHeight, - format - ) - ); - - var textureSlice = new TextureSlice(texture, new Rect(0, 0, levelWidth, levelHeight), 0, (uint) i, (uint) j); - commandBuffer.SetTextureData(textureSlice, pixels); - } - } - - return texture; + texture = CreateTextureCube(graphicsDevice, (uint) width, format, TextureUsageFlags.Sampler, (uint) levels); + faces = 6; } + else + { + texture = CreateTexture2D(graphicsDevice, (uint) width, (uint) height, format, TextureUsageFlags.Sampler, (uint) levels); + faces = 1; + } + + for (int i = 0; i < faces; i += 1) + { + for (int j = 0; j < levels; j += 1) + { + var levelWidth = width >> j; + var levelHeight = height >> j; + + var levelSize = CalculateDDSLevelSize(levelWidth, levelHeight, format); + var byteBuffer = NativeMemory.Alloc((nuint) levelSize); + var byteSpan = new Span(byteBuffer, levelSize); + stream.ReadExactly(byteSpan); + + var textureSlice = new TextureSlice(texture, new Rect(0, 0, levelWidth, levelHeight), 0, (uint) i, (uint) j); + commandBuffer.SetTextureData(textureSlice, (nint) byteBuffer, (uint) levelSize); + + NativeMemory.Free(byteBuffer); + } + } + + return texture; } /// diff --git a/src/Video/Video.cs b/src/Video/Video.cs index 0c7b6966..38feed4b 100644 --- a/src/Video/Video.cs +++ b/src/Video/Video.cs @@ -1,6 +1,8 @@ /* Heavily based on https://github.com/FNA-XNA/FNA/blob/master/src/Media/Xiph/VideoPlayer.cs */ using System; +using System.IO; using System.Runtime.InteropServices; +using SDL2; namespace MoonWorks.Video { @@ -16,6 +18,7 @@ namespace MoonWorks.Video internal IntPtr Handle; private IntPtr rwData; private void* videoData; + private int videoDataLength; public double FramesPerSecond => fps; public int Width => yWidth; @@ -31,16 +34,19 @@ namespace MoonWorks.Video public Video(string filename) { - if (!System.IO.File.Exists(filename)) + if (!File.Exists(filename)) { throw new ArgumentException("Video file not found!"); } - var bytes = System.IO.File.ReadAllBytes(filename); - videoData = NativeMemory.Alloc((nuint) bytes.Length); - Marshal.Copy(bytes, 0, (IntPtr) videoData, bytes.Length); - rwData = SDL2.SDL.SDL_RWFromMem((IntPtr) videoData, bytes.Length); + var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read); + videoDataLength = (int) fileStream.Length; + videoData = NativeMemory.Alloc((nuint) videoDataLength); + var fileBufferSpan = new Span(videoData, videoDataLength); + fileStream.ReadExactly(fileBufferSpan); + fileStream.Close(); + rwData = SDL.SDL_RWFromMem((IntPtr) videoData, videoDataLength); if (Theorafile.tf_open_callbacks(rwData, out Handle, callbacks) < 0) { throw new ArgumentException("Invalid video file!"); @@ -98,6 +104,7 @@ namespace MoonWorks.Video // free unmanaged resources (unmanaged objects) Theorafile.tf_close(ref Handle); + SDL.SDL_RWclose(rwData); NativeMemory.Free(videoData); IsDisposed = true;