using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Runtime.InteropServices; using NAudio.Wave; using NAudio.Wave.SampleProviders; namespace CsbBuilder.Audio { /// /// Represents a NAudio WaveStream for VGMSTREAM playback. /// public class VGMStreamReader : WaveStream { private IntPtr vgmstream; private WaveFormat waveFormat; private int sampleCount; private bool loopFlag; private byte[] cache; /// /// Gets the wave format of the VGMSTREAM. /// public override WaveFormat WaveFormat { get { return waveFormat; } } /// /// Gets the length of this VGMSTREAM in bytes. /// public override long Length { get { return sampleCount * waveFormat.Channels * 2; } } /// /// Gets or sets the position of this VGMSTREAM in bytes. /// public override long Position { get { return VGMStreamNative.GetCurrentSample(vgmstream) * waveFormat.Channels * 2; } set { CurrentSample = (int)(value / waveFormat.Channels / 2); } } /// /// Gets or sets the current sample of this VGMSTREAM. /// public int CurrentSample { get { return VGMStreamNative.GetCurrentSample(vgmstream); } set { int currentSample = VGMStreamNative.GetCurrentSample(vgmstream); if (value == currentSample || value > sampleCount) { return; } if (value > currentSample) { value = value - currentSample; } else if (value < currentSample) { VGMStreamNative.Reset(vgmstream); } int cacheSampleLength = cache.Length / waveFormat.Channels / 2; int length = 0; while (length < value) { if (length + cacheSampleLength >= value) { cacheSampleLength = value - length; } VGMStreamNative.Render8(cache, cacheSampleLength, vgmstream); length += cacheSampleLength; } } } /// /// Determines whether the loop is enabled for the VGMSTREAM. /// public bool LoopFlag { get { return loopFlag; } } /// /// Gets the loop start sample of the VGMSTREAM. /// public long LoopStartSample { get { return VGMStreamNative.GetLoopStartSample(vgmstream); } } /// /// Gets the loop start position of the VGMSTREAM in bytes. /// public long LoopStartPosition { get { return LoopStartSample * waveFormat.Channels * 2; } } /// /// Gets the loop end sample of the VGMSTREAM. /// public long LoopEndSample { get { return VGMStreamNative.GetLoopEndSample(vgmstream); } } /// /// Gets the loop end position of the VGMSTREAM in bytes. /// public long LoopEndPosition { get { return LoopEndSample * waveFormat.Channels * 2; } } /// /// Forces the VGMSTREAM to loop from start to end. /// This method destroys the previous loop points. /// public void ForceLoop() { VGMStreamNative.SetLoopFlag(vgmstream, true); VGMStreamNative.SetLoopStartSample(vgmstream, 0); VGMStreamNative.SetLoopEndSample(vgmstream, sampleCount); loopFlag = true; } /// /// Disables the loop for this VGMSTREAM. /// public void DisableLoop() { loopFlag = false; VGMStreamNative.SetLoopFlag(vgmstream, false); } /// /// Resets the VGMSTREAM to beginning. /// public void Reset() { VGMStreamNative.Reset(vgmstream); } /// /// Renders the VGMSTREAM to byte buffer. /// /// Destination byte buffer. /// Offset within buffer to write to. /// Count of bytes to render. /// Number of bytes read. public override int Read(byte[] buffer, int offset, int count) { if (cache == null || cache.Length < buffer.Length) { cache = new byte[buffer.Length]; } int currentSample = VGMStreamNative.GetCurrentSample(vgmstream); int sampleCount_vgmstream = count / waveFormat.Channels / 2; if (currentSample >= sampleCount && !loopFlag) { return 0; } if (!loopFlag && currentSample + sampleCount_vgmstream > sampleCount) { sampleCount_vgmstream = sampleCount - currentSample; } count = sampleCount_vgmstream * waveFormat.Channels * 2; VGMStreamNative.Render8(cache, sampleCount_vgmstream, vgmstream); Array.Copy(cache, 0, buffer, offset, count); return count; } protected override void Dispose(bool disposing) { if (disposing) { VGMStreamNative.Close(vgmstream); } base.Dispose(disposing); } private void FromPtr(IntPtr vgmstream) { if (vgmstream == IntPtr.Zero) { throw new NullReferenceException("VGMSTREAM pointer is set to null!"); } waveFormat = new WaveFormat(VGMStreamNative.GetSampleRate(vgmstream), 16, VGMStreamNative.GetChannelCount(vgmstream)); sampleCount = VGMStreamNative.GetSampleCount(vgmstream); loopFlag = VGMStreamNative.GetLoopFlag(vgmstream); cache = new byte[4096]; } /// /// Constructs from source file name. /// /// Source file name to load. public VGMStreamReader(string sourceFileName) { if (!File.Exists(sourceFileName)) { throw new FileNotFoundException($"VGMStream could not find file: {sourceFileName}"); } vgmstream = VGMStreamNative.Initialize(sourceFileName); if (vgmstream == IntPtr.Zero) { throw new NullReferenceException($"VGMStream could not initialize file: {sourceFileName}"); } FromPtr(vgmstream); } /// /// Constructs from pointer. /// /// Pointer to VGMSTREAM. public VGMStreamReader(IntPtr vgmstream) { FromPtr(vgmstream); } } }