mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-14 22:33:43 +00:00
282 lines
14 KiB
Odin
282 lines
14 KiB
Odin
#+build windows
|
|
package windows_xaudio2
|
|
|
|
import "core:math"
|
|
|
|
foreign import xa2 "system:xaudio2.lib"
|
|
|
|
/**************************************************************************
|
|
*
|
|
* Effect creation functions.
|
|
*
|
|
* On Xbox the application can link with the debug library to use the debug functionality.
|
|
*
|
|
**************************************************************************/
|
|
|
|
@(default_calling_convention="system")
|
|
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 #packed {
|
|
pPeakLevels: [^]f32 `fmt:"v,ChannelCount"`, // Peak levels table: receives maximum absolute level for each channel over a processing pass, may be nil if pRMSLevls != nil, otherwise must have at least ChannelCount elements.
|
|
pRMSLevels: [^]f32 `fmt:"v,ChannelCount"`, // Root mean square levels table: receives RMS level for each channel over a processing pass, may be nil if pPeakLevels != nil, 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 #packed {
|
|
// 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: b32, // 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: b32 : false
|
|
|
|
// REVERB_I3DL2_PARAMETERS: Parameter set compliant with the I3DL2 standard
|
|
|
|
REVERB_I3DL2_PARAMETERS :: struct #packed {
|
|
// 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: b32 = 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 {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 {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
|
|
}
|