using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.InteropServices; namespace CsbBuilder.Audio { /// /// Represents a static class for calling native VGMSTREAM methods. /// public static class VGMStreamNative { /// /// Path of the VGMSTREAM DLL file. /// public const string DllName = "vgmstream.dll"; /// /// Size of VGMSTREAM structure. /// public const int SizeOfVgmStream = 152; /// /// Size of VGMSTREAMCHANNEL structure. /// public const int SizeOfVgmStreamChannel = 552; #region VGMStream Exports /// /// Initializes a VGMSTREAM from source file name by doing format detection and returns a usable pointer /// to it, or NULL on failure. /// /// Path to source file name. /// Pointer to VGMSTREAM or NULL on failure. [DllImport(DllName, EntryPoint = "init_vgmstream", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr Initialize(string sourceFileName); /// /// Initializes a VGMSTREAM from stream file by doing format detection and returns a usable pointer /// to it, or NULL on failure. /// /// Pointer to stream file. /// Pointer to VGMSTREAM or NULL on failure. [DllImport(DllName, EntryPoint = "init_from_STREAMFILE", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr InitializeFromStreamFile(IntPtr streamFile); /// /// Resets a VGMSTREAM to start of stream. /// /// Pointer to VGMSTREAM. [DllImport(DllName, EntryPoint = "reset_vgmstream", CallingConvention = CallingConvention.Cdecl)] public static extern void Reset(IntPtr vgmstream); /// /// Closes an open VGMSTREAM. /// /// Pointer to VGMSTREAM. [DllImport(DllName, EntryPoint = "close_vgmstream", CallingConvention = CallingConvention.Cdecl)] public static extern void Close(IntPtr vgmstream); /// /// Calculates the number of samples to be played based on looping parameters. /// [DllImport(DllName, EntryPoint = "get_vgmstream_play_samples", CallingConvention = CallingConvention.Cdecl)] public static extern int GetPlaySamples(double loopTimes, double fadeSeconds, double fadeDelaySeconds, IntPtr vgmstream); /// /// Renders VGMSTREAM to sample buffer. /// /// Destination sample buffer. /// Amount of samples to render. /// Pointer to VGMSTREAM. [DllImport(DllName, EntryPoint = "render_vgmstream", CallingConvention = CallingConvention.Cdecl)] public static extern void Render(short[] buffer, int sampleCount, IntPtr vgmstream); /// /// Renders VGMSTREAM to byte buffer. /// /// Destination byte buffer. /// Amount of samples to render. /// Pointer to VGMSTREAM. [DllImport(DllName, EntryPoint = "render_vgmstream", CallingConvention = CallingConvention.Cdecl)] public static extern void Render8(byte[] buffer, int sampleCount, IntPtr vgmstream); /// /// Writes a description of the stream into array pointed by description, /// which must be length bytes long. Will always be null-terminated if length > 0 /// [DllImport(DllName, EntryPoint = "describe_vgmstream", CallingConvention = CallingConvention.Cdecl)] public static extern void Describe(IntPtr vgmstream, IntPtr description, int length); /// /// Returns the average bitrate in bps of all unique files contained within /// this stream. Compares files by absolute paths. /// /// Pointer to VGMSTREAM. [DllImport(DllName, EntryPoint = "get_vgmstream_average_bitrate", CallingConvention = CallingConvention.Cdecl)] public static extern int GetAverageBitrate(IntPtr vgmstream); /// /// Allocates a VGMSTREAM. /// [DllImport(DllName, EntryPoint = "allocate_vgmstream", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr Allocate(int channelCount, [MarshalAs(UnmanagedType.I4)]bool looped); /// /// smallest self-contained group of samples is a frame /// [DllImport(DllName, EntryPoint = "get_vgmstream_samples_per_frame", CallingConvention = CallingConvention.Cdecl)] public static extern int GetSamplesPerFrame(IntPtr vgmstream); /// /// Gets the number of bytes per frame. /// [DllImport(DllName, EntryPoint = "get_vgmstream_frame_size", CallingConvention = CallingConvention.Cdecl)] public static extern int GetFrameSize(IntPtr vgmstream); /// /// in NDS IMA the frame size is the block size, so the last one is short /// [DllImport(DllName, EntryPoint = "get_vgmstream_samples_per_shortframe", CallingConvention = CallingConvention.Cdecl)] public static extern int GetSamplesPerShortframe(IntPtr vgmstream); [DllImport(DllName, EntryPoint = "get_vgmstream_shortframe_size", CallingConvention = CallingConvention.Cdecl)] public static extern int GetShortframeSize(IntPtr vgmstream); /// /// Assumes that we have written samplesWrittem into the buffer already, and we have samplesToDo consecutive /// samples ahead of us. Decode those samples into the buffer. /// [DllImport(DllName, EntryPoint = "decode_vgmstream", CallingConvention = CallingConvention.Cdecl)] public static extern void Decode(IntPtr vgmstream, int samplesWritten, int samplesToDo, short[] buffer); /// /// Assumes additionally that we have samplesToDo consecutive samples in "data", /// and this this is for channel number "channel". /// [DllImport(DllName, EntryPoint = "decode_vgmstream_mem", CallingConvention = CallingConvention.Cdecl)] public static extern void DecodeMem(IntPtr vgmstream, int samplesWritten, int samplesToDo, short[] buffer, byte[] data, int channel); /// /// Calculates number of consecutive samples to do (taking into account stopping for loop start and end.) /// [DllImport(DllName, EntryPoint = "vgmstream_samples_to_do", CallingConvention = CallingConvention.Cdecl)] public static extern int SamplesToDo(int samplesThisBlock, int samplesPerFrame, IntPtr vgmstream); /// /// Detects start and save values, also detects end and restore values. Only works on exact sample values. /// [DllImport(DllName, EntryPoint = "vgmstream_do_loop", CallingConvention = CallingConvention.Cdecl)] public static extern int DoLoop(IntPtr vgmstream); /// /// Opens a stream for reading at offset (standarized taking into account layouts, channels and so on.) /// returns 0 on failure /// [DllImport(DllName, EntryPoint = "vgmstream_open_stream", CallingConvention = CallingConvention.Cdecl)] public static extern int OpenStream(IntPtr vgmstream, IntPtr streamFile, long position); #endregion #region Format Exports /// /// Gets a pointer to the array of supported formats. /// [DllImport(DllName, EntryPoint = "vgmstream_get_formats", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr GetFormatsPtr(); /// /// Gets the length of the format array. /// /// [DllImport(DllName, EntryPoint = "vgmstream_get_formats_length", CallingConvention = CallingConvention.Cdecl)] public static extern int GetFormatsLength(); [DllImport(DllName, EntryPoint = "get_vgmstream_coding_description", CallingConvention = CallingConvention.Cdecl)] public static extern string GetCodingDescription(int codingEnumCode); [DllImport(DllName, EntryPoint = "get_vgmstream_layout_description", CallingConvention = CallingConvention.Cdecl)] public static extern string GetLayoutDescription(int layoutEnumCode); [DllImport(DllName, EntryPoint = "get_vgmstream_meta_description", CallingConvention = CallingConvention.Cdecl)] public static extern string GetMetaDescription(int metaEnumCode); #endregion #region Helper Methods /// /// Gets the sample count of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static int GetSampleCount(IntPtr vgmstream) { return Marshal.ReadInt32(vgmstream); } /// /// Gets the sample rate of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static int GetSampleRate(IntPtr vgmstream) { return Marshal.ReadInt32(vgmstream, 4); } /// /// Gets the channel count of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static int GetChannelCount(IntPtr vgmstream) { return Marshal.ReadInt32(vgmstream, 8); } /// /// Gets the absolute sample count of a VGMSTREAM. /// (sample count * channel count) /// /// Pointer to VGMSTREAM. public static int GetAbsoluteSampleCount(IntPtr vgmstream) { return GetSampleCount(vgmstream) * GetChannelCount(vgmstream); } /// /// Gets the loop flag of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static bool GetLoopFlag(IntPtr vgmstream) { return Marshal.ReadInt32(vgmstream, 28) != 0; } /// /// Sets the loop flag of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static void SetLoopFlag(IntPtr vgmstream, bool value) { if (value && !GetLoopFlag(vgmstream)) { Marshal.WriteIntPtr(vgmstream, 48, Marshal.AllocHGlobal(GetChannelCount(vgmstream) * SizeOfVgmStreamChannel)); } Marshal.WriteInt32(vgmstream, 28, value ? 1 : 0); } /// /// Gets the loop start sample of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static int GetLoopStartSample(IntPtr vgmstream) { return Marshal.ReadInt32(vgmstream, 32); } /// /// Sets the loop start sample of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static void SetLoopStartSample(IntPtr vgmstream, int value) { Marshal.WriteInt32(vgmstream, 32, value); } /// /// Gets the loop end sample of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static int GetLoopEndSample(IntPtr vgmstream) { return Marshal.ReadInt32(vgmstream, 36); } /// /// Sets the loop end sample of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static void SetLoopEndSample(IntPtr vgmstream, int value) { Marshal.WriteInt32(vgmstream, 36, value); } /// /// Gets the current sample of a VGMSTREAM. /// /// Pointer to VGMSTREAM. public static int GetCurrentSample(IntPtr vgmstream) { return Marshal.ReadInt32(vgmstream, 52); } /// /// Gets an array of supported formats. /// /// Pointer to VGMSTREAM. public static string[] GetFormats() { string[] formats = new string[GetFormatsLength()]; IntPtr ptr = GetFormatsPtr(); for (int i = 0; i < formats.Length; i++) { IntPtr stringPtr = Marshal.ReadIntPtr(ptr, i * IntPtr.Size); formats[i] = Marshal.PtrToStringAnsi(stringPtr); } return formats; } #endregion } }