Forráskód Böngészése

First implementation of XAudio2

Vincent Billet 4 hónapja
szülő
commit
125b60aee0

+ 233 - 0
vendor/windows/XAudio2/x3daudio.odin

@@ -0,0 +1,233 @@
+#+build windows
+
+/* NOTES:
+    1.  Definition of terms:
+	    LFE: Low Frequency Effect -- always omnidirectional.
+	    LPF: Low Pass Filter, divided into two classifications:
+		 Direct -- Applied to the direct signal path,
+			   used for obstruction/occlusion effects.
+		 Reverb -- Applied to the reverb signal path,
+			   used for occlusion effects only.
+
+    2.  Volume level is expressed as a linear amplitude scaler:
+	1.0f represents no attenuation applied to the original signal,
+	0.5f denotes an attenuation of 6dB, and 0.0f results in silence.
+	Amplification (volume > 1.0f) is also allowed, and is not clamped.
+
+	LPF values range from 1.0f representing all frequencies pass through,
+	to 0.0f which results in silence as all frequencies are filtered out.
+
+    3.  X3DAudio uses a left-handed Cartesian coordinate system with values
+	on the x-axis increasing from left to right, on the y-axis from
+	bottom to top, and on the z-axis from near to far.
+	Azimuths are measured clockwise from a given reference direction.
+
+	Distance measurement is with respect to user-defined world units.
+	Applications may provide coordinates using any system of measure
+	as all non-normalized calculations are scale invariant, with such
+	operations natively occurring in user-defined world unit space.
+	Metric constants are supplied only as a convenience.
+	Distance is calculated using the Euclidean norm formula.
+
+    4.  Only real values are permissible with functions using 32-bit
+	float parameters -- NAN and infinite values are not accepted.
+	All computation occurs in 32-bit precision mode.                    */
+
+
+package windows_xaudio2
+
+import "core:math"
+
+foreign import xa2 "system:xaudio2.lib"
+
+//--------------<D-E-F-I-N-I-T-I-O-N-S>-------------------------------------//
+// speaker geometry configuration flags, specifies assignment of channels to speaker positions, defined as per WAVEFORMATEXTENSIBLE.dwChannelMask
+SPEAKER_FLAGS :: distinct bit_set[SPEAKER_FLAG; u32]
+SPEAKER_FLAG :: enum u32 {
+	FRONT_LEFT            = 0,
+	FRONT_RIGHT           = 1,
+	FRONT_CENTER          = 2,
+	LOW_FREQUENCY         = 3,
+	BACK_LEFT             = 4,
+	BACK_RIGHT            = 5,
+	FRONT_LEFT_OF_CENTER  = 6,
+	FRONT_RIGHT_OF_CENTER = 7,
+	BACK_CENTER           = 8,
+	SIDE_LEFT             = 9,
+	SIDE_RIGHT            = 10,
+	TOP_CENTER            = 11,
+	TOP_FRONT_LEFT        = 12,
+	TOP_FRONT_CENTER      = 13,
+	TOP_FRONT_RIGHT       = 14,
+	TOP_BACK_LEFT         = 15,
+	TOP_BACK_CENTER       = 16,
+	TOP_BACK_RIGHT        = 17,
+	//RESERVED            = 0x7FFC0000, // bit mask locations reserved for future use
+	ALL                   = 31,         // used to specify that any possible permutation of speaker configurations
+}
+
+// standard speaker geometry configurations, used with Initialize
+SPEAKER_MONO             :: SPEAKER_FLAGS{.FRONT_CENTER}
+SPEAKER_STEREO           :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT}
+SPEAKER_2POINT1          :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .LOW_FREQUENCY}
+SPEAKER_SURROUND         :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .BACK_CENTER}
+SPEAKER_QUAD             :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .BACK_LEFT, .BACK_RIGHT}
+SPEAKER_4POINT1          :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .LOW_FREQUENCY, .BACK_LEFT, .BACK_RIGHT}
+SPEAKER_5POINT1          :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .LOW_FREQUENCY, .BACK_LEFT, .BACK_RIGHT}
+SPEAKER_7POINT1          :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .LOW_FREQUENCY, .BACK_LEFT, .BACK_RIGHT, .FRONT_LEFT_OF_CENTER, .FRONT_RIGHT_OF_CENTER}
+SPEAKER_5POINT1_SURROUND :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .LOW_FREQUENCY, .SIDE_LEFT, .SIDE_RIGHT}
+SPEAKER_7POINT1_SURROUND :: SPEAKER_FLAGS{.FRONT_LEFT, .FRONT_RIGHT, .FRONT_CENTER, .LOW_FREQUENCY, .BACK_LEFT, .BACK_RIGHT, .SIDE_LEFT, .SIDE_RIGHT}
+
+// size of instance handle in bytes
+HANDLE_BYTESIZE :: 20
+
+// speed of sound in meters per second for dry air at approximately 20C, used with Initialize
+SPEED_OF_SOUND :: 343.5
+
+// calculation control flags, used with Calculate
+CALCULATE_FLAGS :: distinct bit_set[CALCULATE_FLAG; u32]
+CALCULATE_FLAG :: enum u32 {
+	MATRIX        = 0, // enable matrix coefficient table calculation
+	DELAY         = 1, // enable delay time array calculation (stereo final mix only)
+	LPF_DIRECT    = 2, // enable LPF direct-path coefficient calculation
+	LPF_REVERB    = 3, // enable LPF reverb-path coefficient calculation
+	REVERB        = 4, // enable reverb send level calculation
+	DOPPLER       = 5, // enable doppler shift factor calculation
+	EMITTER_ANGLE = 6, // enable emitter-to-listener interior angle calculation
+
+	ZEROCENTER      = 16, // do not position to front center speaker, signal positioned to remaining speakers instead, front center destination channel will be zero in returned matrix coefficient table, valid only for matrix calculations with final mix formats that have a front center channel
+	REDIRECT_TO_LFE = 17, // apply equal mix of all source channels to LFE destination channel, valid only for matrix calculations with sources that have no LFE channel and final mix formats that have an LFE channel
+}
+
+//--------------<D-A-T-A---T-Y-P-E-S>---------------------------------------//
+VECTOR :: [3]f32 // float 3D vector
+
+// instance handle of precalculated constants
+HANDLE :: [HANDLE_BYTESIZE]byte
+
+// Distance curve point:
+// Defines a DSP setting at a given normalized distance.
+DISTANCE_CURVE_POINT :: struct {
+	Distance:   f32,   // normalized distance, must be within [0.0f, 1.0f]
+	DSPSetting: f32,   // DSP setting
+}
+
+// Distance curve:
+// A piecewise curve made up of linear segments used to define DSP behaviour with respect to normalized distance.
+//
+// Note that curve point distances are normalized within [0.0f, 1.0f].
+// EMITTER.CurveDistanceScaler must be used to scale the normalized distances to user-defined world units.
+// For distances beyond CurveDistanceScaler * 1.0f, pPoints[PointCount-1].DSPSetting is used as the DSP setting.
+//
+// All distance curve spans must be such that:
+//      pPoints[k-1].DSPSetting + ((pPoints[k].DSPSetting-pPoints[k-1].DSPSetting) / (pPoints[k].Distance-pPoints[k-1].Distance)) * (pPoints[k].Distance-pPoints[k-1].Distance) != NAN or infinite values
+// For all points in the distance curve where 1 <= k < PointCount.
+DISTANCE_CURVE :: struct {
+	pPoints:    [^]DISTANCE_CURVE_POINT,    // distance curve point array, must have at least PointCount elements with no duplicates and be sorted in ascending order with respect to Distance
+	PointCount: u32,                        // number of distance curve points, must be >= 2 as all distance curves must have at least two endpoints, defining DSP settings at 0.0f and 1.0f normalized distance
+}
+Default_LinearCurvePoints := [2]DISTANCE_CURVE_POINT{{0.0, 1.0}, {1.0, 0.0}}
+Default_LinearCurve       := DISTANCE_CURVE{&Default_LinearCurvePoints[0], 2}
+
+CONE :: struct {
+	InnerAngle:  f32,   // inner cone angle in radians, must be within [0.0f, TAU]
+	OuterAngle:  f32,   // outer cone angle in radians, must be within [InnerAngle, TAU]
+
+	InnerVolume: f32,   // volume level scaler on/within inner cone, used only for matrix calculations, must be within [0.0f, 2.0f] when used
+	OuterVolume: f32,   // volume level scaler on/beyond outer cone, used only for matrix calculations, must be within [0.0f, 2.0f] when used
+	InnerLPF:    f32,   // LPF (both direct and reverb paths) coefficient subtrahend on/within inner cone, used only for LPF (both direct and reverb paths) calculations, must be within [0.0f, 1.0f] when used
+	OuterLPF:    f32,   // LPF (both direct and reverb paths) coefficient subtrahend on/beyond outer cone, used only for LPF (both direct and reverb paths) calculations, must be within [0.0f, 1.0f] when used
+	InnerReverb: f32,   // reverb send level scaler on/within inner cone, used only for reverb calculations, must be within [0.0f, 2.0f] when used
+	OuterReverb: f32,   // reverb send level scaler on/beyond outer cone, used only for reverb calculations, must be within [0.0f, 2.0f] when used
+}
+Default_DirectionalCone := CONE{math.PI / 2, math.PI, 1.0, 0.708, 0.0, 0.25, 0.708, 1.0}
+
+// Listener:
+// Defines a point of 3D audio reception.
+//
+// The cone is directed by the listener's front orientation.
+LISTENER :: struct {
+	OrientFront: VECTOR,   // orientation of front direction, used only for matrix and delay calculations or listeners with cones for matrix, LPF (both direct and reverb paths), and reverb calculations, must be normalized when used
+	OrientTop:   VECTOR,   // orientation of top direction, used only for matrix and delay calculations, must be orthonormal with OrientFront when used
+
+	Position: VECTOR,      // position in user-defined world units, does not affect Velocity
+	Velocity: VECTOR,      // velocity vector in user-defined world units/second, used only for doppler calculations, does not affect Position
+
+	pCone: ^CONE,                     // sound cone, used only for matrix, LPF (both direct and reverb paths), and reverb calculations, NULL specifies omnidirectionality
+}
+
+// Emitter:
+// Defines a 3D audio source, divided into two classifications:
+//
+// Single-point -- For use with single-channel sounds.
+//                 Positioned at the emitter base, i.e. the channel radius and azimuth are ignored if the number of channels == 1.
+//
+//                 May be omnidirectional or directional using a cone.
+//                 The cone originates from the emitter base position, and is directed by the emitter's front orientation.
+//
+// Multi-point  -- For use with multi-channel sounds.
+//                 Each non-LFE channel is positioned using an azimuth along the channel radius with respect to the front orientation vector in the plane orthogonal to the top orientation vector.
+//                 An azimuth of TAU specifies a channel is an LFE. Such channels are positioned at the emitter base and are calculated with respect to pLFECurve only, never pVolumeCurve.
+//
+//                 Multi-point emitters are always omnidirectional, i.e. the cone is ignored if the number of channels > 1.
+//
+// Note that many properties are shared among all channel points, locking certain behaviour with respect to the emitter base position.
+// For example, doppler shift is always calculated with respect to the emitter base position and so is constant for all its channel points.
+// Distance curve calculations are also with respect to the emitter base position, with the curves being calculated independently of each other.
+// For instance, volume and LFE calculations do not affect one another.
+EMITTER :: struct {
+	pCone: ^CONE,   // sound cone, used only with single-channel emitters for matrix, LPF (both direct and reverb paths), and reverb calculations, NULL specifies omnidirectionality
+
+	OrientFront: VECTOR,   // orientation of front direction, used only for emitter angle calculations or with multi-channel emitters for matrix calculations or single-channel emitters with cones for matrix, LPF (both direct and reverb paths), and reverb calculations, must be normalized when used
+	OrientTop:   VECTOR,   // orientation of top direction, used only with multi-channel emitters for matrix calculations, must be orthonormal with OrientFront when used
+
+	Position: VECTOR,   // position in user-defined world units, does not affect Velocity
+	Velocity: VECTOR,   // velocity vector in user-defined world units/second, used only for doppler calculations, does not affect Position
+
+	InnerRadius:      f32,    // inner radius, must be within [0.0f, max(f32)]
+	InnerRadiusAngle: f32,    // inner radius angle, must be within [0.0f, PI/4.0)
+
+	ChannelCount:     u32,       // number of sound channels, must be > 0
+	ChannelRadius:    f32,       // channel radius, used only with multi-channel emitters for matrix calculations, must be >= 0.0f when used
+	pChannelAzimuths: [^]f32,    // channel azimuth array, used only with multi-channel emitters for matrix calculations, contains positions of each channel expressed in radians along the channel radius with respect to the front orientation vector in the plane orthogonal to the top orientation vector, or TAU to specify an LFE channel, must have at least ChannelCount elements, all within [0.0f, TAU] when used
+
+	pVolumeCurve:    ^DISTANCE_CURVE,    // volume level distance curve, used only for matrix calculations, NULL specifies a default curve that conforms to the inverse square law, calculated in user-defined world units with distances <= CurveDistanceScaler clamped to no attenuation
+	pLFECurve:       ^DISTANCE_CURVE,    // LFE level distance curve, used only for matrix calculations, NULL specifies a default curve that conforms to the inverse square law, calculated in user-defined world units with distances <= CurveDistanceScaler clamped to no attenuation
+	pLPFDirectCurve: ^DISTANCE_CURVE,    // LPF direct-path coefficient distance curve, used only for LPF direct-path calculations, NULL specifies the default curve: [0.0f,1.0f], [1.0f,0.75f]
+	pLPFReverbCurve: ^DISTANCE_CURVE,    // LPF reverb-path coefficient distance curve, used only for LPF reverb-path calculations, NULL specifies the default curve: [0.0f,0.75f], [1.0f,0.75f]
+	pReverbCurve:    ^DISTANCE_CURVE,    // reverb send level distance curve, used only for reverb calculations, NULL specifies the default curve: [0.0f,1.0f], [1.0f,0.0f]
+
+	CurveDistanceScaler: f32,   // curve distance scaler, used to scale normalized distance curves to user-defined world units and/or exaggerate their effect, used only for matrix, LPF (both direct and reverb paths), and reverb calculations, must be within [min(f32), max(f32)] when used
+	DopplerScaler:       f32,   // doppler shift scaler, used to exaggerate doppler shift effect, used only for doppler calculations, must be within [0.0f, max(f32)] when used
+}
+
+// DSP settings:
+// Receives results from a call to Calculate to be sent to the low-level audio rendering API for 3D signal processing.
+//
+// The user is responsible for allocating the matrix coefficient table, delay time array, and initializing the channel counts when used.
+DSP_SETTINGS :: struct {
+	pMatrixCoefficients: [^]f32,  // [inout] matrix coefficient table, receives an array representing the volume level used to send from source channel S to destination channel D, stored as pMatrixCoefficients[SrcChannelCount * D + S], must have at least SrcChannelCount*DstChannelCount elements
+	pDelayTimes:         [^]f32,  // [inout] delay time array, receives delays for each destination channel in milliseconds, must have at least DstChannelCount elements (stereo final mix only)
+	SrcChannelCount:     u32,     // [in] number of source channels, must equal number of channels in respective emitter
+	DstChannelCount:     u32,     // [in] number of destination channels, must equal number of channels of the final mix
+
+	LPFDirectCoefficient:   f32,  // [out] LPF direct-path coefficient
+	LPFReverbCoefficient:   f32,  // [out] LPF reverb-path coefficient
+	ReverbLevel:            f32,  // [out] reverb send level
+	DopplerFactor:          f32,  // [out] doppler shift factor, scales resampler ratio for doppler shift effect, where the effective frequency = DopplerFactor * original frequency
+	EmitterToListenerAngle: f32,  // [out] emitter-to-listener interior angle, expressed in radians with respect to the emitter's front orientation
+
+	EmitterToListenerDistance: f32,  // [out] distance in user-defined world units from the emitter base to listener position, always calculated
+	EmitterVelocityComponent:  f32,  // [out] component of emitter velocity vector projected onto emitter->listener vector in user-defined world units/second, calculated only for doppler
+	ListenerVelocityComponent: f32,  // [out] component of listener velocity vector projected onto emitter->listener vector in user-defined world units/second, calculated only for doppler
+}
+
+//--------------<F-U-N-C-T-I-O-N-S>-----------------------------------------//
+@(default_calling_convention="system", link_prefix="X3DAudio")
+foreign xa2 {
+	// initializes instance handle
+	Initialize :: proc(SpeakerChannelMask: SPEAKER_FLAGS, SpeedOfSound: f32, Instance: HANDLE) -> HRESULT ---
+
+	// calculates DSP settings with respect to 3D parameters
+	Calculate :: proc(Instance: HANDLE, pListener: ^LISTENER, pEmitter: ^EMITTER, Flags: CALCULATE_FLAGS, pDSPSettings: ^DSP_SETTINGS) ---
+}

+ 821 - 0
vendor/windows/XAudio2/xaudio2.odin

@@ -0,0 +1,821 @@
+#+build windows
+/*
+	Bindings for Windows XAudio2:
+	https://learn.microsoft.com/en-us/windows/win32/xaudio2/xaudio2-introduction
+
+	Compiling for Windows 10 RS5 (1809) and later
+*/
+
+package windows_xaudio2
+
+import win "core:sys/windows"
+import "core:math"
+
+foreign import xa2 "system:xaudio2.lib"
+
+HRESULT :: win.HRESULT
+IUnknown :: win.IUnknown
+IUnknown_VTable :: win.IUnknown_VTable
+IID :: win.IID
+
+/**************************************************************************
+ *
+ * XAudio2 constants, flags and error codes.
+ *
+ **************************************************************************/
+
+// Numeric boundary values
+MAX_BUFFER_BYTES     :: 0x80000000      // Maximum bytes allowed in a source buffer
+MAX_QUEUED_BUFFERS   :: 64              // Maximum buffers allowed in a voice queue
+MAX_BUFFERS_SYSTEM   :: 2               // Maximum buffers allowed for system threads (Xbox 360 only)
+MAX_AUDIO_CHANNELS   :: 64              // Maximum channels in an audio stream
+MIN_SAMPLE_RATE      :: 1000            // Minimum audio sample rate supported
+MAX_SAMPLE_RATE      :: 200000          // Maximum audio sample rate supported
+MAX_VOLUME_LEVEL     :: 16777216.0      // Maximum acceptable volume level (2^24)
+MIN_FREQ_RATIO       :: (1.0 / 1024.0)  // Minimum SetFrequencyRatio argument
+MAX_FREQ_RATIO       :: 1024.0          // Maximum MaxFrequencyRatio argument
+DEFAULT_FREQ_RATIO   :: 2.0             // Default MaxFrequencyRatio argument
+MAX_FILTER_ONEOVERQ  :: 1.5             // Maximum FILTER_PARAMETERS.OneOverQ
+MAX_FILTER_FREQUENCY :: 1.0             // Maximum FILTER_PARAMETERS.Frequency
+MAX_LOOP_COUNT       :: 254             // Maximum non-infinite BUFFER.LoopCount
+MAX_INSTANCES        :: 8               // Maximum simultaneous XAudio2 objects on Xbox 360
+
+// For XMA voices on Xbox 360 there is an additional restriction on the MaxFrequencyRatio argument and the voice's sample rate: the product of these numbers cannot exceed 600000 for one-channel voices or 300000 for voices with more than one channel.
+MAX_RATIO_TIMES_RATE_XMA_MONO         :: 600000
+MAX_RATIO_TIMES_RATE_XMA_MULTICHANNEL :: 300000
+
+// Numeric values with special meanings
+COMMIT_NOW         :: 0             // Used as an OperationSet argument
+COMMIT_ALL         :: 0             // Used in IXAudio2.CommitChanges
+INVALID_OPSET      :: 0xffffffff    // Not allowed for OperationSet arguments
+NO_LOOP_REGION     :: 0             // Used in BUFFER.LoopCount
+LOOP_INFINITE      :: 255           // Used in BUFFER.LoopCount
+DEFAULT_CHANNELS   :: 0             // Used in CreateMasteringVoice
+DEFAULT_SAMPLERATE :: 0             // Used in CreateMasteringVoice
+
+// Flags
+FLAGS :: distinct bit_set[FLAG; u32]
+FLAG :: enum u32 {
+	DEBUG_ENGINE            = 0,    // Used in Create
+	VOICE_NOPITCH           = 1,    // Used in IXAudio2.CreateSourceVoice
+	VOICE_NOSRC             = 2,    // Used in IXAudio2.CreateSourceVoice
+	VOICE_USEFILTER         = 3,    // Used in IXAudio2.CreateSource/SubmixVoice
+	PLAY_TAILS              = 5,    // Used in IXAudio2SourceVoice.Stop
+	END_OF_STREAM           = 6,    // Used in BUFFER.Flags
+	SEND_USEFILTER          = 7,    // Used in SEND_DESCRIPTOR.Flags
+	VOICE_NOSAMPLESPLAYED   = 8,    // Used in IXAudio2SourceVoice.GetState
+	STOP_ENGINE_WHEN_IDLE   = 13,   // Used in Create to force the engine to Stop when no source voices are Started, and Start when a voice is Started
+	QUANTUM_1024            = 15,   // Used in Create to specify nondefault processing quantum of 21.33 ms (1024 samples at 48KHz)
+	NO_VIRTUAL_AUDIO_CLIENT = 16,   // Used in CreateMasteringVoice to create a virtual audio client
+}
+
+// Default parameters for the built-in filter
+DEFAULT_FILTER_TYPE      :: FILTER_TYPE.LowPassFilter
+DEFAULT_FILTER_FREQUENCY :: MAX_FILTER_FREQUENCY
+DEFAULT_FILTER_ONEOVERQ  :: 1.0
+
+// Internal XAudio2 constants
+// The audio frame quantum can be calculated by reducing the fraction:
+//     SamplesPerAudioFrame / SamplesPerSecond
+QUANTUM_NUMERATOR   :: 1                 // On Windows, XAudio2 processes audio
+QUANTUM_DENOMINATOR :: 100               //  in 10ms chunks (= 1/100 seconds)
+QUANTUM_MS          :: (1000.0 * QUANTUM_NUMERATOR / QUANTUM_DENOMINATOR)
+
+// XAudio2 error codes
+INVALID_CALL         :: HRESULT(-0x7769FFFF)    // An API call or one of its arguments was illegal
+XMA_DECODER_ERROR    :: HRESULT(-0x7769FFFE)    // The XMA hardware suffered an unrecoverable error
+XAPO_CREATION_FAILED :: HRESULT(-0x7769FFFD)    // XAudio2 failed to initialize an XAPO effect
+DEVICE_INVALIDATED   :: HRESULT(-0x7769FFFC)    // An audio device became unusable (unplugged, etc)
+
+
+/**************************************************************************
+ *
+ * XAudio2 structures and enumerations.
+ *
+ **************************************************************************/
+
+// Used in Create, specifies which CPU(s) to use.
+PROCESSOR_FLAGS :: distinct bit_set[PROCESOR_FLAG; u32]
+PROCESOR_FLAG :: enum u32 {
+	Processor1  = 0,
+	Processor2  = 1,
+	Processor3  = 2,
+	Processor4  = 3,
+	Processor5  = 4,
+	Processor6  = 5,
+	Processor7  = 6,
+	Processor8  = 7,
+	Processor9  = 8,
+	Processor10 = 9,
+	Processor11 = 10,
+	Processor12 = 11,
+	Processor13 = 12,
+	Processor14 = 13,
+	Processor15 = 14,
+	Processor16 = 15,
+	Processor17 = 16,
+	Processor18 = 17,
+	Processor19 = 18,
+	Processor20 = 19,
+	Processor21 = 20,
+	Processor22 = 21,
+	Processor23 = 22,
+	Processor24 = 23,
+	Processor25 = 24,
+	Processor26 = 25,
+	Processor27 = 26,
+	Processor28 = 27,
+	Processor29 = 28,
+	Processor30 = 29,
+	Processor31 = 30,
+	Processor32 = 31,
+}
+
+USE_DEFAULT_PROCESSOR :: PROCESSOR_FLAGS{}
+
+// Returned by IXAudio2Voice.GetVoiceDetails
+VOICE_DETAILS :: struct {
+	CreatingFlags:   FLAGS,
+	ActiveFlags:     FLAGS,
+	InputChannels:   u32,
+	InputSampleRate: u32,
+}
+
+// Used in VOICE_SENDS below
+SEND_DESCRIPTOR :: struct {
+	Flags:        FLAGS,              // Either 0 or SEND_USEFILTER.
+	pOutputVoice: ^IXAudio2Voice,     // This send's destination voice.
+}
+
+// Used in the voice creation functions and in IXAudio2Voice.SetOutputVoices
+VOICE_SENDS :: struct {
+	SendCount: u32,                  // Number of sends from this voice.
+	pSends:    [^]SEND_DESCRIPTOR,   // Array of SendCount send descriptors.
+}
+
+// Used in EFFECT_CHAIN below
+EFFECT_DESCRIPTOR :: struct {
+	pEffect:        ^IUnknown,      // Pointer to the effect object's IUnknown interface.
+	InitialState:   bool,           // TRUE if the effect should begin in the enabled state.
+	OutputChannels: u32,            // How many output channels the effect should produce.
+}
+
+// Used in the voice creation functions and in IXAudio2Voice.SetEffectChain
+EFFECT_CHAIN :: struct {
+	EffectCount:        u32,                    // Number of effects in this voice's effect chain.
+	pEffectDescriptors: [^]EFFECT_DESCRIPTOR,   // Array of effect descriptors.
+}
+
+// Used in FILTER_PARAMETERS below
+FILTER_TYPE :: enum i32 {
+	LowPassFilter,                   // Attenuates frequencies above the cutoff frequency (state-variable filter).
+	BandPassFilter,                  // Attenuates frequencies outside a given range      (state-variable filter).
+	HighPassFilter,                  // Attenuates frequencies below the cutoff frequency (state-variable filter).
+	NotchFilter,                     // Attenuates frequencies inside a given range       (state-variable filter).
+	LowPassOnePoleFilter,            // Attenuates frequencies above the cutoff frequency (one-pole filter, FILTER_PARAMETERS.OneOverQ has no effect)
+	HighPassOnePoleFilter,           // Attenuates frequencies below the cutoff frequency (one-pole filter, FILTER_PARAMETERS.OneOverQ has no effect)
+}
+
+// Used in IXAudio2Voice.Set/GetFilterParameters and Set/GetOutputFilterParameters
+FILTER_PARAMETERS :: struct {
+	Type:      FILTER_TYPE,         // Filter type.
+	Frequency: f32,                 // Filter coefficient. Must be >= 0 and <= MAX_FILTER_FREQUENCY. See CutoffFrequencyToRadians() for state-variable filter types and CutoffFrequencyToOnePoleCoefficient() for one-pole filter types.
+	OneOverQ:  f32,                 // Reciprocal of the filter's quality factor Q; must be > 0 and <= MAX_FILTER_ONEOVERQ. Has no effect for one-pole filters.
+}
+
+// Used in IXAudio2SourceVoice.SubmitSourceBuffer
+BUFFER :: struct {
+	Flags:      FLAGS,              // Either 0 or END_OF_STREAM.
+	AudioBytes: u32,                // Size of the audio data buffer in bytes.
+	pAudioData: [^]byte,            // Pointer to the audio data buffer.
+	PlayBegin:  u32,                // First sample in this buffer to be played.
+	PlayLength: u32,                // Length of the region to be played in samples, or 0 to play the whole buffer.
+	LoopBegin:  u32,                // First sample of the region to be looped.
+	LoopLength: u32,                // Length of the desired loop region in samples, or 0 to loop the entire buffer.
+	LoopCount:  u32,                // Number of times to repeat the loop region, or LOOP_INFINITE to loop forever.
+	pContext:   rawptr,             // Context value to be passed back in callbacks.
+}
+
+// Used in IXAudio2SourceVoice.SubmitSourceBuffer when submitting XWMA data.
+// NOTE: If an XWMA sound is submitted in more than one buffer, each buffer's pDecodedPacketCumulativeBytes[PacketCount-1] value must be subtracted from all the entries in the next buffer's pDecodedPacketCumulativeBytes array.
+// And whether a sound is submitted in more than one buffer or not, the final buffer of the sound should use the END_OF_STREAM flag, or else the client must call IXAudio2SourceVoice.Discontinuity after submitting it.
+BUFFER_WMA :: struct {
+	pDecodedPacketCumulativeBytes: [^]u32,  // Decoded packet's cumulative size array. Each element is the number of bytes accumulated when the corresponding XWMA packet is decoded in order.  The array must have PacketCount elements.
+	PacketCount:                   u32,     // Number of XWMA packets submitted. Must be >= 1 and divide evenly into BUFFER.AudioBytes.
+}
+
+// Returned by IXAudio2SourceVoice.GetState
+VOICE_STATE :: struct {
+	pCurrentBufferContext: rawptr,      // The pContext value provided in the BUFFER that is currently being processed, or NULL if there are no buffers in the queue.
+	BuffersQueued:         u32,         // Number of buffers currently queued on the voice (including the one that is being processed).
+	SamplesPlayed:         u64,         // Total number of samples produced by the voice since it began processing the current audio stream. If VOICE_NOSAMPLESPLAYED is specified in the call to IXAudio2SourceVoice.GetState, this member will not be calculated, saving CPU.
+}
+
+// Returned by IXAudio2.GetPerformanceData
+PERFORMANCE_DATA :: struct {
+	// CPU usage information
+	AudioCyclesSinceLastQuery: u64,     // CPU cycles spent on audio processing since the last call to StartEngine or GetPerformanceData.
+	TotalCyclesSinceLastQuery: u64,     // Total CPU cycles elapsed since the last call (only counts the CPU XAudio2 is running on).
+	MinimumCyclesPerQuantum:   u32,     // Fewest CPU cycles spent processing any one audio quantum since the last call.
+	MaximumCyclesPerQuantum:   u32,     // Most CPU cycles spent processing any one audio quantum since the last call.
+
+	// Memory usage information
+	MemoryUsageInBytes: u32,            // Total heap space currently in use.
+
+	// Audio latency and glitching information
+	CurrentLatencyInSamples:    u32,    // Minimum delay from when a sample is read from a source buffer to when it reaches the speakers.
+	GlitchesSinceEngineStarted: u32,    // Audio dropouts since the engine was started.
+
+	// Data about XAudio2's current workload
+	ActiveSourceVoiceCount: u32,        // Source voices currently playing.
+	TotalSourceVoiceCount:  u32,        // Source voices currently existing.
+	ActiveSubmixVoiceCount: u32,        // Submix voices currently playing/existing.
+
+	ActiveResamplerCount: u32,          // Resample xAPOs currently active.
+	ActiveMatrixMixCount: u32,          // MatrixMix xAPOs currently active.
+
+	// Usage of the hardware XMA decoder (Xbox 360 only)
+	ActiveXmaSourceVoices: u32,         // Number of source voices decoding XMA data.
+	ActiveXmaStreams:      u32,         // A voice can use more than one XMA stream.
+}
+
+// Used in IXAudio2.SetDebugConfiguration
+DEBUG_CONFIGURATION :: struct {
+	TraceMask:       DEBUG_CONFIG_FLAGS,      // Bitmap of enabled debug message types.
+	BreakMask:       DEBUG_CONFIG_FLAGS,      // Message types that will break into the debugger.
+	LogThreadID:     bool,			  // Whether to log the thread ID with each message.
+	LogFileline:     bool,			  // Whether to log the source file and line number.
+	LogFunctionName: bool,			  // Whether to log the function name.
+	LogTiming:       bool,			  // Whether to log message timestamps.
+}
+
+// Values for the TraceMask and BreakMask bitmaps. Only ERRORS and WARNINGS are valid in BreakMask.
+// WARNINGS implies ERRORS, DETAIL implies INFO, and FUNC_CALLS implies API_CALLS.
+// By default, TraceMask is ERRORS and WARNINGS and all the other settings are zero.
+DEBUG_CONFIG_FLAGS :: distinct bit_set[DEBUG_CONFIG_FLAG; u32]
+DEBUG_CONFIG_FLAG :: enum u32 {
+	ERRORS     = 0,   // For handled errors with serious effects.
+	WARNINGS   = 1,   // For handled errors that may be recoverable.
+	INFO       = 2,   // Informational chit-chat (e.g. state changes).
+	DETAIL     = 3,   // More detailed chit-chat.
+	API_CALLS  = 4,   // Public API function entries and exits.
+	FUNC_CALLS = 5,   // Internal function entries and exits.
+	TIMING     = 6,   // Delays detected and other timing data.
+	LOCKS      = 7,   // Usage of critical sections and mutexes.
+	MEMORY     = 8,   // Memory heap usage information.
+	STREAMING  = 12,  // Audio streaming information.
+}
+
+/**************************************************************************
+ *
+ * IXAudio2: Top-level XAudio2 COM interface.
+ *
+ **************************************************************************/
+
+IXAudio2_UUID_STRING :: "2B02E3CF-2E0B-4ec3-BE45-1B2A3FE7210D"
+IXAudio2_UUID := &IID{0x2B02E3CF, 0x2E0B, 0x4ec3, {0xBE, 0x45, 0x1B, 0x2A, 0x3F, 0xE7, 0x21, 0x0D}}
+IXAudio2 :: struct #raw_union {
+	#subtype iunknown: IUnknown,
+	using ixaudio2_vtable: ^IXAudio2_VTable,
+}
+IXAudio2_VTable :: struct {
+	using iunknown_vtable: IUnknown_VTable,
+
+	// NAME: IXAudio2.RegisterForCallbacks
+	// DESCRIPTION: Adds a new client to receive XAudio2's engine callbacks.
+	// ARGUMENTS:
+	//  pCallback - Callback interface to be called during each processing pass.
+	RegisterForCallbacks: proc "system" (this: ^IXAudio2, pCallback: ^IXAudio2EngineCallback) -> HRESULT,
+
+	// NAME: IXAudio2.UnregisterForCallbacks
+	// DESCRIPTION: Removes an existing receiver of XAudio2 engine callbacks.
+	// ARGUMENTS:
+	//  pCallback - Previously registered callback interface to be removed.
+	UnregisterForCallbacks: proc "system" (this: ^IXAudio2, pCallback: ^IXAudio2EngineCallback),
+
+	// NAME: IXAudio2.CreateSourceVoice
+	// DESCRIPTION: Creates and configures a source voice.
+	// ARGUMENTS:
+	//  ppSourceVoice - Returns the new object's IXAudio2SourceVoice interface.
+	//  pSourceFormat - Format of the audio that will be fed to the voice.
+	//  Flags - VOICE flags specifying the source voice's behavior.
+	//  MaxFrequencyRatio - Maximum SetFrequencyRatio argument to be allowed.
+	//  pCallback - Optional pointer to a client-provided callback interface.
+	//  pSendList - Optional list of voices this voice should send audio to.
+	//  pEffectChain - Optional list of effects to apply to the audio data.
+	CreateSourceVoice: proc "system" (this: ^IXAudio2, ppSourceVoice: ^^IXAudio2SourceVoice, pSourceFormat: ^win.WAVEFORMATEX, Flags: FLAGS = {}, MaxFrequencyRatio: f32 = DEFAULT_FREQ_RATIO, pCallback: ^IXAudio2VoiceCallback = nil, pSendList: [^]VOICE_SENDS = nil, pEffectChain: [^]EFFECT_CHAIN = nil) -> HRESULT,
+
+	// NAME: IXAudio2.CreateSubmixVoice
+	// DESCRIPTION: Creates and configures a submix voice.
+	// ARGUMENTS:
+	//  ppSubmixVoice - Returns the new object's IXAudio2SubmixVoice interface.
+	//  InputChannels - Number of channels in this voice's input audio data.
+	//  InputSampleRate - Sample rate of this voice's input audio data.
+	//  Flags - VOICE flags specifying the submix voice's behavior.
+	//  ProcessingStage - Arbitrary number that determines the processing order.
+	//  pSendList - Optional list of voices this voice should send audio to.
+	//  pEffectChain - Optional list of effects to apply to the audio data.
+	CreateSubmixVoice: proc "system" (this: ^IXAudio2, ppSubmixVoice: ^^IXAudio2SubmixVoice, InputChannels: u32, InputSampleRate: u32, Flags: FLAGS = {}, ProcessingStage: u32 = 0, pSendList: [^]VOICE_SENDS = nil, pEffectChain: [^]EFFECT_CHAIN = nil) -> HRESULT,
+
+	// NAME: IXAudio2.CreateMasteringVoice
+	// DESCRIPTION: Creates and configures a mastering voice.
+	// ARGUMENTS:
+	//  ppMasteringVoice - Returns the new object's IXAudio2MasteringVoice interface.
+	//  InputChannels - Number of channels in this voice's input audio data.
+	//  InputSampleRate - Sample rate of this voice's input audio data.
+	//  Flags - VOICE flags specifying the mastering voice's behavior.
+	//  szDeviceId - Identifier of the device to receive the output audio.
+	//  pEffectChain - Optional list of effects to apply to the audio data.
+	//  StreamCategory - The audio stream category to use for this mastering voice
+	CreateMasteringVoice: proc "system" (this: ^IXAudio2, ppMasteringVoice: ^^IXAudio2MasteringVoice, InputChannels: u32 = DEFAULT_CHANNELS, InputSampleRate: u32 = DEFAULT_SAMPLERATE, Flags: FLAGS = {}, szDeviceId: win.LPCWSTR = nil, pEffectChain: [^]EFFECT_CHAIN = nil, StreamCategory: AUDIO_STREAM_CATEGORY = .GameEffects) -> HRESULT,
+
+	// NAME: IXAudio2.:StartEngine
+	// DESCRIPTION: Creates and starts the audio processing thread.
+	StartEngine: proc "system" (this: ^IXAudio2) -> HRESULT,
+
+	// NAME: IXAudio2.StopEngine
+	// DESCRIPTION: Stops and destroys the audio processing thread.
+	StopEngine: proc "system" (this: ^IXAudio2),
+
+	// NAME: IXAudio2.CommitChanges
+	// DESCRIPTION: Atomically applies a set of operations previously tagged
+	//              with a given identifier.
+	// ARGUMENTS:
+	//  OperationSet - Identifier of the set of operations to be applied.
+	CommitChanges: proc "system" (this: ^IXAudio2, OperationSet: u32) -> HRESULT,
+
+	// NAME: IXAudio2.GetPerformanceData
+	// DESCRIPTION: Returns current resource usage details: memory, CPU, etc.
+	// ARGUMENTS:
+	//  pPerfData - Returns the performance data structure.
+	GetPerformanceData: proc "system" (this: ^IXAudio2, pPerfData: ^PERFORMANCE_DATA),
+
+	// NAME: IXAudio2.SetDebugConfiguration
+	// DESCRIPTION: Configures XAudio2's debug output (in debug builds only).
+	// ARGUMENTS:
+	//  pDebugConfiguration - Structure describing the debug output behavior.
+	//  pReserved - Optional parameter; must be NULL.
+	SetDebugConfiguration: proc "system" (this: ^IXAudio2, pDebugConfiguration: ^DEBUG_CONFIGURATION, pReserved: rawptr = nil),
+}
+
+// This interface extends IXAudio2 with additional functionality.
+// Use IXAudio2.QueryInterface to obtain a pointer to this interface.
+IXAudio2Extension_UUID_STRING :: "84ac29bb-d619-44d2-b197-e4acf7df3ed6"
+IXAudio2Extension_UUID := &IID{0x84ac29bb, 0xd619, 0x44d2, {0xb1, 0x97, 0xe4, 0xac, 0xf7, 0xdf, 0x3e, 0xd6}}
+IXAudio2Extension :: struct #raw_union {
+	#subtype iunknown: IUnknown,
+	using ixaudio2extension_vtable: ^IXAudio2Extension_VTable,
+}
+IXAudio2Extension_VTable :: struct {
+	using iunknown_vtable: IUnknown_VTable,
+
+	// NAME: IXAudio2Extension.GetProcessingQuantum
+	// DESCRIPTION: Returns the processing quantum
+	//              quantumMilliseconds = (1000.0f * quantumNumerator / quantumDenominator)
+	// ARGUMENTS:
+	//  quantumNumerator - Quantum numerator
+	//  quantumDenominator - Quantum denominator
+	GetProcessingQuantum: proc "system" (this: ^IXAudio2Extension, quantumNumerator: ^u32, quantumDenominator: ^u32),
+
+	// NAME: IXAudio2Extension.GetProcessor
+	// DESCRIPTION: Returns the number of the processor used by XAudio2
+	// ARGUMENTS:
+	//  processor - Non-zero Processor number
+	GetProcessor: proc "system" (this: ^IXAudio2Extension, processor: ^PROCESSOR_FLAGS),
+}
+
+/**************************************************************************
+ *
+ * IXAudio2Voice: Base voice management interface.
+ *
+ **************************************************************************/
+
+IXAudio2Voice :: struct {
+	using ixaudio2voice_vtable: ^IXAudio2Voice_VTable,
+}
+IXAudio2Voice_VTable :: struct {
+	// NAME: IXAudio2Voice.GetVoiceDetails
+	// DESCRIPTION: Returns the basic characteristics of this voice.
+	// ARGUMENTS:
+	//  pVoiceDetails - Returns the voice's details.
+	GetVoiceDetails: proc "system" (this: ^IXAudio2Voice, pVoiceDetails: ^VOICE_DETAILS),
+
+	// NAME: IXAudio2Voice.SetOutputVoices
+	// DESCRIPTION: Replaces the set of submix/mastering voices that receive
+	//              this voice's output.
+	// ARGUMENTS:
+	//  pSendList - Optional list of voices this voice should send audio to.
+	SetOutputVoices: proc "system" (this: ^IXAudio2Voice, pSendList: [^]VOICE_SENDS) -> HRESULT,
+
+	// NAME: IXAudio2Voice.SetEffectChain
+	// DESCRIPTION: Replaces this voice's current effect chain with a new one.
+	// ARGUMENTS:
+	//  pEffectChain - Structure describing the new effect chain to be used.
+	SetEffectChain: proc "system" (this: ^IXAudio2Voice, pEffectChain: ^EFFECT_CHAIN) -> HRESULT,
+
+	// NAME: IXAudio2Voice.EnableEffect
+	// DESCRIPTION: Enables an effect in this voice's effect chain.
+	// ARGUMENTS:
+	//  EffectIndex - Index of an effect within this voice's effect chain.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	EnableEffect: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2Voice.DisableEffect
+	// DESCRIPTION: Disables an effect in this voice's effect chain.
+	// ARGUMENTS:
+	//  EffectIndex - Index of an effect within this voice's effect chain.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	DisableEffect: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2Voice.GetEffectState
+	// DESCRIPTION: Returns the running state of an effect.
+	// ARGUMENTS:
+	//  EffectIndex - Index of an effect within this voice's effect chain.
+	//  pEnabled - Returns the enabled/disabled state of the given effect.
+	GetEffectState: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, pEnabled: ^bool),
+
+	// NAME: IXAudio2Voice.SetEffectParameters
+	// DESCRIPTION: Sets effect-specific parameters.
+	// REMARKS: Unlike IXAPOParameters.SetParameters, this method may be called from any thread. XAudio2 implements appropriate synchronization to copy the parameters to the realtime audio processing thread.
+	// ARGUMENTS:
+	//  EffectIndex - Index of an effect within this voice's effect chain.
+	//  pParameters - Pointer to an effect-specific parameters block.
+	//  ParametersByteSize - Size of the pParameters array  in bytes.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	SetEffectParameters: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, pParameters: rawptr, ParametersByteSize: u32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2Voice.GetEffectParameters
+	// DESCRIPTION: Obtains the current effect-specific parameters.
+	// ARGUMENTS:
+	//  EffectIndex - Index of an effect within this voice's effect chain.
+	//  pParameters - Returns the current values of the effect-specific parameters.
+	//  ParametersByteSize - Size of the pParameters array in bytes.
+	GetEffectParameters: proc "system" (this: ^IXAudio2Voice, EffectIndex: u32, pParameters: rawptr, ParametersByteSize: u32) -> HRESULT,
+
+	// NAME: IXAudio2Voice.SetFilterParameters
+	// DESCRIPTION: Sets this voice's filter parameters.
+	// ARGUMENTS:
+	//  pParameters - Pointer to the filter's parameter structure.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	SetFilterParameters: proc "system" (this: ^IXAudio2Voice, pParameters: ^FILTER_PARAMETERS, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2Voice.GetFilterParameters
+	// DESCRIPTION: Returns this voice's current filter parameters.
+	// ARGUMENTS:
+	//  pParameters - Returns the filter parameters.
+	GetFilterParameters: proc "system" (this: ^IXAudio2Voice, pParameters: ^FILTER_PARAMETERS),
+
+	// NAME: IXAudio2Voice.SetOutputFilterParameters
+	// DESCRIPTION: Sets the filter parameters on one of this voice's sends.
+	// ARGUMENTS:
+	//  pDestinationVoice - Destination voice of the send whose filter parameters will be set.
+	//  pParameters - Pointer to the filter's parameter structure.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	SetOutputFilterParameters: proc "system" (this: ^IXAudio2Voice, pDestinationVoice: ^IXAudio2Voice, pParameters: ^FILTER_PARAMETERS, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2Voice.GetOutputFilterParameters
+	// DESCRIPTION: Returns the filter parameters from one of this voice's sends.
+	// ARGUMENTS:
+	//  pDestinationVoice - Destination voice of the send whose filter parameters will be read.
+	//  pParameters - Returns the filter parameters.
+	GetOutputFilterParameters: proc "system" (this: ^IXAudio2Voice, pDestinationVoice: ^IXAudio2Voice, pParameters: ^FILTER_PARAMETERS),
+
+	// NAME: IXAudio2Voice.SetVolume
+	// DESCRIPTION: Sets this voice's overall volume level.
+	// ARGUMENTS:
+	//  Volume - New overall volume level to be used, as an amplitude factor.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	SetVolume: proc "system" (this: ^IXAudio2Voice, Volume: f32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2Voice.GetVolume
+	// DESCRIPTION: Obtains this voice's current overall volume level.
+	// ARGUMENTS:
+	//  pVolume: Returns the voice's current overall volume level.
+	GetVolume: proc "system" (this: ^IXAudio2Voice, pVolume: ^f32),
+
+	// NAME: IXAudio2Voice.SetChannelVolumes
+	// DESCRIPTION: Sets this voice's per-channel volume levels.
+	// ARGUMENTS:
+	//  Channels - Used to confirm the voice's channel count.
+	//  pVolumes - Array of per-channel volume levels to be used.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	SetChannelVolumes: proc "system" (this: ^IXAudio2Voice, Channels: u32, pVolumes: [^]f32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2Voice.GetChannelVolumes
+	// DESCRIPTION: Returns this voice's current per-channel volume levels.
+	// ARGUMENTS:
+	//  Channels - Used to confirm the voice's channel count.
+	//  pVolumes - Returns an array of the current per-channel volume levels.
+	GetChannelVolumes: proc "system" (this: ^IXAudio2Voice, Channels: u32, pVolumes: [^]f32),
+
+	// NAME: IXAudio2Voice.SetOutputMatrix
+	// DESCRIPTION: Sets the volume levels used to mix from each channel of this voice's output audio to each channel of a given destination voice's input audio.
+	// ARGUMENTS:
+	//  pDestinationVoice - The destination voice whose mix matrix to change.
+	//  SourceChannels - Used to confirm this voice's output channel count (the number of channels produced by the last effect in the chain).
+	//  DestinationChannels - Confirms the destination voice's input channels.
+	//  pLevelMatrix - Array of [SourceChannels * DestinationChannels] send levels. The level used to send from source channel S to destination channel D should be in pLevelMatrix[S + SourceChannels * D].
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	SetOutputMatrix: proc "system" (this: ^IXAudio2Voice, pDestinationVoice: ^IXAudio2Voice, SourceChannels: u32, DestinationChannels: u32, pLevelMatrix: [^]f32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2Voice.GetOutputMatrix
+	// DESCRIPTION: Obtains the volume levels used to send each channel of this voice's output audio to each channel of a given destination voice's input audio.
+	// ARGUMENTS:
+	//  pDestinationVoice - The destination voice whose mix matrix to obtain.
+	//  SourceChannels - Used to confirm this voice's output channel count (the number of channels produced by the last effect in the chain).
+	//  DestinationChannels - Confirms the destination voice's input channels.
+	//  pLevelMatrix - Array of send levels, as above.
+	GetOutputMatrix: proc "system" (this: ^IXAudio2Voice, pDestinationVoice: ^IXAudio2Voice, SourceChannels: u32, DestinationChannels: u32, pLevelMatrix: [^]f32),
+
+	// NAME: IXAudio2Voice.DestroyVoice
+	// DESCRIPTION: Destroys this voice, stopping it if necessary and removing it from the XAudio2 graph.
+	DestroyVoice: proc "system" (this: ^IXAudio2Voice),
+}
+
+/**************************************************************************
+ *
+ * IXAudio2SourceVoice: Source voice management interface.
+ *
+ **************************************************************************/
+
+IXAudio2SourceVoice :: struct #raw_union {
+	#subtype ixaudio2voice: IXAudio2Voice,
+	using ixaudio2sourcevoice_vtable: ^IXAudio2SourceVoice_VTable,
+}
+IXAudio2SourceVoice_VTable :: struct {
+	using ixaudio2voice_vtable: IXAudio2Voice_VTable,
+
+	// NAME: IXAudio2SourceVoice.Start
+	// DESCRIPTION: Makes this voice start consuming and processing audio.
+	// ARGUMENTS:
+	//  Flags - Flags controlling how the voice should be started.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	Start: proc "system" (this: ^IXAudio2SourceVoice, Flags: FLAGS = {}, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2SourceVoice.Stop
+	// DESCRIPTION: Makes this voice stop consuming audio.
+	// ARGUMENTS:
+	//  Flags - Flags controlling how the voice should be stopped.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	Stop: proc "system" (this: ^IXAudio2SourceVoice, Flags: FLAGS = {}, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2SourceVoice.SubmitSourceBuffer
+	// DESCRIPTION: Adds a new audio buffer to this voice's input queue.
+	// ARGUMENTS:
+	//  pBuffer - Pointer to the buffer structure to be queued.
+	//  pBufferWMA - Additional structure used only when submitting XWMA data.
+	SubmitSourceBuffer: proc "system" (this: ^IXAudio2SourceVoice, pBuffer: ^BUFFER, pBufferWMA: ^BUFFER_WMA = nil) -> HRESULT,
+
+	// NAME: IXAudio2SourceVoice.FlushSourceBuffers
+	// DESCRIPTION: Removes all pending audio buffers from this voice's queue.
+	FlushSourceBuffers: proc "system" (this: ^IXAudio2SourceVoice) -> HRESULT,
+
+	// NAME: IXAudio2SourceVoice.Discontinuity
+	// DESCRIPTION: Notifies the voice of an intentional break in the stream of audio buffers (e.g. the end of a sound), to prevent XAudio2 from interpreting an empty buffer queue as a glitch.
+	Discontinuity: proc "system" (this: ^IXAudio2SourceVoice) -> HRESULT,
+
+	// NAME: IXAudio2SourceVoice.ExitLoop
+	// DESCRIPTION: Breaks out of the current loop when its end is reached.
+	// ARGUMENTS:
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	ExitLoop: proc "system" (this: ^IXAudio2SourceVoice, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2SourceVoice.GetState
+	// DESCRIPTION: Returns the number of buffers currently queued on this voice, the pContext value associated with the currently processing buffer (if any), and other voice state information.
+	// ARGUMENTS:
+	//  pVoiceState - Returns the state information.
+	//  Flags - Flags controlling what voice state is returned.
+	GetState: proc "system" (this: ^IXAudio2SourceVoice, pVoiceState: ^VOICE_STATE, Flags: FLAGS = {}),
+
+	// NAME: IXAudio2SourceVoice.SetFrequencyRatio
+	// DESCRIPTION: Sets this voice's frequency adjustment, i.e. its pitch.
+	// ARGUMENTS:
+	//  Ratio - Frequency change, expressed as source frequency / target frequency.
+	//  OperationSet - Used to identify this call as part of a deferred batch.
+	SetFrequencyRatio: proc "system" (this: ^IXAudio2SourceVoice, Ratio: f32, OperationSet: u32 = COMMIT_NOW) -> HRESULT,
+
+	// NAME: IXAudio2SourceVoice.GetFrequencyRatio
+	// DESCRIPTION: Returns this voice's current frequency adjustment ratio.
+	// ARGUMENTS:
+	//  pRatio - Returns the frequency adjustment.
+	GetFrequencyRatio: proc "system" (this: ^IXAudio2SourceVoice, pRatio: ^f32),
+
+	// NAME: IXAudio2SourceVoice.SetSourceSampleRate
+	// DESCRIPTION: Reconfigures this voice to treat its source data as being at a different sample rate than the original one specified in CreateSourceVoice's pSourceFormat argument.
+	// ARGUMENTS:
+	//  UINT32 - The intended sample rate of further submitted source data.
+	SetSourceSampleRate: proc "system" (this: ^IXAudio2SourceVoice, NewSourceSampleRate: u32) -> HRESULT,
+}
+
+/**************************************************************************
+ *
+ * IXAudio2SubmixVoice: Submixing voice management interface.
+ *
+ **************************************************************************/
+
+IXAudio2SubmixVoice :: struct #raw_union {
+	#subtype ixaudio2voice: IXAudio2Voice,
+	using ixaudio2submixvoice_vtable: ^IXAudio2SubmixVoice_VTable,
+}
+IXAudio2SubmixVoice_VTable :: struct {
+	using ixaudio2voice_vtable: IXAudio2Voice_VTable,
+	// There are currently no methods specific to submix voices.
+}
+
+ /**************************************************************************
+  *
+  * IXAudio2MasteringVoice: Mastering voice management interface.
+  *
+  **************************************************************************/
+
+IXAudio2MasteringVoice :: struct #raw_union {
+	#subtype ixaudio2voice: IXAudio2Voice,
+	using ixaudio2masteringvoice_vtable: ^IXAudio2MasteringVoice_VTable,
+}
+IXAudio2MasteringVoice_VTable :: struct {
+	using ixaudio2voice_vtable: IXAudio2Voice_VTable,
+
+	// NAME: IXAudio2MasteringVoice.GetChannelMask
+	// DESCRIPTION: Returns the channel mask for this voice
+	// ARGUMENTS:
+	//  pChannelMask - returns the channel mask for this voice.  This corresponds to the dwChannelMask member of WAVEFORMATEXTENSIBLE.
+	GetChannelMask: proc "system" (this: ^IXAudio2MasteringVoice, pChannelmask: ^win.DWORD) -> HRESULT,
+}
+
+/**************************************************************************
+ *
+ * IXAudio2EngineCallback: Client notification interface for engine events.
+ *
+ * REMARKS: Contains methods to notify the client when certain events happen in the XAudio2 engine. This interface should be implemented by the client.
+ *          XAudio2 will call these methods via the interface pointer provided by the client when it calls IXAudio2.RegisterForCallbacks.
+ *
+ **************************************************************************/
+
+IXAudio2EngineCallback :: struct {
+	using ixaudio2enginecallback_vtable: ^IXAudio2EngineCallback_VTable,
+}
+IXAudio2EngineCallback_VTable :: struct {
+	// Called by XAudio2 just before an audio processing pass begins.
+	OnProcessingPassStart: proc "system" (this: ^IXAudio2EngineCallback),
+
+	// Called just after an audio processing pass ends.
+	OnProcessingPassEnd: proc "system" (this: ^IXAudio2EngineCallback),
+
+	// Called in the event of a critical system error which requires XAudio2 to be closed down and restarted. The error code is given in Error.
+	OnCriticalError: proc "system" (this: ^IXAudio2EngineCallback, Error: HRESULT),
+}
+
+ /**************************************************************************
+  *
+  * IXAudio2VoiceCallback: Client notification interface for voice events.
+  *
+  * REMARKS: Contains methods to notify the client when certain events happen in an XAudio2 voice. This interface should be implemented by the client.
+  *          XAudio2 will call these methods via an interface pointer provided by the client in the IXAudio2.CreateSourceVoice call.
+  *
+  **************************************************************************/
+
+IXAudio2VoiceCallback :: struct {
+	using ixaudio2voicecallback_vtable: ^IXAudio2VoiceCallback_VTable,
+}
+IXAudio2VoiceCallback_VTable :: struct {
+	// Called just before this voice's processing pass begins.
+	OnVoiceProcessingPassStart: proc "system" (this: ^IXAudio2VoiceCallback, BytesRequired: u32),
+
+	// Called just after this voice's processing pass ends.
+	OnVoiceProcessingPassEnd: proc "system" (this: ^IXAudio2VoiceCallback),
+
+	// Called when this voice has just finished playing a buffer stream (as marked with the END_OF_STREAM flag on the last buffer).
+	OnStreamEnd: proc "system" (this: ^IXAudio2VoiceCallback),
+
+	// Called when this voice is about to start processing a new buffer.
+	OnBufferStart: proc "system" (this: ^IXAudio2VoiceCallback, pBufferContext: rawptr),
+
+	// Called when this voice has just finished processing a buffer.
+	// The buffer can now be reused or destroyed.
+	OnBufferEnd: proc "system" (this: ^IXAudio2VoiceCallback, pBufferContext: rawptr),
+
+	// Called when this voice has just reached the end position of a loop.
+	OnLoopEnd: proc "system" (this: ^IXAudio2VoiceCallback, pBufferContext: rawptr),
+
+	// Called in the event of a critical error during voice processing, such as a failing xAPO or an error from the hardware XMA decoder.
+	// The voice may have to be destroyed and re-created to recover from the error.
+	// The callback arguments report which buffer was being processed when the error occurred, and its HRESULT code.
+	OnVoiceError: proc "system" (this: ^IXAudio2VoiceCallback, pBufferContext: rawptr, Error: HRESULT),
+}
+
+/**************************************************************************
+ *
+ * XAudio2Create: Top-level function that creates an XAudio2 instance.
+ *
+ * ARGUMENTS:
+ *
+ *  Flags - Flags specifying the XAudio2 object's behavior.
+ *
+ *  XAudio2Processor - A PROCESSOR_FLAGS value that specifies the hardware threads (Xbox) or processors (Windows) that XAudio2 will use.
+ *          Note that XAudio2 supports concurrent processing on multiple threads, using any combination of PROCESSOR_FLAGS flags.
+ *          The values are platform-specific; platform-independent code can use USE_DEFAULT_PROCESSOR to use the default on each platform.
+ *
+ **************************************************************************/
+
+// We're an xaudio2 client
+@(default_calling_convention="system", link_prefix="XAudio2")
+foreign xa2 {
+	Create :: proc(ppXAudio2: ^^IXAudio2, Flags: FLAGS = {}, XAudio2Processor: PROCESSOR_FLAGS = USE_DEFAULT_PROCESSOR) -> HRESULT ---
+}
+
+
+/**************************************************************************
+ *
+ * Utility functions used to convert from pitch in semitones and volume in decibels to the frequency and amplitude ratio units used by XAudio2.
+ *
+ **************************************************************************/
+
+// Calculate the argument to SetVolume from a decibel value
+DecibelsToAmplitudeRatio :: proc "contextless" (Decibels: f32) -> f32 {
+	return math.pow_f32(10.0, Decibels / 20.0)
+}
+
+// Recover a volume in decibels from an amplitude factor
+AmplitudeRatioToDecibels :: proc "contextless" (Volume: f32) -> f32 {
+	if Volume == 0 {
+		return min(f32)
+	}
+	return 20.0 * math.log10_f32(Volume)
+}
+
+// Calculate the argument to SetFrequencyRatio from a semitone value
+SemitonesToFrequencyRatio :: proc "contextless" (Semitones: f32) -> f32 {
+	// FrequencyRatio = 2 ^ Octaves
+	//                = 2 ^ (Semitones / 12)
+	return math.pow_f32(2.0, Semitones / 12.0)
+}
+
+// Recover a pitch in semitones from a frequency ratio
+FrequencyRatioToSemitones :: proc "contextless" (FrequencyRatio: f32) -> f32 {
+	// Semitones = 12 * log2(FrequencyRatio)
+	//           = 12 * log2(10) * log10(FrequencyRatio)
+	return 12.0 * math.log2_f32(FrequencyRatio)
+}
+
+// Convert from filter cutoff frequencies expressed in Hertz to the radian frequency values used in FILTER_PARAMETERS.Frequency, state-variable filter types only.
+// Use CutoffFrequencyToOnePoleCoefficient() for one-pole filter types.
+// Note that the highest CutoffFrequency supported is SampleRate/6.
+// Higher values of CutoffFrequency will return MAX_FILTER_FREQUENCY.
+CutoffFrequencyToRadians :: proc "contextless" (CutoffFrequency: f32, SampleRate: u32) -> f32 {
+	if u32(CutoffFrequency * 6.0) >= SampleRate {
+		return MAX_FILTER_FREQUENCY
+	}
+	return 2.0 * math.sin_f32(math.PI * CutoffFrequency / f32(SampleRate))
+}
+
+// Convert from radian frequencies back to absolute frequencies in Hertz
+RadiansToCutoffFrequency :: proc "contextless" (Radians: f32, SampleRate: f32) -> f32 {
+	return SampleRate * math.asin_f32(Radians / 2.0) / math.PI
+}
+
+// Convert from filter cutoff frequencies expressed in Hertz to the filter coefficients used with FILTER_PARAMETERS.Frequency,
+// LowPassOnePoleFilter and HighPassOnePoleFilter filter types only.
+// Use CutoffFrequencyToRadians() for state-variable filter types.
+CutoffFrequencyToOnePoleCoefficient :: proc "contextless" (CutoffFrequency: f32, SampleRate: u32) -> f32 {
+	if u32(CutoffFrequency) >= SampleRate {
+		return MAX_FILTER_FREQUENCY
+	}
+	return 1.0 - math.pow_f32(1.0 - 2.0 * CutoffFrequency / f32(SampleRate), 2.0)
+}
+
+//-------------------------------------------------------------------------
+// Description: Audio stream categories
+//
+// Other                   - All other streams (default)
+// ForegroundOnlyMedia     - (deprecated for Win10) Music, Streaming audio
+// BackgroundCapableMedia  - (deprecated for Win10) Video with audio
+// Communications          - VOIP, chat, phone call
+// Alerts                  - Alarm, Ring tones
+// SoundEffects            - Sound effects, clicks, dings
+// GameEffects             - Game sound effects
+// GameMedia               - Background audio for games
+// GameChat                - In game player chat
+// Speech                  - Speech recognition
+// Media                   - Music, Streaming audio
+// Movie                   - Video with audio
+// FarFieldSpeech          - Capture of far field speech
+// UniformSpeech           - Uniform, device agnostic speech processing
+// VoiceTyping             - Dictation, typing by voice
+//
+AUDIO_STREAM_CATEGORY :: enum i32 {
+	Other          = 0,
+	//ForegroundOnlyMedia = 1,
+	//BackgroundCapableMedia = 2,
+	Communications = 3,
+	Alerts         = 4,
+	SoundEffects   = 5,
+	GameEffects    = 6,
+	GameMedia      = 7,
+	GameChat       = 8,
+	Speech         = 9,
+	Movie          = 10,
+	Media          = 11,
+	FarFieldSpeech = 12,
+	UniformSpeech  = 13,
+	VoiceTyping    = 14,
+}

+ 287 - 0
vendor/windows/XAudio2/xaudio2fx.odin

@@ -0,0 +1,287 @@
+#+build windows
+
+package windows_xaudio2
+
+import "core:math"
+
+foreign import xa2 "system:xaudio2.lib"
+
+CLSID_AudioVolumeMeter_UUID_STRING :: "4FC3B166-972A-40CF-BC37-7DB03DB2FBA3"
+CLSID_AudioVolumeMeter_UUID := &IID{0x4FC3B166, 0x972A, 0x40CF, {0xBC, 0x37, 0x7D, 0xB0, 0x3D, 0xB2, 0xFB, 0xA3}}
+
+CLSID_AudioReverb_UUID_STRING :: "C2633B16-471B-4498-B8C5-4F0959E2EC09"
+CLSID_AudioReverb_UUID := &IID{0xC2633B16, 0x471B, 0x4498, {0xB8, 0xC5, 0x4F, 0x09, 0x59, 0xE2, 0xEC, 0x09}}
+
+/**************************************************************************
+ *
+ * Effect creation functions.
+ *
+ * On Xbox the application can link with the debug library to use the debug
+ * functionality.
+ *
+ **************************************************************************/
+
+ @(default_calling_convention="system", link_prefix="XAudio2")
+foreign xa2 {
+	CreateAudioVolumeMeter :: proc(ppApo: ^^IUnknown) -> HRESULT ---
+	CreateAudioReverb      :: proc(ppApo: ^^IUnknown) -> HRESULT ---
+}
+
+/**************************************************************************
+ *
+ * Volume meter parameters.
+ * The volume meter supports f32 audio formats and must be used in-place.
+ *
+ **************************************************************************/
+
+// VOLUMEMETER_LEVELS: Receives results from GetEffectParameters().
+// The user is responsible for allocating pPeakLevels, pRMSLevels, and initializing ChannelCount accordingly.
+// The volume meter does not support SetEffectParameters().
+VOLUMEMETER_LEVELS :: struct {
+	pPeakLevels:  [^]f32,   // Peak levels table: receives maximum absolute level for each channel over a processing pass, may be NULL if pRMSLevls != NULL, otherwise must have at least ChannelCount elements.
+	pRMSLevels:   [^]f32,   // Root mean square levels table: receives RMS level for each channel over a processing pass, may be NULL if pPeakLevels != NULL, otherwise must have at least ChannelCount elements.
+	ChannelCount: u32, 	    // Number of channels being processed by the volume meter APO
+}
+
+/**************************************************************************
+ *
+ * Reverb parameters.
+ * The reverb supports only f32 audio with the following channel configurations:
+ *     Input: Mono   Output: Mono
+ *     Input: Mono   Output: 5.1
+ *     Input: Stereo Output: Stereo
+ *     Input: Stereo Output: 5.1
+ * The framerate must be within [20000, 48000] Hz.
+ *
+ * When using mono input, delay filters associated with the right channel are not executed.
+ * In this case, parameters such as PositionRight and PositionMatrixRight have no effect.
+ * This also means the reverb uses less CPU when hosted in a mono submix.
+ *
+ **************************************************************************/
+
+REVERB_MIN_FRAMERATE :: 20000
+REVERB_MAX_FRAMERATE :: 48000
+
+// REVERB_PARAMETERS: Native parameter set for the reverb effect
+
+REVERB_PARAMETERS :: struct {
+	// ratio of wet (processed) signal to dry (original) signal
+	WetDryMix: f32,               // [0, 100] (percentage)
+	// Delay times
+	ReflectionsDelay: u32,        // [0, 300] in ms
+	ReverbDelay:      byte,       // [0, 85] in ms
+	RearDelay:        byte,       // 7.1: [0, 20] in ms, all other: [0, 5] in ms
+	SideDelay:        byte,       // 7.1: [0, 5] in ms, all other: not used, but still validated
+	// Indexed parameters
+	PositionLeft:        byte,    // [0, 30] no units
+	PositionRight:       byte,    // [0, 30] no units, ignored when configured to mono
+	PositionMatrixLeft:  byte,    // [0, 30] no units
+	PositionMatrixRight: byte,    // [0, 30] no units, ignored when configured to mono
+	EarlyDiffusion:      byte,    // [0, 15] no units
+	LateDiffusion:       byte,    // [0, 15] no units
+	LowEQGain:           byte,    // [0, 12] no units
+	LowEQCutoff:         byte,    // [0, 9] no units
+	HighEQGain:          byte,    // [0, 8] no units
+	HighEQCutoff:        byte,    // [0, 14] no units
+	// Direct parameters
+	RoomFilterFreq:  f32,         // [20, 20000] in Hz
+	RoomFilterMain:  f32,         // [-100, 0] in dB
+	RoomFilterHF:    f32,         // [-100, 0] in dB
+	ReflectionsGain: f32,         // [-100, 20] in dB
+	ReverbGain:      f32,         // [-100, 20] in dB
+	DecayTime:       f32,         // [0.1, inf] in seconds
+	Density:         f32,         // [0, 100] (percentage)
+	RoomSize:        f32,         // [1, 100] in feet
+	// component control
+	DisableLateField: bool,       // TRUE to disable late field reflections
+}
+
+// Maximum, minimum and default values for the parameters above
+REVERB_MIN_WET_DRY_MIX        :: 0.0
+REVERB_MIN_REFLECTIONS_DELAY  :: 0
+REVERB_MIN_REVERB_DELAY       :: 0
+REVERB_MIN_REAR_DELAY         :: 0
+REVERB_MIN_7POINT1_SIDE_DELAY :: 0
+REVERB_MIN_7POINT1_REAR_DELAY :: 0
+REVERB_MIN_POSITION           :: 0
+REVERB_MIN_DIFFUSION          :: 0
+REVERB_MIN_LOW_EQ_GAIN        :: 0
+REVERB_MIN_LOW_EQ_CUTOFF      :: 0
+REVERB_MIN_HIGH_EQ_GAIN       :: 0
+REVERB_MIN_HIGH_EQ_CUTOFF     :: 0
+REVERB_MIN_ROOM_FILTER_FREQ   :: 20.0
+REVERB_MIN_ROOM_FILTER_MAIN   :: -100.0
+REVERB_MIN_ROOM_FILTER_HF     :: -100.0
+REVERB_MIN_REFLECTIONS_GAIN   :: -100.0
+REVERB_MIN_REVERB_GAIN        :: -100.0
+REVERB_MIN_DECAY_TIME         :: 0.1
+REVERB_MIN_DENSITY            :: 0.0
+REVERB_MIN_ROOM_SIZE          :: 0.0
+
+REVERB_MAX_WET_DRY_MIX        :: 100.0
+REVERB_MAX_REFLECTIONS_DELAY  :: 300
+REVERB_MAX_REVERB_DELAY       :: 85
+REVERB_MAX_REAR_DELAY         :: 5
+REVERB_MAX_7POINT1_SIDE_DELAY :: 5
+REVERB_MAX_7POINT1_REAR_DELAY :: 20
+REVERB_MAX_POSITION           :: 30
+REVERB_MAX_DIFFUSION          :: 15
+REVERB_MAX_LOW_EQ_GAIN        :: 12
+REVERB_MAX_LOW_EQ_CUTOFF      :: 9
+REVERB_MAX_HIGH_EQ_GAIN       :: 8
+REVERB_MAX_HIGH_EQ_CUTOFF     :: 14
+REVERB_MAX_ROOM_FILTER_FREQ   :: 20000.0
+REVERB_MAX_ROOM_FILTER_MAIN   :: 0.0
+REVERB_MAX_ROOM_FILTER_HF     :: 0.0
+REVERB_MAX_REFLECTIONS_GAIN   :: 20.0
+REVERB_MAX_REVERB_GAIN        :: 20.0
+REVERB_MAX_DENSITY            :: 100.0
+REVERB_MAX_ROOM_SIZE          :: 100.0
+
+REVERB_DEFAULT_WET_DRY_MIX        :: 100.0
+REVERB_DEFAULT_REFLECTIONS_DELAY  :: 5
+REVERB_DEFAULT_REVERB_DELAY       :: 5
+REVERB_DEFAULT_REAR_DELAY         :: 5
+REVERB_DEFAULT_7POINT1_SIDE_DELAY :: 5
+REVERB_DEFAULT_7POINT1_REAR_DELAY :: 20
+REVERB_DEFAULT_POSITION           :: 6
+REVERB_DEFAULT_POSITION_MATRIX    :: 27
+REVERB_DEFAULT_EARLY_DIFFUSION    :: 8
+REVERB_DEFAULT_LATE_DIFFUSION     :: 8
+REVERB_DEFAULT_LOW_EQ_GAIN        :: 8
+REVERB_DEFAULT_LOW_EQ_CUTOFF      :: 4
+REVERB_DEFAULT_HIGH_EQ_GAIN       :: 8
+REVERB_DEFAULT_HIGH_EQ_CUTOFF     :: 4
+REVERB_DEFAULT_ROOM_FILTER_FREQ   :: 5000.0
+REVERB_DEFAULT_ROOM_FILTER_MAIN   :: 0.0
+REVERB_DEFAULT_ROOM_FILTER_HF     :: 0.0
+REVERB_DEFAULT_REFLECTIONS_GAIN   :: 0.0
+REVERB_DEFAULT_REVERB_GAIN        :: 0.0
+REVERB_DEFAULT_DECAY_TIME         :: 1.0
+REVERB_DEFAULT_DENSITY            :: 100.0
+REVERB_DEFAULT_ROOM_SIZE          :: 100.0
+REVERB_DEFAULT_DISABLE_LATE_FIELD :: false
+
+// REVERB_I3DL2_PARAMETERS: Parameter set compliant with the I3DL2 standard
+
+REVERB_I3DL2_PARAMETERS :: struct {
+	// ratio of wet (processed) signal to dry (original) signal
+	WetDryMix: f32,            // [0, 100] (percentage)
+
+	// Standard I3DL2 parameters
+	Room:              i32,    // [-10000, 0] in mB (hundredths of decibels)
+	RoomHF:            i32,    // [-10000, 0] in mB (hundredths of decibels)
+	RoomRolloffFactor: f32,    // [0.0, 10.0]
+	DecayTime:         f32,    // [0.1, 20.0] in seconds
+	DecayHFRatio:      f32,    // [0.1, 2.0]
+	Reflections:       i32,    // [-10000, 1000] in mB (hundredths of decibels)
+	ReflectionsDelay:  f32,    // [0.0, 0.3] in seconds
+	Reverb:            i32,    // [-10000, 2000] in mB (hundredths of decibels)
+	ReverbDelay:       f32,    // [0.0, 0.1] in seconds
+	Diffusion:         f32,    // [0.0, 100.0] (percentage)
+	Density:           f32,    // [0.0, 100.0] (percentage)
+	HFReference:       f32,    // [20.0, 20000.0] in Hz
+}
+
+/**************************************************************************
+ *
+ * Standard I3DL2 reverb presets (100% wet).
+ *
+ **************************************************************************/
+
+I3DL2_PRESET_DEFAULT         := REVERB_I3DL2_PARAMETERS{100.0,-10000,    0,0.0, 1.00,0.50,-10000,0.020,-10000,0.040,100.0,100.0,5000.0}
+I3DL2_PRESET_GENERIC         := REVERB_I3DL2_PARAMETERS{100.0, -1000, -100,0.0, 1.49,0.83, -2602,0.007,   200,0.011,100.0,100.0,5000.0}
+I3DL2_PRESET_PADDEDCELL      := REVERB_I3DL2_PARAMETERS{100.0, -1000,-6000,0.0, 0.17,0.10, -1204,0.001,   207,0.002,100.0,100.0,5000.0}
+I3DL2_PRESET_ROOM            := REVERB_I3DL2_PARAMETERS{100.0, -1000, -454,0.0, 0.40,0.83, -1646,0.002,    53,0.003,100.0,100.0,5000.0}
+I3DL2_PRESET_BATHROOM        := REVERB_I3DL2_PARAMETERS{100.0, -1000,-1200,0.0, 1.49,0.54,  -370,0.007,  1030,0.011,100.0, 60.0,5000.0}
+I3DL2_PRESET_LIVINGROOM      := REVERB_I3DL2_PARAMETERS{100.0, -1000,-6000,0.0, 0.50,0.10, -1376,0.003, -1104,0.004,100.0,100.0,5000.0}
+I3DL2_PRESET_STONEROOM       := REVERB_I3DL2_PARAMETERS{100.0, -1000, -300,0.0, 2.31,0.64,  -711,0.012,    83,0.017,100.0,100.0,5000.0}
+I3DL2_PRESET_AUDITORIUM      := REVERB_I3DL2_PARAMETERS{100.0, -1000, -476,0.0, 4.32,0.59,  -789,0.020,  -289,0.030,100.0,100.0,5000.0}
+I3DL2_PRESET_CONCERTHALL     := REVERB_I3DL2_PARAMETERS{100.0, -1000, -500,0.0, 3.92,0.70, -1230,0.020,    -2,0.029,100.0,100.0,5000.0}
+I3DL2_PRESET_CAVE            := REVERB_I3DL2_PARAMETERS{100.0, -1000,    0,0.0, 2.91,1.30,  -602,0.015,  -302,0.022,100.0,100.0,5000.0}
+I3DL2_PRESET_ARENA           := REVERB_I3DL2_PARAMETERS{100.0, -1000, -698,0.0, 7.24,0.33, -1166,0.020,    16,0.030,100.0,100.0,5000.0}
+I3DL2_PRESET_HANGAR          := REVERB_I3DL2_PARAMETERS{100.0, -1000,-1000,0.0,10.05,0.23,  -602,0.020,   198,0.030,100.0,100.0,5000.0}
+I3DL2_PRESET_CARPETEDHALLWAY := REVERB_I3DL2_PARAMETERS{100.0, -1000,-4000,0.0, 0.30,0.10, -1831,0.002, -1630,0.030,100.0,100.0,5000.0}
+I3DL2_PRESET_HALLWAY         := REVERB_I3DL2_PARAMETERS{100.0, -1000, -300,0.0, 1.49,0.59, -1219,0.007,   441,0.011,100.0,100.0,5000.0}
+I3DL2_PRESET_STONECORRIDOR   := REVERB_I3DL2_PARAMETERS{100.0, -1000, -237,0.0, 2.70,0.79, -1214,0.013,   395,0.020,100.0,100.0,5000.0}
+I3DL2_PRESET_ALLEY           := REVERB_I3DL2_PARAMETERS{100.0, -1000, -270,0.0, 1.49,0.86, -1204,0.007,    -4,0.011,100.0,100.0,5000.0}
+I3DL2_PRESET_FOREST          := REVERB_I3DL2_PARAMETERS{100.0, -1000,-3300,0.0, 1.49,0.54, -2560,0.162,  -613,0.088, 79.0,100.0,5000.0}
+I3DL2_PRESET_CITY            := REVERB_I3DL2_PARAMETERS{100.0, -1000, -800,0.0, 1.49,0.67, -2273,0.007, -2217,0.011, 50.0,100.0,5000.0}
+I3DL2_PRESET_MOUNTAINS       := REVERB_I3DL2_PARAMETERS{100.0, -1000,-2500,0.0, 1.49,0.21, -2780,0.300, -2014,0.100, 27.0,100.0,5000.0}
+I3DL2_PRESET_QUARRY          := REVERB_I3DL2_PARAMETERS{100.0, -1000,-1000,0.0, 1.49,0.83,-10000,0.061,   500,0.025,100.0,100.0,5000.0}
+I3DL2_PRESET_PLAIN           := REVERB_I3DL2_PARAMETERS{100.0, -1000,-2000,0.0, 1.49,0.50, -2466,0.179, -2514,0.100, 21.0,100.0,5000.0}
+I3DL2_PRESET_PARKINGLOT      := REVERB_I3DL2_PARAMETERS{100.0, -1000,    0,0.0, 1.65,1.50, -1363,0.008, -1153,0.012,100.0,100.0,5000.0}
+I3DL2_PRESET_SEWERPIPE       := REVERB_I3DL2_PARAMETERS{100.0, -1000,-1000,0.0, 2.81,0.14,   429,0.014,   648,0.021, 80.0, 60.0,5000.0}
+I3DL2_PRESET_UNDERWATER      := REVERB_I3DL2_PARAMETERS{100.0, -1000,-4000,0.0, 1.49,0.10,  -449,0.007,  1700,0.011,100.0,100.0,5000.0}
+I3DL2_PRESET_SMALLROOM       := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.10,0.83,  -400,0.005,   500,0.010,100.0,100.0,5000.0}
+I3DL2_PRESET_MEDIUMROOM      := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.30,0.83, -1000,0.010,  -200,0.020,100.0,100.0,5000.0}
+I3DL2_PRESET_LARGEROOM       := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.50,0.83, -1600,0.020, -1000,0.040,100.0,100.0,5000.0}
+I3DL2_PRESET_MEDIUMHALL      := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.80,0.70, -1300,0.015,  -800,0.030,100.0,100.0,5000.0}
+I3DL2_PRESET_LARGEHALL       := REVERB_I3DL2_PARAMETERS{100.0, -1000, -600,0.0, 1.80,0.70, -2000,0.030, -1400,0.060,100.0,100.0,5000.0}
+I3DL2_PRESET_PLATE           := REVERB_I3DL2_PARAMETERS{100.0, -1000, -200,0.0, 1.30,0.90,     0,0.002,     0,0.010,100.0, 75.0,5000.0}
+
+// ReverbConvertI3DL2ToNative: Utility function to map from I3DL2 to native parameters
+
+ReverbConvertI3DL2ToNative :: proc "contextless" (pI3DL2: ^REVERB_I3DL2_PARAMETERS, pNative: ^REVERB_PARAMETERS, sevenDotOneReverb: bool = true) {
+	reflectionsDelay: f32
+	reverbDelay:      f32
+
+	// RoomRolloffFactor is ignored
+
+	// These parameters have no equivalent in I3DL2
+	if sevenDotOneReverb {
+		pNative.RearDelay = REVERB_DEFAULT_7POINT1_REAR_DELAY // 20
+	} else {
+		pNative.RearDelay = REVERB_DEFAULT_REAR_DELAY // 5
+	}
+	pNative.SideDelay           = REVERB_DEFAULT_7POINT1_SIDE_DELAY // 5
+	pNative.PositionLeft        = REVERB_DEFAULT_POSITION // 6
+	pNative.PositionRight       = REVERB_DEFAULT_POSITION // 6
+	pNative.PositionMatrixLeft  = REVERB_DEFAULT_POSITION_MATRIX // 27
+	pNative.PositionMatrixRight = REVERB_DEFAULT_POSITION_MATRIX // 27
+	pNative.RoomSize            = REVERB_DEFAULT_ROOM_SIZE // 100
+	pNative.LowEQCutoff         = 4
+	pNative.HighEQCutoff        = 6
+
+	// The rest of the I3DL2 parameters map to the native property set
+	pNative.RoomFilterMain = f32(pI3DL2.Room) / 100.0
+	pNative.RoomFilterHF   = f32(pI3DL2.RoomHF) / 100.0
+
+	if pI3DL2.DecayHFRatio >= 1.0 {
+		index := i32(-4.0 * math.log10_f32(pI3DL2.DecayHFRatio))
+		if index < -8 do index = -8
+		pNative.LowEQGain  = byte((index < 0) ? index + 8 : 8)
+		pNative.HighEQGain = 8
+		pNative.DecayTime  = pI3DL2.DecayTime * pI3DL2.DecayHFRatio
+	} else {
+		index := i32(4.0 * math.log10_f32(pI3DL2.DecayHFRatio))
+		if index < -8 do index = -8
+		pNative.LowEQGain  = 8
+		pNative.HighEQGain = byte((index < 0) ? index + 8 : 8)
+		pNative.DecayTime  = pI3DL2.DecayTime
+	}
+
+	reflectionsDelay = pI3DL2.ReflectionsDelay * 1000.0
+	if reflectionsDelay >= REVERB_MAX_REFLECTIONS_DELAY { // 300
+		reflectionsDelay = f32(REVERB_MAX_REFLECTIONS_DELAY - 1)
+	} else if reflectionsDelay <= 1 {
+		reflectionsDelay = 1
+	}
+	pNative.ReflectionsDelay = u32(reflectionsDelay)
+
+	reverbDelay = pI3DL2.ReverbDelay * 1000.0
+	if reverbDelay >= REVERB_MAX_REVERB_DELAY { // 85
+		reverbDelay = f32(REVERB_MAX_REVERB_DELAY - 1)
+	}
+	pNative.ReverbDelay = byte(reverbDelay)
+
+	pNative.ReflectionsGain = f32(pI3DL2.Reflections) / 100.0
+	pNative.ReverbGain      = f32(pI3DL2.Reverb) / 100.0
+	pNative.EarlyDiffusion  = byte(15.0 * pI3DL2.Diffusion / 100.0)
+	pNative.LateDiffusion   = pNative.EarlyDiffusion
+	pNative.Density         = pI3DL2.Density
+	pNative.RoomFilterFreq  = pI3DL2.HFReference
+
+	pNative.WetDryMix        = pI3DL2.WetDryMix
+	pNative.DisableLateField = false
+}