Ambient Gain Formula
This function converts energy values to ambient gain values, and is invoked twice per emitter:
- once with low-frequency energy values
- once with high-frequency energy values
The default formula specifies that 10% of ambient occlusion energy, or 10% of ambient permeation energy is required for ambient sounds to be at max volume:
emitter.AmbientGainFormula = (
bool lowFrequency,
int ambientOcclusionRayCount,
int ambientPermeationRayCount,
int ambientPermeationBounceCount,
float ambientOcclusionEnergy,
float ambientPermeationEnergy)
{
float gain = 0.0f;
if (ambientOcclusionRayCount > 0)
{
// 10% of ambient occlusion energy is required for ambient sounds to be at max volume
float energyThreshold = 0.1f * ambientOcclusionRayCount;
gain += ambientOcclusionEnergy / energyThreshold;
}
if (ambientPermeationRayCount > 0 && ambientPermeationBounceCount > 0)
{
// 10% of ambient permeation energy is required for ambient sounds to be at max volume
float energyThreshold = 0.1f * (ambientPermeationRayCount * ambientPermeationBounceCount);
gain += ambientPermeationEnergy / energyThreshold;
}
return MathF.Min(1, gain);
}
In JS, the ambient gain formula is not configurable. Instead, set the energy cap properties on the emitter to define how much energy is required for ambient sounds to be at full volume:
// 10% of ambient occlusion energy required for ambient sounds to be at max volume
emitter.ambientOcclusionEnergyCap = 0.1;
// 10% of ambient permeation energy required for ambient sounds to be at max volume
emitter.ambientPermeationEnergyCap = 0.1;
float MyAmbientGainFormula(
bool lowFrequency,
int occlusionRayCount,
int permeationRayCount,
int permeationBounceCount,
float occlusionEnergy,
float permeationEnergy)
{
float gain = 0.0f;
if (occlusionRayCount > 0)
{
// 10% of ambient occlusion energy is required for ambient sounds to be at max volume
float energyThreshold = 0.1f * occlusionRayCount;
gain += occlusionEnergy / energyThreshold;
}
if (permeationRayCount > 0 && permeationBounceCount > 0)
{
// 10% of ambient permeation energy is required for ambient sounds to be at max volume
float energyThreshold = 0.1f * (permeationRayCount * permeationBounceCount);
gain += permeationEnergy / energyThreshold;
}
return gain < 1.0f ? gain : 1.0f;
}
void Initialise()
{
VAEmitter* emitter = vaEmitterCreate();
vaEmitterSetAmbientGainFormula(emitter, MyAmbientGainFormula);
}
For example if there's 5% energy in all ambient occlusion rays, and 5% energy in all ambient permeation rays, they will accumulate to the 10% energy threshold, and ambience will be at maximum volume.
Parameters:
lowFrequency- indicates whether this formula is invoked with low-frequency energy values or high-frequency energy values (this formula is invoked twice)ambientOcclusionRayCountis the number of ambient occlusion rays that were cast by the source emitterambientPermeationRayCountis the number of ambient permeation rays that were cast by the source emitterambientPermeationBounceCountis the number of bounces per ambient permeation rayambientOcclusionEnergyis in the range 0.0 to 1.0, where 1.0 means all ambient occlusion rays reached the world edge with maximum energy remaining (i.e. no occlusion)ambientPermeationEnergyis in the range 0.0 to 1.0, where 1.0 means all ambient permeation rays reached the world edge with maximum energy remaining (i.e. all rays passed through air)
The results of these functions are stored on the Emitter.AmbientFilterEmitter.ambientFiltervaEmitterGetAmbientFilter() low pass filter, which is null until raytracing has completed at least once:
if (emitter.AmbientFilter != null)
{
var gainLF = emitter.AmbientFilter.GainLF;
var gainHF = emitter.AmbientFilter.GainHF;
}
if (emitter.ambientFilter != null)
{
var gainLF = emitter.ambientFilter.gainLF;
var gainHF = emitter.ambientFilter.gainHF;
}
VALowPassFilter* ambientFilter = vaEmitterGetAmbientFilter(emitter);
if (ambientFilter != NULL)
{
float gainLF = ambientFilter->gainLF;
float gainHF = ambientFilter->gainHF;
}
Custom EAX Formulas
EAX reverb variables are currently calculated using generic formulas that provide a decent starting point for most environments. But you may want to adjust these formulas to suit the environment in your game.
These formula are not customisable in JS.
These overridden functions will run on background threads. Do not attempt to access data from the main thread here.
To use custom formulas, create a class that overrides CustomEAXFormulas.
public class MyEAXFormulas : CustomEAXFormulas
{
// This function will run on a background thread
public override float CalculateDiffusion()
{
return 0.5f;
}
}
Then set it on the raytracing world:
var world = new World();
world.CustomEAXFormulas = new MyEAXFormulas();
To use custom formulas, create an instance of CustomEAXFormulas and override its functions:
float MyCalculateDiffusion(VACustomEAXFormulas* formulas)
{
return 0.5f;
}
VACustomEAXFormulas* formulas = vaCustomEAXFormulasCreate();
formulas->calculateDiffusion = MyCalculateDiffusion;
vaWorldSetCustomEAXFormulas(ctx, formulas);
Reference
View the CustomEAXFormulas.cs file in the SDK > References folder to see how each reverb property is currently calculated.
EAX Presets
The below tables contain all values for every EAX reverb preset. Use these as a reference if you're calculating reverb settings yourself.
See Custom EAX Formulas for more details.
Density
Preset Density
psychotic 0.0625
driving_incar_racer 0.0832
driving_incar_sports 0.0832
pipe_resonant 0.1373
paddedcell 0.1715
bathroom 0.1715
spacestation_cupboard 0.1715
spacestation_smallroom 0.2109
spacestation_shortpassage 0.2109
spacestation_mediumroom 0.2109
spacestation_alcove 0.2109
pipe_longthin 0.256
driving_incar_luxury 0.256
sewerpipe 0.3071
factory_cupboard 0.3071
factory_courtyard 0.3071
hallway 0.3645
underwater 0.3645
dizzy 0.3645
factory_smallroom 0.3645
factory_shortpassage 0.3645
factory_longpassage 0.3645
factory_alcove 0.3645
spacestation_largeroom 0.3645
dustyroom 0.3645
prefab_practiseroom 0.4022
room 0.4287
carpetedhallway 0.4287
drugged 0.4287
factory_mediumroom 0.4287
factory_largeroom 0.4287
factory_hall 0.4287
spacestation_longpassage 0.4287
spacestation_hall 0.4287
prefab_workshop 0.4287
driving_pitgarage 0.4287
livingroom 0.9766
none 1
generic 1
stoneroom 1
auditorium 1
concerthall 1
cave 1
arena 1
hangar 1
stonecorridor 1
castle_smallroom 1
castle_shortpassage 1
castle_mediumroom 1
castle_largeroom 1
castle_longpassage 1
castle_hall 1
castle_cupboard 1
castle_courtyard 1
castle_alcove 1
icepalace_smallroom 1
icepalace_shortpassage 1
icepalace_mediumroom 1
icepalace_largeroom 1
icepalace_longpassage 1
icepalace_hall 1
icepalace_cupboard 1
icepalace_courtyard 1
icepalace_alcove 1
wooden_smallroom 1
wooden_shortpassage 1
wooden_mediumroom 1
wooden_largeroom 1
wooden_longpassage 1
wooden_hall 1
wooden_cupboard 1
wooden_courtyard 1
wooden_alcove 1
sport_emptystadium 1
sport_squashcourt 1
sport_smallswimmingpool 1
sport_largeswimmingpool 1
sport_gymnasium 1
sport_fullstadium 1
sport_stadiumtannoy 1
prefab_outhouse 1
prefab_caravan 1
dome_tomb 1
pipe_small 1
dome_saintpauls 1
pipe_large 1
backyard 1
rollingplains 1
deepcanyon 1
creek 1
valley 1
alley 1
forest 1
city 1
mountains 1
quarry 1
plain 1
parkinglot 1
mood_heaven 1
mood_hell 1
mood_memory 1
driving_commentator 1
driving_fullgrandstand 1
driving_emptygrandstand 1
driving_tunnel 1
city_streets 1
city_subway 1
city_museum 1
city_library 1
city_underpass 1
city_abandoned 1
chapel 1
smallwaterroom 1
Diffusion
Preset Diffusion
rollingplains 0
driving_commentator 0
plain 0.21
mountains 0.27
valley 0.28
alley 0.3
forest 0.3
creek 0.35
castle_courtyard 0.42
backyard 0.45
drugged 0.5
psychotic 0.5
city 0.5
spacestation_cupboard 0.56
dustyroom 0.56
factory_courtyard 0.57
mood_hell 0.57
factory_alcove 0.59
icepalace_courtyard 0.59
driving_pitgarage 0.59
dizzy 0.6
factory_cupboard 0.63
factory_shortpassage 0.64
factory_longpassage 0.64
wooden_courtyard 0.65
city_abandoned 0.69
spacestation_smallroom 0.7
sport_smallswimmingpool 0.7
smallwaterroom 0.7
deepcanyon 0.74
city_subway 0.74
factory_largeroom 0.75
factory_hall 0.75
icepalace_shortpassage 0.75
spacestation_mediumroom 0.75
sport_squashcourt 0.75
icepalace_hall 0.76
icepalace_longpassage 0.77
spacestation_alcove 0.78
sport_stadiumtannoy 0.78
city_streets 0.78
dome_tomb 0.79
sewerpipe 0.8
driving_incar_racer 0.8
driving_incar_sports 0.8
castle_hall 0.81
icepalace_largeroom 0.81
spacestation_largeroom 0.81
sport_gymnasium 0.81
driving_tunnel 0.81
castle_largeroom 0.82
factory_smallroom 0.82
factory_mediumroom 0.82
spacestation_longpassage 0.82
sport_largeswimmingpool 0.82
prefab_outhouse 0.82
city_museum 0.82
city_library 0.82
city_underpass 0.82
icepalace_cupboard 0.83
icepalace_smallroom 0.84
icepalace_alcove 0.84
chapel 0.84
mood_memory 0.85
icepalace_mediumroom 0.87
spacestation_shortpassage 0.87
spacestation_hall 0.87
prefab_practiseroom 0.87
dome_saintpauls 0.87
castle_smallroom 0.89
castle_shortpassage 0.89
castle_longpassage 0.89
castle_cupboard 0.89
castle_alcove 0.89
pipe_longthin 0.91
pipe_resonant 0.91
castle_mediumroom 0.93
mood_heaven 0.94
none 1
generic 1
paddedcell 1
room 1
bathroom 1
livingroom 1
stoneroom 1
auditorium 1
concerthall 1
cave 1
arena 1
hangar 1
carpetedhallway 1
hallway 1
stonecorridor 1
underwater 1
wooden_smallroom 1
wooden_shortpassage 1
wooden_mediumroom 1
wooden_largeroom 1
wooden_longpassage 1
wooden_hall 1
wooden_cupboard 1
wooden_alcove 1
sport_emptystadium 1
sport_fullstadium 1
prefab_workshop 1
prefab_caravan 1
pipe_small 1
pipe_large 1
quarry 1
parkinglot 1
driving_incar_luxury 1
driving_fullgrandstand 1
driving_emptygrandstand 1
Gain
Preset Gain
none 0
factory_* 0.2512
other 0.3162
driving_commentator 3.1623
GainLF
Preset GainLF
city_library 0.0891
castle_smallroom 0.1
castle_shortpassage 0.1
castle_mediumroom 0.1
castle_longpassage 0.1
castle_cupboard 0.1
castle_alcove 0.1
castle_largeroom 0.1259
prefab_caravan 0.1259
prefab_outhouse 0.1585
valley 0.1585
castle_hall 0.1778
city_museum 0.1778
castle_courtyard 0.1995
icepalace_cupboard 0.2239
dome_tomb 0.2239
pipe_small 0.2239
dome_saintpauls 0.2239
pipe_large 0.2239
icepalace_smallroom 0.2818
icepalace_shortpassage 0.2818
icepalace_alcove 0.2818
wooden_mediumroom 0.2818
wooden_largeroom 0.2818
wooden_hall 0.2818
pipe_longthin 0.2818
pipe_resonant 0.2818
icepalace_courtyard 0.3162
wooden_smallroom 0.3162
wooden_shortpassage 0.3162
wooden_longpassage 0.3162
wooden_cupboard 0.3162
wooden_courtyard 0.3162
wooden_alcove 0.3162
mood_memory 0.3548
icepalace_longpassage 0.3981
prefab_workshop 0.3981
icepalace_mediumroom 0.4467
icepalace_largeroom 0.4467
mood_heaven 0.4467
mood_hell 0.4467
factory_smallroom 0.5012
factory_shortpassage 0.5012
factory_mediumroom 0.5012
factory_longpassage 0.5012
factory_cupboard 0.5012
factory_alcove 0.5012
sport_stadiumtannoy 0.5012
prefab_practiseroom 0.5012
backyard 0.5012
creek 0.5012
driving_commentator 0.5012
driving_incar_luxury 0.5012
icepalace_hall 0.5623
driving_pitgarage 0.5623
factory_largeroom 0.631
factory_hall 0.631
factory_courtyard 0.631
rollingplains 0.631
deepcanyon 0.631
driving_fullgrandstand 0.631
dustyroom 0.7079
sport_emptystadium 0.7943
sport_squashcourt 0.7943
sport_fullstadium 0.7943
driving_incar_racer 0.7943
driving_emptygrandstand 0.7943
spacestation_smallroom 0.8913
spacestation_shortpassage 0.8913
spacestation_mediumroom 0.8913
spacestation_largeroom 0.8913
spacestation_longpassage 0.8913
spacestation_hall 0.8913
spacestation_cupboard 0.8913
spacestation_alcove 0.8913
sport_smallswimmingpool 0.8913
sport_gymnasium 0.8913
driving_tunnel 0.8913
city_streets 0.8913
city_subway 0.8913
city_underpass 0.8913
city_abandoned 0.8913
none 1
generic 1
paddedcell 1
room 1
bathroom 1
livingroom 1
stoneroom 1
auditorium 1
concerthall 1
cave 1
arena 1
hangar 1
carpetedhallway 1
hallway 1
stonecorridor 1
sewerpipe 1
underwater 1
drugged 1
dizzy 1
psychotic 1
sport_largeswimmingpool 1
alley 1
forest 1
city 1
mountains 1
quarry 1
plain 1
parkinglot 1
driving_incar_sports 1
chapel 1
smallwaterroom 1
GainHF
Preset GainHF
none 0
paddedcell 0.001
livingroom 0.001
carpetedhallway 0.01
underwater 0.01
rollingplains 0.0112
forest 0.0224
valley 0.0282
mountains 0.0562
sport_fullstadium 0.0708
wooden_hall 0.0794
wooden_courtyard 0.0794
wooden_largeroom 0.0891
prefab_caravan 0.0891
wooden_mediumroom 0.1
wooden_longpassage 0.1
plain 0.1
driving_incar_luxury 0.1
wooden_smallroom 0.1122
prefab_outhouse 0.1122
wooden_shortpassage 0.1259
wooden_alcove 0.1259
wooden_cupboard 0.1413
prefab_workshop 0.1413
deepcanyon 0.1778
creek 0.1778
city_museum 0.1778
bathroom 0.2512
backyard 0.2512
castle_mediumroom 0.2818
castle_largeroom 0.2818
castle_hall 0.2818
castle_cupboard 0.2818
icepalace_courtyard 0.2818
driving_fullgrandstand 0.2818
city_library 0.2818
hangar 0.3162
sewerpipe 0.3162
castle_shortpassage 0.3162
factory_courtyard 0.3162
sport_squashcourt 0.3162
quarry 0.3162
dome_tomb 0.3548
pipe_small 0.3548
dome_saintpauls 0.3548
pipe_large 0.3548
mood_hell 0.3548
castle_smallroom 0.3981
castle_longpassage 0.3981
prefab_practiseroom 0.3981
city 0.3981
driving_tunnel 0.3981
castle_courtyard 0.4467
icepalace_hall 0.4467
sport_emptystadium 0.4467
sport_gymnasium 0.4467
pipe_longthin 0.4467
pipe_resonant 0.4467
city_underpass 0.4467
arena 0.4477
smallwaterroom 0.4477
castle_alcove 0.5012
icepalace_cupboard 0.5012
concerthall 0.5623
icepalace_smallroom 0.5623
icepalace_shortpassage 0.5623
icepalace_mediumroom 0.5623
icepalace_largeroom 0.5623
icepalace_longpassage 0.5623
icepalace_alcove 0.5623
sport_stadiumtannoy 0.5623
driving_commentator 0.5623
chapel 0.5623
auditorium 0.5781
room 0.5929
dizzy 0.631
spacestation_shortpassage 0.631
spacestation_mediumroom 0.631
spacestation_largeroom 0.631
spacestation_longpassage 0.631
spacestation_hall 0.631
mood_memory 0.631
driving_incar_sports 0.631
stoneroom 0.7079
hallway 0.7079
factory_largeroom 0.7079
factory_hall 0.7079
spacestation_smallroom 0.7079
spacestation_cupboard 0.7079
spacestation_alcove 0.7079
driving_pitgarage 0.7079
city_streets 0.7079
city_subway 0.7079
alley 0.7328
stonecorridor 0.7612
factory_smallroom 0.7943
factory_shortpassage 0.7943
factory_mediumroom 0.7943
factory_longpassage 0.7943
factory_cupboard 0.7943
factory_alcove 0.7943
sport_smallswimmingpool 0.7943
sport_largeswimmingpool 0.7943
mood_heaven 0.7943
city_abandoned 0.7943
dustyroom 0.7943
psychotic 0.8404
generic 0.8913
cave 1
drugged 1
parkinglot 1
driving_incar_racer 1
driving_emptygrandstand 1
DecayTime
Preset DecayTime
driving_incar_luxury 0.13
paddedcell 0.17
driving_incar_racer 0.17
driving_incar_sports 0.17
carpetedhallway 0.3
room 0.4
prefab_caravan 0.43
factory_cupboard 0.49
livingroom 0.5
wooden_cupboard 0.56
castle_cupboard 0.67
icepalace_cupboard 0.76
prefab_workshop 0.76
spacestation_cupboard 0.79
wooden_smallroom 0.79
prefab_practiseroom 1.12
backyard 1.12
spacestation_alcove 1.16
castle_smallroom 1.22
wooden_alcove 1.22
prefab_outhouse 1.38
wooden_mediumroom 1.47
none 1.49
generic 1.49
bathroom 1.49
hallway 1.49
underwater 1.49
alley 1.49
forest 1.49
city 1.49
mountains 1.49
quarry 1.49
plain 1.49
icepalace_smallroom 1.51
smallwaterroom 1.51
castle_alcove 1.64
parkinglot 1.65
factory_smallroom 1.72
spacestation_smallroom 1.72
driving_pitgarage 1.72
wooden_shortpassage 1.75
icepalace_shortpassage 1.79
wooden_courtyard 1.79
city_streets 1.79
dustyroom 1.79
wooden_longpassage 1.99
castle_mediumroom 2.04
icepalace_courtyard 2.04
castle_courtyard 2.13
rollingplains 2.13
creek 2.13
icepalace_mediumroom 2.22
sport_squashcourt 2.22
stoneroom 2.31
castle_shortpassage 2.32
factory_courtyard 2.32
driving_commentator 2.42
castle_largeroom 2.53
factory_shortpassage 2.53
sport_stadiumtannoy 2.53
wooden_largeroom 2.65
stonecorridor 2.7
factory_mediumroom 2.76
icepalace_alcove 2.76
sport_smallswimmingpool 2.76
city_library 2.76
sewerpipe 2.81
valley 2.88
cave 2.91
icepalace_longpassage 3.01
spacestation_mediumroom 3.01
driving_fullgrandstand 3.01
city_subway 3.01
castle_hall 3.14
factory_alcove 3.14
icepalace_largeroom 3.14
sport_gymnasium 3.14
city_museum 3.28
city_abandoned 3.28
castle_longpassage 3.42
driving_tunnel 3.42
wooden_hall 3.45
spacestation_shortpassage 3.57
mood_hell 3.57
city_underpass 3.57
spacestation_largeroom 3.89
deepcanyon 3.89
concerthall 3.92
factory_longpassage 4.06
mood_memory 4.06
dome_tomb 4.18
factory_largeroom 4.24
auditorium 4.32
spacestation_longpassage 4.62
driving_emptygrandstand 4.62
chapel 4.62
pipe_small 5.04
mood_heaven 5.04
sport_fullstadium 5.25
icepalace_hall 5.49
sport_largeswimmingpool 5.49
sport_emptystadium 6.26
pipe_resonant 6.81
spacestation_hall 7.11
arena 7.24
factory_hall 7.43
psychotic 7.56
drugged 8.39
pipe_large 8.45
pipe_longthin 9.21
hangar 10.05
dome_saintpauls 10.48
dizzy 17.23
DecayLFRatio
Preset DecayLFRatio
dome_tomb 0.1
pipe_small 0.1
dome_saintpauls 0.1
pipe_longthin 0.1
pipe_large 0.1
pipe_resonant 0.1
prefab_practiseroom 0.18
dustyroom 0.21
castle_courtyard 0.23
icepalace_cupboard 0.26
icepalace_smallroom 0.27
icepalace_shortpassage 0.28
icepalace_longpassage 0.28
icepalace_alcove 0.28
castle_smallroom 0.31
castle_shortpassage 0.31
castle_longpassage 0.31
castle_cupboard 0.31
castle_alcove 0.31
icepalace_mediumroom 0.32
icepalace_largeroom 0.32
prefab_outhouse 0.35
valley 0.35
icepalace_hall 0.38
icepalace_courtyard 0.38
driving_incar_racer 0.41
driving_incar_sports 0.41
city_library 0.41
castle_mediumroom 0.46
backyard 0.46
rollingplains 0.46
deepcanyon 0.46
creek 0.46
driving_incar_luxury 0.46
castle_largeroom 0.5
spacestation_smallroom 0.55
spacestation_shortpassage 0.55
spacestation_mediumroom 0.55
spacestation_longpassage 0.55
spacestation_cupboard 0.55
spacestation_alcove 0.55
factory_courtyard 0.56
mood_heaven 0.56
mood_memory 0.56
city_museum 0.57
spacestation_largeroom 0.61
spacestation_hall 0.61
castle_hall 0.62
sport_stadiumtannoy 0.68
driving_commentator 0.68
wooden_longpassage 0.79
wooden_courtyard 0.79
sport_fullstadium 0.8
wooden_mediumroom 0.82
wooden_largeroom 0.82
wooden_hall 0.82
wooden_smallroom 0.87
wooden_shortpassage 0.87
driving_pitgarage 0.87
wooden_cupboard 0.91
wooden_alcove 0.91
city_streets 0.91
city_subway 0.91
city_underpass 0.91
city_abandoned 0.91
none 1
generic 1
paddedcell 1
room 1
bathroom 1
livingroom 1
stoneroom 1
auditorium 1
concerthall 1
cave 1
arena 1
hangar 1
carpetedhallway 1
hallway 1
stonecorridor 1
sewerpipe 1
underwater 1
drugged 1
dizzy 1
psychotic 1
prefab_workshop 1
prefab_caravan 1
alley 1
forest 1
city 1
mountains 1
quarry 1
plain 1
parkinglot 1
sport_emptystadium 1.1
sport_smallswimmingpool 1.14
sport_largeswimmingpool 1.14
smallwaterroom 1.14
sport_squashcourt 1.16
chapel 1.23
driving_fullgrandstand 1.28
factory_smallroom 1.31
factory_shortpassage 1.31
factory_mediumroom 1.31
factory_largeroom 1.31
factory_longpassage 1.31
factory_hall 1.31
factory_cupboard 1.31
factory_alcove 1.31
driving_tunnel 1.31
sport_gymnasium 1.35
driving_emptygrandstand 1.4
mood_hell 2
DecayHFRatio
Preset DecayHFRatio
paddedcell 0.1
livingroom 0.1
carpetedhallway 0.1
underwater 0.1
pipe_small 0.1
pipe_large 0.1
sewerpipe 0.14
sport_fullstadium 0.17
pipe_longthin 0.18
pipe_resonant 0.18
dome_saintpauls 0.19
dome_tomb 0.21
rollingplains 0.21
deepcanyon 0.21
creek 0.21
mountains 0.21
hangar 0.23
valley 0.26
factory_courtyard 0.29
wooden_hall 0.3
wooden_smallroom 0.32
arena 0.33
wooden_largeroom 0.33
backyard 0.34
wooden_courtyard 0.35
spacestation_largeroom 0.38
spacestation_hall 0.38
prefab_outhouse 0.38
dustyroom 0.38
wooden_longpassage 0.4
driving_incar_luxury 0.41
wooden_mediumroom 0.42
wooden_cupboard 0.46
mood_hell 0.49
spacestation_shortpassage 0.5
spacestation_mediumroom 0.5
wooden_shortpassage 0.5
plain 0.5
factory_largeroom 0.51
factory_hall 0.51
sport_emptystadium 0.51
bathroom 0.54
forest 0.54
dizzy 0.56
prefab_practiseroom 0.56
auditorium 0.59
hallway 0.59
castle_courtyard 0.61
spacestation_longpassage 0.62
wooden_alcove 0.62
stoneroom 0.64
chapel 0.64
factory_smallroom 0.65
factory_shortpassage 0.65
factory_mediumroom 0.65
factory_longpassage 0.65
factory_cupboard 0.65
factory_alcove 0.65
city 0.67
concerthall 0.7
driving_incar_sports 0.75
stonecorridor 0.79
castle_hall 0.79
spacestation_cupboard 0.81
spacestation_alcove 0.81
spacestation_smallroom 0.82
mood_memory 0.82
none 0.83
generic 0.83
room 0.83
castle_smallroom 0.83
castle_shortpassage 0.83
castle_mediumroom 0.83
castle_largeroom 0.83
castle_longpassage 0.83
quarry 0.83
alley 0.86
castle_cupboard 0.87
castle_alcove 0.87
sport_stadiumtannoy 0.88
driving_commentator 0.88
city_library 0.89
psychotic 0.91
sport_squashcourt 0.91
driving_pitgarage 0.93
driving_tunnel 0.94
prefab_workshop 1
sport_gymnasium 1.06
mood_heaven 1.12
city_streets 1.12
city_underpass 1.12
city_abandoned 1.17
icepalace_courtyard 1.2
city_subway 1.23
sport_smallswimmingpool 1.25
smallwaterroom 1.25
cave 1.3
sport_largeswimmingpool 1.31
driving_fullgrandstand 1.37
drugged 1.39
city_museum 1.4
icepalace_shortpassage 1.46
icepalace_longpassage 1.46
icepalace_alcove 1.46
prefab_caravan 1.5
parkinglot 1.5
icepalace_smallroom 1.53
icepalace_mediumroom 1.53
icepalace_largeroom 1.53
icepalace_hall 1.53
icepalace_cupboard 1.53
driving_emptygrandstand 1.75
driving_incar_racer 2
ReflectionsGain
Preset ReflectionsGain
quarry 0
mood_hell 0
mood_memory 0.0398
mountains 0.0407
generic 0.05
forest 0.0525
plain 0.0585
factory_hall 0.0631
sport_emptystadium 0.0631
city 0.073
sport_fullstadium 0.1
icepalace_hall 0.1122
carpetedhallway 0.1215
dizzy 0.1392
valley 0.1413
room 0.1503
castle_hall 0.1778
factory_largeroom 0.1778
spacestation_hall 0.1778
dome_saintpauls 0.1778
rollingplains 0.1778
driving_commentator 0.1995
livingroom 0.2051
parkinglot 0.2082
driving_emptygrandstand 0.2082
castle_courtyard 0.2239
factory_courtyard 0.2239
concerthall 0.2427
mood_heaven 0.2427
hallway 0.2458
stonecorridor 0.2472
paddedcell 0.25
alley 0.25
icepalace_largeroom 0.2512
city_museum 0.2512
arena 0.2612
factory_mediumroom 0.2818
sport_stadiumtannoy 0.2818
city_streets 0.2818
icepalace_courtyard 0.3162
spacestation_largeroom 0.3162
deepcanyon 0.3162
driving_fullgrandstand 0.3548
city_library 0.3548
dome_tomb 0.3868
icepalace_mediumroom 0.3981
spacestation_mediumroom 0.3981
sport_gymnasium 0.3981
pipe_large 0.3981
creek 0.3981
city_underpass 0.3981
auditorium 0.4032
stoneroom 0.4411
castle_largeroom 0.4467
sport_squashcourt 0.4467
sport_largeswimmingpool 0.4467
backyard 0.4467
city_abandoned 0.4467
chapel 0.4467
psychotic 0.4864
cave 0.5
hangar 0.5
icepalace_shortpassage 0.5012
pipe_small 0.5012
dustyroom 0.5012
wooden_courtyard 0.5623
driving_pitgarage 0.5623
underwater 0.5963
castle_mediumroom 0.631
sport_smallswimmingpool 0.631
bathroom 0.6531
factory_smallroom 0.7079
pipe_longthin 0.7079
pipe_resonant 0.7079
driving_tunnel 0.7079
city_subway 0.7079
icepalace_longpassage 0.7943
spacestation_smallroom 0.7943
driving_incar_luxury 0.7943
drugged 0.876
castle_smallroom 0.8913
castle_shortpassage 0.8913
castle_longpassage 0.8913
icepalace_smallroom 0.8913
wooden_shortpassage 0.8913
wooden_mediumroom 0.8913
wooden_largeroom 0.8913
wooden_hall 0.8913
prefab_outhouse 0.8913
smallwaterroom 0.8913
none 1
castle_alcove 1
factory_shortpassage 1
factory_longpassage 1
spacestation_shortpassage 1
spacestation_longpassage 1
wooden_smallroom 1
wooden_longpassage 1
prefab_workshop 1
prefab_caravan 1
driving_incar_sports 1
icepalace_cupboard 1.122
icepalace_alcove 1.122
wooden_cupboard 1.122
wooden_alcove 1.122
factory_cupboard 1.2589
prefab_practiseroom 1.2589
castle_cupboard 1.4125
factory_alcove 1.4125
spacestation_cupboard 1.4125
spacestation_alcove 1.4125
sewerpipe 1.6387
driving_incar_racer 1.7783
LateReverbGain
Preset LateReverbGain
none 0
wooden_courtyard 0.1
plain 0.1089
city 0.1427
carpetedhallway 0.1531
driving_fullgrandstand 0.1778
mountains 0.1919
creek 0.1995
city_streets 0.1995
driving_commentator 0.2512
driving_emptygrandstand 0.2512
parkinglot 0.2652
livingroom 0.2805
sport_fullstadium 0.2818
city_abandoned 0.2818
icepalace_courtyard 0.3162
deepcanyon 0.3548
factory_courtyard 0.3981
sport_emptystadium 0.3981
valley 0.3981
wooden_longpassage 0.4467
rollingplains 0.4467
dizzy 0.4937
sport_largeswimmingpool 0.5012
sport_stadiumtannoy 0.5012
sport_gymnasium 0.5623
driving_incar_sports 0.5623
icepalace_hall 0.631
spacestation_hall 0.631
wooden_shortpassage 0.631
prefab_outhouse 0.631
cave 0.7063
castle_courtyard 0.7079
wooden_alcove 0.7079
pipe_longthin 0.7079
backyard 0.7079
driving_incar_racer 0.7079
driving_tunnel 0.7079
auditorium 0.717
forest 0.7682
wooden_largeroom 0.7943
wooden_hall 0.7943
sport_squashcourt 0.7943
sport_smallswimmingpool 0.7943
chapel 0.7943
factory_hall 0.8913
icepalace_alcove 0.8913
spacestation_largeroom 0.8913
wooden_smallroom 0.8913
wooden_mediumroom 0.8913
city_museum 0.8913
city_library 0.8913
city_underpass 0.8913
alley 0.9954
concerthall 0.9977
factory_alcove 1
icepalace_largeroom 1
spacestation_alcove 1
pipe_resonant 1
arena 1.0186
room 1.0629
stoneroom 1.1003
castle_hall 1.122
factory_largeroom 1.122
icepalace_shortpassage 1.122
icepalace_mediumroom 1.122
spacestation_shortpassage 1.122
spacestation_mediumroom 1.122
wooden_cupboard 1.122
prefab_workshop 1.122
mood_memory 1.122
hangar 1.256
generic 1.2589
castle_shortpassage 1.2589
castle_largeroom 1.2589
factory_shortpassage 1.2589
factory_longpassage 1.2589
icepalace_longpassage 1.2589
spacestation_longpassage 1.2589
dome_saintpauls 1.2589
mood_heaven 1.2589
driving_pitgarage 1.2589
city_subway 1.2589
dustyroom 1.2589
paddedcell 1.2691
castle_longpassage 1.4125
castle_alcove 1.4125
factory_mediumroom 1.4125
icepalace_smallroom 1.4125
spacestation_smallroom 1.4125
prefab_practiseroom 1.4125
mood_hell 1.4125
smallwaterroom 1.4125
stonecorridor 1.5758
castle_mediumroom 1.5849
pipe_large 1.5849
driving_incar_luxury 1.5849
hallway 1.6615
dome_tomb 1.6788
factory_smallroom 1.7783
spacestation_cupboard 1.7783
quarry 1.7783
castle_smallroom 1.9953
factory_cupboard 1.9953
icepalace_cupboard 1.9953
prefab_caravan 1.9953
psychotic 2.4378
pipe_small 2.5119
drugged 3.1081
sewerpipe 3.2471
bathroom 3.2734
castle_cupboard 3.5481
underwater 7.0795
LateReverbDelay
Preset LateReverbDelay
mood_memory 0
driving_incar_sports 0
paddedcell 0.002
room 0.003
livingroom 0.004
dustyroom 0.006
castle_cupboard 0.007
driving_incar_luxury 0.01
none 0.011
generic 0.011
bathroom 0.011
hallway 0.011
underwater 0.011
castle_smallroom 0.011
castle_mediumroom 0.011
icepalace_smallroom 0.011
sport_squashcourt 0.011
prefab_practiseroom 0.011
alley 0.011
city 0.011
prefab_workshop 0.012
prefab_caravan 0.012
parkinglot 0.012
spacestation_smallroom 0.013
pipe_small 0.015
driving_incar_racer 0.015
castle_largeroom 0.016
icepalace_cupboard 0.016
spacestation_shortpassage 0.016
driving_pitgarage 0.016
stoneroom 0.017
driving_commentator 0.017
spacestation_cupboard 0.018
spacestation_alcove 0.018
icepalace_shortpassage 0.019
rollingplains 0.019
deepcanyon 0.019
stonecorridor 0.02
city_library 0.02
sewerpipe 0.021
cave 0.022
dome_tomb 0.022
pipe_longthin 0.022
pipe_resonant 0.022
castle_shortpassage 0.023
castle_longpassage 0.023
factory_mediumroom 0.023
factory_largeroom 0.023
backyard 0.023
castle_hall 0.024
factory_smallroom 0.024
wooden_shortpassage 0.024
wooden_alcove 0.024
city_abandoned 0.024
icepalace_longpassage 0.025
quarry 0.025
factory_hall 0.027
icepalace_mediumroom 0.027
icepalace_largeroom 0.027
wooden_cupboard 0.028
city_streets 0.028
city_subway 0.028
concerthall 0.029
wooden_smallroom 0.029
wooden_mediumroom 0.029
mood_heaven 0.029
auditorium 0.03
arena 0.03
hangar 0.03
carpetedhallway 0.03
drugged 0.03
dizzy 0.03
psychotic 0.03
icepalace_alcove 0.03
sport_smallswimmingpool 0.03
mood_hell 0.03
smallwaterroom 0.03
spacestation_longpassage 0.031
creek 0.031
factory_cupboard 0.032
wooden_courtyard 0.032
pipe_large 0.032
castle_alcove 0.034
city_museum 0.034
spacestation_mediumroom 0.035
spacestation_largeroom 0.035
castle_courtyard 0.036
wooden_longpassage 0.036
factory_longpassage 0.037
city_underpass 0.037
factory_shortpassage 0.038
factory_alcove 0.038
sport_emptystadium 0.038
sport_fullstadium 0.038
factory_courtyard 0.039
dome_saintpauls 0.042
icepalace_courtyard 0.043
prefab_outhouse 0.044
sport_gymnasium 0.045
spacestation_hall 0.047
driving_tunnel 0.047
wooden_largeroom 0.049
sport_largeswimmingpool 0.049
driving_fullgrandstand 0.049
driving_emptygrandstand 0.049
chapel 0.049
icepalace_hall 0.052
wooden_hall 0.063
sport_stadiumtannoy 0.063
forest 0.088
valley 0.1
mountains 0.1
plain 0.1
EchoTime
Preset EchoTime
prefab_practiseroom 0.095
factory_cupboard 0.107
mood_hell 0.11
factory_alcove 0.114
factory_smallroom 0.119
prefab_outhouse 0.121
alley 0.125
forest 0.125
quarry 0.125
city_subway 0.125
sport_squashcourt 0.126
city_museum 0.13
city_library 0.13
factory_shortpassage 0.135
factory_longpassage 0.135
castle_smallroom 0.138
castle_shortpassage 0.138
castle_longpassage 0.138
castle_cupboard 0.138
castle_alcove 0.138
icepalace_cupboard 0.143
sport_gymnasium 0.146
castle_mediumroom 0.155
icepalace_alcove 0.161
icepalace_smallroom 0.164
spacestation_shortpassage 0.172
factory_mediumroom 0.174
icepalace_shortpassage 0.177
dome_tomb 0.177
sport_smallswimmingpool 0.179
smallwaterroom 0.179
spacestation_cupboard 0.181
castle_largeroom 0.185
icepalace_mediumroom 0.186
icepalace_longpassage 0.186
spacestation_smallroom 0.188
spacestation_alcove 0.192
dustyroom 0.202
spacestation_mediumroom 0.209
icepalace_largeroom 0.214
driving_tunnel 0.214
backyard 0.218
creek 0.218
sport_largeswimmingpool 0.222
icepalace_hall 0.226
factory_largeroom 0.231
spacestation_largeroom 0.233
icepalace_courtyard 0.235
none 0.25
generic 0.25
paddedcell 0.25
room 0.25
bathroom 0.25
livingroom 0.25
stoneroom 0.25
auditorium 0.25
concerthall 0.25
cave 0.25
arena 0.25
hangar 0.25
carpetedhallway 0.25
hallway 0.25
stonecorridor 0.25
sewerpipe 0.25
underwater 0.25
drugged 0.25
dizzy 0.25
psychotic 0.25
castle_hall 0.25
castle_courtyard 0.25
factory_hall 0.25
factory_courtyard 0.25
spacestation_longpassage 0.25
spacestation_hall 0.25
wooden_smallroom 0.25
wooden_shortpassage 0.25
wooden_mediumroom 0.25
wooden_largeroom 0.25
wooden_longpassage 0.25
wooden_hall 0.25
wooden_cupboard 0.25
wooden_courtyard 0.25
wooden_alcove 0.25
sport_emptystadium 0.25
sport_fullstadium 0.25
sport_stadiumtannoy 0.25
prefab_workshop 0.25
prefab_caravan 0.25
pipe_small 0.25
dome_saintpauls 0.25
pipe_longthin 0.25
pipe_large 0.25
pipe_resonant 0.25
rollingplains 0.25
deepcanyon 0.25
valley 0.25
city 0.25
mountains 0.25
plain 0.25
parkinglot 0.25
mood_heaven 0.25
mood_memory 0.25
driving_commentator 0.25
driving_pitgarage 0.25
driving_incar_racer 0.25
driving_incar_sports 0.25
driving_incar_luxury 0.25
driving_fullgrandstand 0.25
driving_emptygrandstand 0.25
city_streets 0.25
city_underpass 0.25
city_abandoned 0.25
chapel 0.25
EchoDepth
Preset EchoDepth
none 0
generic 0
paddedcell 0
room 0
bathroom 0
livingroom 0
stoneroom 0
auditorium 0
concerthall 0
cave 0
arena 0
hangar 0
carpetedhallway 0
hallway 0
stonecorridor 0
sewerpipe 0
underwater 0
drugged 0
psychotic 0
castle_hall 0
wooden_smallroom 0
wooden_shortpassage 0
wooden_mediumroom 0
wooden_largeroom 0
wooden_longpassage 0
wooden_hall 0
wooden_cupboard 0
wooden_courtyard 0
wooden_alcove 0
sport_emptystadium 0
sport_fullstadium 0
prefab_workshop 0
prefab_caravan 0
pipe_small 0
pipe_longthin 0
pipe_large 0
pipe_resonant 0
city 0
parkinglot 0
mood_memory 0
driving_incar_racer 0
driving_incar_sports 0
driving_incar_luxury 0
driving_fullgrandstand 0
driving_emptygrandstand 0
chapel 0
castle_mediumroom 0.03
icepalace_longpassage 0.04
mood_hell 0.04
driving_tunnel 0.05
dustyroom 0.05
castle_largeroom 0.07
factory_smallroom 0.07
factory_mediumroom 0.07
factory_largeroom 0.07
factory_hall 0.07
factory_cupboard 0.07
castle_smallroom 0.08
castle_shortpassage 0.08
castle_longpassage 0.08
castle_cupboard 0.08
castle_alcove 0.08
icepalace_cupboard 0.08
mood_heaven 0.08
icepalace_shortpassage 0.09
icepalace_alcove 0.09
factory_alcove 0.1
icepalace_largeroom 0.11
icepalace_hall 0.11
driving_pitgarage 0.11
icepalace_mediumroom 0.12
dome_saintpauls 0.12
icepalace_smallroom 0.14
sport_gymnasium 0.14
prefab_practiseroom 0.14
city_underpass 0.14
sport_smallswimmingpool 0.15
smallwaterroom 0.15
prefab_outhouse 0.17
city_museum 0.17
city_library 0.17
sport_squashcourt 0.19
dome_tomb 0.19
spacestation_shortpassage 0.2
sport_stadiumtannoy 0.2
city_streets 0.2
city_abandoned 0.2
spacestation_alcove 0.21
city_subway 0.21
factory_shortpassage 0.23
factory_longpassage 0.23
spacestation_longpassage 0.23
spacestation_hall 0.25
spacestation_smallroom 0.26
spacestation_largeroom 0.28
factory_courtyard 0.29
spacestation_mediumroom 0.31
spacestation_cupboard 0.31
backyard 0.34
creek 0.34
valley 0.34
castle_courtyard 0.37
icepalace_courtyard 0.48
sport_largeswimmingpool 0.55
quarry 0.7
alley 0.95
dizzy 1
rollingplains 1
deepcanyon 1
forest 1
mountains 1
plain 1
driving_commentator 1
ModulationTime
Preset ModulationTime
none 0.25
generic 0.25
paddedcell 0.25
room 0.25
bathroom 0.25
livingroom 0.25
stoneroom 0.25
auditorium 0.25
concerthall 0.25
cave 0.25
arena 0.25
hangar 0.25
carpetedhallway 0.25
hallway 0.25
stonecorridor 0.25
sewerpipe 0.25
drugged 0.25
castle_smallroom 0.25
castle_shortpassage 0.25
castle_mediumroom 0.25
castle_largeroom 0.25
castle_longpassage 0.25
castle_hall 0.25
castle_cupboard 0.25
castle_courtyard 0.25
castle_alcove 0.25
factory_smallroom 0.25
factory_shortpassage 0.25
factory_mediumroom 0.25
factory_largeroom 0.25
factory_longpassage 0.25
factory_hall 0.25
factory_cupboard 0.25
factory_courtyard 0.25
factory_alcove 0.25
icepalace_smallroom 0.25
icepalace_shortpassage 0.25
icepalace_mediumroom 0.25
icepalace_largeroom 0.25
icepalace_longpassage 0.25
icepalace_hall 0.25
icepalace_cupboard 0.25
icepalace_courtyard 0.25
icepalace_alcove 0.25
spacestation_smallroom 0.25
spacestation_shortpassage 0.25
spacestation_mediumroom 0.25
spacestation_largeroom 0.25
spacestation_longpassage 0.25
spacestation_hall 0.25
spacestation_cupboard 0.25
spacestation_alcove 0.25
wooden_smallroom 0.25
wooden_shortpassage 0.25
wooden_mediumroom 0.25
wooden_largeroom 0.25
wooden_longpassage 0.25
wooden_hall 0.25
wooden_cupboard 0.25
wooden_courtyard 0.25
wooden_alcove 0.25
sport_emptystadium 0.25
sport_squashcourt 0.25
sport_gymnasium 0.25
sport_fullstadium 0.25
sport_stadiumtannoy 0.25
prefab_workshop 0.25
prefab_practiseroom 0.25
prefab_outhouse 0.25
prefab_caravan 0.25
dome_tomb 0.25
pipe_small 0.25
dome_saintpauls 0.25
pipe_longthin 0.25
pipe_large 0.25
pipe_resonant 0.25
backyard 0.25
rollingplains 0.25
deepcanyon 0.25
creek 0.25
valley 0.25
alley 0.25
forest 0.25
city 0.25
mountains 0.25
quarry 0.25
plain 0.25
parkinglot 0.25
driving_commentator 0.25
driving_pitgarage 0.25
driving_incar_racer 0.25
driving_incar_sports 0.25
driving_incar_luxury 0.25
driving_fullgrandstand 0.25
driving_emptygrandstand 0.25
driving_tunnel 0.25
city_streets 0.25
city_subway 0.25
city_museum 0.25
city_library 0.25
city_underpass 0.25
city_abandoned 0.25
dustyroom 0.25
chapel 0.25
mood_memory 0.474
dizzy 0.81
sport_smallswimmingpool 0.895
smallwaterroom 0.895
sport_largeswimmingpool 1.159
underwater 1.18
mood_hell 2.109
mood_heaven 2.742
psychotic 4
ModulationDepth
Preset ModulationDepth
none 0
generic 0
paddedcell 0
room 0
bathroom 0
livingroom 0
stoneroom 0
auditorium 0
concerthall 0
cave 0
arena 0
hangar 0
carpetedhallway 0
hallway 0
stonecorridor 0
sewerpipe 0
castle_smallroom 0
castle_shortpassage 0
castle_mediumroom 0
castle_largeroom 0
castle_longpassage 0
castle_hall 0
castle_cupboard 0
castle_courtyard 0
castle_alcove 0
factory_smallroom 0
factory_shortpassage 0
factory_mediumroom 0
factory_largeroom 0
factory_longpassage 0
factory_hall 0
factory_cupboard 0
factory_courtyard 0
factory_alcove 0
icepalace_smallroom 0
icepalace_shortpassage 0
icepalace_mediumroom 0
icepalace_largeroom 0
icepalace_longpassage 0
icepalace_hall 0
icepalace_cupboard 0
icepalace_courtyard 0
icepalace_alcove 0
spacestation_smallroom 0
spacestation_shortpassage 0
spacestation_mediumroom 0
spacestation_largeroom 0
spacestation_longpassage 0
spacestation_hall 0
spacestation_cupboard 0
spacestation_alcove 0
wooden_smallroom 0
wooden_shortpassage 0
wooden_mediumroom 0
wooden_largeroom 0
wooden_longpassage 0
wooden_hall 0
wooden_cupboard 0
wooden_courtyard 0
wooden_alcove 0
sport_emptystadium 0
sport_squashcourt 0
sport_gymnasium 0
sport_fullstadium 0
sport_stadiumtannoy 0
prefab_workshop 0
prefab_practiseroom 0
prefab_outhouse 0
prefab_caravan 0
dome_tomb 0
pipe_small 0
dome_saintpauls 0
pipe_longthin 0
pipe_large 0
pipe_resonant 0
backyard 0
rollingplains 0
deepcanyon 0
creek 0
valley 0
alley 0
forest 0
city 0
mountains 0
quarry 0
plain 0
parkinglot 0
driving_commentator 0
driving_pitgarage 0
driving_incar_racer 0
driving_incar_sports 0
driving_incar_luxury 0
driving_fullgrandstand 0
driving_emptygrandstand 0
driving_tunnel 0
city_streets 0
city_subway 0
city_museum 0
city_library 0
city_underpass 0
city_abandoned 0
dustyroom 0
mood_heaven 0.05
chapel 0.11
sport_smallswimmingpool 0.19
smallwaterroom 0.19
sport_largeswimmingpool 0.21
dizzy 0.31
underwater 0.348
mood_memory 0.45
mood_hell 0.52
drugged 1
psychotic 1
AirAbsorptionGainHF
Preset AirAbsorptionGainHF
mood_memory 0.9886
driving_commentator 0.9886
dustyroom 0.9886
city_underpass 0.992
smallwaterroom 0.992
none 0.9943
generic 0.9943
paddedcell 0.9943
room 0.9943
bathroom 0.9943
livingroom 0.9943
stoneroom 0.9943
auditorium 0.9943
concerthall 0.9943
cave 0.9943
arena 0.9943
hangar 0.9943
carpetedhallway 0.9943
hallway 0.9943
stonecorridor 0.9943
sewerpipe 0.9943
underwater 0.9943
drugged 0.9943
dizzy 0.9943
psychotic 0.9943
castle_smallroom 0.9943
castle_shortpassage 0.9943
castle_mediumroom 0.9943
castle_largeroom 0.9943
castle_longpassage 0.9943
castle_hall 0.9943
castle_cupboard 0.9943
castle_courtyard 0.9943
castle_alcove 0.9943
factory_smallroom 0.9943
factory_shortpassage 0.9943
factory_mediumroom 0.9943
factory_largeroom 0.9943
factory_longpassage 0.9943
factory_hall 0.9943
factory_cupboard 0.9943
factory_courtyard 0.9943
factory_alcove 0.9943
icepalace_smallroom 0.9943
icepalace_shortpassage 0.9943
icepalace_mediumroom 0.9943
icepalace_largeroom 0.9943
icepalace_longpassage 0.9943
icepalace_hall 0.9943
icepalace_cupboard 0.9943
icepalace_courtyard 0.9943
icepalace_alcove 0.9943
spacestation_smallroom 0.9943
spacestation_shortpassage 0.9943
spacestation_mediumroom 0.9943
spacestation_largeroom 0.9943
spacestation_longpassage 0.9943
spacestation_hall 0.9943
spacestation_cupboard 0.9943
spacestation_alcove 0.9943
wooden_smallroom 0.9943
wooden_shortpassage 0.9943
wooden_mediumroom 0.9943
wooden_largeroom 0.9943
wooden_longpassage 0.9943
wooden_hall 0.9943
wooden_cupboard 0.9943
wooden_courtyard 0.9943
wooden_alcove 0.9943
sport_emptystadium 0.9943
sport_squashcourt 0.9943
sport_smallswimmingpool 0.9943
sport_largeswimmingpool 0.9943
sport_gymnasium 0.9943
sport_fullstadium 0.9943
sport_stadiumtannoy 0.9943
prefab_workshop 0.9943
prefab_practiseroom 0.9943
prefab_outhouse 0.9943
prefab_caravan 0.9943
dome_tomb 0.9943
pipe_small 0.9943
dome_saintpauls 0.9943
pipe_longthin 0.9943
pipe_large 0.9943
pipe_resonant 0.9943
backyard 0.9943
rollingplains 0.9943
deepcanyon 0.9943
creek 0.9943
valley 0.9943
alley 0.9943
forest 0.9943
city 0.9943
mountains 0.9943
quarry 0.9943
plain 0.9943
parkinglot 0.9943
mood_hell 0.9943
driving_pitgarage 0.9943
driving_incar_racer 0.9943
driving_incar_sports 0.9943
driving_incar_luxury 0.9943
driving_fullgrandstand 0.9943
driving_emptygrandstand 0.9943
driving_tunnel 0.9943
city_streets 0.9943
city_subway 0.9943
city_museum 0.9943
city_library 0.9943
chapel 0.9943
city_abandoned 0.9966
mood_heaven 0.9977
LFReference
Preset LFReference
dome_tomb 20
pipe_small 20
dome_saintpauls 20
pipe_longthin 20
pipe_large 20
pipe_resonant 20
icepalace_smallroom 99.6
icepalace_shortpassage 99.6
icepalace_mediumroom 99.6
icepalace_largeroom 99.6
icepalace_longpassage 99.6
icepalace_hall 99.6
icepalace_cupboard 99.6
icepalace_courtyard 99.6
icepalace_alcove 99.6
wooden_smallroom 99.6
wooden_shortpassage 99.6
wooden_mediumroom 99.6
wooden_largeroom 99.6
wooden_longpassage 99.6
wooden_hall 99.6
wooden_cupboard 99.6
wooden_courtyard 99.6
wooden_alcove 99.6
prefab_outhouse 107.5
valley 107.5
city_museum 107.5
city_library 107.5
castle_smallroom 139.5
castle_shortpassage 139.5
castle_mediumroom 139.5
castle_largeroom 139.5
castle_longpassage 139.5
castle_hall 139.5
castle_cupboard 139.5
castle_alcove 139.5
mood_hell 139.5
driving_tunnel 155.3
dustyroom 163.3
sport_squashcourt 211.2
sport_gymnasium 211.2
prefab_practiseroom 211.2
backyard 242.9
rollingplains 242.9
deepcanyon 242.9
creek 242.9
none 250
generic 250
paddedcell 250
room 250
bathroom 250
livingroom 250
stoneroom 250
auditorium 250
concerthall 250
cave 250
arena 250
hangar 250
carpetedhallway 250
hallway 250
stonecorridor 250
sewerpipe 250
underwater 250
drugged 250
dizzy 250
psychotic 250
castle_courtyard 250
sport_emptystadium 250
sport_smallswimmingpool 250
sport_largeswimmingpool 250
sport_fullstadium 250
sport_stadiumtannoy 250
prefab_workshop 250
prefab_caravan 250
alley 250
forest 250
city 250
mountains 250
quarry 250
plain 250
parkinglot 250
mood_heaven 250
mood_memory 250
driving_commentator 250
driving_pitgarage 250
driving_fullgrandstand 250
driving_emptygrandstand 250
city_streets 250
city_subway 250
city_underpass 250
city_abandoned 250
chapel 250
smallwaterroom 250
driving_incar_racer 251
driving_incar_sports 251
driving_incar_luxury 251
factory_smallroom 362.5
factory_shortpassage 362.5
factory_mediumroom 362.5
factory_largeroom 362.5
factory_longpassage 362.5
factory_hall 362.5
factory_cupboard 362.5
factory_courtyard 362.5
factory_alcove 362.5
spacestation_smallroom 458.2
spacestation_shortpassage 458.2
spacestation_mediumroom 458.2
spacestation_largeroom 458.2
spacestation_longpassage 458.2
spacestation_hall 458.2
spacestation_cupboard 458.2
spacestation_alcove 458.2
HFReference
Preset HFReference
prefab_outhouse 2854.4
dome_tomb 2854.4
pipe_small 2854.4
dome_saintpauls 2854.4
pipe_longthin 2854.4
pipe_large 2854.4
pipe_resonant 2854.4
valley 2854.4
city_museum 2854.4
city_library 2854.4
spacestation_smallroom 3316.1
spacestation_shortpassage 3316.1
spacestation_mediumroom 3316.1
spacestation_largeroom 3316.1
spacestation_longpassage 3316.1
spacestation_hall 3316.1
spacestation_cupboard 3316.1
spacestation_alcove 3316.1
factory_smallroom 3762.6
factory_shortpassage 3762.6
factory_mediumroom 3762.6
factory_largeroom 3762.6
factory_longpassage 3762.6
factory_hall 3762.6
factory_cupboard 3762.6
factory_courtyard 3762.6
factory_alcove 3762.6
backyard 4399.1
rollingplains 4399.1
deepcanyon 4399.1
creek 4399.1
wooden_smallroom 4705
wooden_shortpassage 4705
wooden_mediumroom 4705
wooden_largeroom 4705
wooden_longpassage 4705
wooden_hall 4705
wooden_cupboard 4705
wooden_courtyard 4705
wooden_alcove 4705
none 5000
generic 5000
paddedcell 5000
room 5000
bathroom 5000
livingroom 5000
stoneroom 5000
auditorium 5000
concerthall 5000
cave 5000
arena 5000
hangar 5000
carpetedhallway 5000
hallway 5000
stonecorridor 5000
sewerpipe 5000
underwater 5000
drugged 5000
dizzy 5000
psychotic 5000
castle_courtyard 5000
sport_emptystadium 5000
sport_smallswimmingpool 5000
sport_largeswimmingpool 5000
sport_fullstadium 5000
sport_stadiumtannoy 5000
prefab_workshop 5000
prefab_caravan 5000
alley 5000
forest 5000
city 5000
mountains 5000
quarry 5000
plain 5000
parkinglot 5000
mood_heaven 5000
mood_hell 5000
mood_memory 5000
driving_commentator 5000
driving_pitgarage 5000
driving_tunnel 5000
city_streets 5000
city_subway 5000
city_underpass 5000
city_abandoned 5000
chapel 5000
smallwaterroom 5000
castle_smallroom 5168.6
castle_shortpassage 5168.6
castle_mediumroom 5168.6
castle_largeroom 5168.6
castle_longpassage 5168.6
castle_hall 5168.6
castle_cupboard 5168.6
castle_alcove 5168.6
sport_squashcourt 7176.9
sport_gymnasium 7176.9
prefab_practiseroom 7176.9
driving_incar_racer 10268.2
driving_incar_sports 10268.2
driving_incar_luxury 10268.2
driving_fullgrandstand 10420.2
driving_emptygrandstand 10420.2
icepalace_smallroom 12428.5
icepalace_shortpassage 12428.5
icepalace_mediumroom 12428.5
icepalace_largeroom 12428.5
icepalace_longpassage 12428.5
icepalace_hall 12428.5
icepalace_cupboard 12428.5
icepalace_courtyard 12428.5
icepalace_alcove 12428.5
dustyroom 13046
RoomRolloffFactor
Preset RoomRolloffFactor
all 0
DecayHFLimit
Preset DecayHFLimit
cave 0
drugged 0
dizzy 0
psychotic 0
castle_courtyard 0
sport_smallswimmingpool 0
sport_largeswimmingpool 0
prefab_workshop 0
prefab_outhouse 0
prefab_caravan 0
dome_tomb 0
pipe_longthin 0
pipe_resonant 0
backyard 0
rollingplains 0
deepcanyon 0
creek 0
valley 0
mountains 0
parkinglot 0
mood_hell 0
mood_memory 0
driving_pitgarage 0
driving_fullgrandstand 0
driving_emptygrandstand 0
city_museum 0
city_library 0
smallwaterroom 0
none 1
generic 1
paddedcell 1
room 1
bathroom 1
livingroom 1
stoneroom 1
auditorium 1
concerthall 1
arena 1
hangar 1
carpetedhallway 1
hallway 1
stonecorridor 1
sewerpipe 1
underwater 1
castle_smallroom 1
castle_shortpassage 1
castle_mediumroom 1
castle_largeroom 1
castle_longpassage 1
castle_hall 1
castle_cupboard 1
castle_alcove 1
factory_smallroom 1
factory_shortpassage 1
factory_mediumroom 1
factory_largeroom 1
factory_longpassage 1
factory_hall 1
factory_cupboard 1
factory_courtyard 1
factory_alcove 1
icepalace_smallroom 1
icepalace_shortpassage 1
icepalace_mediumroom 1
icepalace_largeroom 1
icepalace_longpassage 1
icepalace_hall 1
icepalace_cupboard 1
icepalace_courtyard 1
icepalace_alcove 1
spacestation_smallroom 1
spacestation_shortpassage 1
spacestation_mediumroom 1
spacestation_largeroom 1
spacestation_longpassage 1
spacestation_hall 1
spacestation_cupboard 1
spacestation_alcove 1
wooden_smallroom 1
wooden_shortpassage 1
wooden_mediumroom 1
wooden_largeroom 1
wooden_longpassage 1
wooden_hall 1
wooden_cupboard 1
wooden_courtyard 1
wooden_alcove 1
sport_emptystadium 1
sport_squashcourt 1
sport_gymnasium 1
sport_fullstadium 1
sport_stadiumtannoy 1
prefab_practiseroom 1
pipe_small 1
dome_saintpauls 1
pipe_large 1
alley 1
forest 1
city 1
quarry 1
plain 1
mood_heaven 1
driving_commentator 1
driving_incar_racer 1
driving_incar_sports 1
driving_incar_luxury 1
driving_tunnel 1
city_streets 1
city_subway 1
city_underpass 1
city_abandoned 1
dustyroom 1
chapel 1
Emitter Gain Formula
This function converts energy values to gain values, and is invoked twice per emitter:
- once with low-frequency energy values
- once with high-frequency energy values
The default formula specifies that 10% of occlusion energy, or 10% of permeation energy is required for this emitter to be at max volume:
emitter.GainFormula = (
bool lowFrequency,
int occlusionRayCount,
int permeationRayCount,
int permeationBounceCount,
float occlusionEnergy,
float permeationEnergy)
{
float gain = 0.0f;
if (occlusionRayCount > 0)
{
// 10% of occlusion energy is required for this emitter to be at max volume
float energyThreshold = 0.1f * occlusionRayCount;
gain += occlusionEnergy / energyThreshold;
}
if (permeationRayCount > 0 && permeationBounceCount > 0)
{
// 10% of permeation energy is required for this emitter to be at max volume
float energyThreshold = 0.1f * (permeationRayCount * permeationBounceCount);
gain += permeationEnergy / energyThreshold;
}
return MathF.Min(1, gain);
}
In JS, the gain formula is not configurable. Instead, set the energy cap properties on the emitter to define how much energy is required for it to be at full volume:
// 10% of occlusion energy is required for this emitter to be at max volume
emitter.occlusionEnergyCap = 0.1;
// 10% of permeation energy is required for this emitter to be at max volume
emitter.permeationEnergyCap = 0.1;
float GainFormula(
bool lowFrequency,
int occlusionRayCount,
int permeationRayCount,
int permeationBounceCount,
float occlusionEnergy,
float permeationEnergy)
{
float gain = 0.0f;
if (occlusionRayCount > 0)
{
// 10% of occlusion energy is required for this emitter to be at max volume
float energyThreshold = 0.1f * occlusionRayCount;
gain += occlusionEnergy / energyThreshold;
}
if (permeationRayCount > 0 && permeationBounceCount > 0)
{
// 10% of permeation energy is required for this emitter to be at max volume
float energyThreshold = 0.1f * (permeationRayCount * permeationBounceCount);
gain += permeationEnergy / energyThreshold;
}
return gain < 1.0f ? gain : 1.0f;
}
vaEmitterSetGainFormula(emitter, GainFormula);
This function determines the volume of this emitter when it is discovered by other emitters. The ray/bounce parameters match the number of rays/bounces cast by the other emitter
These settings determine the volume of this emitter when it is discovered by other emitters
For example if there's 5% energy in all occlusion rays, and 5% energy in all permeation rays, they will accumulate to the 10% energy threshold, and the target will be at maximum volume.
Parameters:
lowFrequency- indicates whether this formula is invoked with low-frequency energy values or high-frequency energy values (this formula is invoked twice)occlusionRayCountis the number of occlusion rays that were cast by the other emitterpermeationRayCountis the number of permeation rays that were cast by the other emitterpermeationBounceCountis the number of bounces per permeation rayocclusionEnergyis in the range 0.0 to 1.0, where 1.0 means all occlusion rays reached the target with maximum energy remaining (i.e. no occlusion)permeationEnergyis in the range 0.0 to 1.0, where 1.0 means all permeation rays reached the target with maximum energy remaining (i.e. all rays passed through air)
The results of these functions are stored directly on the emitter that cast occlusion/permeation rays, can can be accessed via listener.GetTargetFilter(target)).
Debug Window
When RenderingEnabled is set to true, a separate debug window will appear that renders the raytracing scene (3D primitives and emitters).
You can move the camera with the mouse and WASD keys, or control the camera via code:
Not available in JS yet
Not available in C yet
Rendering can be toggled at runtime, but can only be set to true on one World at a time.
There is no debug window for JS. Rendering with WebGL to a HTML canvas is a planned feature. Roadmap
The debug window is not available in the C SDK. A workaround is planned for the C# SDK to read data from a native raytracing world and render it in a debug window. Roadmap
Full Code Example
using vaudio;
public class Demo
{
World world;
PrismPrimitive prism;
Emitter listener;
Emitter enemy;
public Demo()
{
// Create a raytracing world
world = new World()
{
WorldSize = new Vector(100),
RenderingEnabled = true,
OnReverbUpdated = OnReverbUpdated,
};
// Create a rectangular prism
prism = new PrismPrimitive()
{
// Set its material
material = MaterialType.Concrete,
// Set its size
size = new Vector(20, 120, 20),
// Rotate and position it
transform = Matrix.CreateRotationY(MathF.PI / 4) *
Matrix.CreateTranslation(50, 50, 50)
};
world.AddPrimitive(prism);
// Create a listener emitter with all ray types enabled
listener = new Emitter()
{
Name = "Listener",
Position = new Vector(10, 50, 10),
ReverbRayCount = 128,
ReverbBounceCount = 64,
ReverbEnergyCap = 0.2f,
MaxEchogramTime = 5000,
EchogramGranularity = 50,
OcclusionRayCount = 512,
OcclusionBounceCount = 8,
PermeationRayCount = 128,
PermeationBounceCount = 3,
AmbientOcclusionRayCount = 512,
AmbientOcclusionBounceCount = 8,
AmbientPermeationRayCount = 128,
AmbientPermeationBounceCount = 4
};
world.AddEmitter(listener);
// Create an enemy emitter
enemy = new Emitter()
{
Name = "Enemy",
Position = new Vector(90, 50, 10)
};
enemy.OnRaytracedByAnotherEmitter = (Emitter other) =>
{
var filter = other.GetTargetFilter(enemy);
// PSEUDOCODE
// Godot.PlaySound(SoundType.EnemySpawn, enemy.position, filter);
}
world.AddEmitter(enemy);
// Tell the listener to cast occlusion and permeation rays towards the enemy
listener.AddTarget(enemy);
}
void OnReverbUpdated()
{
// Access the listener's reverb stats
var returnedPercent = listener.ProcessedReverb.ReturningPercent;
var decayTime = listener.EAX.DecayTime;
// Access ambient stats
var outsidePercent = listener.OutsidePercent;
var ambientGainLF = listener.AmbientFilter.GainLF;
}
Stopwatch watch = Stopwatch.StartNew();
public void Update()
{
// Rotate and position the prism in real time
var rotation = watch.ElapsedMilliseconds / 1000.0f;
prism.transform = Matrix.CreateRotationY(rotation) *
Matrix.CreateTranslation(50, 50, 50);
// Perform raytracing on background threads
world.Update();
}
}
import { dotnet } from './_framework/dotnet.js';
import { initVA, MaterialType } from './vaudio-wrapper.js';
// Set this to the maximum number of threads you'll use
const YOUR_THREAD_COUNT = 4;
// Initialise the .NET runtime and wrap Web Assembly calls into JS objects with get/setters
const va = await initVA(dotnet, YOUR_THREAD_COUNT);
// Create a raytracing world
const world = va.World_Create();
world.worldSize = { x: 100, y: 100, z: 100 };
world.maximumConcurrencyLevel = YOUR_THREAD_COUNT;
world.onReverbUpdated = () =>
{
// Access the listener's reverb stats
const returnedPercent = listener.processedReverb.returningPercent;
const decayTime = listener.eax.decayTime;
// Access ambient stats
const outsidePercent = listener.outsidePercent;
const ambientGainLF = listener.ambientFilter.gainLF;
};
// Create a rectangular prism
const prism = va.PrismPrimitive_Create();
prism.material = MaterialType.Concrete;
prism.size = { x: 20, y: 120, z: 20 };
prism.transform = va.Matrix.Multiply(
va.Matrix.CreateRotationY(Math.PI / 4),
va.Matrix.CreateTranslation(50, 50, 50)
);
world.addPrimitive(prism);
// Create a listener emitter with all ray types enabled
const listener = va.Emitter_Create();
listener.name = "Listener";
listener.setPosition(10, 50, 10);
listener.reverbRayCount = 128;
listener.reverbBounceCount = 64;
listener.reverbEnergyCap = 0.2;
listener.maxEchogramTime = 5000;
listener.echogramGranularity = 50;
listener.occlusionRayCount = 512;
listener.occlusionBounceCount = 8;
listener.permeationRayCount = 128;
listener.permeationBounceCount = 3;
listener.ambientOcclusionRayCount = 512;
listener.ambientOcclusionBounceCount = 8;
listener.ambientPermeationRayCount = 128;
listener.ambientPermeationBounceCount = 4;
world.addEmitter(listener);
// Create an enemy emitter
const enemy = va.Emitter_Create();
enemy.name = "Enemy";
enemy.setPosition(90, 50, 10);
enemy.onRaytracedByAnotherEmitter = (other) => {
const filter = other.getTargetFilter(enemy);
// PSEUDOCODE
// Godot.PlaySound(SoundType.EnemySpawn, enemy.position, filter);
}
world.addEmitter(enemy);
// Tell the listener to cast occlusion and permeation rays towards the enemy
listener.addTarget(enemy);
// Update loop
const start = performance.now();
function update()
{
const elapsed = performance.now() - start;
// Rotate and position the prism in real time
var rotation = elapsed / 1000.0;
prism.transform = va.Matrix.Multiply(
va.Matrix.CreateRotationY(rotation),
va.Matrix.CreateTranslation(50, 50, 50)
);
// Perform raytracing on background threads
world.update();
requestAnimationFrame(update);
}
requestAnimationFrame(update);
#include <stdio.h>
#include <math.h>
#include "vaudio.h"
#ifdef _WIN32
#include <windows.h>
#define sleep_ms(ms) Sleep(ms)
#else
#include <time.h>
#define sleep_ms(ms) do { struct timespec ts = { (ms)/1000, ((ms)%1000)*1000000 }; nanosleep(&ts, NULL); } while(0)
#endif
VAWorld* world;
VAEmitter* listener;
void on_reverb_updated(void)
{
// Access processed properties
VAProcessedReverb* processed = vaEmitterGetProcessedReverb(listener);
float returningPercent = processed->returningPercent;
// Access precalculated EAX properties
VAEAXReverb* eax = vaEmitterGetEAX(listener);
float decayTime = eax->decayTime;
// Access ambient stats
float outsidePercent = vaEmitterGetOutsidePercent(listener);
VALowPassFilter* ambientFilter = vaEmitterGetAmbientFilter(listener);
float ambientGainLF = ambientFilter->gainLF;
float ambientGainHF = ambientFilter->gainHF;
}
void on_raytraced_by_another_emitter(VAEmitter* source, VAEmitter* target)
{
VALowPassFilter* filter = vaEmitterGetTargetFilter(source, target);
printf("Emitter raytraced by another. GainLF: %f. GainHF: %f\n", filter->gainLF, filter->gainHF);
// PSEUDOCODE
// UE.PlaySound(soundType, position, filter);
}
int main(void)
{
// Create a raytracing world
VAWorld* world = vaWorldCreate();
vaWorldSetWorldSize(world, vaVectorCreate(100.0f, 100.0f, 100.0f));
// Create a rectangular prism
VAPrismPrimitive* prism = vaPrismPrimitiveCreate();
vaPrismPrimitiveSetMaterial(prism, VAMaterialConcrete);
vaPrismPrimitiveSetSize(prism, vaVectorCreate(15.0f, 15.0f, 15.0f));
VAMatrix rotation = vaMatrixCreateRotationY(M_PI / 4);
VAMatrix translate = vaMatrixCreateTranslation(50, 50, 50);
VAMatrix transform = vaMatrixMultiply(&rotation, &translate);
vaPrismPrimitiveSetTransform(prism, &transform);
vaWorldAddPrimitive(world, prism);
// Create a listener emitter with all ray types enabled
VAEmitter* listener = vaEmitterCreate();
vaEmitterSetName(listener, "Listener");
vaEmitterSetPosition(listener, vaVectorCreate(10.0f, 50.0f, 10.0f));
vaEmitterSetReverbRayCount(listener, 128);
vaEmitterSetReverbBounceCount(listener, 64);
vaEmitterSetReverbEnergyCap(listener, 0.2f);
vaEmitterSetOcclusionRayCount(listener, 512);
vaEmitterSetOcclusionBounceCount(listener, 8);
vaEmitterSetPermeationRayCount(listener, 128);
vaEmitterSetPermeationBounceCount(listener, 3);
vaEmitterSetAmbientOcclusionRayCount(listener, 512);
vaEmitterSetAmbientOcclusionBounceCount(listener, 8);
vaEmitterSetAmbientPermeationRayCount(listener, 128);
vaEmitterSetAmbientPermeationBounceCount(listener, 3);
VAResult result = vaWorldAddEmitter(world, listener);
assert(result == VA_SUCCESS);
// Create an enemy emitter
VAEmitter* source = vaEmitterCreate();
vaEmitterSetName(source, "Enemy");
vaEmitterSetPosition(source, vaVectorCreate(90.0f, 50.0f, 10.0f));
vaEmitterSetOnRaytracedByAnotherEmitterCallback(source, on_raytraced_by_another_emitter);
result = vaWorldAddEmitter(world, source);
assert(result == VA_SUCCESS);
// Tell the listener to cast occlusion and permeation rays towards the enemy
vaEmitterAddTarget(listener, source);
float elapsed = 0.0f;
while (true)
{
// Rotate and position the prism in real time
float angle = elapsed / 1000.0f;
VAMatrix rotation = vaMatrixCreateRotationY(angle);
translate = vaMatrixCreateTranslation(50, 50, 50);
transform = vaMatrixMultiply(&rotation, &translate);
vaPrismPrimitiveSetTransform(prism, &transform);
// Perform raytracing on background threads
vaWorldUpdate(world);
sleep_ms(16);
elapsed += 16.0f;
}
return 0;
}
SDK Changelog
Version 1.3.1 - Released 17th July 2026
Fixes:
- Default material transmission values now use the new system, rather than the old dB/m units
- The EAX UI in the debug panel no longer crashes when there are no emitters with relative reverb
Version 1.3.0 - Released 17th July 2026
New: Unreal Plugin
Fixes:
- Fixed a crash when reducing
World.MaximumConcurrencyLevelat runtime in all SDKs - Added missing documentation for
vaWorldGetEpsilonandMeshPrimitive vaEaxReverbGetRelativeGainnow returns the correct value in the C SDKWorld.RaysCastThisFramenow returns the correct value in all SDKs- Fixed a crash when primitives move into the world bounds, from outside the world bounds
- Fixed ray rendering when reducing
OcclusionBounceCountorPermeationBounceCountat runtime - Setting
OverridePositionsorOverrideRayDirectionsto null no longer unnecessarily invalidates the ray cache vaMaterialConvertMetersToTransmissionandvaMaterialConvertTransmissionToMetersfunctions are now correctly exported in the C SDK- Fixed a crash when rendering ambient permeation rays in the debug window
World.Epsilonis now correctly used in all primitive intersection logic- Fixed an issue where rays could pass through mesh primitives
- Fixed an issue where trails were incorrectly trimmed when the world contains mesh primitives
- Material
TransmissionHFcan now be updated correctly in the C SDK - Fixed an issue where some primitives were not raytraced against in complex scenes in the C SDK
- Permeation ray energy is now tracked correctly when intersecting with mesh primitives that do not support 3D permeation in the C SDK
- Fixed echogram rendering position in the debug window
- Fixed an issue where reverb rays would not be re-cast when the emitter moves
- Godot - geometry with scale matrices are now correctly added to the world
- Fixed
EAXReverb.DensityreturningNaNwhenEmitter.EchogramGranularityis higher than 150 - Fixed a rare crash when an Emitter only casts visualisation rays
- Fixed incomplete visualisation data in the visualisation callback in the C SDK - the full array will now be populated with position and normal data
- Emitters in the C# SDK now correctly have the default
GroupedEAXIndexvalue of -1 (was previously incorrectly set to 0) - Fixed a crash in the C SDK when creating a
VAMeshwhile background threads are running - Fixed an issue where the C SDK could invoke
OnRaytracedByAnotherEmittercallbacks for targets that were not raytraced yet - Fixed an issue where rays could reflect inside primitives when the ray hits the intersection between the world bounds and the primitive
- Fixed a threading freeze on Mac in the C# SDK
Breaking changes:
- Material
TransmissionLFandTransmissionHFproperties now control how many meters a ray must pass through a primitive before it loses all energy. For example,material.TransmissionLF = 20;means a ray must travel 20 meters before losing all its low-frequency energy. Previously these properties were in decibels per meter (dB/m). Use the newTransmissionToMetershelper function to convert between units. - Renamed
TransmissionForThicknesstoMetersToTransmissionin all SDKs - Renamed
vaEmitterSetAffectsGroupedEaxtovaEmitterSetAffectsGroupedEAX - Renamed
vaEmitterGetAffectsGroupedEaxtovaEmitterGetAffectsGroupedEAX - Renamed
vaEmitterGetGroupedEaxIndextovaEmitterGetGroupedEAXIndex - Renamed
vaEaxReverbGetRelativeDirectiontovaEAXReverbGetRelativeDirection - Renamed
vaEaxReverbGetRelativeGaintovaEAXReverbGetRelativeGain - Renamed
vaCustomEaxFormulasCreatetovaCustomEAXFormulasCreate - Renamed
vaCustomEaxFormulasDestroytovaCustomEAXFormulasDestroy - Renamed
vaEaxUtilsFindBestMatchtovaEAXUtilsFindBestMatch - Renamed
vaEaxUtilsGetSimilaritytovaEAXUtilsGetSimilarity - Renamed
vaEmitterReverbEnabledtovaEmitterGetReverbEnabled - Renamed
vaEmitterOcclusionEnabledtovaEmitterGetOcclusionEnabled - Renamed
vaEmitterPermeationEnabledtovaEmitterGetPermeationEnabled - Renamed
vaEmitterAmbientOcclusionEnabledtovaEmitterGetAmbientOcclusionEnabled - Renamed
vaEmitterAmbientPermeationEnabledtovaEmitterGetAmbientPermeationEnabled - Renamed
vaEmitterVisualisationRaysEnabledtovaEmitterGetVisualisationRaysEnabled - Renamed
vaEmitterCastsRaystovaEmitterGetCastsAnyRays - Renamed
vaWorldGetMaxBoundstovaWorldGetMaximumBounds - Renamed
vaWorldSetMaxBoundstovaWorldSetMaximumBounds - Renamed
vaWorldGetReverbCalculatedtovaWorldGetInitialising, which is true until raytracing completes for the first time - Renamed
World.ReverbCalculatedtoWorld.Initialising, which is true until raytracing completes for the first time - Removed
World.TotalPossibleRayCountorWorld.RayCachePercent- the old values were incorrect, and predicting them correctly at runtime is inefficient WorldfieldsMainThreadTime,PreparationTime,RaytracingTime,AnalysisTimeandLatencychanged from float to double- The static
CalculateListenerRelativePan()function is now an instance method on theWorldclass, and takes its coordinate system into account when calculating the result - Removed
Emitter.ReservedEmitterTargetsandvaEmitterSetReservedEmitterTargets- it is not feasible to know the maximum number of targets for an emitter ahead of time, due to the new reverb tail system
Changes:
- Echogram UI only renders when the world has emitters that cast reverb rays
vaWorldSetMaximumGroupedEAXCountnow accepts1as a value (previously had to be 2 or greater)- Emitters that cast reverb rays and have
RaytraceOnce=trueandAffectsGroupedEAX=truewill no longer be removed instantly from the scene. Instead, they will remain until their reverb tail is no longer audible, to prevent reverb from cutting off
Additions:
- New
VoxelPrimitivethat represents a 3D grid of voxels - New
MeshandVAMeshclass for sharing a mesh across multiple mesh primitives - Created a new vaudio-unreal repo (experimental Unreal Engine 5.7 plugin)
- New debug window controls and settings
- New EAX view in the debug window
- New
World.CameraSpeedvariable for adjusting the camera speed in the debug window - New
World.ShouldRenderRaysandWorld.ShouldRenderPrimitivesvariables for customising rendering in the debug window - New
World.CoordinateSystemfield that affects how the debug window renders the world, and how listener-relative reverb directions are calculated for OpenAL - New
Emitter.OnRemovedcallback that is invoked when the emitter is actually removed (e.g. once its reverb tail finishes playing) - New error codes for the C SDK:
VA_INVALID_COUNT, VA_WORLD_CONFLICT, VA_ERROR_FILE_OPEN, VA_ERROR_FILE_WRITE, VA_ERROR_FILE_VERSION, VA_ERROR_FILE_CORRUPT, VA_INVALID_MATERIAL, VA_MATERIAL_DOES_NOT_EXIST, VA_NOT_ADDED_TO_WORLD - New
vaWorldHasMaterialfunction in the C SDK vaWorldAddPrimitiveandvaWorldRemovePrimitivenow return VAResult error codes- Brand new EAX reverb grouping/priority system
- Support for multiple coordinate systems (Y-up, Z-up, Unreal ,Godot, Unity, Blender)
Optimisations:
- 2-5% faster ray intersections with primitives with
transformmatrices (prism, cylinder, etc), that only contain translation components (no rotation) - Calling
vaWorldSetMaximumConcurrencyLevelat runtime no longer stalls the main thread when next performing raytracing in the C SDK - Large refactor and clean up of all ray intersection logic - more stable with nearby/overlapping primitives
- Faster preparation thread time when there are many moving primitives (C# and C SDK)
- Creating
Meshinstances no longer causes CPU spikes on the main thread (all heavy logic moved to background threads)
Version 1.2.2 - Released 3rd July 2026
Fixes:
- Processed and EAX reverb data is now available in
Emitter.OnRaytracingCompletecallbacks (fixes #22)
Changes:
vaWorldSetPendingShutdownnow takes abool valueparameter
Additions:
- New
vaWorldGetPendingShutdownfunction - Created a new vaudio-dotnet-examples repo
- Created a new vaudio-native-wrapper repo
Optimisations:
- 5-10% faster ray intersections with primitives with
transformmatrices (prism, cylinder, etc)
Version 1.2.1 - Released 2nd July 2026
Fixes:
Additions:
Version 1.2.0 - Released 1st July 2026
Fixes:
- Fixed a crash when reducing
world.MaximumConcurrencyLeveldown to1at runtime - Fixed an issue where meshes would incorrectly block permeation rays
- Fixed a crash when disabling occlusion/permeation on an Emitter that has targets
- Fixed an issue where trails wouldn't be trimmed when line-of-sight is lost
- Fixed reverb ray rendering in the debug window
- Trails are now correctly regenerated when an emitter passes through geometry
- Fixed a crash when running multiple worlds in parallel
- Rays now correctly bounce off the edge/rim of cylinders, half spheres, rectangular cones, triangular cones and triangular prisms
- Fixed an issue where line-of-sight was incorrectly achieved on an earlier bounce when a later bounce becomes occluded by a primitive
- Fixed an issue where rays could pass through nearby primitives with high scattering values
- Improved scattering distribution
- Permeation energy is now correctly lost when primitives overlap emitters
- Fixed an issue where rays could be incorrectly reflected back inside a prism or sphere that it just bounced off, near where two prisms/spheres intersect
- Fixed an issue where rays could pass through primitives that intersected with the world bounds
World.RayCachePercentno longer returns infinity when there are no emitters- Rays are now correctly re-cast when a primitive's material changes
Changes:
- Many methods and classes renamed and cleaned up.
RaytracingContextrenamed toWorld- Improved debug window UI and shading
Additions:
- Added support for Web Assembly
- Ambient volume now use the same system as emitter volume (both occlusion and permeation energy contribute to volume and can be customised via an
AmbientGainFormula) - New
AmbienceOcclusionRayCountandAmbienceOcclusionBounceCountemitter settings. Ambience occlusion was previously tied to reverb (for calculating outside percentage) - New
RefreshDistanceThresholdemitter setting that controls how far a ray must deviate from its old position before the trail is refreshed - New
Emitter.ResetTrails()function that clears the ray trail cache, cleans up memory and re-casts all rays - New
World.Epsilonvalue for adjusting the epsilon value used for ray offsets, world bounds clamping and line-of-sight tests - New
Emitter.ScatteringSeedsetting for deterministic scattering - New
Emitter.OcclusionEnergyPercentandEmitter.PermeationEnergyPercentfields. It's not possible to use the customGainFormulain the JavaScript API, so these fields can be customised to control the percentage of energy required for the emitter to be at max volume, when discovered by another emitter - Added support for instanced meshes - create a
Meshonce, and use it to create multipleMeshPrimitiveinstances
Improvements:
- Materials and air absorption settings can now be changed at any time, and all updates will be synced automatically to the background threads.
Removed:
- Room volume calculation (expensive and unnecessary)
- Reverb wetness (use emitter gain instead)
- Removed the
World.MaterialsDirtyproperty. All material updates are now detected automatically
Optimisations:
- Overall 60% faster raytracing
- Expensive collation work from the single analysis thread is now spread across multiple raytracing threads
- Reduced the amount of rays that are cast when emitters have different ray/bounce counts for occlusion/permeation/reverb/ambience
- Some trails are no longer unnecessarily re-generated when an emitter moves
- Prevented memory allocation on background threads when tracking reverb bounce positions
- Prevented memory allocation on background threads when calculating directional EAX
- Primitive updates no longer cause memory allocations on background threads
- Improved thread scheduling for reduced latency (40ms down to 20ms on Windows)
- Removed memory allocations when changing material/air absorption/echogram settings
- Main thread no longer burns CPU when calling
World.Wait() - Faster ray traversal through the world (surface area heuristic for BVH rather than longest axis split)
- Faster traversal through
MeshPrimitivewith custom BVH and testing multiple triangles at once - Faster bounding box intersections
- Improved memory layout and less cache misses
- Faster material lookups
Version 1.1.1
Fixes:
- Rays now collide with cones, cylinders, disks and half-spheres correctly
- Fixed
RaytracingContext.TotalPossibleRayCountresult - now includes refresh rays and trail verification rays - Meshes now render correctly in the debug window when disabling and enabling rendering
- Fixed EAX decay time calculation - previously it would produce large values when the echogram contained a tiny amount of data much later on. Now it stops checking for data after the first empty bin. If your echogram has gaps (red lines in the debug window), increase the new
Emitter.MaxEchogramTimesetting from 10ms (default) to 50ms or 100ms.
Additions:
- New
RaytracingContext.WorldIsIndoorssetting that controls whether the entire world is considered indoors or outdoors. When false, reverb rays stop checking for line-of-sight after hitting the world edge. Defaults to false - The length and granularity of each Emitter's echogram can now be adjusted via the new
Emitter.MaxEchogramTimeandEmitter.EchogramGranularitysettings - Emitters can now cast rays from multiple positions via the new
Emitter.OverridePositionssetting. Rays are cast round-robin style, e.g. with 128 rays and 2 vectors inEmitter.OverridePositions, even rays will be cast from the 1st vector and odd rays will be cast from the 2nd vector. - Emitters can now cast rays in custom directions via the new
Emitter.OverrideRayDirectionssetting. Same round-robin style as above
Changes:
- Capitalised the ray colour fields in Emitter:
TrailColor,ReverbColor,OcclusionColor,PermeationColor,AmbientPermeationColor - The debug window now renders each Emitter's echogram
Optimisations:
- Faster permeation intersection for meshes
Version 1.1.0
Fixes:
- Voice reverb ray lengths now account for
metersPerUnitcorrectly - Listener and Voice UI in the debug window now matches the 3D position of the emitter correctly
- Fixed stale data when disabling permeation at runtime
- Fixed NaN gain values from the default
voiceEnergyFormulawhen occlusion or permeation is disabled - The order of groupedEAX indices is now stable
- Fixed an edge case were occlusion/LOS rays were not re-cast when primitives moved
- Fixed an exception when reducing
MaximumGroupedEAXCount - Removed hardcoded SIMD code for bounding box checks (broken on Mac Parallels)
- All rays are now correctly re-cast when materials are updated
- Fixed ray reflections off world bounds edges
- Fixed an issue where reverb line-of-sight was incorrectly blocked by the same surface the ray just bounced off
- Fixed an issue where rays that collided with meshes were re-cast every frame, rather than being cached
- Fixed reverb bounds and center calculation when
MetersPerUnitis a value other than1.0f - Rays now correctly bounce within concave meshes
Changes:
- All settings can be changed at runtime with no stutters on the main thread
- More accurate reverb pan/directionality calculation, with new
Emitter.EAXPanInnerThresholdandEmitter.EAXPanOuterThresholdsettings
Additions:
- New
Emittersystem - support for multiple listeners, customisable ray counts and customisable bounce counts - Two new ray types: Ambient Permeation and Visualisation
- The debug rendering window can now render occlusion and permeation rays. Set the
emitter.occlusionColorandemitter.permeationColorvariables to render them. - New
PlaneTransmissionLFandPlaneTransmissionHFmaterial settings that control how much energy is lost when rays pass through flat planes
Optimisations:
- Slightly less memory usage
- Heavy memory allocations moved to background threads
- Post-raytracing analysis logic moved to background threads
- No more stutters when changing ray counts, bounce counts, or maximum concurrency level on the main thread at runtime
- Slightly faster primitive-ray intersections
Version 1.0.6
Fixes:
- More robust world bounds checks - rays should never leave the world bounds
Changes:
- Removed some debug logging
- Logs are now prefixed with
[vaudio]
Optimisations:
- Raytracing should be slightly faster as the world bounds is no longer included in the BVH
Version 1.0.5
Fixes:
- EAX
DecayTimeno longer flickers with sparse echograms - Fixed a
NullReferenceExceptionwhen creating aWorldwith bothocclusionRayCountandpermeationRayCountset to 0 - Fixed an edge case where rays could leave the world when colliding with a primitive exactly on the world edge
- Rays will now bounce correctly within a concave
MeshPrimitive - Fixed an edge case where voices wouldn't render in the debug window
- EAX gainLF and gainHF are now calculated correctly when
occlusionRayCountorpermeationRayCountare higher thanreverbRayCount - Shader errors use the custom logging callback rather than
Console.WriteLine
Changes:
- Removed all
outsideEAXfunctionality - usegroupedEAXinstread
Additions:
- Added a new
supportsPemeationfield onMeshPrimitive, for meshes that are watertight. Currently only supports permeation between the first 2 intersections (complex concave shapes are not supported yet) - New
EAXReverbResults.ALEffectSlotGainparameter that is automatically calculated for grouped EAX effects for voices - New
EAXReverbResults.BoundsMinandEAXReverbResults.BoundsMaxproperties for the min and max 3D bounds of the reverb space
Version 1.0.4
Changes:
Voice.filteris now initiallynull, to prevent accidentally accessing it before raytracing completes
Additions:
- New
RaytracingContext.OnReverbUpdatedcallback, which fires after reverb is updated and beforeVoice.OnRaytracingCompletefires. This gives you a chance to update your EAX reverb effects, beforeVoice.OnRaytracingCompletetries to access them.
Version 1.0.3
Fixes:
- Fixed a crash when creating a
WorldwithWorldSettings.airAbsorptionset to null - Fixed an edge case where occlusion energy did not reset to 0 when no occlusion rays could reach a voice
Changes:
- Renamed
RaytracingContext.ListenerEAXReverbtoRaytracingContext.ListenerEAX - Renamed
RaytracingContext.OutsideEAXReverbtoRaytracingContext.OutsideEAX - Renamed
RaytracingContext.BlendedEAXReverbtoRaytracingContext.GroupedEAX - Renamed
settings.blendedReverbPresetAmounttosettings.maximumGroupedEAXCount - Renamed
voice.blendedEAXIndextovoice.groupedEAXIndex - Renamed
ProcessedReverbResults.ReturningPercenttoProcessedReverbResults.ReturnedPercent - Removed Sabine / Eyring / Theoretical RT60 properties from
ProcessedReverbResults. UseMeasuredDecayTimeLFandMeasuredDecayTimeHFinstead
Additions:
- Added XML documentation to
WorldSettings - Added XML documentation to
World - Added XML documentation to all primitives
- Added XML documentation to
MaterialProperties - Added XML documentation to
RawReverbResults
Optimisations:
- Line-of-sight rays for reverb are no longer cast back to the listener after the ray escapes outside (hits the world bounds)
Version 1.0.2
Fixes:
- Rays now properly lose energy when bouncing off primitives when
reverbRayCountis 0 - Fixed voice energy calculation when occlusion and permeation rays are different
- Rays will no longer incorrectly travel through the primitive they just bounced off, when their next hit was trimmed
- Rays will no longer have LOS blocked by the primitive they just bounced off
- More accurate reflection positions when rays bounce off primitives
- The rendering window now shows the correct ray counts
Changes:
- Warnings are now logged when creating voices when
occlusionRayCountandpermeationRayCountare zero (as no rays will be cast against voices) - Renamed
RaytracingContext.trailColourtoRaytracingContext.trailColor - Renamed
RaytracingContext.reverbColourtoRaytracingContext.reverbColor - Renamed
MaterialSettings.colourstoMaterialSettings.colors - Occlusion rays are no longer fired directly from the listener to each voice (only on each bounce)
Optimisations:
- Slightly faster bounding box intersection tests
- Slightly less memory usage
- Slightly faster primitive updates
- Slightly less CPU usage on the main thread
- Removed a decent amount of unused code
Version 1.0.1
Fixes:
- Fixed air absorption - it was not applied at all
Worldcan now be initialised with 0reverbRayCount- Occlusion now works correctly when
reverbRayCountis 0
Changes:
- Air absorption now uses Celsius instead of Farenheit
- Occlusion energy is now accumulated even after rays bounce off the edge of the map. Might make this a setting
Threading Architecture
To reduce the performance impact on your application, all heavy lifting and raytracing is performed on background threads. The main thread does a small amount of work when World.Update()World.update()raytracing_world_update() is invoked, but you shouldn't notice any stutters or FPS drops.
The heavy work that runs on background threads includes:
- Preparation - calculating a bounding volume hierarchy (BVH) for faster raytracing, allocating memory, preparing new primitives/emitters and applying new settings
- Raytracing - trimming and building trails, casting rays (see Trail-Based Raytracing)
- Analysis - calculating reverb and muffling properties
The flow is:
- 1 background thread runs the Preparation task
- Then, N threads run all Raytracing tasks
- Then 1 background thread runs the Analysis task
Trail-Based Raytracing
When a ray is cast from an emitter, it bounces multiple times around the environment. Data about each bounce is stored and persists across updates.
The number of trails is equal to the largest ray count among:
OcclusionRayCountocclusionRayCountvaEmitterGetOcclusionRayCount()PermeationRayCountpermeationRayCountvaEmitterGetPermeationRayCount()ReverbRayCountreverbRayCountvaEmitterGetReverbRayCount()AmbientOcclusionRayCountambientOcclusionRayCountvaEmitterGetAmbientOcclusionRayCount()AmbientPermeationRayCountambientPermeationRayCountvaEmitterGetAmbientPermeationRayCount()
The amount of bounces in a trail is equal to the largest bounce count among:
OcclusionBounceCountocclusionBounceCountvaEmitterGetOcclusionBounceCount()PermeationBounceCountpermeationBounceCountvaEmitterGetPermeationBounceCount()ReverbBounceCountreverbBounceCountvaEmitterGetReverbBounceCount()AmbientOcclusionBounceCountambientOcclusionBounceCountvaEmitterGetAmbientOcclusionBounceCount()AmbientPermeationBounceCountambientPermeationBounceCountvaEmitterGetAmbientPermeationBounceCount()
For example with ReverbBounceCountreverbBounceCountvaEmitterSetReverbBounceCount() set to 8 and OcclusionBounceCountocclusionBounceCountvaEmitterSetOcclusionBounceCount() set to 4, the trail will consist of 8 bounces. Reverb rays will fire back towards the emitter on every bounce, but occlusion line-of-sight rays will only fire on the first 4 bounces.
Trails are cached and only updated when primitives or emitters move. This cache is an important optimisation that enables raytracing to run in real time in complex scenes.
There are a few scenarios to be aware of that will clear this cache for all emitters:
- changing
World.PositionWorld.positionvaWorldSetPosition()orWorld.SizeWorld.sizevaWorldSetSize() - updating existing materials
- updating, enabling or disabling air absorption
An emitter's cache can also be cleared if:
- it is teleported a large distance
- its ray or bounce counts are changed
- its
Emitter.MaxEchogramTimeEmitter.maxEchogramTimevaEmitterSetMaxEchogramTime()orEmitter.EchogramGranularityEmitter.echogramGranularityvaEmitterSetEchogramGranularity()or changed
For best performance, avoid changing material / air absorption / echogram / ray count / bounce count settings at runtime.
Visualisation Rays
Visualisation rays are separate from the above trails - they are cast in random directions every frame and do not benefit from caching.
CapsulePrimitive
A circular capsule with rounded ends.
class CapsulePrimitive : Primitive
{
// Length of the cylindrical body between the hemispherical caps
float length;
// Radius of the capsule
float radius;
// Must only contain rotation and translation components, not scale
Matrix transform;
}
interface CapsulePrimitive
{
// Created via: va.CapsulePrimitive_Create()
get length(); set length(v);
get material(); set material(v);
get radius(); set radius(v);
get transform(); set transform(v);
}
// Creates a new CapsulePrimitive with default values
VACapsulePrimitive* vaCapsulePrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaCapsulePrimitiveDestroy(VACapsulePrimitive* capsule);
VAResult vaCapsulePrimitiveSetMaterial(VACapsulePrimitive* capsule, VAMaterialType material);
// Get the material for this capsule
VAMaterialType vaCapsulePrimitiveGetMaterial(const VACapsulePrimitive* capsule);
// Set the radius of the cylinder and end caps
void vaCapsulePrimitiveSetRadius(VACapsulePrimitive* capsule, float radius);
// Get the radius of the cylinder and end caps
float vaCapsulePrimitiveGetRadius(const VACapsulePrimitive* capsule);
// Set the length of the capsule cylinder, excluding end caps
void vaCapsulePrimitiveSetLength(VACapsulePrimitive* capsule, float length);
// Get the length of the capsule cylinder, excluding end caps
float vaCapsulePrimitiveGetLength(const VACapsulePrimitive* capsule);
// Set the world-space transform. Must not contain scale components
void vaCapsulePrimitiveSetTransform(VACapsulePrimitive* capsule, const VAMatrix* transform);
// Get the world-space transform of this capsule
const VAMatrix* vaCapsulePrimitiveGetTransform(const VACapsulePrimitive* capsule);
This is the same as CylinderPrimitive but with rounded caps on either end that extend outside the ends of the cylinder, i.e. the real length of this primitive is length + radius * 2.
The transform matrix should only contain rotation and translation operations, not scale. The size of the capsule should be controlled via the radius and length properties
ConePrimitive
A cone with a circular base.
interface ConePrimitive
{
// Created via: va.ConePrimitive_Create()
get height(); set height(v);
get material(); set material(v);
get radius(); set radius(v);
get transform(); set transform(v);
}
// Creates a new ConePrimitive with default values
VAConePrimitive* vaConePrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaConePrimitiveDestroy(VAConePrimitive* cone);
// Radius of the cone base
float vaConePrimitiveGetRadius(const VAConePrimitive* cone);
// Height of the cone
float vaConePrimitiveGetHeight(const VAConePrimitive* cone);
// World-space transform of the cone
const VAMatrix* vaConePrimitiveGetTransform(const VAConePrimitive* cone);
// Radius of the cone base
void vaConePrimitiveSetRadius(VAConePrimitive* cone, float radius);
// Height of the cone
void vaConePrimitiveSetHeight(VAConePrimitive* cone, float height);
// Set the world-space transform. Must not contain scale components
void vaConePrimitiveSetTransform(VAConePrimitive* cone, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaConePrimitiveGetMaterial(const VAConePrimitive* cone);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaConePrimitiveSetMaterial(VAConePrimitive* cone, VAMaterialType material);
The base of the cone is centered at (0, 0, 0), and the tip extends upwards to (0, height, 0)
The transform matrix should only contain rotation and translation operations, not scale. The size of the cone should be controlled via the radius and height properties
CylinderPrimitive
A cylinder.
interface CylinderPrimitive
{
// Created via: va.CylinderPrimitive_Create()
get length(); set length(v);
get material(); set material(v);
get radius(); set radius(v);
get transform(); set transform(v);
}
// Creates a new CylinderPrimitive with default values
VACylinderPrimitive* vaCylinderPrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaCylinderPrimitiveDestroy(VACylinderPrimitive* cylinder);
// Radius of the cylinder
float vaCylinderPrimitiveGetRadius(const VACylinderPrimitive* cylinder);
// Length of the cylinder along its axis
float vaCylinderPrimitiveGetLength(const VACylinderPrimitive* cylinder);
// World-space transform of the cylinder
const VAMatrix* vaCylinderPrimitiveGetTransform(const VACylinderPrimitive* cylinder);
// Radius of the cylinder
void vaCylinderPrimitiveSetRadius(VACylinderPrimitive* cylinder, float radius);
// Length of the cylinder along its axis
void vaCylinderPrimitiveSetLength(VACylinderPrimitive* cylinder, float length);
// Set the world-space transform. Must not contain scale components
void vaCylinderPrimitiveSetTransform(VACylinderPrimitive* cylinder, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaCylinderPrimitiveGetMaterial(const VACylinderPrimitive* cylinder);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaCylinderPrimitiveSetMaterial(VACylinderPrimitive* cylinder, VAMaterialType material);
The transform matrix should only contain rotation and translation operations, not scale. The size of the cylinder should be controlled via the radius and length properties
DiskPrimitive
A flat circular disk.
interface DiskPrimitive
{
// Created via: va.DiskPrimitive_Create()
get material(); set material(v);
get radius(); set radius(v);
get transform(); set transform(v);
}
// Creates a new DiskPrimitive with default values
VADiskPrimitive* vaDiskPrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaDiskPrimitiveDestroy(VADiskPrimitive* disk);
// Radius of the disk
float vaDiskPrimitiveGetRadius(const VADiskPrimitive* disk);
// World-space transform of the disk
const VAMatrix* vaDiskPrimitiveGetTransform(const VADiskPrimitive* disk);
// Radius of the disk
void vaDiskPrimitiveSetRadius(VADiskPrimitive* disk, float radius);
// Set the world-space transform. Must not contain scale components
void vaDiskPrimitiveSetTransform(VADiskPrimitive* disk, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaDiskPrimitiveGetMaterial(const VADiskPrimitive* disk);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaDiskPrimitiveSetMaterial(VADiskPrimitive* disk, VAMaterialType material);
The transform matrix should only contain rotation and translation operations, not scale. The size of the disk should be controlled via the radius property
HalfSpherePrimitive
Half of a sphere.
interface HalfSpherePrimitive
{
// Created via: va.HalfSpherePrimitive_Create()
get material(); set material(v);
get radius(); set radius(v);
get transform(); set transform(v);
}
// Creates a new HalfSpherePrimitive with default values
VAHalfSpherePrimitive* vaHalfSpherePrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaHalfSpherePrimitiveDestroy(VAHalfSpherePrimitive* halfSphere);
// Radius of the half-sphere
float vaHalfSpherePrimitiveGetRadius(VAHalfSpherePrimitive* halfSphere);
// World-space transform of the half-sphere
const VAMatrix* vaHalfSpherePrimitiveGetTransform(VAHalfSpherePrimitive* halfSphere);
// Radius of the half-sphere
void vaHalfSpherePrimitiveSetRadius(VAHalfSpherePrimitive* halfSphere, float radius);
// Set the world-space transform. Must not contain scale components
void vaHalfSpherePrimitiveSetTransform(VAHalfSpherePrimitive* halfSphere, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaHalfSpherePrimitiveGetMaterial(const VAHalfSpherePrimitive* halfSphere);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaHalfSpherePrimitiveSetMaterial(VAHalfSpherePrimitive* halfSphere, VAMaterialType material);
WARNING: The transform matrix should only contain rotation and translation operations, not scale. The size of the prism should be controlled via the size property
MeshPrimitive
A triangulated mesh.
class MeshPrimitive : Primitive
{
// If this is a watertight mesh, permeation rays will lose energy based on how far the ray travels through this mesh. If not, set this field to false and permeation rays will lose a flat percentage of energy when they touch this mesh (controlled by PlaneTransmissionLF and PlaneTransmissionHF).
bool Supports3DPermeation;
// Can contain scale, rotation and translation components
Matrix transform;
// Create a mesh primitive from a list of vertices
MeshPrimitive(MaterialType material, List<Vector> vertices, Vector minBounds, Vector maxBounds, Matrix transform);
// Create a mesh primitive that shares geometry with a Mesh. The BVH is built once in the Mesh and reused by every instance.
MeshPrimitive(MaterialType material, Mesh mesh, Matrix transform);
// Create a mesh primitive from an unmanaged array of vertices
MeshPrimitive(MaterialType material, Vector* vertices, int verticesCount, Vector minBounds, Vector maxBounds, Matrix transform);
// Create a mesh primitive from an array of vertices
MeshPrimitive(MaterialType material, Vector[] vertices, Vector minBounds, Vector maxBounds, Matrix transform);
// Dispose the OpenGL vertex buffer (dev build only)
void DisposeBuffer();
}
interface MeshPrimitive
{
// Created via: va.MeshPrimitive_Create()
// Also: va.MeshPrimitive_CreateFromMesh(material, mesh, transform)
get material(); set material(v);
get supports3DPermeation(); set supports3DPermeation(v);
get transform(); set transform(v);
}
VAMeshPrimitive* vaMeshPrimitiveCreate(VAMaterialType material, const VAVector* vertices, int vertexCount, VAVector minBounds, VAVector maxBounds, const VAMatrix* transform);
// Creates a new MeshPrimitive that shares geometry from a VAMesh
VAMeshPrimitive* vaMeshPrimitiveCreateFromMesh(VAMaterialType material, VAMesh* mesh, const VAMatrix* transform);
// Free all resources owned by this primitive
VAResult vaMeshPrimitiveDestroy(VAMeshPrimitive* primitive);
// World-space transform matrix for this mesh
const VAMatrix* vaMeshPrimitiveGetTransform(const VAMeshPrimitive* primitive);
// Set the world-space transform matrix; updates all triangle bounds
void vaMeshPrimitiveSetTransform(VAMeshPrimitive* primitive, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaMeshPrimitiveGetMaterial(const VAMeshPrimitive* cylinder);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaMeshPrimitiveSetMaterial(VAMeshPrimitive* cylinder, VAMaterialType material);
// Whether sound can permeate through this mesh. Set to true for watertight meshes
bool vaMeshPrimitiveGetSupports3DPermeation(const VAMeshPrimitive* primitive);
// Whether sound can permeate through this mesh. Set to true for watertight meshes
void vaMeshPrimitiveSetSupports3DPermeation(VAMeshPrimitive* primitive, bool supports3DPermeation);
Unlike other primitives, a MeshPrimitive must be initialised by its constructor, with all data already available.
Once created, only transform and material can be updated. The vertices will be processed into a BVH and cannot be updated.
After creation, the vertices list can be safely cleared/disposed, or used to create another MeshPrimitive.
minBounds and maxBounds must be the minimum and maximum bounds of the 3D model when at rest, i.e. Matrix.Identity.
If the mesh is enclosed, set {C#:supports3DPermeation|C:mesh_primitive_set_supports_3d_permeation() to true. If it is not enclosed (e.g. heightmap), 3D permeation is not supported, and rays will lose energy based on the PlaneTransmissionLF/HF fields instead. See Materials.
When calculating 3D permeation, only 1 entry and 1 exit point are used to determine the permeation depth. This may not produce accurate results for complex concave meshes.
PlanePrimitive
A flat rectangular plane.
interface PlanePrimitive
{
// Created via: va.PlanePrimitive_Create()
get height(); set height(v);
get material(); set material(v);
get transform(); set transform(v);
get width(); set width(v);
}
// Creates a new PlanePrimitive with default values
VAPlanePrimitive* vaPlanePrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaPlanePrimitiveDestroy(VAPlanePrimitive* plane);
// Width of the plane
float vaPlanePrimitiveGetWidth(VAPlanePrimitive* plane);
// Height of the plane
float vaPlanePrimitiveGetHeight(VAPlanePrimitive* plane);
// World-space transform of the plane
const VAMatrix* vaPlanePrimitiveGetTransform(VAPlanePrimitive* plane);
// Width of the plane
void vaPlanePrimitiveSetWidth(VAPlanePrimitive* plane, float width);
// Height of the plane
void vaPlanePrimitiveSetHeight(VAPlanePrimitive* plane, float height);
// Set the world-space transform. Must not contain scale components
void vaPlanePrimitiveSetTransform(VAPlanePrimitive* plane, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaPlanePrimitiveGetMaterial(const VAPlanePrimitive* plane);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaPlanePrimitiveSetMaterial(VAPlanePrimitive* plane, VAMaterialType material);
Primitive
Base primitive class with a common material field.
abstract class Primitive
{
// Determines the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
MaterialType material;
}
Base primitive class with a common material field.
interface Primitive
{
get material(); set material(v);
}
Not available in C.
PrismPrimitive
A rectangular prism.
class PrismPrimitive : Primitive
{
// Dimensions of the prism along each axis
Vector size;
// Must only contain rotation and translation components, not scale
Matrix transform;
}
interface PrismPrimitive
{
// Created via: va.PrismPrimitive_Create()
get material(); set material(v);
get size(); set size(v);
get transform(); set transform(v);
}
// Creates a new PrismPrimitive with default values
VAPrismPrimitive* vaPrismPrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaPrismPrimitiveDestroy(VAPrismPrimitive* prism);
// Width, height, and depth of the prism
VAVector vaPrismPrimitiveGetSize(const VAPrismPrimitive* prism);
// World-space transform of the prism
const VAMatrix* vaPrismPrimitiveGetTransform(const VAPrismPrimitive* prism);
// Width, height, and depth of the prism
void vaPrismPrimitiveSetSize(VAPrismPrimitive* prism, VAVector size);
// Set the world-space transform. Must not contain scale components
void vaPrismPrimitiveSetTransform(VAPrismPrimitive* prism, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaPrismPrimitiveGetMaterial(const VAPrismPrimitive* prism);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaPrismPrimitiveSetMaterial(VAPrismPrimitive* prism, VAMaterialType material);
The transform matrix should only contain rotation and translation operations, not scale. The size of the prism should be controlled via the size property
RectangularConePrimitive
A cone with a rectangle base.
interface RectangularConePrimitive
{
// Created via: va.RectangularConePrimitive_Create()
get height(); set height(v);
get length(); set length(v);
get material(); set material(v);
get transform(); set transform(v);
get width(); set width(v);
}
// Creates a new RectangularConePrimitive with default values
VARectangularConePrimitive* vaRectangularConePrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaRectangularConePrimitiveDestroy(VARectangularConePrimitive* rectCone);
// Width of the rectangular cone base
float vaRectangularConePrimitiveGetWidth(VARectangularConePrimitive* rectCone);
// Length of the rectangular cone base
float vaRectangularConePrimitiveGetLength(VARectangularConePrimitive* rectCone);
// Height of the rectangular cone
float vaRectangularConePrimitiveGetHeight(VARectangularConePrimitive* rectCone);
// World-space transform of the rectangular cone
const VAMatrix* vaRectangularConePrimitiveGetTransform(VARectangularConePrimitive* rectCone);
// Width of the rectangular cone base
void vaRectangularConePrimitiveSetWidth(VARectangularConePrimitive* rectCone, float width);
// Length of the rectangular cone base
void vaRectangularConePrimitiveSetLength(VARectangularConePrimitive* rectCone, float length);
// Height of the rectangular cone
void vaRectangularConePrimitiveSetHeight(VARectangularConePrimitive* rectCone, float height);
// Set the world-space transform. Must not contain scale components
void vaRectangularConePrimitiveSetTransform(VARectangularConePrimitive* rectCone, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaRectangularConePrimitiveGetMaterial(const VARectangularConePrimitive* rectCone);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaRectangularConePrimitiveSetMaterial(VARectangularConePrimitive* rectCone, VAMaterialType material);
The base of the cone is centered at (0, 0, 0), and the tip extends upwards to (0, height, 0).
The transform matrix should only contain rotation and translation operations, not scale. The size of the cone should be controlled via the width, length and height properties
SpherePrimitive
A sphere.
interface SpherePrimitive
{
// Created via: va.SpherePrimitive_Create()
get center(); set center(v);
get material(); set material(v);
get radius(); set radius(v);
}
// Creates a new SpherePrimitive with default values
VASpherePrimitive* vaSpherePrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaSpherePrimitiveDestroy(VASpherePrimitive* sphere);
// Center of the sphere
VAVector vaSpherePrimitiveGetCenter(VASpherePrimitive* sphere);
// Radius of the sphere
float vaSpherePrimitiveGetRadius(VASpherePrimitive* sphere);
// Center of the sphere
void vaSpherePrimitiveSetCenter(VASpherePrimitive* sphere, VAVector center);
// Radius of the sphere
void vaSpherePrimitiveSetRadius(VASpherePrimitive* sphere, float radius);
// Material assigned to this primitive
VAMaterialType vaSpherePrimitiveGetMaterial(const VASpherePrimitive* sphere);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaSpherePrimitiveSetMaterial(VASpherePrimitive* sphere, VAMaterialType material);
TrianglePrimitive
A triangle.
// Creates a new TrianglePrimitive with default values
VATrianglePrimitive* vaTrianglePrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaTrianglePrimitiveDestroy(VATrianglePrimitive* triangle);
// First vertex position
VAVector vaTrianglePrimitiveGetPosition0(VATrianglePrimitive* triangle);
// Second vertex position
VAVector vaTrianglePrimitiveGetPosition1(VATrianglePrimitive* triangle);
// Third vertex position
VAVector vaTrianglePrimitiveGetPosition2(VATrianglePrimitive* triangle);
// First vertex position
void vaTrianglePrimitiveSetPosition0(VATrianglePrimitive* triangle, VAVector position);
// Second vertex position
void vaTrianglePrimitiveSetPosition1(VATrianglePrimitive* triangle, VAVector position);
// Third vertex position
void vaTrianglePrimitiveSetPosition2(VATrianglePrimitive* triangle, VAVector position);
// Material assigned to this primitive
VAMaterialType vaTrianglePrimitiveGetMaterial(const VATrianglePrimitive* triangle);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaTrianglePrimitiveSetMaterial(VATrianglePrimitive* triangle, VAMaterialType material);
TriangularConePrimitive
A cone with a triangle base.
interface TriangularConePrimitive
{
// Created via: va.TriangularConePrimitive_Create()
get height(); set height(v);
get material(); set material(v);
get radius(); set radius(v);
get transform(); set transform(v);
}
// Creates a new TriangularConePrimitive with default values
VATriangularConePrimitive* vaTriangularConePrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaTriangularConePrimitiveDestroy(VATriangularConePrimitive* cone);
// Radius of the triangular cone base
float vaTriangularConePrimitiveGetRadius(VATriangularConePrimitive* cone);
// Height of the triangular cone
float vaTriangularConePrimitiveGetHeight(VATriangularConePrimitive* cone);
// World-space transform of the triangular cone
const VAMatrix* vaTriangularConePrimitiveGetTransform(VATriangularConePrimitive* cone);
// Radius of the triangular cone base
void vaTriangularConePrimitiveSetRadius(VATriangularConePrimitive* cone, float radius);
// Height of the triangular cone
void vaTriangularConePrimitiveSetHeight(VATriangularConePrimitive* cone, float height);
// Set the world-space transform. Must not contain scale components
void vaTriangularConePrimitiveSetTransform(VATriangularConePrimitive* cone, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaTriangularConePrimitiveGetMaterial(const VATriangularConePrimitive* cone);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaTriangularConePrimitiveSetMaterial(VATriangularConePrimitive* cone, VAMaterialType material);
The base of the cone is centered at (0, 0, 0), and the tip extends upwards to (0, height, 0).
The transform matrix should only contain rotation and translation operations, not scale. The size of the cone should be controlled via the radius and height properties
TriangularPrismPrimitive
A prism with a triangle base.
interface TriangularPrismPrimitive
{
// Created via: va.TriangularPrismPrimitive_Create()
get length(); set length(v);
get material(); set material(v);
get radius(); set radius(v);
get transform(); set transform(v);
}
// Creates a new TriangularPrismPrimitive with default values
VATriangularPrismPrimitive* vaTriangularPrismPrimitiveCreate(void);
// Free all resources owned by this primitive
VAResult vaTriangularPrismPrimitiveDestroy(VATriangularPrismPrimitive* prism);
// Radius of the triangular prism cross-section
float vaTriangularPrismPrimitiveGetRadius(VATriangularPrismPrimitive* prism);
// Length of the triangular prism along its axis
float vaTriangularPrismPrimitiveGetLength(VATriangularPrismPrimitive* prism);
// World-space transform of the triangular prism
const VAMatrix* vaTriangularPrismPrimitiveGetTransform(VATriangularPrismPrimitive* prism);
// Radius of the triangular prism cross-section
void vaTriangularPrismPrimitiveSetRadius(VATriangularPrismPrimitive* prism, float radius);
// Length of the triangular prism along its axis
void vaTriangularPrismPrimitiveSetLength(VATriangularPrismPrimitive* prism, float length);
// Set the world-space transform. Must not contain scale components
void vaTriangularPrismPrimitiveSetTransform(VATriangularPrismPrimitive* prism, const VAMatrix* transform);
// Material assigned to this primitive
VAMaterialType vaTriangularPrismPrimitiveGetMaterial(const VATriangularPrismPrimitive* prism);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaTriangularPrismPrimitiveSetMaterial(VATriangularPrismPrimitive* prism, VAMaterialType material);
The transform matrix should only contain rotation and translation operations, not scale. The size of the prism should be controlled via the radius and length properties
VoxelPrimitive
A 3D grid of voxels.
class VoxelPrimitive : Primitive
{
// Voxel data
readonly MaterialType[,,] data;
// Helper overload for accessing and editing voxel data
MaterialType this[int x, int y, int z];
// Scale of the voxels
float scale;
// Must only contain rotation and translation components, not scale
Matrix transform;
// Create a new voxel primitive with the specified grid size
VoxelPrimitive(int width, int height, int depth);
// Call this after editing voxel data
void SetDataDirty();
}
interface VoxelPrimitive
{
// Created via: va.VoxelPrimitive_Create()
get material(); set material(v);
get scale(); set scale(v);
get transform(); set transform(v);
get depth();
get height();
get width();
getData();
getVoxel(x, y, z);
setData(data);
setVoxel(x, y, z, value);
}
// Creates a new VoxelPrimitive with the specified grid size. All voxels default to VAMaterialAir
VAVoxelPrimitive* vaVoxelPrimitiveCreate(int width, int height, int depth);
// Free all resources owned by this primitive
VAResult vaVoxelPrimitiveDestroy(VAVoxelPrimitive* voxel);
// Scale of the voxels
float vaVoxelPrimitiveGetScale(VAVoxelPrimitive* voxel);
// Scale of the voxels
void vaVoxelPrimitiveSetScale(VAVoxelPrimitive* voxel, float scale);
// Must only contain rotation and translation components, not scale
const VAMatrix* vaVoxelPrimitiveGetTransform(const VAVoxelPrimitive* voxel);
// Must only contain rotation and translation components, not scale
void vaVoxelPrimitiveSetTransform(VAVoxelPrimitive* voxel, const VAMatrix* transform);
// Grid dimensions, set at creation time
void vaVoxelPrimitiveGetSize(const VAVoxelPrimitive* voxel, int* width, int* height, int* depth);
// Material of the voxel at the given grid cell
VAMaterialType vaVoxelPrimitiveGetVoxel(const VAVoxelPrimitive* voxel, int x, int y, int z);
// Set the material of the voxel at the given grid cell. Call vaVoxelPrimitiveSetDataDirty after editing
void vaVoxelPrimitiveSetVoxel(VAVoxelPrimitive* voxel, int x, int y, int z, VAMaterialType material);
// Call this after editing voxel data
void vaVoxelPrimitiveSetDataDirty(VAVoxelPrimitive* voxel);
// Material assigned to this primitive
VAMaterialType vaVoxelPrimitiveGetMaterial(const VAVoxelPrimitive* voxel);
// Material assigned to this primitive. Must not be VAMaterialAir
void vaVoxelPrimitiveSetMaterial(VAVoxelPrimitive* voxel, VAMaterialType material);
The transform matrix should only contain rotation and translation operations, not scale. The size of the cone should be controlled via the width, length and height properties
CustomEAXFormulas
Virtual class that can be overridden with custom formulas.
class CustomEAXFormulas
{
// Calculates the EAX density parameter based on the number of energy peaks in the early echogram
virtual float CalculateDensity();
// Calculates the EAX diffusion parameter based on how smoothly energy accumulates over time
virtual float CalculateDiffusion();
// Calculates the low and high frequency gain values relative to a reference energy level
virtual void CalculateFrequencyGains(float referenceEnergy, out float lfGain, out float hfGain);
// Calculates the delay in seconds before late reverb begins, based on the 25% energy accumulation point
virtual float CalculateLateReverbDelay();
// Calculates the gain for early reflections and late reverb by splitting the echogram at a transition point
virtual void CalculateReflectionsAndLateReverbGain(float earlyLateTransitionMs, float referenceEnergy, out float reflectionsGain, out float lateReverbGain);
// Calculates the delay in seconds before the first significant reflection is detected
virtual float CalculateReflectionsDelay(float energyThreshold);
// Calculates the RT60 reverberation time in seconds for a given echogram using linear regression on the decay slope
virtual float CalculateRT60(float[] echogram);
// Initialises the formula calculator with echogram data from a processed reverb result
virtual void Initialise(ProcessedReverb processed, float totalReturnedEnergyLF, float totalReturnedEnergyHF, float echogramGranularity, float[] echogramLF, float[] echogramHF, float[] echogramAverage);
}
struct VACustomEAXFormulas
{
// Virtual function pointers
CustomEAXFormulas_InitialiseFunc initialise;
CustomEAXFormulas_CalculateDiffusionFunc calculateDiffusion;
CustomEAXFormulas_CalculateDensityFunc calculateDensity;
CustomEAXFormulas_CalculateReflectionsDelayFunc calculateReflectionsDelay;
CustomEAXFormulas_CalculateLateReverbDelayFunc calculateLateReverbDelay;
CustomEAXFormulas_CalculateFrequencyGainsFunc calculateFrequencyGains;
CustomEAXFormulas_CalculateReflectionsAndLateReverbGainFunc calculateReflectionsAndLateReverbGain;
CustomEAXFormulas_CalculateRT60Func calculateRt60;
CustomEAXFormulas_TryCalculateDecayTimeFunc tryCalculateDecayTime;
// Data members
float* tempDiffusionCumulative;
int tempDiffusionCumulativeLength;
float* echogramDb;
int echogramDbLength;
float binDurationMs;
VAProcessedReverb* processed;
float totalReturnedEnergyLF;
float totalReturnedEnergyHF;
float totalReturnedEnergyAverage;
float totalReturnedEnergy;
float* echogramLF;
float* echogramHF;
float* echogramAverage;
int echogramLength;
float maxEnergy;
VAWorld* world;
};
VACustomEAXFormulas* vaCustomEAXFormulasCreate(void);
// Free all resources owned by these formulas. Returns VA_ERROR_IN_USE if the formulas are still assigned to a world
VAResult vaCustomEAXFormulasDestroy(VACustomEAXFormulas* formulas);
EAXConstants
EAX reverb constants from OpenAL.
class EAXConstants
{
const float AIR_ABSORPTION_GAIN_HF_MAX = 1.0f;
const float AIR_ABSORPTION_GAIN_HF_MIN = 0.892f;
const int DECAY_HF_LIMIT_MAX = 1;
const int DECAY_HF_LIMIT_MIN = 0;
const float DECAY_HF_RATIO_MAX = 2.0f;
const float DECAY_HF_RATIO_MIN = 0.1f;
const float DECAY_LF_RATIO_MAX = 2.0f;
const float DECAY_LF_RATIO_MIN = 0.1f;
const float DECAY_TIME_MAX = 20.0f;
const float DECAY_TIME_MIN = 0.1f;
const float DENSITY_MAX = 1.0f;
const float DENSITY_MIN = 0.0f;
const float DIFFUSION_MAX = 1.0f;
const float DIFFUSION_MIN = 0.0f;
const float ECHO_DEPTH_MAX = 1.0f;
const float ECHO_DEPTH_MIN = 0.0f;
const float ECHO_TIME_MAX = 0.25f;
const float ECHO_TIME_MIN = 0.075f;
const float GAIN_HF_MAX = 1.0f;
const float GAIN_HF_MIN = 0.0f;
const float GAIN_LF_MAX = 1.0f;
const float GAIN_LF_MIN = 0.0f;
const float GAIN_MAX = 1.0f;
const float GAIN_MIN = 0.0f;
const float HF_REFERENCE_MAX = 20000f;
const float HF_REFERENCE_MIN = 1000f;
const float LATE_REVERB_DELAY_MAX = 0.1f;
const float LATE_REVERB_DELAY_MIN = 0.0f;
const float LATE_REVERB_GAIN_MAX = 10.0f;
const float LATE_REVERB_GAIN_MIN = 0.0f;
const float LF_REFERENCE_MAX = 1000f;
const float LF_REFERENCE_MIN = 20f;
const float MODULATION_DEPTH_MAX = 1.0f;
const float MODULATION_DEPTH_MIN = 0.0f;
const float MODULATION_TIME_MAX = 4.0f;
const float MODULATION_TIME_MIN = 0.04f;
const float REFLECTIONS_DELAY_MAX = 0.3f;
const float REFLECTIONS_DELAY_MIN = 0.0f;
const float REFLECTIONS_GAIN_MAX = 3.16f;
const float REFLECTIONS_GAIN_MIN = 0.0f;
const float ROOM_ROLLOFF_FACTOR_MAX = 10.0f;
const float ROOM_ROLLOFF_FACTOR_MIN = 0.0f;
static Vector LATE_REVERB_PAN_MAX = new(1);
static Vector LATE_REVERB_PAN_MIN = new(-1);
static Vector REFLECTIONS_PAN_MAX = new(1);
static Vector REFLECTIONS_PAN_MIN = new(-1);
}
const EAXConstants = va.EAXConstants;
class EAXConstants
{
// All values are read-only
get AIR_ABSORPTION_GAIN_HF_MAX();
get AIR_ABSORPTION_GAIN_HF_MIN();
get DECAY_HF_LIMIT_MAX();
get DECAY_HF_LIMIT_MIN();
get DECAY_HF_RATIO_MAX();
get DECAY_HF_RATIO_MIN();
get DECAY_LF_RATIO_MAX();
get DECAY_LF_RATIO_MIN();
get DECAY_TIME_MAX();
get DECAY_TIME_MIN();
get DENSITY_MAX();
get DENSITY_MIN();
get DIFFUSION_MAX();
get DIFFUSION_MIN();
get ECHO_DEPTH_MAX();
get ECHO_DEPTH_MIN();
get ECHO_TIME_MAX();
get ECHO_TIME_MIN();
get GAIN_HF_MAX();
get GAIN_HF_MIN();
get GAIN_LF_MAX();
get GAIN_LF_MIN();
get GAIN_MAX();
get GAIN_MIN();
get HF_REFERENCE_MAX();
get HF_REFERENCE_MIN();
get LATE_REVERB_DELAY_MAX();
get LATE_REVERB_DELAY_MIN();
get LATE_REVERB_GAIN_MAX();
get LATE_REVERB_GAIN_MIN();
get LATE_REVERB_PAN_MAX();
get LATE_REVERB_PAN_MIN();
get LF_REFERENCE_MAX();
get LF_REFERENCE_MIN();
get MODULATION_DEPTH_MAX();
get MODULATION_DEPTH_MIN();
get MODULATION_TIME_MAX();
get MODULATION_TIME_MIN();
get REFLECTIONS_DELAY_MAX();
get REFLECTIONS_DELAY_MIN();
get REFLECTIONS_GAIN_MAX();
get REFLECTIONS_GAIN_MIN();
get REFLECTIONS_PAN_MAX();
get REFLECTIONS_PAN_MIN();
get ROOM_ROLLOFF_FACTOR_MAX();
get ROOM_ROLLOFF_FACTOR_MIN();
}
EAXReverb
Contains EAX properties and per-emitter gain and pan.
class EAXReverb
{
// Linear gain applied per meter of distance for high-frequency air absorption (0.892–1)
float AirAbsorptionGainHF;
// Whether to limit high-frequency decay time to the air absorption limit (0 or 1)
int DecayHFLimit;
// Ratio of high-frequency decay time to mid-frequency decay time (0.1–2)
float DecayHFRatio;
// Ratio of low-frequency decay time to mid-frequency decay time (0.1–2)
float DecayLFRatio;
// Reverberation decay time at mid frequencies, in seconds (0.1–20)
float DecayTime;
// Modal density of the late reverberation (0–1)
float Density;
// Echo diffusion of the late reverberation (0–1)
float Diffusion;
// Amplitude of the echo effect (0–1)
float EchoDepth;
// Cycling time of the echo effect, in seconds (0.075–0.25)
float EchoTime;
// Overall linear gain of the reverb (0–1)
float Gain;
// High-frequency gain of the reverb (0–1)
float GainHF;
// Low-frequency gain of the reverb (0–1)
float GainLF;
// Reference frequency for high-frequency decay ratio, in Hz (1000–20000)
float HFReference;
// Delay of late reverberation relative to early reflections, in seconds (0–0.1)
float LateReverbDelay;
// Linear gain of late reverberation (0–10)
float LateReverbGain;
// Reference frequency for low-frequency decay ratio, in Hz (20–1000)
float LFReference;
// Amplitude of the modulation effect (0–1)
float ModulationDepth;
// Cycling time of the modulation effect, in seconds (0.04–4)
float ModulationTime;
// Delay before early reflections are heard, in seconds (0–0.3)
float ReflectionsDelay;
// Linear gain of early reflections (0–3.16)
float ReflectionsGain;
// The direction that both early reflections and late reverb should be heard from, relative to each emitters with HasRelativeReverb set to true
Dictionary<Emitter, Vector> RelativeDirections;
// The gain for this reverb effect, relative to each emitter with HasRelativeReverb set to true
Dictionary<Emitter, float> RelativeGains;
// Rolloff factor for reflected sound sources (0–10)
float RoomRolloffFactor;
override bool Equals(object obj);
override int GetHashCode();
// Create an empty EAXReverb object
static EAXReverb CreateEmpty();
}
interface EAXReverb
{
get airAbsorptionGainHF();
get decayHFLimit();
get decayHFRatio();
get decayLFRatio();
get decayTime();
get density();
get diffusion();
get echoDepth();
get echoTime();
get gain();
get gainHF();
get gainLF();
get hfReference();
get lateReverbDelay();
get lateReverbGain();
get lfReference();
get modulationDepth();
get modulationTime();
get reflectionsDelay();
get reflectionsGain();
get roomRolloffFactor();
relativeDirections(emitter);
relativeGains(emitter);
}
typedef struct VAEAXReverb
{
float outsidePercent;
float returnedPercent;
float materialAbsorptionLF;
float materialAbsorptionHF;
float materialRoughness;
VAEmitter** relativeEmitters; /* Emitter keys for relativeDirections/relativeGains */
VAVector* relativeDirections; /* Direction that reverb should be heard from, relative to each emitter with hasRelativeReverb=true. Access via vaEAXReverbGetRelativeDirection */
float* relativeGains; /* Gain for this reverb effect, relative to each emitter with hasRelativeReverb=true. Access via vaEAXReverbGetRelativeGain */
int relativeCount;
int relativeCapacity;
float reflectionsDelay; /* Delay before early reflections are heard, in seconds (0–0.3) */
float density; /* Modal density of the late reverberation (0–1) */
float diffusion; /* Echo diffusion of the late reverberation (0–1) */
float gainLF; /* Low-frequency gain of the reverb (0–1) */
float gainHF; /* High-frequency gain of the reverb (0–1) */
float gain; /* Overall linear gain of the reverb (0–1) */
float decayTime; /* Reverberation decay time at mid frequencies, in seconds (0.1–20) */
float decayLFRatio; /* Ratio of low-frequency decay time to mid-frequency decay time (0.1–2) */
float decayHFRatio; /* Ratio of high-frequency decay time to mid-frequency decay time (0.1–2) */
float reflectionsGain; /* Linear gain of early reflections (0–3.16) */
float lateReverbGain; /* Linear gain of late reverberation (0–10) */
float lateReverbDelay; /* Delay of late reverberation relative to early reflections, in seconds (0–0.1) */
float echoTime; /* Cycling time of the echo effect, in seconds (0.075–0.25) */
float echoDepth; /* Amplitude of the echo effect (0–1) */
float modulationTime; /* Cycling time of the modulation effect, in seconds (0.04–4) */
float modulationDepth; /* Amplitude of the modulation effect (0–1) */
float airAbsorptionGainHF; /* Linear gain applied per meter of distance for high-frequency air absorption (0.892–1) */
float hfReference; /* Reference frequency for high-frequency decay ratio, in Hz (1000–20000) */
float lfReference; /* Reference frequency for low-frequency decay ratio, in Hz (20–1000) */
float roomRolloffFactor; /* Rolloff factor for reflected sound sources (0–10) */
int decayHFLimit; /* Whether to limit high-frequency decay time to the air absorption limit (0 or 1) */
int isTempBackground;
} VAEAXReverb;
// Get the direction that reverb should be heard from for the provided emitter. Returns NULL if no entry exists for this emitter (the emitter must have hasRelativeReverb=true)
VAVector* vaEAXReverbGetRelativeDirection(const VAEAXReverb* eax, const VAEmitter* emitter);
// Get the volume of this reverb preset, relative to the provided emitter. Returns NULL if no entry exists for this emitter (the emitter must have hasRelativeReverb=true)
float* vaEAXReverbGetRelativeGain(const VAEAXReverb* eax, const VAEmitter* emitter);
EAXUtils
Helper functions for comparing EAX objects.
static class EAXUtils
{
// Finds the candidate reverb preset most similar to the target
static EAXReverb FindBestMatch(EAXReverb target, List<EAXReverb> candidates, out int index);
// Computes how long a removed-but-tailing emitter should be kept alive and raytraced, using the slower of the low/high frequency decay ratios
static float GetEffectiveTailSeconds(EAXReverb eax, float maxVolume = 1.0f);
// Computes a similarity score between two reverb presets
static float GetSimilarity(EAXReverb a, EAXReverb b);
}
const EAXUtils = va.EAXUtils;
class EAXUtils
{
// Returns a similarity score between two EAX reverb result objects
static getSimilarity(a, b);
// Returns { result, index } — result is the best-matching candidate or null
static findBestMatch(target, candidates);
}
ProcessedReverb
Contains reverb data that has been processed into a more dev-friendly format.
class ProcessedReverb
{
// The average absorption of all surfaces that all rays hit.
float MaterialAbsorption;
// The average high-frequency absorption of all surfaces that all rays hit.
float MaterialAbsorptionHF;
// The average low-frequency absorption of all surfaces that all rays hit.
float MaterialAbsorptionLF;
// The average roughness/scattering of all surfaces that all rays hit.
float MaterialRoughness;
// Inverse of MaterialRoughness.
float MaterialSmoothness;
// The high-frequency reverberation decay time measured from the echogram.
// Calculated using RT20, RT30, or RT60 method depending on echogram length and room size.
// Used to calculate EAX DecayTime and DecayHFRatio.
float MeasuredDecayTimeHF;
// The low-frequency reverberation decay time measured from the echogram.
// Calculated using RT20, RT30, or RT60 method depending on echogram length and room size.
// Used to calculate EAX DecayTime and DecayLFRatio.
float MeasuredDecayTimeLF;
// The percentage of energy that escaped outside (hit the world edge).
// Calculated as raw.OutsideTotal / (ReverbRayCount * ReverbBounceCount)
float OutsidePercent;
// The percentage of energy that returned back to the emitter.
// Calculated as raw.ReturnedTotal / (ReverbRayCount * ReverbBounceCount)
float ReturnedPercent;
}
interface ProcessedReverb
{
get materialAbsorption();
get materialAbsorptionHF();
get materialAbsorptionLF();
get materialRoughness();
get materialSmoothness();
get measuredDecayTimeHF();
get measuredDecayTimeLF();
get outsidePercent();
get returnedPercent();
}
typedef struct VAProcessedReverb
{
// percentages
float returnedPercent; /* Percentage of energy that returned to the emitter. Calculated as raw.returned_total / (reverb_ray_count * reverb_bounce_count) */
float outsidePercent; /* Percentage of energy that escaped outside. Calculated as raw.outside_total / (reverb_ray_count * reverb_bounce_count) */
// decay times
float measuredDecayTimeLF; /* Low-frequency reverberation decay time from the echogram, in seconds. Calculated using RT20/RT30/RT60 method */
float measuredDecayTimeHF; /* High-frequency reverberation decay time from the echogram, in seconds. Calculated using RT20/RT30/RT60 method */
// scattering
float materialRoughness; /* Average roughness/scattering of all surfaces hit by rays */
// absorption
float materialAbsorptionLF; /* Average low-frequency absorption of all surfaces hit by rays */
float materialAbsorptionHF; /* Average high-frequency absorption of all surfaces hit by rays */
} VAProcessedReverb;
// Returns the average of material_absorption_lf and material_absorption_hf
float vaProcessedReverbGetMaterialAbsorption(const VAProcessedReverb* p);
RawReverb
Contains raw reverb data.
class RawReverb
{
// The total length of all rays that returned to the listener. Use this to calculate the average returning ray length, i.e (DistanceTotal / ReverbRayCount)
float DistanceTotal;
// The total number of times that a ray escaped outside (hit the world bounds). Will be in the range 0.0 to ReverbRayCount * ReverbBounceCount
float OutsideTotal;
// The total number of times that a ray returned to the listener. Will be in the range 0.0 to ReverbRayCount * ReverbBounceCount
float ReturnedTotal;
}
interface RawReverb {
// Total length of all rays that returned to the listener
get distanceTotal();
// Total number of times a ray escaped outside the world bounds (0 to reverbRayCount * reverbBounceCount)
get outsideTotal();
// Total number of times a ray returned to the listener (0 to reverbRayCount * reverbBounceCount)
get returnedTotal();
}
typedef struct VARawReverb
{
float distanceTotal; /* Total length of all rays that returned to the listener. Divide by reverb_ray_count for average distance */
float returnedTotal; /* Total energy that returned to the listener (range: 0 to reverb_ray_count * reverb_bounce_count) */
float outsideTotal; /* Total energy that escaped outside the world bounds (range: 0 to reverb_ray_count * reverb_bounce_count) */
} VARawReverb;
AirAbsorptionSettings
Controls how rays lose energy when travelling through air.
class AirAbsorptionSettings
{
// Custom air absorption formula for high frequencies. Takes distance (meters) and returns energy loss as a percentage
Func<float, float> CustomFormulaHF;
// Custom formula for low frequency sound. Takes distance (meters) and returns energy loss as a percentage
Func<float, float> CustomFormulaLF;
// Relative humidity as a percentage (0–1). Defaults to 0.1f
float Humidity;
// Atmospheric pressure in Pascals. Defaults to 101325
float Pressure;
// Air temperature in degrees Celsius. Defaults to 26
float Temperature;
// Create a new AirAbsorptionSettings with default settings
AirAbsorptionSettings();
}
interface AirAbsorptionSettings
{
// Created via: va.AirAbsorptionSettings_Create()
get humidity(); set humidity(v);
get pressure(); set pressure(v);
get temperature(); set temperature(v);
}
// Creates a new AirAbsorptionSettings with default values
VAAirAbsorptionSettings* vaAirAbsorptionCreate();
// Returns true if all settings values are within valid ranges
bool vaAirAbsorptionValidate(const VAAirAbsorptionSettings* settings);
// Setters — return VA_SUCCESS, VA_INVALID_VALUE if NaN/Infinity, or VA_OUT_OF_RANGE if out of range
// Relative humidity as a percentage (0–1)
VAResult vaAirAbsorptionSetHumidity(VAAirAbsorptionSettings* settings, float value);
// Air temperature in degrees Celsius (must be > -273.15)
VAResult vaAirAbsorptionSetTemperature(VAAirAbsorptionSettings* settings, float value);
// Atmospheric pressure in Pascals (must be > 0)
VAResult vaAirAbsorptionSetPressure(VAAirAbsorptionSettings* settings, float value);
// Custom formula for low frequency sound. NULL to use default
void vaAirAbsorptionSetCustomFormulaLF(VAAirAbsorptionSettings* settings, VAAirAbsorptionCustomFormula value);
// Custom formula for high frequency sound. NULL to use default
void vaAirAbsorptionSetCustomFormulaHF(VAAirAbsorptionSettings* settings, VAAirAbsorptionCustomFormula value);
// Getters
// Relative humidity as a percentage (0–1)
float vaAirAbsorptionGetHumidity(const VAAirAbsorptionSettings* settings);
// Air temperature in degrees Celsius
float vaAirAbsorptionGetTemperature(const VAAirAbsorptionSettings* settings);
// Atmospheric pressure in Pascals
float vaAirAbsorptionGetPressure(const VAAirAbsorptionSettings* settings);
// Custom LF formula. NULL means default is used
VAAirAbsorptionCustomFormula vaAirAbsorptionGetCustomFormulaLF(const VAAirAbsorptionSettings* settings);
// Custom HF formula. NULL means default is used
VAAirAbsorptionCustomFormula vaAirAbsorptionGetCustomFormulaHF(const VAAirAbsorptionSettings* settings);
// Free all resources owned by this settings object
void vaAirAbsorptionDestroy(VAAirAbsorptionSettings* settings);
Color
Determines the color of trails and primitives in the debug window. Used in the dev build only.
struct Color
{
// Alpha channel
byte A;
// Packed 32-bit ARGB value
uint ARGB;
// Blue channel
byte B;
// Green channel
byte G;
// Red channel
byte R;
// Packed 32-bit ABGR value
uint Value;
// Creates a color from byte components. Alpha defaults to 255
Color(byte red, byte green, byte blue, byte alpha = 255);
// Creates a color from another color with a alpha value
Color(Color other, float alpha);
// Creates a color from a packed ABGR value
Color(uint abgr);
override bool Equals(object obj);
override int GetHashCode();
override string ToString();
// Returns a copy of this color with the specified normalized alpha
Color WithAlpha(float alpha);
// Returns true if two colors differ
static bool operator !=(Color a, Color b);
// Returns true if two colors have identical values
static bool operator ==(Color a, Color b);
// Linearly interpolates between two colors
static Color Interpolate(Color A, Color B, double pos);
}
Not available in JS or C.
Emitter
A 3D position that casts rays, and can be discovered by other emitters.
Read more: Emitters.
class Emitter
{
// Controls whether this Emitter's EAX is blended to produced grouped EAX. Set this to false for listener emitters
bool AffectsGroupedEAX = true;
// This object contains the LF and HF gain for ambient sounds, to be applied to a low pass filter
LowPassFilter AmbientFilter;
// The custom formula for calculating the low- and high-frequency gains in for AmbientFilter. This is invoked for both low-frequency and high-frequency energy. Set to null to use the default formula.
// This formula is invoked on the main thread.
// Parameters: (bool lowFrequency, int ambientOcclusionRayCount, int ambientPermeationRayCount, int ambientPermeationBounceCount, float ambientOcclusionEnergy, float ambientPermeationEnergy)
// Returns: float
Func<bool, int, int, int, float, float, float> AmbientGainFormula;
// Number of bounces per ambient occlusion ray
int AmbientOcclusionBounceCount;
// True if both AmbientOcclusionRayCount and AmbientOcclusionBounceCount are greater than zero
bool AmbientOcclusionEnabled;
// The percentage of occlusion energy required for the emitter to be at full volume. Defaults to 15% of this emitter's AmbientOcclusionRayCount.
float AmbientOcclusionEnergyCap = 0.15f;
// Number of ambient occlusion rays cast
int AmbientOcclusionRayCount;
// Number of bounces per ambient permeation ray
int AmbientPermeationBounceCount;
// The color of ambientPermeation rays in the debug window (dev build only)
Color AmbientPermeationColor = Color.Yellow.WithAlpha(0.2f);
// True if both AmbientPermeationRayCount and AmbientPermeationBounceCount are greater than zero
bool AmbientPermeationEnabled;
// The percentage of permeation energy required for the emitter to be at full volume. Defaults to 15% of this emitter's AmbientPermeationRayCount * AmbientPermeationBounceCount.
float AmbientPermeationEnergyCap = 0.15f;
// Number of ambient permeation rays cast
int AmbientPermeationRayCount;
// Whether this emitter casts rays. False if all ray counts and/or bounce counts are set to 0.
bool CastsRays;
// Whether to clamp this emitter's position to the world bounds, to prevent it from going out of bounds
bool ClampPosition = true;
// EAX reverb properties for the listener. Contains parameters compatible with EAX reverb effects
EAXReverb EAX;
// The length (in milliseconds) of each entry in the echogram
int EchogramGranularity = 200;
// The custom formula for calculating this emitter's low- and high-frequency gains when it is the target of another emitter. This is invoked for both low-frequency and high-frequency energy. Set to null to use the default formula.
// This formula is invoked on background threads. Do not attempt to access data from the main thread within this function.
// Parameters: (bool lowFrequency, int occlusionRayCount, int permeationRayCount, int permeationBounceCount, float occlusionEnergy, float permeationEnergy)
// Returns: float
Func<bool, int, int, int, float, float, float> GainFormula;
// Read-only index indicating which GroupedEAX reverb effect should be used for this emitter
int GroupedEAXIndex;
// Whether this emitter is used as a reference point for calculating relative reverb gain and direction
bool HasRelativeReverb;
// True if this emitter hasn't cast its own rays yet.
bool Initialising = true;
// A custom logging callback. Defaults to WriteLine()
Action<string> LogCallback;
// A custom logging callback. Defaults to Error.WriteLine()
Action<string> LogErrorCallback;
// How long (in milliseconds) the echogram records data for. Returning reverb rays after this period will be ignored
int MaxEchogramTime = 5000;
// The loudest linear volume (0–1) this emitter's dry source will ever be played at by the consuming application. Used to estimate how long the emitter's reverb tail stays audible in GetEffectiveTailSeconds - a quieter source reaches an inaudible reverb tail sooner. Defaults to 1 (full volume)
float MaxVolume = 1.0f;
// The threshold below which permeation rays are cancelled to prevent unnecessary traversal. Clamped to minimum of 0
float MinimumPermeationEnergy = 0.01f;
// User-defined name of this emitter
string Name = string.Empty;
// Maximum number of bounces per occlusion ray
int OcclusionBounceCount;
// The color of occlusion rays in the debug window (dev build only)
Color OcclusionColor = Color.Green.WithAlpha(0.2f);
// True if both OcclusionRayCount and OcclusionBounceCount are greater than zero
bool OcclusionEnabled;
// The percentage of occlusion energy required for this emitter to be at full volume. Defaults to 15% of the other emitter's OcclusionRayCount.
float OcclusionEnergyCap = 0.15f;
// Number of occlusion rays cast
int OcclusionRayCount;
// Whether to only scatter rays around their yaw (no pitch or Y component)
bool OnlyScatterXZ;
// A callback that is invoked when another emitter raytraces this emitter for the first time. The first argument is the other emitter that raytraced this emitter.
Action<Emitter> OnRaytracedByAnotherEmitter;
// A callback that is invoked after this emitter casts its rays for the first time.
Action OnRaytracingComplete;
// A callback that is invoked when this emitter is actually removed from the World. If it casts reverb rays and affects grouped EAX, it won't be removed until its reverb tail finishes playing.
Action OnRemoved;
// The percentage of ambient occlusion rays that reached the edge of the world. Ranges from 0.0 to 1.0
float OutsidePercent;
// When defined, rays will be cast out of these positions rather than the default Position
Vector[] OverridePositions;
// When defined, rays will be cast in these directions rather than the default spherical ray distribution
Vector[] OverrideRayDirections;
// Whether this emitter has been marked for removal when its reverb tail has finished playing
bool PendingRemoval;
// Number of bounces per permeation ray
int PermeationBounceCount;
// The color of permeation rays in the debug window (dev build only)
Color PermeationColor = Color.Orange.WithAlpha(0.2f);
// True if both PermeationRayCount and PermeationBounceCount are greater than zero
bool PermeationEnabled;
// The percentage of permeation energy required for this emitter to be at full volume. Defaults to 15% of the other emitter's PermeationRayCount * PermeationBounceCount.
float PermeationEnergyCap = 0.15f;
// Number of permeation rays cast
int PermeationRayCount;
// The position of this Emitter. Can be a Vector3F or IPosition
IPosition Position = Vector.Zero;
// Contains reverb data that has been transformed into more usable parameters
ProcessedReverb ProcessedReverb;
// Whether to render each trail a different color (dev build only)
bool RandomTrailColor;
// A ray trail will be re-created if an old ray bounce position is too far away from the new ray bounce position. This setting controls the allowed distance between old and new ray bounce positions. Defaults to 1.0f. Clamped to minimum of 0.
float RefreshDistanceThreshold = 1.0f;
// The number of trails that are rebuilt from scratch each frame to prevent staleness when the listener moves. Clamped to minimum of 0.
int RefreshRayCount = 16;
// The lower bound of the relative reverb blend range. This affects the directional reverb that is heard by this emitter.
float RelativeReverbInnerThreshold = 0.6f;
// The upper bound of the relative reverb blend range. This affects the directional reverb that is heard by this emitter.
float RelativeReverbOuterThreshold = 0.8f;
// Number of bounces per reverb ray
int ReverbBounceCount;
// The color of reverb rays in the debug window (dev build only)
Color ReverbColor = Color.Cyan.WithAlpha(0.2f);
// True if both ReverbRayCount and ReverbBounceCount are greater than zero
bool ReverbEnabled;
// The percentage of returning energy required for reverb to be at maximum volume. Defaults to 20%.
float ReverbEnergyCap = 0.2f;
// Number of reverb rays cast
int ReverbRayCount;
// A seed used to randomise scattering vectors
int ScatteringSeed;
// Number of bounces per trail
int TrailBounceCount;
// The color of ray trails in the debug window (dev build only)
Color TrailColor = Color.White.WithAlpha(0.1f);
// Number of trails this emitter will create
int TrailCount;
// User-defined type for this emitter
int Type;
// Number of bounces per visualisation ray
int VisualisationBounceCount;
// Callback that is invoked with VisualisationData for each bounce produced by an emitter's visualisation rays. Do not modify the array or access it outside this callback.
Action<VisualisationData[]> VisualisationCallback;
// True if both VisualisationRayCount and VisualisationBounceCount are greater than zero
bool VisualisationEnabled;
// Number of visualisation rays cast
int VisualisationRayCount;
// How often to cast visualisation rays (milliseconds)
int VisualisationUpdateFrequency;
// Emitters outside the world bounds will not be raytraced. Set ClampPosition to true to keep this emitter inside the world bounds
bool WithinWorldBounds;
// Create a new Emitter with default settings
Emitter();
// Adds an emitter to this emitter's target list
void AddTarget(Emitter target);
// Get an object containing the LF and HF gain for a target emitter, to be applied to a low pass filter.
// Only access when HasRaytracedTarget(Emitter) is true.
LowPassFilter GetTargetFilter(Emitter target);
// Returns whether a target emitter has been raytraced. If true, it is safe to check the target's filter via GetTargetFilter
bool HasRaytracedTarget(Emitter target);
// Returns whether the target emitter is in this emitter's target list
bool HasTarget(Emitter target);
// Removes an emitter from this emitter's target list
void RemoveTarget(Emitter target);
// Call this function to invalidate the ray cache - all memory will be cleaned up and all rays will be re-cast
void ResetTrails();
// Validates this emitter's configuration
void Validate();
}
interface Emitter
{
// Created via: va.Emitter_Create()
get affectsGroupedEAX(); set affectsGroupedEAX(v);
get ambientOcclusionBounceCount(); set ambientOcclusionBounceCount(v);
get ambientOcclusionEnergyCap(); set ambientOcclusionEnergyCap(v);
get ambientOcclusionRayCount(); set ambientOcclusionRayCount(v);
get ambientPermeationBounceCount(); set ambientPermeationBounceCount(v);
get ambientPermeationEnergyCap(); set ambientPermeationEnergyCap(v);
get ambientPermeationRayCount(); set ambientPermeationRayCount(v);
get clampPosition(); set clampPosition(v);
get echogramGranularity(); set echogramGranularity(v);
get groupedEAXIndex(); set groupedEAXIndex(v);
get hasRelativeReverb(); set hasRelativeReverb(v);
get initialising(); set initialising(v);
get maxEchogramTime(); set maxEchogramTime(v);
get minimumPermeationEnergy(); set minimumPermeationEnergy(v);
get occlusionBounceCount(); set occlusionBounceCount(v);
get occlusionEnergyCap(); set occlusionEnergyCap(v);
get occlusionRayCount(); set occlusionRayCount(v);
get onlyScatterXZ(); set onlyScatterXZ(v);
get outsidePercent(); set outsidePercent(v);
get permeationBounceCount(); set permeationBounceCount(v);
get permeationEnergyCap(); set permeationEnergyCap(v);
get permeationRayCount(); set permeationRayCount(v);
get position(); set position(v);
get refreshDistanceThreshold(); set refreshDistanceThreshold(v);
get refreshRayCount(); set refreshRayCount(v);
get relativeReverbInnerThreshold(); set relativeReverbInnerThreshold(v);
get relativeReverbOuterThreshold(); set relativeReverbOuterThreshold(v);
get reverbBounceCount(); set reverbBounceCount(v);
get reverbEnergyCap(); set reverbEnergyCap(v);
get reverbRayCount(); set reverbRayCount(v);
get type(); set type(v);
get visualisationBounceCount(); set visualisationBounceCount(v);
get visualisationRayCount(); set visualisationRayCount(v);
get visualisationUpdateFrequency(); set visualisationUpdateFrequency(v);
get ambientFilter();
get ambientOcclusionEnabled();
get ambientPermeationEnabled();
get castsRays();
get eax();
get occlusionEnabled();
get permeationEnabled();
get processedReverb();
get reverbEnabled();
get trailBounceCount();
get trailCount();
get visualisationEnabled();
get withinWorldBounds();
set overridePositions(v);
set overrideRayDirections(v);
set visualisationCallback(v);
set onRaytracedByAnotherEmitter(fn);
set onRaytracingComplete(fn);
addTarget(target);
getTargetFilter(target);
getWasmTrails(prev?);
hasRaytracedTarget(target);
hasTarget(target);
removeTarget(target);
resetTrails();
setPosition(x, y, z);
validate();
}
// Create a new emitter with default settings
VAEmitter* vaEmitterCreate(void);
// Free all resources owned by this emitter. Returns VA_ERROR_IN_USE if the emitter is still added to a world
VAResult vaEmitterDestroy(VAEmitter* emitter);
// Returns true if target has been added to this emitter via vaEmitterAddTarget()
bool vaEmitterHasTarget(VAEmitter* emitter, VAEmitter* target);
// Returns true if raytracing has produced results for this target at least once
bool vaEmitterHasRaytracedTarget(VAEmitter* emitter, VAEmitter* target);
// Get the low pass filter to be applied to the target emitter. Only safe to call when vaEmitterHasRaytracedTarget() returns true for the provided target
VALowPassFilter* vaEmitterGetTargetFilter(VAEmitter* emitter, VAEmitter* target);
VAResult vaEmitterAddTarget(VAEmitter* emitter, VAEmitter* target);
// Remove a previously added target from this emitter
void vaEmitterRemoveTarget(VAEmitter* emitter, VAEmitter* target);
// Invalidate the ray cache. All rays will be re-cast the next time raytracing occurs.
void vaEmitterResetTrails(VAEmitter* emitter);
// Number of reverb rays cast
void vaEmitterSetReverbRayCount(VAEmitter* emitter, int value);
int vaEmitterGetReverbRayCount(const VAEmitter* emitter);
// Maximum number of bounces per reverb ray
void vaEmitterSetReverbBounceCount(VAEmitter* emitter, int value);
int vaEmitterGetReverbBounceCount(const VAEmitter* emitter);
// Number of occlusion rays cast
void vaEmitterSetOcclusionRayCount(VAEmitter* emitter, int value);
int vaEmitterGetOcclusionRayCount(const VAEmitter* emitter);
// Maximum number of bounces per occlusion ray
void vaEmitterSetOcclusionBounceCount(VAEmitter* emitter, int value);
int vaEmitterGetOcclusionBounceCount(const VAEmitter* emitter);
// Number of permeation rays cast
void vaEmitterSetPermeationRayCount(VAEmitter* emitter, int value);
int vaEmitterGetPermeationRayCount(const VAEmitter* emitter);
// Number of bounces per permeation ray
void vaEmitterSetPermeationBounceCount(VAEmitter* emitter, int value);
int vaEmitterGetPermeationBounceCount(const VAEmitter* emitter);
// Number of ambient occlusion rays cast
void vaEmitterSetAmbientOcclusionRayCount(VAEmitter* emitter, int value);
int vaEmitterGetAmbientOcclusionRayCount(const VAEmitter* emitter);
// Maximum number of bounces per ambient occlusion ray
void vaEmitterSetAmbientOcclusionBounceCount(VAEmitter* emitter, int value);
int vaEmitterGetAmbientOcclusionBounceCount(const VAEmitter* emitter);
// Number of ambient permeation rays cast
void vaEmitterSetAmbientPermeationRayCount(VAEmitter* emitter, int value);
int vaEmitterGetAmbientPermeationRayCount(const VAEmitter* emitter);
// Number of bounces per ambient permeation ray
void vaEmitterSetAmbientPermeationBounceCount(VAEmitter* emitter, int value);
int vaEmitterGetAmbientPermeationBounceCount(const VAEmitter* emitter);
// Number of visualisation rays cast
int vaEmitterGetVisualisationRayCount(const VAEmitter* emitter);
void vaEmitterSetVisualisationRayCount(VAEmitter* emitter, int value);
// Number of bounces per visualisation ray
int vaEmitterGetVisualisationBounceCount(const VAEmitter* emitter);
void vaEmitterSetVisualisationBounceCount(VAEmitter* emitter, int value);
// Optimisation field - permeation rays that drop below this threshold will be treated as having 0 energy
void vaEmitterSetMinimumPermeationEnergy(VAEmitter* emitter, float value);
float vaEmitterGetMinimumPermeationEnergy(const VAEmitter* emitter);
VAResult vaEmitterSetMaxVolume(VAEmitter* emitter, float value);
float vaEmitterGetMaxVolume(const VAEmitter* emitter);
VAResult vaEmitterSetOverridePositions(VAEmitter* emitter, VAVector* positions, int count);
VAResult vaEmitterSetOverrideRayDirections(VAEmitter* emitter, VAVector* directions, int count);
VAResult vaEmitterSetGainFormula(VAEmitter* emitter, VAEmitterGainFormula formula);
VAResult vaEmitterSetAmbientGainFormula(VAEmitter* emitter, VAEmitterGainFormula formula);
// True if reverb ray count and bounce count are both greater than zero
bool vaEmitterGetReverbEnabled(const VAEmitter* emitter);
// True if occlusion ray count and bounce count are both greater than zero
bool vaEmitterGetOcclusionEnabled(const VAEmitter* emitter);
// True if permeation ray count and bounce count are both greater than zero
bool vaEmitterGetPermeationEnabled(const VAEmitter* emitter);
// True if ambient occlusion ray count and bounce count are both greater than zero
bool vaEmitterGetAmbientOcclusionEnabled(const VAEmitter* emitter);
// True if ambient permeation ray count and bounce count are both greater than zero
bool vaEmitterGetAmbientPermeationEnabled(const VAEmitter* emitter);
// True if any of the above ray types (except visualisation) are enabled
bool vaEmitterGetCastsAnyRays(const VAEmitter* emitter);
// True if visualisation ray count and bounce count are both greater than zero
bool vaEmitterGetVisualisationEnabled(const VAEmitter* emitter);
// Emitters outside the world bounds will not be raytraced; set clampPosition to true to keep them inside the world
bool vaEmitterGetWithinWorldBounds(const VAEmitter* emitter);
// A callback that is invoked after this emitter casts its rays for the first time
void vaEmitterSetOnRaytracingCompleteCallback(VAEmitter* emitter, VAEmitterOnRaytracingComplete callback);
// A callback that is invoked when another emitter raytraces this emitter for the first time
void vaEmitterSetOnRaytracedByAnotherEmitterCallback(VAEmitter* emitter, VAEmitterOnRaytracedByAnotherEmitter callback);
// A callback that is invoked when this emitter is actually removed from the world. If it casts reverb rays and affects grouped EAX, it won't be removed until its reverb tail finishes playing
void vaEmitterSetOnRemovedCallback(VAEmitter* emitter, VAEmitterOnRemoved callback);
// Invoked with bounce data produced by visualisation rays; do not modify the array or access it outside the callback
void vaEmitterSetVisualisationCallback(VAEmitter* emitter, VAEmitterVisualisationCallback callback);
// Invoked for informational log messages from this emitter
void vaEmitterSetLogCallback(VAEmitter* emitter, VALogCallback callback);
// Invoked for error log messages from this emitter
void vaEmitterSetLogErrorCallback(VAEmitter* emitter, VALogCallback callback);
// World-space position of this emitter
void vaEmitterSetPosition(VAEmitter* emitter, VAVector position);
VAVector vaEmitterGetPosition(const VAEmitter* emitter);
// True if this emitter hasn't cast its own rays yet
bool vaEmitterGetInitialising(const VAEmitter* emitter);
// True once vaWorldRemoveEmitter() has been called on this emitter and it is waiting for its reverb tail to finish before actually being removed
bool vaEmitterGetPendingRemoval(const VAEmitter* emitter);
// Whether this emitter's EAX is blended into grouped EAX. Set to false for listener emitters
void vaEmitterSetAffectsGroupedEAX(VAEmitter* emitter, bool value);
bool vaEmitterGetAffectsGroupedEAX(const VAEmitter* emitter);
// Index into the world's grouped eax list (-1 = none)
int vaEmitterGetGroupedEAXIndex(const VAEmitter* emitter);
// Percentage of energy in ambient occlusion rays that reached the edge of the world (0.0 – 1.0)
float vaEmitterGetOutsidePercent(const VAEmitter* emitter);
// Whether this emitter is used as a reference point for calculating relative reverb gain and direction. Set to true for your main listener emitter
void vaEmitterSetHasRelativeReverb(VAEmitter* emitter, bool value);
bool vaEmitterGetHasRelativeReverb(const VAEmitter* emitter);
// The lower bound of the relative reverb blend range. This affects the directional reverb that is heard by this emitter
void vaEmitterSetRelativeReverbInnerThreshold(VAEmitter* emitter, float value);
float vaEmitterGetRelativeReverbInnerThreshold(const VAEmitter* emitter);
// The upper bound of the relative reverb blend range. This affects the directional reverb that is heard by this emitter
void vaEmitterSetRelativeReverbOuterThreshold(VAEmitter* emitter, float value);
float vaEmitterGetRelativeReverbOuterThreshold(const VAEmitter* emitter);
// Whether to clamp this emitter's position to the world bounds, to prevent it from going out of bounds
void vaEmitterSetClampPosition(VAEmitter* emitter, bool value);
bool vaEmitterGetClampPosition(const VAEmitter* emitter);
// Seed used to randomise scattering vectors
void vaEmitterSetScatteringSeed(VAEmitter* emitter, int value);
int vaEmitterGetScatteringSeed(const VAEmitter* emitter);
// Display name for debugging and logging. The emitter copies the string internally, so the caller does not need to keep the buffer alive after this call
void vaEmitterSetName(VAEmitter* emitter, const char* name);
// Returns the emitter's internal copy of the name. The pointer is valid until the next call to vaEmitterSetName or vaEmitterDestroy; do not free it
const char* vaEmitterGetName(const VAEmitter* emitter);
// User-defined integer tag for categorising emitters
void vaEmitterSetType(VAEmitter* emitter, int value);
int vaEmitterGetType(const VAEmitter* emitter);
// Interval in milliseconds between visualisation ray updates
int vaEmitterGetVisualisationUpdateFrequency(const VAEmitter* emitter);
void vaEmitterSetVisualisationUpdateFrequency(VAEmitter* emitter, int value);
// Number of trails this emitter will create
int vaEmitterGetTrailCount(const VAEmitter* emitter);
// Number of bounces per trail
int vaEmitterGetTrailBounceCount(const VAEmitter* emitter);
// How long (in milliseconds) the echogram records data for. Returning reverb rays after this period will be ignored
int vaEmitterGetMaxEchogramTime(const VAEmitter* emitter);
void vaEmitterSetMaxEchogramTime(VAEmitter* emitter, int value);
// The length (in milliseconds) of each entry in the echogram
int vaEmitterGetEchogramGranularity(const VAEmitter* emitter);
void vaEmitterSetEchogramGranularity(VAEmitter* emitter, int value);
// Number of trails rebuilt from scratch each frame to prevent staleness when the emitter moves
int vaEmitterGetRefreshRayCount(const VAEmitter* emitter);
void vaEmitterSetRefreshRayCount(VAEmitter* emitter, int value);
// A trail is re-created when an old bounce position is this far from the new position
float vaEmitterGetRefreshDistanceThreshold(const VAEmitter* emitter);
void vaEmitterSetRefreshDistanceThreshold(VAEmitter* emitter, float value);
VAResult vaEmitterSetReverbEnergyCap(VAEmitter* emitter, float value);
// Percentage of returning energy required for reverb to be at maximum volume
float vaEmitterGetReverbEnergyCap(const VAEmitter* emitter);
VAResult vaEmitterSetOcclusionEnergyCap(VAEmitter* emitter, float value);
// Percentage of occlusion energy required for this emitter to be at full volume. Defaults to 15% of the other emitter's occlusionRayCount
float vaEmitterGetOcclusionEnergyCap(const VAEmitter* emitter);
VAResult vaEmitterSetPermeationEnergyCap(VAEmitter* emitter, float value);
// Percentage of permeation energy required for this emitter to be at full volume. Defaults to 15% of the other emitter's permeationRayCount * permeationBounceCount
float vaEmitterGetPermeationEnergyCap(const VAEmitter* emitter);
// Percentage of ambient occlusion energy required for this emitter to be at full volume
float vaEmitterGetAmbientOcclusionEnergyCap(const VAEmitter* emitter);
VAResult vaEmitterSetAmbientOcclusionEnergyCap(VAEmitter* emitter, float value);
// Percentage of ambient permeation energy required for this emitter to be at full volume
float vaEmitterGetAmbientPermeationEnergyCap(const VAEmitter* emitter);
VAResult vaEmitterSetAmbientPermeationEnergyCap(VAEmitter* emitter, float value);
// Get this emitter's processed reverb results. This is NULL until raytracing has run at least once
VAProcessedReverb* vaEmitterGetProcessedReverb(const VAEmitter* emitter);
// Get this emitter's EAX reverb results. This is NULL until raytracing has run at least once
VAEAXReverb* vaEmitterGetEAX(const VAEmitter* emitter);
// Get the low pass filter to be applied to ambient sounds. This is NULL until raytracing has run at least once
VALowPassFilter* vaEmitterGetAmbientFilter(const VAEmitter* emitter);
FuncPosition
Interface for setting the position of an emitter.
Invoked when RaytracingContext.Update() is called.
class FuncPosition : IPosition
{
FuncPosition(Func<Vector> position);
Vector GetPosition();
}
Not available in JS or C.
IPosition
Interface for setting the position of an emitter.
Invoked when RaytracingContext.Update() is called.
Not available in JS or C.
LowPassFilter
Contains low-frequency and high-frequency gains.
class LowPassFilter
{
// High-frequency gain in the range 0.0 to 1.0
float GainHF;
// Low-frequency gain in the range 0.0 to 1.0
float GainLF;
}
interface LowPassFilter
{
// Created via: va.LowPassFilter_Create()
get gainHF(); set gainHF(v);
get gainLF(); set gainLF(v);
}
typedef struct VALowPassFilter
{
float gainLF;
float gainHF;
} VALowPassFilter;
MaterialProperties
Stores properties for a single material.
Read more: Materials.
class MaterialProperties
{
// Percentage of high-frequency energy that is lost on each bounce (0.0 to 1.0).
float AbsorptionHF;
// Percentage of low-frequency energy that is lost on each bounce (0.0 to 1.0).
float AbsorptionLF;
// Percentage of high-frequency energy lost when a ray passes through a flat PlanePrimitive, DiskPrimitive, TrianglePrimitive or non-watertight MeshPrimitive. Ranges from 0.0 to 1.0
float PlaneTransmissionHF;
// Percentage of low-frequency energy lost when a ray passes through a flat PlanePrimitive, DiskPrimitive, TrianglePrimitive or non-watertight MeshPrimitive. Ranges from 0.0 to 1.0
float PlaneTransmissionLF;
// Scattering strength (0.0 to 1.0), where 0.0 has no scattering and 1.0 skews the ray reflection direction by up to 90 degrees
float Scattering;
// How many meters a ray must travel through a primitive before it loses all high-frequency energy
float TransmissionHF;
// How many meters a ray must travel through a primitive before it loses all low-frequency energy
float TransmissionLF;
// Create a MaterialProperties with all values set to 0
MaterialProperties();
// Create a MaterialProperties with all values provided
MaterialProperties(float absorptionLF, float absorptionHF, float scattering, float transmissionLF, float transmissionHF, float planeTransmissionLF, float planeTransmissionHF);
// Create a MaterialProperties with the same values as another
MaterialProperties(MaterialProperties prop);
// Copy all properties from another MaterialProperties
void Update(MaterialProperties prop);
// Update all properties
void Update(float absorptionLF, float absorptionHF, float scattering, float transmissionLF, float transmissionHF, float planeTransmissionLF, float planeTransmissionHF);
// Returns the dB/m transmission value that reduces energy to 0.1% after travelling meters meters through a material
static float MetersToTransmission(float meters);
// Returns the distance in meters that reduces energy to 0.1% for the provided dB/m transmission value
static float TransmissionToMeters(float transmission);
}
interface MaterialProperties
{
// Created via: va.MaterialProperties_Create()
// Also: va.MaterialProperties_CreateWith(absorptionLF, absorptionHF, scattering, transmissionLF, transmissionHF, planeTransmissionLF, planeTransmissionHF)
get absorptionHF(); set absorptionHF(v);
get absorptionLF(); set absorptionLF(v);
get planeTransmissionHF(); set planeTransmissionHF(v);
get planeTransmissionLF(); set planeTransmissionLF(v);
get scattering(); set scattering(v);
get transmissionHF(); set transmissionHF(v);
get transmissionLF(); set transmissionLF(v);
}
MaterialType
Materials define how much energy is lost when rays hit and pass through primitives.
There are 23 default materials. You can customise them and create your own. Read more: Materials.
enum MaterialType
{
Air = 0,
Brick,
Cloth,
Concrete,
ConcretePolished,
Dirt,
Glass,
Grass,
Gravel,
Gyprock,
Ice,
Leaf,
Marble,
Metal,
Mud,
Rock,
Sand,
Snow,
Tile,
Tree,
Water,
WoodIndoor,
WoodOutdoor,
}
// Available via: import { MaterialType } from 'vaudio';
const MaterialType = {
Air: 0,
Brick: 1,
Cloth: 2,
Concrete: 3,
ConcretePolished: 4,
Dirt: 5,
Glass: 6,
Grass: 7,
Gravel: 8,
Gyprock: 9,
Ice: 10,
Leaf: 11,
Marble: 12,
Metal: 13,
Mud: 14,
Rock: 15,
Sand: 16,
Snow: 17,
Tile: 18,
Tree: 19,
Water: 20,
WoodIndoor: 21,
WoodOutdoor: 22,
};
typedef enum VAMaterialType {
VAMaterialAir = 0,
VAMaterialBrick,
VAMaterialCloth,
VAMaterialConcrete,
VAMaterialConcretePolished,
VAMaterialDirt,
VAMaterialGlass,
VAMaterialGrass,
VAMaterialGravel,
VAMaterialGyprock,
VAMaterialIce,
VAMaterialLeaf,
VAMaterialMarble,
VAMaterialMetal,
VAMaterialMud,
VAMaterialRock,
VAMaterialSand,
VAMaterialSnow,
VAMaterialTile,
VAMaterialTree,
VAMaterialWater,
VAMaterialWoodIndoor,
VAMaterialWoodOutdoor,
VAMaterialTypeCount,
} VAMaterialType;
Default Materials
For every material:
PlaneTransmissionLFdefaults to0.1PlaneTransmissionHFdefaults to0.25
The transmission columns contain two values:
- the left value is the raw transmission number, in decibels per meter
- the right value shows how many meters a ray must travel through the material before it loses all of its energy
| Material | Absorption LF | Absorption HF | Scattering | Transmission LF | Transmission HF | Source |
|---|---|---|---|---|---|---|
| Brick | 0.31 | 0.52 | 0.4 | 60 m | 120 m | Link |
| Cloth | 0.72 | 0.91 | 0.7 | 10 m | 60 m | Link |
| Concrete | 0.31 | 0.55 | 0.4 | 50 m | 100 m | Link |
| Concrete Polished | 0.06 | 0.12 | 0.1 | 50 m | 100 m | Link |
| Dirt | 0.48 | 0.88 | 0.5 | 20 m | 75 m | Link |
| Glass | 0.08 | 0.12 | 0.05 | 150 m | 600 m | |
| Grass | 0.71 | 0.93 | 0.8 | 7 m | 35 m | Link |
| Gravel | 0.06 | 0.12 | 0.7 | 30 m | 100 m | Link |
| Gyprock | 0.04 | 0.08 | 0.05 | 75 m | 200 m | Link |
| Ice | 0.01 | 0.03 | 0.01 | 200 m | 375 m | |
| Leaf | 0.71 | 0.91 | 0.8 | 6 m | 30 m | Link |
| Marble | 0.06 | 0.12 | 0.03 | 60 m | 150 m | Link |
| Metal | 0.05 | 0.02 | 0.01 | 300 m | 600 m | Link |
| Mud | 0.56 | 0.82 | 0.5 | 25 m | 85 m | Link |
| Rock | 0.31 | 0.51 | 0.4 | 50 m | 100 m | Link |
| Sand | 0.65 | 0.92 | 0.7 | 15 m | 60 m | Link |
| Snow | 0.65 | 0.92 | 0.7 | 10 m | 50 m | Link |
| Tile | 0.06 | 0.12 | 0.03 | 60 m | 150 m | Link |
| Tree | 0.22 | 0.46 | 0.3 | 20 m | 75 m | Link |
| Water | 0.06 | 0.12 | 0.1 | 60 m | 150 m | |
| Wood Indoor | 0.12 | 0.3 | 0.1 | 75 m | 200 m | Link |
| Wood Outdoor | 0.16 | 0.37 | 0.2 | 60 m | 150 m | Link |
Matrix
A 4x4 matrix.
struct Matrix
{
// Returns true if this matrix only contains translation components
bool IsTranslationOnly;
float M11;
float M12;
float M13;
float M14;
float M21;
float M22;
float M23;
float M24;
float M31;
float M32;
float M33;
float M34;
float M41;
float M42;
float M43;
float M44;
override bool Equals(object obj);
override int GetHashCode();
// Returns the inverse of this matrix
Matrix Inverse();
// Returns true if all elements are exactly equal to those in other
bool IsEqual(Matrix other);
// Converts this matrix to a Matrix4x4
System.Numerics.Matrix4x4 ToNumerics();
// Returns true if a does not equal b
static bool operator !=(Matrix a, Matrix b);
// The identity matrix
static Matrix Identity = new(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
// Returns true if a equals b
static bool operator ==(Matrix a, Matrix b);
// Create a pure rotation/reflection matrix that reorients the whole rendered scene so that geometry imported verbatim from a CoordinateSystem-authored source (whose raw axis components land straight into this SDK's internal Y+-up/Z--forward fields with no conversion at import time) displays upright, for insertion into the shared view/projection chain (<c>cache_mvp</c>) so every draw call (primitives, grid, bounding boxes) rotates identically. Coefficients mirror ToInternal(CoordinateSystem, Vector) (not <c>ToConsumer</c> — that maps the opposite direction and rotates the scene around the wrong axis, e.g. spinning a Z-up import horizontally instead of standing it upright). Row convention: row i is where internal basis axis i is displayed (row-vector, v' = v * M).
static Matrix CreateCoordinateSystemRotation(CoordinateSystem system);
// Create a rotation matrix from a Quaternion
static Matrix CreateFromQuaternion(System.Numerics.Quaternion q);
// Create a transposed rotation matrix from a Quaternion
static Matrix CreateFromQuaternionTransposed(System.Numerics.Quaternion q);
// Create an orthographic projection matrix
static Matrix CreateOrthoProjection(float left, float top, float right, float bottom, float near, float far);
// Create a rotation matrix around the X axis
static Matrix CreateRotationX(float rads);
// Create a rotation matrix around the Y axis
static Matrix CreateRotationY(float rads);
// Create a rotation matrix around the Z axis
static Matrix CreateRotationZ(float rads);
// Create a scale matrix. Note scale matrices can only be set on MeshPrimitive and not any other primitive
static Matrix CreateScale(float scaleX, float scaleY, float scaleZ);
// Create a scale matrix. Note scale matrices can only be set on MeshPrimitive and not any other primitive
static Matrix CreateScale(float scaleFactor);
// Create a scale matrix. Note scale matrices can only be set on MeshPrimitive and not any other primitive
static Matrix CreateScale(Vector v);
// Create a translation matrix
static Matrix CreateTranslation(System.Numerics.Vector3 vec);
// Create a translation matrix
static Matrix CreateTranslation(Vector vec);
// Create a translation matrix
static Matrix CreateTranslation(float x, float y, float z);
// Multiply two matrices together
static Matrix operator *(Matrix a, Matrix b);
}
const Matrix = va.Matrix;
class Matrix
{
// Static properties
static Identity; // { m11, m12, ..., m44 }
// Static factory methods
static CreateTranslation(x, y, z);
static CreateRotationX(rads);
static CreateRotationY(rads);
static CreateRotationZ(rads);
static CreateScale(x, y?, z?);
static CreateFromQuaternion(x, y, z, w);
static Multiply(a, b);
}
typedef struct VAMatrix
{
float m11;
float m12;
float m13;
float m14;
float m21;
float m22;
float m23;
float m24;
float m31;
float m32;
float m33;
float m34;
float m41;
float m42;
float m43;
float m44;
} VAMatrix;
// Initializes a matrix with all 16 elements in column-major order (each group of 4 is one column)
VAMatrix vaMatrixCreate(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44);
// Returns the inverse of the matrix. Asserts if the determinant is zero (no inverse exists)
VAMatrix vaMatrixInverse(const VAMatrix* mat);
// Creates a translation matrix
VAMatrix vaMatrixCreateTranslation(float x, float y, float z);
// Creates a rotation matrix around the X axis. rads is the angle in radians
VAMatrix vaMatrixCreateRotationX(float rads);
// Creates a rotation matrix around the Y axis. rads is the angle in radians
VAMatrix vaMatrixCreateRotationY(float rads);
// Creates a rotation matrix around the Z axis. rads is the angle in radians
VAMatrix vaMatrixCreateRotationZ(float rads);
// Creates a scale matrix. Note scale matrices can only be set on mesh primitives and not any other primitive
VAMatrix vaMatrixCreateScale(float scaleX, float scaleY, float scaleZ);
// Multiplies two matrices together
VAMatrix vaMatrixMultiply(const VAMatrix* a, const VAMatrix* b);
// Returns true if all elements of a are exactly equal to those in b
bool vaMatrixIsEqual(const VAMatrix* a, const VAMatrix* b);
// Returns true if the matrix only contains translation components
bool vaMatrixIsTranslationOnly(const VAMatrix* a);
ThreadStatistics
Helper class for retrieving and caching the number of threads available.
static class ThreadStatistics
{
// The number of threads available for background tasks, excluding the main thread
static int BackgroundThreadCount;
// The minimum number of threads in the thread pool
static int ThreadCount;
}
Not available in JS or C.
VAResult
Integer return code indicating the result of a C API call.
typedef int VAResult;
#define VA_SUCCESS 0
#define VA_INVALID_VALUE 1
#define VA_OUT_OF_RANGE 2
#define VA_ALREADY_EXISTS 3
#define VA_FEATURE_DISABLED 4
#define VA_ERROR_IN_USE 5
#define VA_INVALID_COUNT 6
#define VA_WORLD_CONFLICT 7
#define VA_ERROR_FILE_OPEN 8
#define VA_ERROR_FILE_WRITE 9
#define VA_ERROR_FILE_VERSION 10
#define VA_ERROR_FILE_CORRUPT 11
#define VA_INVALID_MATERIAL 12
#define VA_MATERIAL_DOES_NOT_EXIST 13
#define VA_NOT_ADDED_TO_WORLD 14
Example:
#include <math.h>
// Returns VA_SUCCESS
VAResult result = vaWorldSetMaterialAbsorptionLF(world, VAMaterialConcrete, 0.5f);
// Returns VA_INVALID_VALUE
VAResult result = vaWorldSetMaterialAbsorptionLF(world, VAMaterialConcrete, NAN);
// Returns VA_OUT_OF_RANGE
VAResult result = vaWorldSetMaterialAbsorptionLF(world, VAMaterialConcrete, -13.5f);
Not available in C# or JS.
Vector
A 3D vector.
struct Vector : IPosition
{
// Get or set a component by index (0=X, 1=Y, 2=Z)
float this[int index];
// The length of this vector
float Magnitude;
// Returns a normalized copy of this vector
Vector Normalized;
// The X component
float X;
// The Y component
float Y;
// The Z component
float Z;
// Creates a vector with all components set to v
Vector(float v);
// Creates a vector with the specified components
Vector(float x, float y, float z);
// Creates a vector from a Vector3
Vector(System.Numerics.Vector3 v);
override bool Equals(object obj);
override int GetHashCode();
// IPosition implementation, returns itself
Vector GetPosition();
// Returns true if any component of this vector is greater than the corresponding component in the other vector
bool GreaterAny(Vector v);
// Returns true if any component of this vector is greater than or equal to the corresponding component in the other vector
bool GreaterEqualAny(Vector v);
// Returns true if all components of this vector equal those of other
bool IsEqual(Vector other);
// Returns true if any component of this vector is less than the corresponding component in the other vector
bool LessAny(Vector v);
// Returns true if any component of this vector is less than or equal to the corresponding component in the other vector
bool LessEqualAny(Vector v);
// Normalizes this vector in place
void Normalize();
// Converts to Vector3
System.Numerics.Vector3 ToNumerics();
override string ToString();
// Returns true if a does not equal b
static bool operator !=(Vector a, Vector b);
// Returns true if all components of a are greater than or equal to those of b
static bool operator >=(Vector a, Vector b);
// A vector with all components set to MaxValue
static Vector MAX = new(float.MaxValue);
// A vector with all components set to MinValue
static Vector MIN = new(float.MinValue);
// A vector with all components set to one
static Vector One = new(1);
// Returns true if a equals b
static bool operator ==(Vector a, Vector b);
// Returns true if all components of a are less than or equal to those of b
static bool operator <=(Vector a, Vector b);
// A unit vector pointing upward (0, 1, 0)
static Vector Up = new(0, 1, 0);
// A vector with all components set to zero
static Vector Zero = new(0);
// Returns true if all components of a are greater than those of b
static bool operator >(Vector a, Vector b);
// Returns the cross product of a and b
static Vector Cross(Vector a, Vector b);
// Returns the dot product of a and b
static float Dot(Vector a, Vector b);
// Creates a direction vector from pitch and yaw angles in radians
static Vector FromPitchYaw(float pitch, float yaw);
// Returns the component-wise maximum of a and b
static Vector GetMax(Vector a, Vector b);
// Returns the component-wise minimum of a and b
static Vector GetMin(Vector a, Vector b);
// Interpolates from one vector to another
static Vector Lerp(Vector a, Vector b, float c);
// Returns true if all components of a are less than those of b
static bool operator <(Vector a, Vector b);
// Reflects direction about the normal
static Vector Reflect(Vector direction, Vector normal);
// Transforms a normal vector by a matrix, ignoring translation
static Vector TransformNormal(Vector a, Matrix b);
// Transforms a normal vector by the transpose of the given inverse matrix (i.e. the inverse-transpose of the forward transform), supporting non-uniform scale
static Vector TransformNormalByInverseTranspose(Vector a, Matrix inverseMatrix);
// Scales a by scalar b
static Vector operator *(float b, Vector a);
// Scales a by scalar b
static Vector operator *(Vector a, float b);
// Transforms a vector by a matrix, including translation
static Vector operator *(Vector a, Matrix b);
// Returns the component-wise product of a and b
static Vector operator *(Vector a, Vector b);
// Returns the component-wise sum of a and b
static Vector operator +(Vector a, Vector b);
// Returns the negation of a
static Vector operator -(Vector a);
// Returns the component-wise difference of a and b
static Vector operator -(Vector a, Vector b);
// Divides scalar b by each component of a
static Vector operator /(float b, Vector a);
// Divides a by scalar b
static Vector operator /(Vector a, float b);
// Returns the component-wise quotient of a and b
static Vector operator /(Vector a, Vector b);
}
typedef struct VAVector
{
float x;
float y;
float z;
} VAVector;
// Creates a vector with the specified components
VAVector vaVectorCreate(float x, float y, float z);
// Creates a vector with all components set to value
VAVector vaVectorCreateUniform(float value);
// Returns the length of v
float vaVectorMagnitude(VAVector v);
// Returns the component-wise sum of a and b
VAVector vaVectorAdd(VAVector a, VAVector b);
// Returns the component-wise difference of a and b
VAVector vaVectorSubtract(VAVector a, VAVector b);
// Multiplies the vector by a scalar value
VAVector vaVectorMultiplyScalar(VAVector a, float scalar);
// Divides the vector by a scalar value
VAVector vaVectorDivideScalar(VAVector a, float scalar);
// Returns true if all components of a are greater than or equal to those of b
bool vaVectorGreaterEqual(VAVector a, VAVector b);
// Returns true if all components of a are less than or equal to those of b
bool vaVectorLessEqual(VAVector a, VAVector b);
// Returns true if any component of a is less than the corresponding component of b
bool vaVectorLessAny(VAVector a, VAVector b);
// Returns true if any component of a is greater than the corresponding component of b
bool vaVectorGreaterAny(VAVector a, VAVector b);
// Returns true if all components of a equal those of b
bool vaVectorEqual(VAVector a, VAVector b);
// Returns the component-wise minimum of a and b
VAVector vaVectorMin(VAVector a, VAVector b);
// Returns the component-wise maximum of a and b
VAVector vaVectorMax(VAVector a, VAVector b);
// Get a component via an index (0 = x, 1 = y, 2 = z)
float vaVectorGetComponent(VAVector v, int index);
// Returns the dot product of a and b
float vaVectorDot(VAVector a, VAVector b);
// Returns the cross product of a and b
VAVector vaVectorCross(VAVector a, VAVector b);
// Returns the negation of a
VAVector vaVectorNegate(VAVector a);
// Returns a normalized copy of a
VAVector vaVectorNormalize(VAVector a);
// Creates a directional vector from pitch and yaw angles (in radians)
VAVector vaVectorFromPitchYaw(float pitch, float yaw);
// Transforms a vector by a matrix, including translation (w = 1)
VAVector vaVectorMultiplyMatrix(VAVector a, const VAMatrix* b);
// Transforms a direction vector by a matrix, ignoring translation, and normalizes the result
VAVector vaVectorTransformNormal(VAVector a, const VAMatrix* b);
// Transforms a normal by the transpose of the given inverse matrix (inverse-transpose of the forward transform), supporting non-uniform scale
VAVector vaVectorTransformNormalByInverseTranspose(VAVector a, const VAMatrix* inverseMatrix);
// Returns true if any component of vec is NaN or infinity
bool vaVectorIsNanOrInfinity(VAVector vec);
// Reflects a direction vector about a normalised vector
VAVector vaVectorReflect(VAVector direction, VAVector normal);
VisualisationData
Struct that contains position and normal data.
struct VisualisationData
{
// The world-space normal of the ray bounce. Guaranteed to be normalised
Vector normal;
// The world-space position of the ray bounce
Vector position;
}
interface VisualisationData
{
// Created via: va.VisualisationData_Create()
get normal(); set normal(v);
get position(); set position(v);
}
World
Contains emitters, primitives, materials and many customisable settings. Manages its own raytracing and multithreading.
class World
{
// Air absorption settings. Set to null to disable air absorption.
AirAbsorptionSettings AirAbsorption;
// The average time (in milliseconds) spent in the analysing thread after the raytracing threads complete. This phase runs after raytracing completes and calculate reverb properties.
// Use this to monitor raytracing performance and adjust ray counts if needed.
double AnalysisTime;
// True while background raytracing threads are still running.
// After setting PendingShutdown, continue calling Update until this is false, then call Dispose.
bool AreThreadsRunning;
// The pitch of the camera in the debug window (dev build only)
float CameraPitch;
// The position of the camera in the debug window (dev build only)
Vector CameraPosition;
// The speed of the camera in the debug window (dev build only)
float CameraSpeed;
// The yaw of the camera in the debug window (dev build only)
float CameraYaw;
// Coordinate system used in the debug window and when calculating listener-relative reverb directionality
CoordinateSystem CoordinateSystem;
// Custom formulas for calculating EAX properties (diffusion, density, etc). Set to null to use default formulas.
CustomEAXFormulas CustomEAXFormulas;
// Whether Emitters outside the world have 0 occlusion/permeation energy (true) or maximum energy (false).
bool EmittersOutsideTheWorldAreMuffled;
// The epsilon value used for raytracing and primitive intersections. Defaults to 0.01f
float Epsilon;
// The field of view (in radians) of the camera in the debug window (dev build only)
float FieldOfView;
// List of grouped EAX reverb properties for all emitters. Contains parameters compatible with EAX reverb effects.
List<EAXReverb> GroupedEAX = [];
// True until raytracing has run at least once
bool Initialising;
// Inverse speed of sound in seconds per meter. Defaults to 1.0f / 343.0f. Affects reverb calculation
float InverseSpeedOfSound;
// The average time (in milliseconds) between when Update is invoked, and when OnReverbUpdated is invoked.
double Latency;
// A custom logging callback. Defaults to WriteLine()
Action<string> LogCallback;
// Whether to log memory allocation warnings
bool LogMemoryAllocationWarnings = true;
// The average time (in milliseconds) spent by Update on the main thread. This includes time spent handling previous raytracing results, applying new settings,
// processing primitive updates and submitting work to background threads.
// Use this metric to monitor main thread performance impact.
double MainThreadTime;
// Whether the debug window can independently move its camera around (dev build only)
bool ManualCamera;
// The maximum bounds of the world (Position + Size)
Vector MaxBounds;
// The maximum amount of threads that can run in parallel for this world
int MaximumConcurrencyLevel;
// The maximum number of grouped EAX reverb properties shared across all emitters that affect grouped EAX. Higher values increase accuracy but are more expensive to run.
int MaximumGroupedEAXCount = 3;
// Get meters per world unit. Affects air absorption and reverb calculation.
float MetersPerUnit = 1;
// Callback that is invoked when keys are pressed (dev build only)
Action<KeyEvent> OnKeyEvent;
// This callback is invoked right after the background threads finish running. At this point, the results of the background threads have not been processed yet (EAX is not updated yet, low pass filters not updated, ambience not updated, etc). After this, OnReverbUpdated will be invoked next.
Action OnRaytracingCompleted = null;
// This callback is invoked after all raytracing results (EAX, filters, etc) have been processed. After this, new emitter/primitive settings will be applied, and the background threads will start again.
Action OnRaytracingResultsHandled = null;
// This callback is invoked after EAX reverb results are updated. This gives you a chance to update your EAX effects, so they can be applied to an emitter in it's OnRaytracingComplete callback. After this, each emitter's callback are invoked, and then OnRaytracingResultsHandled will be invoked next.
Action OnReverbUpdated = null;
// When set to true, Update will stop submitting work to background threads.
// When ThreadsRunning becomes false, it is safe to call Dispose.
bool PendingShutdown = false;
// The minimum bounds of the world.
// Emitters outside the world will not be raytraced, and Primitives that are fully outside these bounds will not affect raytracing.
Vector Position;
// The average time (in milliseconds) spent in the preparation thread before the raytracing threads begin. This phase updates the BVH (Bounding Volume Hierarchy) acceleration structure with new, modified,
// and removed primitives. Higher values are a result of complex scene changes.
double PreparationTime;
// The number of rays cast this frame.
int RaysCastThisFrame = 0;
// The average time (in milliseconds) spent in the raytracing threads. This is the real-world elapsed time it takes for raytracing to complete, not the sum of all thread times.
// This value is automatically adjusted based on how many threads perform raytracing.
// Use this to monitor raytracing performance and adjust ray counts if needed.
double RaytracingTime;
// High-frequency reference (Hz) for air absorption, reverb, and material scattering
float ReferenceFrequencyHF = 4000;
// Low-frequency reference (Hz) for air absorption, reverb, and material scattering
float ReferenceFrequencyLF = 300;
// Whether to render the raytracing scene in a separate window (dev build only)
bool RenderingEnabled;
// The time spent (in milliseconds) rendering the debug window (dev build only)
double RenderTime;
// Whether to render primitives in the debug window (dev build only)
bool ShouldRenderPrimitives;
// Whether to render rays in the debug window (dev build only)
bool ShouldRenderRays;
// The size of the world.
// Emitters outside the world will not be raytraced, and Primitives that are fully outside these bounds will be ignored
Vector Size;
// The position of the debug window (dev build only)
Vector2D WindowPosition;
// The size of the debug window (dev build only)
Vector2D WindowSize;
// The number of work items to split trails across for load balancing. A higher value helps evenly distribute work across all threads.
int WorkItemCount = 128;
// Whether the entire world is considered indoors or outdoors. When false, reverb rays stop accumulating energy after hitting the world edge. Defaults to false.
bool WorldIsIndoors;
// Create a new world
World();
// Add an Emitter to the world.
// This method is thread-safe and will not affect the current raytracing threads.
Emitter AddEmitter(Emitter emitter);
// Add a new material to this world
void AddMaterial(MaterialType type, MaterialProperties properties, Color color);
// Adds a 3D object to the raytracing scene.
// This method is thread-safe and will not affect the current raytracing threads.
// Primitives completely outside the world bounds will be ignored during raytracing.
void AddPrimitive(Primitive primitive);
// Waits for background thread to complete, then disposes everything.
// After calling this method, this world cannot be reused.
void Dispose();
// Get properties for a specific material.
MaterialProperties GetMaterial(MaterialType type);
// Get the debug rendering color for a specific material type (dev build only)
Color GetMaterialColor(MaterialType type);
// Returns true if a material exists
bool HasMaterial(MaterialType type);
// Returns true if a key is pressed in the debug window (dev build only)
bool IsKeyPressed(Silk.NET.Input.Key keyCode);
// Remove an Emitter from the world.
// This method is thread-safe and will not affect the current raytracing threads.
// This emitter's OnRaytracingComplete callback will not be invoked.
void RemoveEmitter(Emitter emitter);
// Removes a 3D object from the raytracing scene.
// This method is thread-safe and will not affect the current raytracing threads.
void RemovePrimitive(Primitive primitive);
// Set the debug rendering color for a specific material type (dev build only)
void SetMaterialColor(MaterialType type, Color color);
// Updates the raytracing simulation. Call this method regularly to process raytracing results and submit new work.
//
// This method does nothing if background raytracing threads are still running. When threads are idle, it performs the following operations:
// - Handles the last raytracing results, updating reverb objects and invoking OnRaytracedByAnotherEmitter callbacks
// - Applies new settings and resizes memory buffers if needed (e.g. if ray counts were changed)
// - Processes new, modified, and removed primitives
// - Starts raytracing again on background threads
//
// This method must be called from the main thread. Calling this more frequently is safe and can reduce latency for emitter updates.
void Update();
// Blocks the calling thread until all background raytracing threads complete, then handles the results (updates reverb objects and invokes OnRaytracingComplete and OnRaytracedByAnotherEmitter callbacks for each emitter).
void Wait();
}
interface World
{
// Created via: va.World_Create()
get airAbsorption(); set airAbsorption(v);
get emittersOutsideTheWorldAreMuffled(); set emittersOutsideTheWorldAreMuffled(v);
get epsilon(); set epsilon(v);
get exposeTrails(); set exposeTrails(v);
get inverseSpeedOfSound(); set inverseSpeedOfSound(v);
get logMemoryAllocationWarnings(); set logMemoryAllocationWarnings(v);
get maxBounds(); set maxBounds(v);
get maximumConcurrencyLevel(); set maximumConcurrencyLevel(v);
get maximumGroupedEAXCount(); set maximumGroupedEAXCount(v);
get metersPerUnit(); set metersPerUnit(v);
get pendingShutdown(); set pendingShutdown(v);
get position(); set position(v);
get referenceFrequencyHF(); set referenceFrequencyHF(v);
get referenceFrequencyLF(); set referenceFrequencyLF(v);
get size(); set size(v);
get workItemCount(); set workItemCount(v);
get worldIsIndoors(); set worldIsIndoors(v);
get analysisTime();
get areThreadsRunning();
get groupedEAX();
get initialising();
get mainThreadTime();
get preparationTime();
get raytracingTime();
set onReverbUpdated(fn);
addEmitter(e);
addMaterial(materialType, properties);
addPrimitive(p);
export();
getBackgroundThreadCount();
getMaterial(materialType);
getThreadCount();
hasMaterial(materialType);
import(data);
removeEmitter(e);
removePrimitive(p);
update();
wait();
}
// Create a new world
VAWorld* vaWorldCreate();
// Wait for background thread to complete, then frees all resources. After calling this method, this world cannot be reused
void vaWorldDestroy(VAWorld* world);
void vaWorldUpdate(VAWorld* world);
// Block the calling thread until all background raytracing threads complete, then handles the results (updates reverb objects and invokes onRaytracingComplete and onRaytracedByAnotherEmitter callbacks for each emitter)
void vaWorldWait(VAWorld* world);
void vaWorldSetPendingShutdown(VAWorld* world, bool value);
bool vaWorldGetPendingShutdown(VAWorld* world);
bool vaWorldGetThreadsRunning(VAWorld* world);
// Number of rays cast this frame
int vaWorldGetRaysCastThisFrame(const VAWorld* world);
// List of grouped EAX reverb properties for all emitters. Contains parameters compatible with EAX reverb effects
const VAEAXReverb** vaWorldGetGroupedEAX(const VAWorld* world);
// Current number of grouped EAX effects (may be less than the maximum set via vaWorldSetMaximumGroupedEAXCount)
int vaWorldGetGroupedEAXCount(const VAWorld* world);
VAResult vaWorldAddPrimitive_(VAWorld* world, void* primitive);
VAResult vaWorldRemovePrimitive_(VAWorld* world, void* primitive);
// The minimum bounds of the world. Emitters outside the world will not be raytraced, and primitives fully outside these bounds will not affect raytracing
VAVector vaWorldGetPosition(const VAWorld* world);
VAResult vaWorldSetPosition(VAWorld* world, VAVector position);
// The size of the world. Emitters outside the world will not be raytraced, and primitives fully outside these bounds will not affect raytracing
VAVector vaWorldGetSize(const VAWorld* world);
VAResult vaWorldSetSize(VAWorld* world, VAVector size);
// The maximum bounds of the world
VAVector vaWorldGetMaximumBounds(const VAWorld* world);
VAResult vaWorldSetMaximumBounds(VAWorld* world, VAVector maxBounds);
// Get the number of emitters in this world
int vaWorldGetEmitterCount(const VAWorld* world);
VAResult vaWorldSetMaximumGroupedEAXCount(VAWorld* world, int value);
// Get the maximum number of grouped EAX reverb properties shared all emitters that affect grouped EAX
int vaWorldGetMaximumGroupedEAXCount(const VAWorld* world);
VAResult vaWorldSetWorkItemCount(VAWorld* world, int value);
// Get the number of work items to split trails across for load balancing
int vaWorldGetWorkItemCount(const VAWorld* world);
VAResult vaWorldSetMaximumConcurrencyLevel(VAWorld* world, int value);
// The maximum number of threads that can run in parallel for this world
int vaWorldGetMaximumConcurrencyLevel(const VAWorld* world);
VAResult vaWorldSetMetersPerUnit(VAWorld* world, float value);
// Get the meters per world unit. Affects air absorption and reverb
float vaWorldGetMetersPerUnit(const VAWorld* world);
VAResult vaWorldSetInverseSpeedOfSound(VAWorld* world, float value);
// Get the inverse speed of sound (in seconds per meter). Affects air absorption and reverb
float vaWorldGetInverseSpeedOfSound(const VAWorld* world);
VAResult vaWorldSetReferenceFrequencyLF(VAWorld* world, float value);
// Get the low-frequency reference (in Hz). Affects air absorption and reverb
float vaWorldGetReferenceFrequencyLF(const VAWorld* world);
VAResult vaWorldSetReferenceFrequencyHF(VAWorld* world, float value);
// Get the high-frequency reference (in Hz). Affects air absorption and reverb
float vaWorldGetReferenceFrequencyHF(const VAWorld* world);
// Get the air absorption settings
void* vaWorldGetAirAbsorption(VAWorld* world);
VAResult vaWorldSetAirAbsorption(VAWorld* world, VAAirAbsorptionSettings* value);
VAResult vaWorldSetAirAbsorptionHumidity(VAWorld* world, float value);
// Get the air humidity percentage
float vaWorldGetAirAbsorptionHumidity(VAWorld* world);
// Set the air temperature (in Celsius). Returns VA_INVALID_VALUE if NaN/Infinity, or VA_OUT_OF_RANGE if <= -273.15
VAResult vaWorldSetAirAbsorptionTemperature(VAWorld* world, float value);
// Get the air temperature
float vaWorldGetAirAbsorptionTemperature(VAWorld* world);
VAResult vaWorldSetAirAbsorptionPressure(VAWorld* world, float value);
// Get the atmospheric pressure
float vaWorldGetAirAbsorptionPressure(VAWorld* world);
// Custom air absorption formula for low frequencies. Takes distance (meters) and returns energy loss as a percentage. Pass NULL to use the default formula
void vaWorldSetAirAbsorptionCustomFormulaLF(VAWorld* world, VAAirAbsorptionCustomFormula value);
// Custom air absorption formula for high frequencies. Takes distance (meters) and returns energy loss as a percentage. Pass NULL to use the default formula
void vaWorldSetAirAbsorptionCustomFormulaHF(VAWorld* world, VAAirAbsorptionCustomFormula value);
// Custom formulas for calculating EAX properties (diffusion, density, etc). Pass NULL to use default formulas
void vaWorldSetCustomEAXFormulas(VAWorld* world, VACustomEAXFormulas* formulas);
// Whether emitters outside the world have 0 occlusion/permeation energy (true) or maximum energy (false)
void vaWorldSetEmittersOutsideTheWorldAreMuffled(VAWorld* world, bool value);
bool vaWorldGetEmittersOutsideTheWorldAreMuffled(const VAWorld* world);
// Whether the entire world is considered indoors or outdoors. When false, reverb rays stop accumulating energy after hitting the world edge. Defaults to false
bool vaWorldGetWorldIsIndoors(const VAWorld* world);
void vaWorldSetWorldIsIndoors(VAWorld* world, bool value);
// Coordinate system used when calculating listener-relative reverb directionality with vaWorldCalculateListenerRelativePan
VACoordinateSystem vaWorldGetCoordinateSystem(const VAWorld* world);
void vaWorldSetCoordinateSystem(VAWorld* world, VACoordinateSystem value);
// The epsilon value used for raytracing and primitive intersections
float vaWorldGetEpsilon(const VAWorld* world);
void vaWorldSetEpsilon(VAWorld* world, float value);
VAResult vaWorldCreateMaterial(VAWorld* world, int materialId);
// Returns true if this world has a material with the provided id
bool vaWorldHasMaterial(const VAWorld* world, int materialId);
VAResult vaWorldSetMaterialAbsorptionLF(VAWorld* world, int materialId, float value);
// Get the percentage of low-frequency energy that is lost when a ray bounces off this material
float vaWorldGetMaterialAbsorptionLF(const VAWorld* world, int materialId);
VAResult vaWorldSetMaterialAbsorptionHF(VAWorld* world, int materialId, float value);
// Get the percentage of high-frequency energy that is lost when a ray bounces off this material
float vaWorldGetMaterialAbsorptionHF(const VAWorld* world, int materialId);
VAResult vaWorldSetMaterialScattering(VAWorld* world, int materialId, float value);
// Get the scattering strength when a ray bounces off this material. 0.0 = no scattering, 1.0 skews the ray reflection direction by up to 90 degrees)
float vaWorldGetMaterialScattering(const VAWorld* world, int materialId);
VAResult vaWorldSetMaterialTransmissionLF(VAWorld* world, int materialId, float meters);
// Get how many meters a ray must travel through a primitive before it loses all low-frequency energy0
float vaWorldGetMaterialTransmissionLF(const VAWorld* world, int materialId);
VAResult vaWorldSetMaterialTransmissionHF(VAWorld* world, int materialId, float meters);
// Get how many meters a ray must travel through a primitive before it loses all high-frequency energy
float vaWorldGetMaterialTransmissionHF(const VAWorld* world, int materialId);
VAResult vaWorldSetMaterialPlaneTransmissionLF(VAWorld* world, int materialId, float value);
// Get the percentage of low-frequency energy that is lost when a permeation ray passes through a flat VAPlanePrimitive, VADiskPrimitive, VATrianglePrimitive, or non-watertight VAMeshPrimitive
float vaWorldGetMaterialPlaneTransmissionLF(const VAWorld* world, int materialId);
VAResult vaWorldSetMaterialPlaneTransmissionHF(VAWorld* world, int materialId, float value);
// Get the percentage of high-frequency energy that is lost when a permeation ray passes through a flat VAPlanePrimitive, VADiskPrimitive, VATrianglePrimitive, or non-watertight VAMeshPrimitive
float vaWorldGetMaterialPlaneTransmissionHF(const VAWorld* world, int materialId);
// Get the average time (in milliseconds) spent by vaWorldUpdate() on the main thread
double vaWorldGetMainThreadTime(VAWorld* world);
// Get the average time (in milliseconds) spent in background raytracing threads
double vaWorldGetRaytracingTime(VAWorld* world);
// Get the average time (in milliseconds) spent in the preparation thread
double vaWorldGetPreparationTime(VAWorld* world);
// Get the average time (in milliseconds) spent in the analysis thread
double vaWorldGetAnalysisTime(VAWorld* world);
// Get the average time (in milliseconds) between when vaWorldUpdate() is invoked, and when the onReverbUpdated callback is invoked
double vaWorldGetLatency(VAWorld* world);
VAResult vaWorldAddEmitter(VAWorld* world, VAEmitter* emitter);
// Remove an emitter from this world. This emitter's OnRaytracingComplete callback will not be invoked
void vaWorldRemoveEmitter(VAWorld* world, VAEmitter* emitter);
// True until raytracing has run at least once
bool vaWorldGetInitialising(const VAWorld* world);
// This callback is invoked right after the background threads finish running. At this point, the results of the background threads have not been processed yet (EAX is not updated yet, low pass filters not updated, ambience not updated, etc). After this, onReverbUpdated will be invoked next
void vaWorldSetOnRaytracingCompletedCallback(VAWorld* world, void (*callback)(void));
// This callback is invoked after EAX reverb results are updated. This gives you a chance to update your EAX effects, so they can be applied to an emitter in it's onRaytracingComplete callback. After this, each emitter's callback are invoked, and then onRaytracingResultsHandled will be invoked next
void vaWorldSetOnReverbUpdatedCallback(VAWorld* world, void (*callback)(void));
// This callback is invoked after all raytracing results (EAX, filters, etc) have been processed. After this, new emitter/primitive settings will be applied, and the background threads will start again
void vaWorldSetOnRaytracingResultsHandledCallback(VAWorld* world, void (*callback)(void));
// Custom callback for text logs
void vaWorldSetLogCallback(VAWorld* world, VALogCallback callback);
// Whether to invoke vaWorldSetLogCallback each time memory is allocated
void vaWorldSetLogMemoryAllocationWarnings(VAWorld* world, bool value);
// Converts a world-space vector to a listener-relative pan direction given the listener's pitch and yaw angles, using the world's CoordinateSystem
VAVector vaWorldCalculateListenerRelativePan(const VAWorld* world, VAVector worldVector, float listenerPitch, float listenerYaw);
VAResult vaWorldExport(VAWorld* world, const char* fileName);
VAResult vaWorldImport(VAWorld* world, const char* fileName, VAEmitter*** outEmitters, int* outEmitterCount);
WorkItemCount
This setting controls how raytracing work is divided across multiple background threads. If you have 8 threads, it seems logical to split the work into 8 work items (one for each thread). However some threads might finish earlier than others, meaning not all threads will be fully utilised.
To prevent threads from idling while other threads run, you can divide the raytracing work into smaller work items. To do this, set workItemCount to a number higher than maximumConcurrencyLevel:
var world = new World()
{
OcclusionRayCount = 1024,
OcclusionBounceCount = 8,
WorkItemCount = 128,
MaximumConcurrencyLevel = 8,
};
This means 8 background threads will perform raytracing, and will take work from a pool of 128 work items. On average each thread should process 128/8 = 16 work items each.
ALManager
Godot node that represents an OpenAL device and context.
Properties
class ALManager : Node
{
ALDistanceModel DistanceModel;
bool HRTFEnabled;
float ListenerPitch;
Vector3 ListenerPosition;
Vector3 ListenerVelocity;
float ListenerYaw;
float MasterVolume;
int MaximumAuxiliarySends;
int MaximumMonoSources;
int MaximumStereoSources;
float MetersPerUnit;
bool MicrophoneEnabled;
int MicrophoneThreshold;
int SampleRate;
float SpeedOfSound;
}
Code
class ALManager : Node
{
bool Initialised;
event MicrophoneDataCallback OnMicrophoneData;
delegate void MicrophoneDataCallback(IntPtr samples, int sampleCount);
override void _EnterTree();
override void _ExitTree();
override Variant _Get(StringName property);
override Godot.Collections.Array<Godot.Collections.Dictionary> _GetPropertyList();
override void _Process(double delta);
override bool _Set(StringName property, Variant value);
static ALManager instance;
}
ALSource3D
Godot node that represents a 3D spatialised OpenAL source.
Properties
class ALSource3D : Node3D
{
// Whether the sound plays indefinitely on loop
bool Looping;
// The max distance that the sound can be heard at. Also affected by the falloff model in ALManager
float MaxDistance;
// The pitch of the sound
// 0 to 10
float Pitch;
// The distance that sound volume falloff starts at
float ReferenceDistance;
// Whether the sound is spatialised or relative to the listener. Set to true for music, ambience and sounds your own character makes.
bool Relative;
// The name of the sound loaded from res://audio folder. To use a different folder, set the `audio/openal_sound_folder.custom` setting in Project Settings
string SoundName;
// The volume of the sound
// 0 to 10
float Volume;
}
Code
class ALSource3D : Node3D
{
ALReverbEffect effect;
ALFilter filter;
delegate void FinishedEventHandler();
bool IsPlaying();
virtual void OnDeviceDestroyed();
virtual bool Play();
void Stop();
void UpdateFilter(float gain, float gainHF, bool fullReverb = false);
override void _ExitTree();
override string[] _GetConfigurationWarnings();
override void _Process(double delta);
static ALFilter fullFilter = new(1, 1);
static ALFilter silenceFilter = new(0, 0);
}
VAEmitter
Godot node that represents an Emitter.
Properties
class VAEmitter : Node3D
{
// Controls whether this Emitter's EAX is blended to produced grouped EAX. Set this to false for listener emitters
bool AffectsGroupedEAX;
// Number of bounces per ambient occlusion ray
int AmbientOcclusionBounceCount;
// The percentage of occlusion energy required for the emitter to be at full volume. Defaults to 15% of this emitter's AmbientOcclusionRayCount.
// 0 to 1
float AmbientOcclusionEnergyCap;
// Number of ambient occlusion rays cast
int AmbientOcclusionRayCount;
// Number of bounces per ambient permeation ray
int AmbientPermeationBounceCount;
// The color of ambientPermeation rays in the debug window (dev build only)
Godot.Color AmbientPermeationColor;
// The percentage of permeation energy required for the emitter to be at full volume. Defaults to 15% of this emitter's AmbientPermeationRayCount * AmbientPermeationBounceCount.
// 0 to 1
float AmbientPermeationEnergyCap;
// Number of ambient permeation rays cast
int AmbientPermeationRayCount;
// Whether to clamp this emitter's position to the world bounds, to prevent it from going out of bounds
bool ClampPosition;
// The length (in milliseconds) of each entry in the echogram. Defaults to 50ms
int EchogramGranularity;
// Whether this emitter is used as a reference point for calculating relative reverb gain and direction
bool HasRelativeReverb;
bool IsMainListener;
// How long (in milliseconds) the echogram records data for. Returning reverb rays after this period will be ignored. Defaults to 5000ms
int MaxEchogramTime;
// Number of bounces per occlusion ray
int OcclusionBounceCount;
// The color of occlusion rays in the debug window (dev build only)
Godot.Color OcclusionColor;
// The percentage of occlusion energy required for this emitter to be at full volume. Defaults to 15% of the other emitter's OcclusionRayCount.
// 0 to 1
float OcclusionEnergyCap;
// Number of occlusion rays cast
int OcclusionRayCount;
// Number of bounces per permeation ray
int PermeationBounceCount;
// The color of permeation rays in the debug window (dev build only)
Godot.Color PermeationColor;
// The percentage of permeation energy required for this emitter to be at full volume. Defaults to 15% of the other emitter's PermeationRayCount * PermeationBounceCount.
// 0 to 1
float PermeationEnergyCap;
// Number of permeation rays cast
int PermeationRayCount;
float Pitch;
bool RaytraceOnce;
// A ray trail will be re-created if an old ray bounce position is too far away from the new ray bounce position. This setting controls the allowed distance between old and new ray bounce positions. Defaults to 1.0f. Clamped to minimum of 0.
float RefreshDistanceThreshold;
// The number of trails that are rebuilt from scratch each frame to prevent staleness when the listener moves. Clamped to minimum of 0.
int RefreshRayCount;
// The lower bound of the relative reverb blend range
// 0 to 1
float RelativeReverbInnerThreshold;
// The upper bound of the relative reverb blend range
// 0 to 1
float RelativeReverbOuterThreshold;
// The number of emitters that are allocated ahead of time, to prevent runtime allocations. Clamped to minimum of 0
int ReservedEmitterTargets;
// Number of bounces per reverb ray
int ReverbBounceCount;
// The color of reverb rays in the debug window (dev build only)
Godot.Color ReverbColor;
// The percentage of returning energy required for reverb to be at maximum volume. Defaults to 20% of the other emitter's ReverbRayCount * ReverbBounceCount.
// 0 to 1
float ReverbEnergyCap;
// Number of reverb rays cast
int ReverbRayCount;
// A seed used to randomise scattering vectors
int ScatteringSeed;
// The color of ray trails in the debug window (dev build only)
Godot.Color TrailColor;
// User-defined type for this emitter
int Type;
// Number of times each visualisation ray bounces
int VisualisationBounceCount;
// Number of visualisation rays cast
int VisualisationRayCount;
// How often - in milliseconds - to cast visualisation rays. Defaults to 500
int VisualisationUpdateFrequency;
float Yaw;
}
Code
class VAEmitter : Node3D
{
vaudio.LowPassFilter AmbientFilter;
vaudio.EAXReverb EAX;
ALReverbEffect effect;
vaudio.Emitter emitter;
ALFilter filter;
float GainHF;
float GainLF;
int GroupedEAXIndex;
Action<vaudio.Emitter> OnRaytracedByAnotherEmitterCallback;
Action OnRaytracingCompleteCallback;
vaudio.ProcessedReverb ProcessedReverb;
bool Raytraced;
void AddTarget(vaudio.Emitter target);
void CreateEmitter();
vaudio.LowPassFilter GetTargetFilter(VAEmitter target);
vaudio.LowPassFilter GetTargetFilter(vaudio.Emitter target);
bool HasRaytracedTarget(VAEmitter target);
void RemoveEmitter();
void RemoveTarget(vaudio.Emitter target);
override void _EnterTree();
override void _ExitTree();
override string[] _GetConfigurationWarnings();
override void _Process(double delta);
}
VAMaterial
Godot node for creating/customising materials.
Properties
class VAMaterial : Node
{
// High-frequency absorption coefficient (0.0 to 1.0)
// 0 to 1
float AbsorptionHF;
// Low-frequency absorption coefficient (0.0 to 1.0)
// 0 to 1
float AbsorptionLF;
// Debug color for the VAudio debug renderer
Color DebugColor;
// Material name for debugging and identification
string MaterialName;
// Unique material ID. Must be >= 1000 to avoid conflicts with built-in materials
// 1000 to 9999
int MaterialType;
// Percentage of high-frequency energy lost when a ray passes through a flat primitive
// 0 to 1
float PlaneTransmissionHF;
// Percentage of low-frequency energy lost when a ray passes through a flat primitive
// 0 to 1
float PlaneTransmissionLF;
// Scattering coefficient (0.0 to 1.0)
// 0 to 1
float Scattering;
// High-frequency transmission in dB/m (0.0 or greater)
// 0.0001 to 100
float TransmissionHF;
// Low-frequency transmission in dB/m (0.0 or greater)
// 0.0001 to 100
float TransmissionLF;
}
Code
class VAMaterial : Node
{
// Gets the debug color as a vaudio.Color
vaudio.Color GetDebugColor();
override void _EnterTree();
// Validates the material configuration and returns warnings
override string[] _GetConfigurationWarnings();
}
VASource
Godot node that contains an:
- OpenAL source
- Emitter
Properties
class VASource : ALSource3D
{
// Controls whether this Emitter's EAX is blended to produced grouped EAX. Set this to false for listener emitters
bool AffectsGroupedEAX;
// Number of bounces per ambient occlusion ray
int AmbientOcclusionBounceCount;
// The percentage of occlusion energy required for the emitter to be at full volume. Defaults to 15% of this emitter's AmbientOcclusionRayCount.
// 0 to 1
float AmbientOcclusionEnergyCap;
// Number of ambient occlusion rays cast
int AmbientOcclusionRayCount;
// Number of bounces per ambient permeation ray
int AmbientPermeationBounceCount;
// The color of ambientPermeation rays in the debug window (dev build only)
Godot.Color AmbientPermeationColor;
// The percentage of permeation energy required for the emitter to be at full volume. Defaults to 15% of this emitter's AmbientPermeationRayCount * AmbientPermeationBounceCount.
// 0 to 1
float AmbientPermeationEnergyCap;
// Number of ambient permeation rays cast
int AmbientPermeationRayCount;
// Whether to clamp this emitter's position to the world bounds, to prevent it from going out of bounds
bool ClampPosition;
// The length (in milliseconds) of each entry in the echogram. Defaults to 50ms
int EchogramGranularity;
// How long (in milliseconds) the echogram records data for. Returning reverb rays after this period will be ignored. Defaults to 5000ms
int MaxEchogramTime;
// The color of occlusion rays in the debug window (dev build only)
Godot.Color OcclusionColor;
// The percentage of occlusion energy required for this emitter to be at full volume. Defaults to 15% of the other emitter's OcclusionRayCount.
// 0 to 1
float OcclusionEnergyCap;
// The color of permeation rays in the debug window (dev build only)
Godot.Color PermeationColor;
// The percentage of permeation energy required for this emitter to be at full volume. Defaults to 15% of the other emitter's PermeationRayCount * PermeationBounceCount.
// 0 to 1
float PermeationEnergyCap;
bool PlayWhenRaytracingCompletes;
bool RaytraceOnce;
// A ray trail will be re-created if an old ray bounce position is too far away from the new ray bounce position. This setting controls the allowed distance between old and new ray bounce positions. Defaults to 1.0f. Clamped to minimum of 0.
float RefreshDistanceThreshold;
// The number of trails that are rebuilt from scratch each frame to prevent staleness when the listener moves. Clamped to minimum of 0.
int RefreshRayCount;
// Number of bounces per reverb ray
int ReverbBounceCount;
// The color of reverb rays in the debug window (dev build only)
Godot.Color ReverbColor;
// The percentage of returning energy required for reverb to be at maximum volume. Defaults to 20% of the other emitter's ReverbRayCount * ReverbBounceCount.
// 0 to 1
float ReverbEnergyCap;
// Number of reverb rays cast
int ReverbRayCount;
// A seed used to randomise scattering vectors
int ScatteringSeed;
// The color of ray trails in the debug window (dev build only)
Godot.Color TrailColor;
// User-defined type for this emitter
int Type;
// Number of times each visualisation ray bounces
int VisualisationBounceCount;
// Number of visualisation rays cast
int VisualisationRayCount;
// How often - in milliseconds - to cast visualisation rays. Defaults to 500
int VisualisationUpdateFrequency;
}
Code
class VASource : ALSource3D
{
bool Raytraced;
void CreateEmitter();
override void OnDeviceDestroyed();
override bool Play();
override void _EnterTree();
override void _ExitTree();
override string[] _GetConfigurationWarnings();
override void _Process(double delta);
}
VASourceAmbient
Godot node that plays ambient sounds, with a low-pass filter calculating using the main listener's ambient rays.
Code
class VASourceAmbient : ALSource3D
{
override void OnDeviceDestroyed();
override bool Play();
override void _EnterTree();
override void _Process(double delta);
override void _Ready();
}
VASourceRelative
Godot node that plays non-spatialised sounds, using reverb from the main listener.
Code
class VASourceRelative : ALSource3D
{
override bool Play();
override void _EnterTree();
override void _Ready();
}
VAWorld
Godot node that represents a World, containing primitives, emitters, materials and many customisable settings.
Code
class VAWorld : Node
{
List<vaudio.Emitter> emitters = [];
Action<string> Log = (message);
Action<string> LogError = (message);
Action<string> LogWarning = (message);
vaudio.Emitter CreateEmitter(VAEmitter node, Action OnRaytracingComplete, Action<vaudio.Emitter> OnRaytracedByAnotherEmitter);
void RemoveEmitter(vaudio.Emitter emitter);
}
Getting Started
This page describes the steps for setting up the Vercidium Audio SDK and C# Godot plugin.
Ensure you are using the .NET Godot build and have .NET 8 installed. A plugin for regular Godot is on our roadmap.
Step 1 - Create a C# Solution
Open your Godot project and click Project > Tools > C# > Create C# solution.

Step 2 - Clone Plugins
Vercidium Audio has two plugins:
- godot-openal, which replaces Godot's audio engine with OpenAL, for better reverb and spatialisation
- vaudio-godot-openal, which performs raytracing
Clone the both repositories into the addons folder:
cd your_godot_game
mkdir addons
cd addons
git clone [email protected]:vercidium-audio/vaudio-godot-openal.git
git clone [email protected]:vercidium-audio/godot-openal.git
You should see both addons in your addons folder:

Step 3 - Enable Plugins
Open your Godot project, click Project > Project Settings > Plugins, and enable both plugins:

After enabling the plugins, you should see this error in the Godot output window:
ERROR: core/variant/variant_utility.cpp:1034 - [vaudio-godot-openal] csproj is invalid - please replace 'path\to\vaudio.dll' with your actual vaudio.dll path, then disable and enable the Vercidium Audio plugin
This is good. Close your Godot project and continue with the setup below.
Step 4 - Download Vercidium Audio
Download the Vercidium Audio SDK from https://vercidium.com.
The SDK contains a dotnet folder, with a dev and production build. I recommend integrating with the dev build, as it renders the raytracing scene to a separate window.

Ensure your final game ships with the production build for best performance
Step 5 - Configure csproj
Open the .csproj file for your Godot project, and replace path\\to\\vaudio.dll with the location of Vercidium Audio on your computer, e.g. C:\Users\you\Downloads\vercidium_audio_v1.2.0\dotnet\dev\vaudio.dll
<ItemGroup>
<Reference Include="vaudio">
<!-- Replace this with the path to your vaudio SDK -->
<HintPath>path\to\vaudio.dll</HintPath>
</Reference>
</ItemGroup>
Step 6 - Verify Installation
Re-open your Godot project and build it. You should see these messages in the Godot output window:
[vaudio-godot-openal] csproj configured correctly
[vaudio-godot-openal] Vercidium Audio (vaudio) plugin enabled
Step 7 - Add Main Node
In your game's scene, add a VAWorld node:

This node has a few settings that you can customise. Rendering Enabled defaults to On and will render your raytracing world to a separate window at runtime:

Step 8 - Create a Listener
Create a VAEmitter node within your VAWorld node, and set:
Is Main Listenerto trueReverb Ray Countto 128Reverb Bounce Countto 64Affects Grouped EAXto falseHas Relative Reverbto true

There can only be one main listener. This emitter will cast reverb, occlusion, permeation and ambient rays. You can adjust the number of rays in the other Muffling / Ambience / Visualisation sections.
This emitter is a Node3D, and you can adjust its position to control where rays originate from. In my demo, I wrap the listener in another Node3D, which has a script to align the listener emitter with the camera:
extends Node3D
@onready var va_emitter: VAEmitter = $Listener
func _process(_delta: float) -> void:
var camera = get_viewport().get_camera_3d()
if camera:
# Match the camera's position
global_position = camera.global_position
# Match the camera's rotation
var rot = camera.global_rotation
va_emitter.Pitch = rot.x
va_emitter.Yaw = rot.y
The full setup now looks like this:

Step 9 - Sound Playback
To play a 3D sound with raytracing automatically applied, create a new VASource node in your scene.

Set its Sound Name to the path of your sound in the res://audio folder, and set Play When Raytracing Completes to On to play the sound automatically after it has been raytraced.
I've also add a CSGSphere3D child node to the source, so you can see where the sound is in the 3D view
For short sounds I recommend setting Raytrace Once to true, as it's enough to just raytrace the source once to figure out how muffled it is. For longer continuous sounds like music or speech, set Raytrace Once to false to ensure they are automatically muffled/clear as the environment changes.
If your sound files live in a different folder, you can set a custom path by creating a audio/openal_sound_folder.custom setting:

Step 10 - Add Materials to Primitives
For a 3D object to affect raytracing, it must have a vercidium_audio_material string metadata field.
Materials also apply to child nodes. In the screenshot below, I've set a concrete material on the Building node, which sets the material of every child node to concrete. To exclude a child node from raytracing, set its material to air.

To ensure your scene is set up correctly, set
Rendering Enabledto true on yourVAWorldnode and run the project. An extra debug window will appear that renders the raytraced world.
Step 11 - Customise Materials
To create a new material, add a VAMaterial child node to the VAWorld node:

Set Material Name to a custom string, e.g. 'alien', and then set the vercidium_audio_material metadata field to alien on a 3D primitive.
See the Default Materials for reference values.
You can also edit default materials by setting the Material Name to the same name as a Default Material. These default material names must be all lowercase, e.g. 'concrete', 'woodindoor', 'metal'.
Godot Changelog
Version 1.2.0 - 1st July 2026
Updated to Vercidium Audio v1.2.0
Additions:
- Added all new Vercidium Audio fields to the
VA*nodes
Changes:
- Renamed nodes from
VercidiumAudio*toVA*
Version 1.1.0
Updated to Vercidium Audio C# SDK v1.1.0
Changes:
- Using new material system (scattering, plane transmission)
- Material settings can now be updated in real time
Fixes:
- The entire scene tree is now walked when adding 3D primitives to the raytracing scene for the first time
Additions:
- New error messages are displayed when the scene tree is set up incorrectly. All Emitter and Source nodes must be initialised after the
VercidiumAudioWorldnode:
Optimisations:
- Primitive transform/position/scale updates are now done via callback, rather than walking the entire tree every frame
Version 1.0.2
https://github.com/vercidium-patreon/godot_raytraced_audio/pull/2
Additions:
- Added support for these CSG nodes:
CsgCylinder3D,CsgSphere3D,CsgPolygon3DandCsgMesh3D
Changes:
- Logs are now printed to both
Console.WriteLineandGD.Print, meaning logs are always available when launched from either the Godot editor or Visual Studio
Fixes:
- Sources, filters and reverb effects are correctly re-created when switching audio devices
- New primitives that are added to the scene tree at runtime are now added to the raytracing simulation correctly
- Primitives that are removed from the scene tree at runtime are now removed from the raytracing simulation correctly
- The radius of sphere primitives no longer flickers
- Potential fix/workaround for invalid grouped EAX indices on voices
Version 1.0.1
https://github.com/vercidium-patreon/godot_raytraced_audio/pull/1
Additions:
- Rays now collide with
CapsuleShape3D,CylinderShape3D,HeightMapShape3D,WorldBoundaryShape3D,ConvexPolygonShape3DandConcavePolygonShape3D
Changes::
- Now using
GD.PushErrorandGD.PushWarningrather thanGD.Print
Troubleshooting
If you're facing issues with the Vercidium Audio Godot plugin, please visit our support page.
No Audio
If you can't hear any audio, the ALManager node may not have been set up correctly. This should be set up automatically when enabling the godot-openal plugin.
To check it was set up correctly, ensure the ALManagerAutoload.tscn file exists:

Also ensure the autoload is configured in your project settings:

If the above is set up correctly, the wrong speaker may be selected. Double click ALManagerAutoload.tscn to open the scene, and check the correct Output Device Name and Input Device Name is selected:

Air Absorption
Rays lose low- and high-frequency energy based on how far they travel through air.
There is an in-built air absorption formula that's based on humidity, temperature and pressure. You can configure it like so:
world.AirAbsorption.HumidityPercent = 0.1f;
world.AirAbsorption.TemperatureCelsius = 30.0f;
world.AirAbsorption.PressurePascals = 101325.0f;
world.airAbsorption.humidityPercent = 0.1;
world.airAbsorption.temperatureCelsius = 30.0;
world.airAbsorption.pressurePascals = 101325.0;
vaWorldSetAirAbsorptionHumidity(world, 0.1f);
vaWorldSetAirAbsorptionTemperature(world, 30.0f);
vaWorldSetAirAbsorptionPressure(world, 101325.0f);
These settings should be configured once on startup. Changing these values at runtime will clear the ray cache, causing all rays to be re-cast
Air absorption is also affected by the following World settings:
world.MetersPerUnit = 1.0f;
world.InverseSpeedOfSound = 1.0f / 343.0f;
world.ReferenceFrequencyLF = 300.0f; // Hz
world.ReferenceFrequencyHF = 4000.0f; // Hz
world.metersPerUnit = 1.0;
world.inverseSpeedOfSound = 1.0 / 343.0;
world.referenceFrequencyLF = 300.0; // Hz
world.referenceFrequencyHF = 4000.0; // Hz
vaWorldSetMetersPerUnit(world, 1.0f);
vaWorldSetInverseSpeedOfSound(world, 1.0f / 343.0f);
vaWorldSetReferenceFrequencyLF(world, 300.0f); // Hz
vaWorldSetReferenceFrequencyHF(world, 4000.0f); // Hz
These settings should be configured once on startup. Changing these values at runtime will clear the ray cache, causing all rays to be re-cast
You can also provide custom formulas for air absorption:
world.AirAbsorption.CustomFormulaLF = (float distanceMeters) =>
{
// Return how much LF energy should be lost, in the range 0.0 to 1.0
return MathF.Min(1.0f, distanceMeters / 15000.0f);
};
world.AirAbsorption.CustomFormulaHF = (float distanceMeters) =>
{
// Return how much HF energy should be lost, in the range 0.0 to 1.0
return MathF.Min(1.0f, distanceMeters / 2000.0f);
};
You can also provide custom formulas for air absorption:
float CustomFormulaLF(float distanceMeters)
{
// Return how much LF energy should be lost, in the range 0.0 to 1.0
return fminf(1.0f, distanceMeters / 15000.0f);
}
float CustomFormulaHF(float distanceMeters)
{
// Return how much HF energy should be lost, in the range 0.0 to 1.0
return fminf(1.0f, distanceMeters / 2000.0f);
}
void Initialise()
{
vaWorldSetAirAbsorptionCustomFormulaLF(world, CustomFormulaLF);
vaWorldSetAirAbsorptionCustomFormulaHF(world, CustomFormulaHF);
}
You can also completely disable air absorption:
world.AirAbsorption = null;
world.airAbsorption = null;
vaWorldSetAirAbsorption(world, NULL);
Ambience
Emitters can cast rays to determine how muffled ambient sounds should be. To enable this, configure ambient occlusion and/or ambient permeation rays on the emitter:
var listener = new Emitter()
{
AmbientOcclusionRayCount = 1024,
AmbientOcclusionBounceCount = 8,
AmbientPermeationRayCount = 128,
AmbientPermeationBounceCount = 3,
};
const listener = va.Emitter_Create();
listener.ambientOcclusionRayCount = 1024;
listener.ambientOcclusionBounceCount = 8;
listener.ambientPermeationRayCount = 128;
listener.ambientPermeationBounceCount = 3;
VAEmitter* emitter = vaEmitterCreate();
vaEmitterSetAmbientOcclusionRayCount(emitter, 1024);
vaEmitterSetAmbientOcclusionBounceCount(emitter, 8);
vaEmitterSetAmbientPermeationRayCount(emitter, 128);
vaEmitterSetAmbientPermeationBounceCount(emitter, 3);
Ambient occlusion rays bounce around the environment, losing energy based on the materials they hit, and from air absorption. These rays are marked as 'outside' when they hit the edge of the world. The energy remaining in the rays when they hit the world edge is used below to determine ambience volume.
Ambient permeation rays bounce around the environment, but do not lose energy based on materials or air absorption. Instead, on each bounce they cast an extra ray in a random direction. These extra rays travel through the world, losing energy based on the material and thickness of the primitives they pass through. The energy remaining in the rays when they reach the world edge is used below to determine ambience volume.
Materials and air absorption affect low- and high-frequency energy differently, which is why thunder becomes muffled through walls. Read more about this in Materials and Air Absorption.
You must also set the AmbientOcclusionEnergyCapambientOcclusionEnergyCapvaEmitterSetAmbientOcclusionEnergyCap() and AmbientPermeationEnergyCapambientPermeationEnergyCapvaEmitterSetAmbientPermeationEnergyCap() fields, which control the threshold for ambience to be at maximum volume.
// 50% of occlusion energy is required for max ambience volume
emitter.AmbientOcclusionEnergyCap = 0.5;
// 50% of permeation energy is required for max ambience volume
emitter.AmbientPermeationEnergyCap = 0.5;
// 50% of occlusion energy is required for max ambience volume
emitter.ambientOcclusionEnergyCap = 0.5;
// 50% of permeation energy is required for max ambience volume
emitter.ambientPermeationEnergyCap = 0.5;
// 50% of occlusion energy is required for max ambience volume
vaEmitterSetAmbientOcclusionEnergyCap(emitter, 0.5f);
// 50% of permeation energy is required for max ambience volume
vaEmitterSetAmbientPermeationEnergyCap(emitter, 0.5f);
The 'percent' of energy refers to the total possible energy across all rays. For example, if air absorption is disabled and all occlusion rays hit the world edge without bouncing on any materials - or if all permeation rays reach the world edge and don't pass through any primitives - then the rays will still have 100% energy when they reach the world edge.
For example, if there are 10 ambient occlusion rays, the total possible energy is 10. If each ray loses half of its energy by the time it reaches the world edge, the total energy across all rays is 5, meaning 50% of ambient occlusion energy remained. Then if AmbientOcclusionEnergyCap is 0.5 (50%), ambient volume will be at 100% volume.
Volume is calculated separately for occlusion and permeation rays, and then summed together, capped at 100%.
This process occurs for both low- and high-frequency energy, allowing thunder to sound deep / muffled behind a thick wall
These volume values can be accessed via emitter.AmbientFilteremitter.ambientFiltervaEmitterGetAmbientFilter(), which is null until raytracing is performed at least once:
// Wait for raytracing to complete
if (listener.AmbientFilter != null)
{
var gainLF = listener.AmbientFilter.GainLF;
var gainHF = listener.AmbientFilter.GainHF;
}
// Wait for raytracing to complete
if (listener.ambientFilter != null)
{
var gainLF = listener.ambientFilter.gainLF;
var gainHF = listener.ambientFilter.gainHF;
}
VALowPassFilter* ambientFilter = vaEmitterGetAmbientFilter(listener);
if (ambientFilter != NULL)
{
float gainLF = ambientFilter->gainLF;
float gainHF = ambientFilter->gainHF;
}
Custom Formulas
These energy totals are passed to the Emitter.AmbientGainFormula function. This function converts them to low-pass filter gain values, and is invoked once for low-frequency energy and once for high-frequency energy:
listener = new Emitter()
{
AmbientGainFormula = (
bool lowFrequency,
int ambientOcclusionRayCount,
int ambientPermeationRayCount,
int ambientPermeationBounceCount,
float ambientOcclusionEnergy,
float ambientPermeationEnergy
) =>
{
float gain = 0.0f;
if (ambientOcclusionRayCount > 0)
{
// 50% of energy is required for max ambience volume
float energyThreshold = 0.5f * ambientOcclusionRayCount;
gain += ambientOcclusionEnergy / energyThreshold;
}
if (ambientPermeationRayCount > 0 && ambientPermeationBounceCount > 0)
{
// 50% of energy is required for max ambience volume
float energyThreshold = 0.5f * (ambientPermeationRayCount * ambientPermeationBounceCount);
gain += ambientPermeationEnergy / energyThreshold;
}
return MathF.Min(1, gain);
}
}
These energy totals are passed to the ambientGainFormula function. This function converts them to low-pass filter gain values, and is invoked once for low-frequency energy and once for high-frequency energy:
float myAmbientGainFormula(
bool lowFrequency,
int occlusionRayCount,
int permeationRayCount,
int permeationBounceCount,
float occlusionEnergy,
float permeationEnergy
)
{
float gain = 0.0f;
if (occlusionRayCount > 0)
{
// 50% of energy is required for max ambience volume
float energyThreshold = 0.5f * occlusionRayCount;
gain += occlusionEnergy / energyThreshold;
}
if (permeationRayCount > 0 && permeationBounceCount > 0)
{
// 50% of energy is required for max ambience volume
float energyThreshold = 0.5f * (permeationRayCount * permeationBounceCount);
gain += permeationEnergy / energyThreshold;
}
return gain < 1.0f ? gain : 1.0f;
}
vaEmitterSetAmbientGainFormula(listener, myAmbientGainFormula);
This formula is optional. If left untouched or set to null, it will default to requiring 10% of energy for max volume. Read more: Ambient Gain Formula.
Emitters
An Emitter is a 3D position that casts rays, and can be discovered by other emitters.
Emitters can be created at any time, with customisable position and ray count properties:
const emitter = va.Emitter_Create();
emitter.name = 'Player',
emitter.setPosition(10, 10, 10);
emitter.reverbRayCount = 128;
emitter.reverbBounceCount = 64;
world.addEmitter(emitter);
VAEmitter* emitter = vaEmitterCreate();
vaEmitterSetName(emitter, "Player");
vaEmitterSetPosition(emitter, vaVectorCreate(10.0f, 10.0f, 10.0f));
vaEmitterSetReverbRayCount(emitter, 128);
vaEmitterSetReverbBounceCount(emitter, 64);
vaWorldAddEmitter(ctx, emitter);
The full list of properties is here: Emitter.
Position Interface
Rather than manually setting the position of each emitter every frame, it's best to implement the IPosition interface on your game objects, and then assign your object directly to the emitter's position.
For example if you want an emitter to follow an enemy in your game, your Enemy class would implement IPosition:
Then use the enemy directly as the emitter's position
var enemy = new Enemy();
var emitter = new Emitter()
{
Position = enemy
};
This emitter's position will now automatically match the enemy's position.
The Initial Raytrace
When an emitter is created, it starts in an initialising state where it is waiting to be raytraced. Do not play sounds in your game engine yet, as this emitter might be muffled.
New emitters are raytraced when world.Update() is called:
function update()
{
var emitter1 = va.Emitter_Create();
var emitter2 = va.Emitter_Create();
var emitter3 = va.Emitter_Create();
world.addEmitter(emitter1);
world.addEmitter(emitter2);
world.addEmitter(emitter3);
// The 3 emitters will be raytraced together
world.update();
}
void Update()
{
VAEmitter* emitter1 = vaEmitterCreate();
VAEmitter* emitter2 = vaEmitterCreate();
VAEmitter* emitter3 = vaEmitterCreate();
vaWorldAddEmitter(ctx, emitter1);
vaWorldAddEmitter(ctx, emitter2);
vaWorldAddEmitter(ctx, emitter3);
// The 3 emitters will be raytraced together
vaWorldUpdate(ctx);
}
world.Update()world.update()vaWorldUpdate()must be called regularly. Once per frame is typical, but you can call it as often as you like
Raytracing Results
The first time an emitter is raytraced, its OnRaytracingComplete()onRaytracingComplete()vaEmitterSetOnRaytracingCompleteCallback callback will fire. This callback will only contain reverb and ambient data:
var emitter = new Emitter()
{
Position = new Vector(10),
ReverbRayCount = 128,
ReverbBounceCount = 64,
AmbientOcclusionRayCount = 128,
AmbientOcclusionBounceCount = 64,
AmbientPermeationRayCount = 128,
AmbientPermeationBounceCount = 4,
};
// Access stats when raytracing first completes
emitter.OnRaytracingComplete = () =>
{
// Reverb
var decayTime = emitter.EAX.DecayTime;
// Calculated from ambient occlusion rays
var outsidePercent = emitter.OutsidePercent;
// Calculated from emitter.AmbientGainFormula
var ambientGainLF = emitter.AmbientFilter.GainLF;
var ambientGainHF = emitter.AmbientFilter.GainHF;
};
const emitter = va.Emitter_Create();
emitter.setPosition(10, 10, 10);
emitter.reverbRayCount = 128;
emitter.reverbBounceCount = 64;
emitter.ambientOcclusionRayCount = 128;
emitter.ambientOcclusionBounceCount = 64;
emitter.ambientPermeationRayCount = 128;
emitter.ambientPermeationBounceCount = 4;
// Access stats when raytracing first completes
emitter.onRaytracingComplete = () =>
{
// Reverb
const decayTime = emitter.eax.decayTime;
// Calculated from ambient occlusion rays
const outsidePercent = emitter.outsidePercent;
// Calculated from emitter.AmbientGainFormula
const ambientGainLF = emitter.ambientFilter.gainLF;
const ambientGainHF = emitter.ambientFilter.gainHF;
};
VAEmitter* emitter;
void OnRaytracingComplete()
{
// Reverb
VAEAXReverb* eax = vaEmitterGetEAX(emitter);
float decayTime = eax->decayTime;
// Calculated from ambient occlusion rays
float outsidePercent = vaEmitterGetOutsidePercent(emitter);
// Calculated from emitter.AmbientGainFormula
VALowPassFilter* filter = vaEmitterGetAmbientFilter(emitter);
float ambientGainLF = filter->gainLF;
float ambientGainHF = filter->gainHF;
}
void CreateEmitter()
{
VAEmitter* emitter = vaEmitterCreate();
vaEmitterSetPosition(emitter, vaVectorCreate(10.0f, 10.0f, 10.0f));
vaEmitterSetReverbRayCount(emitter, 128);
vaEmitterSetReverbBounceCount(emitter, 64);
vaEmitterSetAmbientOcclusionRayCount(emitter, 128);
vaEmitterSetAmbientOcclusionBounceCount(emitter, 64);
vaEmitterSetAmbientPermeationRayCount(emitter, 128);
vaEmitterSetAmbientPermeationBounceCount(emitter, 4);
vaEmitterSetOnRaytracingCompleteCallback(emitter, OnRaytracingComplete);
}
To determine how muffled an emitter is, it must be raytraced by another emitter. It's typical to have a 'listener' emitter that follows the camera, and multiple 'target' emitters that are raytraced:
// Create listener and enemy
var listener = new Emitter()
{
Name = "Camera",
Position = new FuncPosition(() => cameraPosition),
ReverbRayCount = 128,
ReverbBounceCount = 64,
// Determine how many rays to cast towards other rays
OcclusionRayCount = 1024,
OcclusionBounceCount = 8,
};
world.AddEmitter(listener);
var enemyEmitter = new Emitter()
{
Name = "Enemy",
Position = enemy,
ReverbRayCount = 32,
ReverbBounceCount = 8,
};
world.AddEmitter(enemyEmitter);
listener.AddTarget(enemyEmitter);
// Callback when raytracing completes
enemyEmitter.OnRaytracedByAnotherEmitter = (Emitter other) =>
{
var filter = other.GetTargetFilter(enemyEmitter);
var gainLF = filter.gainLF;
var gainHF = filter.gainHF;
// PSEUDOCODE - play a sound with a low pass filter
Godot.PlaySound(SoundType.Footstep, enemyEmitter.position, filter);
};
// Create listener and enemy
const listener = va.Emitter_Create();
listener.name = 'Camera';
listener.setPosition(cameraPosition.x, cameraPosition.y, cameraPosition.z);
listener.reverbRayCount = 128;
listener.reverbBounceCount = 64;
// Determine how many rays to cast towards other emitters
listener.occlusionRayCount = 1024;
listener.occlusionBounceCount = 8;
world.addEmitter(listener);
const enemyEmitter = va.Emitter_Create();
enemyEmitter.name = 'Enemy';
enemyEmitter.setPosition(enemy.x, enemy.y, enemy.z);
enemyEmitter.reverbRayCount = 32;
enemyEmitter.reverbBounceCount = 64;
world.addEmitter(enemyEmitter);
listener.addTarget(enemyEmitter);
// Callback when raytracing completes
enemyEmitter.onRaytracedByAnotherEmitter = (other) =>
{
const filter = other.getTargetFilter(enemyEmitter);
const gainLF = filter.gainLF;
const gainHF = filter.gainHF;
// PSEUDOCODE - play a sound with a low pass filter
playSound('Footstep', enemyEmitter.position, filter);
};
VAEmitter* enemyEmitter;
void OnEnemyRaytracedByAnother(VAEmitter* other)
{
VALowPassFilter* filter = vaEmitterGetTargetFilter(other, enemyEmitter);
float gainLF = filter->gainLF;
float gainHF = filter->gainHF;
// PSEUDOCODE - play a sound with a low pass filter
play_sound(SOUND_FOOTSTEP, vaEmitterGetPosition(enemyEmitter), filter);
}
void CreateEmitters()
{
// Create listener and enemy
VAEmitter* listener = vaEmitterCreate();
vaEmitterSetName(listener, "Camera");
vaEmitterSetPosition(listener, cameraPosition);
vaEmitterSetReverbRayCount(listener, 128);
vaEmitterSetReverbBounceCount(listener, 64);
// Determine how many rays to cast towards other emitters
vaEmitterSetOcclusionRayCount(listener, 1024);
vaEmitterSetOcclusionBounceCount(listener, 8);
vaWorldAddEmitter(ctx, listener);
enemyEmitter = vaEmitterCreate();
vaEmitterSetName(enemyEmitter, "Enemy");
vaEmitterSetPosition(enemyEmitter, enemyPosition);
vaEmitterSetReverbRayCount(enemyEmitter, 32);
vaEmitterSetReverbBounceCount(enemyEmitter, 64);
vaWorldAddEmitter(ctx, enemyEmitter);
vaEmitterAddTarget(listener, enemyEmitter);
vaEmitterSetOnRaytracedByAnotherEmitterCallback(enemyEmitter, OnEnemyRaytracedByAnother);
}
Getting Started
Download the Vercidium Audio SDK from https://vercidium.com.
The SDK contains:
- .NET 8 build for C#
- .NET 10 Web Assembly build for browser, including a JavaScript wrapper
- (unstable) C build for Windows, Linux, Mac and Android
Use the dropdown in the top-left of this website to choose your preferred language. The content on each page will update to match.
You have selected: C#
You have selected: JS
You have selected: C
Troubleshooting
If you're facing issues with Vercidium Audio, please try the Troubleshooting steps or visit our support page.
C# Setup
The C# SDK comes with two builds:
- dev build, with debug window rendering
- production build
I recommend integrating with the dev build first, so you can check your raytracing scene is set up correctly in the debug window. Ensure your final game ships with the production build for maximum performance and compatibility.
To add Vercidium Audio to your C# project, add this to your .csproj file:
<!-- Add vaudio.dll to your project -->
<ItemGroup>
<Reference Include="vaudio">
<HintPath>path\to\your\vaudio\folder\vaudio.dll</HintPath>
</Reference>
</ItemGroup>
JavaScript / Web Assembly Setup
The JS SDK contains a vaudio-wrapper.js file and a _framework folder:
- the
_frameworkfolder contains the.wasmWeb Assembly binaries and JS .NET runtime - the JS wrapper provides a clean JS API, rather than invoking raw Web Assembly calls
To use this code you must initialise the .NET runtime and create the JS wrapper:
import { dotnet } from './_framework/dotnet.js';
import { initVA } from './vaudio-wrapper.js';
// Set this to the maximum number of threads you'll use
const YOUR_THREAD_COUNT = 4;
// Initialise the .NET runtime and wrap Web Assembly calls into JS objects with get/setters
const VA = await initVA(dotnet, YOUR_THREAD_COUNT);
C Setup
The C SDK package contains:
include/vaudio.h— single header with all declarationswindows/vaudionative.dllandwindows/vaudionative.liblinux/libvaudionative.somacos/libvaudionative.dylibandroid/<abi>/libvaudionative.so(arm64-v8a, armeabi-v7a, x86, x86_64)
Include the header and link against the library for your platform:
#include "vaudio.h"
Windows (MSVC):
cl your_app.c /I path\to\include /link path\to\windows\vaudionative.lib
Linux / macOS:
gcc your_app.c -I path/to/include -L path/to/linux -l vaudionative -o your_app
Copy the shared library (vaudionative.dll / libvaudionative.so / libvaudionative.dylib) to the same directory as your executable, or add its directory to your library search path (PATH on Windows, LD_LIBRARY_PATH on Linux, DYLD_LIBRARY_PATH on macOS).
Create a World
Create a world, which contains emitters, primitives and materials. Read more: Worlds.
const world = va.World_Create();
world.worldSize = { x: 100, y: 100, z: 100 };
int main(void)
{
VAWorld* ctx = vaWorldCreate();
vaWorldSetWorldSize(ctx, vaVectorCreate(100.0f, 100.0f, 100.0f));
}
Primitives
Create a copy of your game's world using low-poly primitives. Read more: Primitives.
var prism = new PrismPrimitive()
{
// Every primitive must have a material
material = MaterialType.Concrete,
// Size must be separate from transform
size = new Vector(15),
// Rotate and position the prism
transform = Matrix.CreateRotationX(MathF.PI / 4) *
Matrix.CreateTranslation(30, 30, 30)
};
world.AddPrimitive(prism);
const prism = va.PrismPrimitive_Create();
// Every primitive must have a material
prism.material = MaterialType.Concrete;
// Size must be separate from transform
prism.size = { x: 15, y: 15, z: 15 };
// Rotate and position the prism
prism.transform = va.Matrix.Multiply(
va.Matrix.CreateRotationY(Math.PI / 4),
va.Matrix.CreateTranslation(30, 30, 30)
);
world.addPrimitive(prism);
#include <math.h> // For M_PI
VAPrismPrimitive* prism = vaPrismPrimitiveCreate();
/ Every primitive must have a material
vaPrismPrimitiveSetMaterial(prism, VAMaterialConcrete);
// Size must be separate from transform
vaPrismPrimitiveSetSize(prism, vaVectorCreate(15.0f, 15.0f, 15.0f));
// Rotate and position the prism
VAMatrix rotation = vaMatrixCreateRotationX(M_PI / 4);
VAMatrix translation = vaMatrixCreateTranslation(30, 30, 30);
VAMatrix transform = vaMatrixMultiply(&rotation, &translation);
vaPrismPrimitiveSetTransform(prism, &transform);
vaWorldAddPrimitive(ctx, prism);
Emitters
Create a listener emitter that casts occlusion rays. Read more: Emitters.
const emitter = va.Emitter_Create();
emitter.setPosition(4, 4, 4);
emitter.occlusionRayCount = 256;
emitter.occlusionBounceCount = 8;
world.addEmitter(emitter);
VAEmitter* listener = vaEmitterCreate();
vaEmitterSetPosition(listener, vaVectorCreate(4, 4, 4));
vaEmitterSetOcclusionRayCount(listener, 256);
vaEmitterSetOcclusionBounceCount(listener, 8);
VAResult result = vaWorldAddEmitter(ctx, listener);
assert(result == VA_SUCCESS);
Create another emitter that is discovered by the listener:
var target = new Emitter()
{
Position = new Vector(10),
};
world.AddEmitter(target);
// Cast occlusion rays towards the emitter
listener.AddTarget(target);
// This callback is invoked when the listener raytraces the target
target.OnRaytracedByAnotherEmitter = (Emitter other) =>
{
// 'other' is the listener emitter
LowPassFilter filter = other.GetTargetFilter(target);
// Print low-pass filter gains
Console.WriteLine($"OnRaytracedByAnotherEmitter: {filter.gainLF}, {filter.gainHF}");
// PSEUDOCODE - play a sound
Godot.PlaySound(SoundType.Explosion, target.position, filter);
};
const target = va.Emitter_Create();
target.setPosition(10, 10, 10);
world.addEmitter(target);
// Cast occlusion rays towards the emitter
listener.addTarget(target);
// This callback is invoked when the listener raytraces the target
target.onRaytracedByAnotherEmitter = (other) => {
// 'other' is the listener emitter
const filter = other.getTargetFilter(target);
// Print low-pass filter gains
console.log(`onRaytracedByAnotherEmitter: ${filter.gainLF}, ${filter.gainHF}`);
// PSEUDOCODE - play a sound
PlaySound('/explosion.ogg', target.position, filter);
}
// This callback is invoked when the listener raytraces the target
void on_raytraced_by_another_emitter(VAEmitter* source, VAEmitter* target)
{
VALowPassFilter* filter = vaEmitterGetTargetFilter(source, target);
// Print low-pass filter gains
printf("on_raytraced_by_another_emitter: %f. GainHF: %f\n", filter->gainLF, filter->gainHF);
}
VAEmitter* target = vaEmitterCreate();
vaEmitterSetPosition(target, vaVectorCreate(10, 10, 10));
VAResult result = vaWorldAddEmitter(ctx, target);
assert(result == VA_SUCCESS);
// Cast occlusion rays towards the emitter
vaEmitterAddTarget(listener, target);
// Set the callback function on the target
vaEmitterSetOnRaytracedByAnotherEmitterCallback(target, on_raytraced_by_another_emitter);
Update Loop
Update the world every frame. This will perform raytracing and handle input for the debug rendering window.
public void Update()
{
// Update anything at any time (emitter, primitive, etc)
listener.Position = new Vector(20, 20, 20);
// Perform raytracing on background threads and invoke callbacks
world.Update();
}
function update()
{
// Update anything at any time (emitter, primitive, etc)
emitter.setPosition(20, 20, 20);
// Perform raytracing on background threads and invoke callbacks
ctx.update();
requestAnimationFrame(update);
}
requestAnimationFrame(update);
void Update()
{
// Update anything at any time (emitter, primitive, etc)
vaEmitterSetPosition(listener, vaVectorCreate(20, 20, 20));
// Perform raytracing on background threads and invoke callbacks
vaWorldUpdate(ctx);
}
Reverb
Enable reverb rays on an emitter and set the energy cap required for max reverb volume. Read more: Reverb.
listener.ReverbRayCount = 32;
listener.ReverbBounceCount = 64;
listener.ReverbEnergyCap = 0.2f;
listener.reverbRayCount = 32;
listener.reverbBounceCount = 64;
listener.reverbEnergyCap = 0.2;
vaEmitterSetReverbRayCount(listener, 32);
vaEmitterSetReverbBounceCount(listener, 64);
vaEmitterSetReverbEnergyCap(listener, 0.2f);
Then access reverb properties via a callback:
world.OnReverbUpdated = () =>
{
// Access processed properties
var returning = listener.ProcessedReverb.ReturningPercent;
Console.WriteLine($"{(int)(returning * 100)}% of energy returns to the listener");
// Access precalculated EAX properties
var decayTime = listener.EAX.DecayTime;
}
world.onReverbUpdated = () =>
{
// Access processed properties
const returning = listener.processedReverb.returningPercent;
console.log(`${Math.floor(returning * 100)}% of energy returns to the listener`);
// Access precalculated EAX properties
const decayTime = listener.eax.decayTime;
}
void OnReverbUpdated()
{
// Access processed properties
VAProcessedReverb* processed = vaEmitterGetProcessedReverb(listener);
printf("%f%% of energy returns to the listener", processed->returningPercent);
// Access precalculated EAX properties
VAEAXReverb* eax = vaEmitterGetEAX(listener);
float decayTime = eax->decayTime;
}
vaWorldSetOnReverbUpdatedCallback(ctx, OnReverbUpdated);
Ambience
Enable ambient rays on an emitter. Read more: Ambience.
listener.AmbientOcclusionRayCount = 128;
listener.AmbientOcclusionBounceCount = 8;
listener.AmbientPermeationRayCount = 128;
listener.AmbientPermeationBounceCount = 3;
listener.ambientOcclusionRayCount = 128;
listener.ambientOcclusionBounceCount = 8;
listener.ambientPermeationRayCount = 128;
listener.ambientPermeationBounceCount = 3;
vaEmitterSetAmbientOcclusionRayCount(listener, 128);
vaEmitterSetAmbientOcclusionBounceCount(listener, 8);
vaEmitterSetAmbientPermeationRayCount(listener, 128);
vaEmitterSetAmbientPermeationBounceCount(listener, 3);
Then access results via a callback:
emitter.OnRaytracingComplete = () =>
{
var ambientGainLF = listener.AmbientFilter.GainLF;
var ambientGainHF = listener.AmbientFilter.GainHF;
}
emitter.onRaytracingComplete = () =>
{
var ambientGainLF = listener.ambientFilter.gainLF;
var ambientGainHF = listener.ambientFilter.gainHF;
}
void OnRaytracingComplete()
{
VALowPassFilter* ambientFilter = vaEmitterGetAmbientFilter(listener);
float ambientGainLF = ambientFilter->gainLF;
float ambientGainHF = ambientFilter->gainHF;
}
vaEmitterSetOnRaytracingCompleteCallback(listener, OnRaytracingComplete);
Continue reading:
Advanced:
Materials
Each material has fields for:
- Absorption = how much energy is lost on each bounce
- Scattering = how much to alter the direction of the reflected ray
- Transmission = how much energy is lost when a ray passes through a 3D primitive
- PlaneTransmission = how much energy is lost when a ray passes through a flat primitive
The MaterialProperties classinterfacestruct contains all of these properties for a single material.
var concrete = world.GetMaterial(MaterialType.Concrete);
// Lose X% of energy on each bounce
concrete.AbsorptionLF = 0.2f; // 20% of low-frequency energy
concrete.AbsorptionHF = 0.3f; // 30% of high-frequency energy
// Randomise ray reflections by up to 45 degrees (1.0 = 90 degrees)
concrete.Scattering = 0.5f;
// Lose all energy after travelling X meters through geometry
concrete.TransmissionLF = 20.0f; // 20 meters
concrete.TransmissionHF = 10.0f; // 10 meters
// Lose X% of energy when passing through flat primitives Plane / Disk / Triangle primitives
concrete.PlaneTransmissionLF = 0.1f; // 10% of energy
concrete.PlaneTransmissionLF = 0.2f; // 20% of energy
var concrete = world.getMaterial(MaterialType.Concrete);
// Lose X% of energy on each bounce
concrete.absorptionLF = 0.2; // 20% of low-frequency energy
concrete.absorptionHF = 0.3; // 30% of high-frequency energy
// Randomise ray reflections by up to 45 degrees (1.0 = 90 degrees)
concrete.scattering = 0.5;
// Lose all energy after travelling X meters through geometry
concrete.transmissionLF = 20; // 20 meters
concrete.transmissionHF = 10; // 10 meters
// Lose X% of energy when passing through flat primitives Plane / Disk / Triangle primitives
concrete.planeTransmissionLF = 0.1; // 10% of energy
concrete.planeTransmissionLF = 0.2; // 20% of energy
// Lose X% of energy on each bounce
vaWorldSetMaterialAbsorptionLF(world, VAMaterialConcrete, 0.2f); // 20% of low-frequency energy
vaWorldSetMaterialAbsorptionHF(world, VAMaterialConcrete, 0.3f); // 30% of high-frequency energy
// Randomise ray reflections by up to 45 degrees (1.0 = 90 degrees)
vaWorldSetMaterialScattering(world, VAMaterialConcrete, 0.5f);
// Lose all energy after travelling X meters through geometry
vaWorldSetMaterialTransmissionLF(world, VAMaterialConcrete, 20.0f); // 20 meters
vaWorldSetMaterialTransmissionHF(world, VAMaterialConcrete, 10.0f); // 10 meters
// Lose X% of energy when passing through flat primitives Plane / Disk / Triangle primitives
vaWorldSetMaterialPlaneTransmissionLF(world, VAMaterialConcrete, 0.1f); // 10% of energy
vaWorldSetMaterialPlaneTransmissionHF(world, VAMaterialConcrete, 0.2f); // 20% of energy
Customising Materials
There are 23 default materials for common materials like dirt, grass, concrete, metal, cloth, etc. You can customise these directly on your raytracing world:
var concrete = world.GetMaterial(MaterialType.Concrete);
concrete.AbsorptionLF = 0.2f; // Lose 20% of low-frequency energy on each bounce
concrete.AbsorptionHF = 0.3f; // Lose 30% of high-frequency energy on each bounce
const concrete = world.getMaterial(MaterialType.Concrete);
concrete.absorptionLF = 0.2; // Lose 20% of low-frequency energy on each bounce
concrete.absorptionHF = 0.3; // Lose 30% of high-frequency energy on each bounce
// Lose 20% of low-frequency energy on each bounce
vaWorldSetMaterialAbsorptionLF(world, VAMaterialConcrete, 0.2f);
// Lose 30% of high-frequency energy on each bounce
vaWorldSetMaterialAbsorptionHF(world, VAMaterialConcrete, 0.3f);
Editing a material at runtime will clear the ray cache and cause all rays to be re-cast
Creating Materials
Materials can be created on a World object.
The first 1000 material IDs (0 to 999 inclusive) are reserved, so start from 1000:
const int ALIEN = 1000;
// Absorption Scattering Transmission
var alienProperties = new MaterialProperties(0.5f, 0.6f, 0.7f, 7, 3.5f, 0.1f, 0.2f);
var alienColour = new Color(255, 0, 255);
world.AddMaterial((MaterialType)ALIEN, alienProperties, alienColour);
const ALIEN = 1000;
// Absorption Scattering Transmission
const alienProperties = va.MaterialProperties_CreateWith(0.5, 0.6, 0.7, 7, 3.5, 0.1, 0.2);
world.addMaterial(ALIEN, alienProperties);
const int MATERIAL_ALIEN = 1000;
vaWorldCreateMaterial(world, MATERIAL_ALIEN);
vaWorldSetMaterialAbsorptionLF(world, MATERIAL_ALIEN, 0.5f);
vaWorldSetMaterialAbsorptionHF(world, MATERIAL_ALIEN, 0.6f);
vaWorldSetMaterialScattering(world, MATERIAL_ALIEN, 0.7f);
vaWorldSetMaterialTransmissionLF(world, MATERIAL_ALIEN, 7);
vaWorldSetMaterialTransmissionHF(world, MATERIAL_ALIEN, 3.5f);
vaWorldSetMaterialPlaneTransmissionLF(world, MATERIAL_ALIEN, 0.1f);
vaWorldSetMaterialPlaneTransmissionHF(world, MATERIAL_ALIEN, 0.2f);
Notes:
- The first 1000 values (0 to 999) in
MaterialTypeare reserved, so set your custom material IDs from 1000 onwards - Worlds are automatically initialised with all default materials
- You can update materials at any time, but your changes will only apply the next time raytracing occurs (i.e. the current background threads won't receive this change)
Transmission Helpers
The TransmissionLF and TransmissionHF fields represent how many meters a ray has to travel through this primitive until they lose all energy.
Internally, the energy reduction is performed using decibels per meter (dB/m), which is a tricky unit to work with. If you know the dB/m unit for your material, you can convert it to meters using the below helper functions:
const concrete = world.GetMaterial(MaterialType.Concrete);
// A ray will lose all low-frequency energy after travelling 20 meters through a concrete primitive
concrete.TransmissionLF = MaterialProperties.TransmissionToMeters(1.5f);
// A ray will lose all high-frequency energy after travelling 5 meters through a concrete primitive
concrete.TransmissionHF = MaterialProperties.TransmissionToMeters(6.0f);
import { transmissionToMeters, MaterialType } from 'vaudio-wrapper.js';
const concrete = world.getMaterial(MaterialType.Concrete);
// A ray loses all low-frequency energy after travelling 20 meters through a concrete primitive
concrete.transmissionLF = transmissionToMeters(1.5f);
// A ray loses all high-frequency energy after travelling 5 meters through a concrete primitive
concrete.transmissionHF = transmissionToMeters(6.0f);
// A ray loses all low-frequency energy after travelling 20 meters through a concrete primitive
vaWorldSetMaterialTransmissionLF(world, VAMaterialConcrete, materialPropertiesTransmissionToMeters(1.5f));
// A ray loses all high-frequency energy after travelling 5 meters through a concrete primitive
vaWorldSetMaterialTransmissionHF(world, VAMaterialConcrete, materialPropertiesTransmissionForThickness(6.0f));
By default, 1 world unit is 1 meter. You can change this via the
World.MetersPerUnitfieldWorld.metersPerUnitfieldvaWorldSetMetersPerUnit()function.
Validation
Editing material fields may throw an exception if it violates any of the below:
- All values must not be
NaNorInfinity Absorption,ScatteringandPlaneTransmissionvalues must be in the range0.0f - 1.0f(inclusive)Transmissionvalues must be in the range0 - float.MaxValue(inclusive)
Muffling
To determine how muffled an emitter is, you must cast occlusion or permeation rays towards it from another emitter (usually your listener).
const listener = va.Emitter_Create();
listener.occlusionRayCount = 1024;
listener.occlusionBounceCount = 8;
listener.permeationRayCount = 128;
listener.permeationBounceCount = 3;
world.addEmitter(listener);
const target = va.Emitter_Create();
world.addEmitter(target);
// Enable muffling
listener.addTarget(target);
VAEmitter* listener = vaEmitterCreate();
vaEmitterSetOcclusionRayCount(listener, 1024);
vaEmitterSetOcclusionBounceCount(listener, 8);
vaEmitterSetPermeationRayCount(listener, 128);
vaEmitterSetPermeationBounceCount(listener, 3);
vaWorldAddEmitter(ctx, listener);
VAEmitter* target = vaEmitterCreate();
vaWorldAddEmitter(ctx, target);
// Enable muffling
vaEmitterAddTarget(listener, target);
AddTarget()addTarget()vaEmitterAddTarget()will throw an exception if both occlusion and permeation are disabled (ray/bounce count is 0) on the listener
Occlusion
Occlusion rays bounce around the environment and lose energy based on the materials they hit and the distance they travel. On each bounce they check for line-of-sight (LOS) with each of the emitter's targets.
Once LOS is found, the ray stops checking for LOS. Occlusion rays only find the shortest path to each target - they are designed for speed.
The maximum energy an occlusion ray can have is 1.0, which means it lost no energy before achieving LOS with the target (material absorption is zero, and air absorption is disabled).
The minimum energy it can have is 0.0, which means it did not achieve LOS with the target.
Permeation
Permeation rays are similar to occlusion rays, but produce more realistic results at the expense of speed. They bounce around the environment and do not lose energy based on materials or air absorption. Instead, on each bounce they cast an extra ray directly towards each target, travelling through primitives and losing energy based on:
- the transmission of the primitive's material (decibel loss per meter)
- the thickness of the primitive
The maximum energy a permeation ray can have is 1.0 x permeationBounceCount, as it accumulates energy on each bounce.
The minimum energy a permeation ray can have is 0.0, which means the geometry is so thick that no energy permeated through.
Converting Energy to Low Pass Filters
Occlusion and permeation energy are converted to low-frequency and high-frequency filter gains via the Emitter.GainFormula functionemitter.occlusionEnergyCap and emitter.permeationEnergyCap fieldsvaEmitterSetGainFormula function.
Read more: Emitter Gain Formula
Accessing Low Pass Filters
When a target emitter is first raytraced, its low-pass filter can be accessed via a callback:
var target = new Emitter();
target.OnRaytracedByAnotherEmitter = (Emitter other) =>
{
var filter = other.GetTargetFilter(target);
// These fields contain the results of GainFormula
// and range from 0.0f to 1.0f
var gainLF = filter.gainLF;
var gainHF = filter.gainHF;
// PSUEDOCODE - apply the gains to a low pass filter
Godot.ApplyLowPassFilter(sound, gainLF, gainHF);
// Play the sound AFTER setting the filter, so it's muffled
// correctly from the beginning
Godot.PlaySound(sound);
}
world.AddEmitter(target);
const target = va.Emitter_Create();
target.onRaytracedByAnotherEmitter = (other) => {
const filter = other.getTargetFilter(target);
// These fields contain the results of gainFormula
// and range from 0.0 to 1.0
const gainLF = filter.gainLF;
const gainHF = filter.gainHF;
// PSEUDOCODE - apply the gains to a low pass filter
applyLowPassFilter(sound, gainLF, gainHF);
// Play the sound AFTER setting the filter, so it's muffled
// correctly from the beginning
playSound(sound);
};
world.addEmitter(target);
void onRaytracedByAnotherEmitter(VAEmitter* source, VAEmitter* target)
{
VALowPassFilter* filter = vaEmitterGetTargetFilter(source, target);
// These fields contain the results of the gain formula
// and range from 0.0f to 1.0f
float gainLF = filter->gainLF;
float gainHF = filter->gainHF;
// PSEUDOCODE - apply the gains to a low pass filter
applyLowPassFilter(sound, gainLF, gainHF);
// Play the sound AFTER setting the filter, so it's muffled
// correctly from the beginning
playSound(sound);
}
vaEmitterSetOnRaytracedByAnotherEmitterCallback(target, onRaytracedByAnotherEmitter);
vaWorldAddEmitter(ctx, target);
Once the emitter has been raytraced, its filter will be updated every frame and can be accessed at any time:
if (listener.HasRaytracedTarget(target))
{
var filter = listener.GetTargetFilter(target);
var gainLF = filter.gainLF;
var gainHF = filter.gainHF;
// PSEUDOCODE
Godot.UpdateLowPassFilter(sound, gainLF, gainHF);
};
if (listener.hasRaytracedTarget(target)) {
const filter = listener.getTargetFilter(target);
const gainLF = filter.gainLF;
const gainHF = filter.gainHF;
// PSEUDOCODE
updateLowPassFilter(sound, gainLF, gainHF);
}
if (vaEmitterHasRaytracedTarget(listener, target))
{
VALowPassFilter* filter = vaEmitterGetTargetFilter(listener, target);
float gainLF = filter->gainLF;
float gainHF = filter->gainHF;
// PSEUDOCODE
updateLowPassFilter(sound, gainLF, gainHF);
}
Optimisations
For short sounds like gunfire and footsteps, I recommend deleting the emitter once it has been raytraced. It's not worth continuously updating an emitter that only plays a short sound:
const target = va.Emitter_Create();
target.onRaytracedByAnotherEmitter = (other) => { /* ... */ };
// This is invoked after onRaytracedByAnotherEmitter
target.onRaytracingComplete = () => {
world.removeEmitter(target);
};
world.addEmitter(target);
VAEmitter* target = vaEmitterCreate();
vaEmitterSetOnRaytracedByAnotherEmitterCallback(target, onRaytracedByAnotherEmitter);
// This is invoked after onRaytracedByAnotherEmitter
void onRaytracingComplete(void)
{
vaWorldRemoveEmitter(ctx, target);
}
vaEmitterSetOnRaytracingCompleteCallback(target, onRaytracingComplete);
vaWorldAddEmitter(ctx, target);
If an entity in your game will play many sounds (e.g. an enemy playing footsteps), I recommend setting the emitter on the entity itself, and re-use it for multiple sounds.
In this case you wouldn't play the sound in the OnRaytracedByAnotherEmitter() callback - instead you'd access the filter via listener.GetTargetFilter(target) each time you play a footstep / gunfire sound.
listener.GetTargetFilter()will throw an exception if you access it before the listener has raytraced the emitter. It's worth waiting for theOnRaytracedByAnotherEmitter()callback to fire first, or checklistener.HasRaytracedTarget(target)when playing each sound
Primitives
Vercidium Audio operates against a low-poly copy of the world, similar to a physics engine.
To add a primitive to the simulation, invoke the AddPrimitive function on the world:
var prism = new PrismPrimitive()
{
// Every primitive must have a material
material = MaterialType.Metal,
// Size MUST be separate to the transform
size = new Vector(15),
// Rotate and position it
transform = Matrix.CreateRotationX(MathF.PI / 4) *
Matrix.CreateTranslation(50, 50, 50)
};
world.AddPrimitive(prism);
// Create a rectangular prism
const prism = va.PrismPrimitive_Create();
prism.material = MaterialType.Concrete;
prism.size = { x: 15, y: 15, z: 15 };
prism.transform = va.Matrix.Multiply(
va.Matrix.CreateRotationY(Math.PI / 4),
va.Matrix.CreateTranslation(50, 50, 50)
);
world.addPrimitive(prism);
// Create a rectangular prism
VAPrismPrimitive* prism = vaPrismPrimitiveCreate();
vaPrismPrimitiveSetMaterial(prism, VAMaterialConcrete);
vaPrismPrimitiveSetSize(prism, vaVectorCreate(15.0f, 15.0f, 15.0f));
VAMatrix rotation = vaMatrixCreateRotationY(M_PI / 4);
VAMatrix translate = vaMatrixCreateTranslation(50, 50, 50);
VAMatrix transform = vaMatrixMultiply(&rotation, &translate);
vaPrismPrimitiveSetTransform(prism, &transform);
vaWorldAddPrimitive(world, prism);
An exception will be thrown if a primitive is created with
MaterialType.AirMaterialType.AirVAMaterialAir.
Below is the full list of primitives.
- Flat primitives:
- Prism primitives:
- Circular primitives:
- Cone primitives:
- Complex primitives:
Updating Primitives
The position/rotation/material/etc of primitives can be updated at any time, even while the background raytracing threads are running. The updates will apply when the background threads finish running.
When a primitive is updated, it will automatically be flagged as 'dirty'. The world keeps track of all dirty primitives, and will refresh the bounding volume hierarchy (BVH) that is used to speed up raytracing intersections.
This all occurs on background threads so there should be minimal impact on the main thread, but raytracing will be slightly delayed by this preparation work. You can check how long preparation and BVH updates take via the world.PreparationTime field.
Instanced Meshes
To reduce memory usage and improve performance, you can create multiple mesh primitives using the same vertex data.
First create a MeshMeshVAMesh, then pass it in the constructor to each mesh primitive:
// vertices is a flat array of numbers [ x0,y0,z0, x1,y1,z1, ... ]
const mesh = va.Mesh_Create(vertices, minX, minY, minZ, maxX, maxY, maxZ);
const transformA = va.Matrix.CreateTranslation(0, 10, 0);
const transformB = va.Matrix.CreateTranslation(0, 20, 0);
const instanceA = va.MeshPrimitive_CreateFromMesh(MaterialType.Concrete, mesh, transformA);
const instanceB = va.MeshPrimitive_CreateFromMesh(MaterialType.Wood, mesh, transformB);
world.addPrimitive(instanceA);
world.addPrimitive(instanceB);
// vertices can either be a List<Vector>, Vector[] or Vector*
var mesh = new Mesh(vertices, minBounds, maxBounds);
// Create two instances of the same mesh
var instanceA = new MeshPrimitive(MaterialType.Concrete, mesh, Matrix.CreateTranslation(0, 10, 0));
var instanceB = new MeshPrimitive(MaterialType.Metal, mesh, Matrix.CreateTranslation(0, 20, 0));
world.AddPrimitive(instanceA);
world.AddPrimitive(instanceB);
// Build the shared mesh once
VAMesh* mesh = vaMeshCreate(vertices, vertexCount, minBounds, maxBounds);
// Create two instances of the same mesh
VAMatrix transformA = vaMatrixCreateTranslation(0, 10, 0);
VAMatrix transformB = vaMatrixCreateTranslation(0, 20, 0);
VAMeshPrimitive* instanceA = vaMeshPrimitiveCreateFromMesh(VA_MATERIAL_CONCRETE, mesh, &transformA);
VAMeshPrimitive* instanceB = vaMeshPrimitiveCreateFromMesh(VA_MATERIAL_METAL, mesh, &transformB);
vaWorldAddPrimitive(world, instanceA);
vaWorldAddPrimitive(world, instanceB);
// Cleanup: destroy primitives first, then the shared mesh
vaWorldRemovePrimitive(world, instanceA);
vaWorldRemovePrimitive(world, instanceB);
vaMeshPrimitiveDestroy(instanceA);
vaMeshPrimitiveDestroy(instanceB);
// Returns VA_ERROR_IN_USE if it's still being used in background threads. Retry
// the destroy later in the vaWorldSetOnRaytracingCompletedCallback callback
vaMeshDestroy(mesh);
See MeshPrimitive for the full API.
Reverb
This file is an overview - more details are provided on each sub-page.
Enable Reverb
Enable reverb on an emitter by providing the following settings:
var listener = new Emitter()
{
// Required
ReverbRayCount = 32,
ReverbBounceCount = 64,
ReverbEnergyCap = 0.2f,
// Optional
MaxEchogramTime = 5000, // milliseconds
EchogramGranularity = 50, // milliseconds
AffectsGroupedEAX = false,
HasRelativeReverb = false,
RelativeReverbInnerThreshold = 0.5f,
RelativeReverbOuterThreshold = 0.8f,
};
world.AddEmitter(listener);
const emitter = va.Emitter_Create();
// Required
emitter.reverbRayCount = 32;
emitter.reverbBounceCount = 64;
emitter.reverbEnergyCap = 0.2f;
// Optional
emitter.maxEchogramTime = 5000; // milliseconds
emitter.echogramGranularity = 50; // milliseconds
emitter.affectsGroupedEAX = false;
emitter.hasRelativeReverb = false;
emitter.relativeReverbInnerThreshold = 0.5;
emitter.relativeReverbOuterThreshold = 0.8;
world.addEmitter(emitter);
VAEmitter* emitter = vaEmitterCreate();
// Required
vaEmitterSetReverbRayCount(emitter, 32);
vaEmitterSetReverbBounceCount(emitter, 64);
vaEmitterSetReverbEnergyCap(emitter, 0.2f);
// Optional
vaEmitterSetMaxEchogramTime(emitter, 5000); // milliseconds
vaEmitterSetEchogramGranularity(emitter, 50); // milliseconds
vaEmitterSetAffectsGroupedEAX(emitter, false);
vaEmitterSetHasRelativeReverb(emitter, false);
vaEmitterSetRelativeReverbInnerThreshold(emitter, 0.5f);
vaEmitterSetRelativeReverbOuterThreshold(emitter, 0.8f);
vaWorldAddEmitter(ctx, emitter);
I recommend having a much higher bounce count for reverb than other ray types (occlusion, permeation, etc) to ensure the echogram is filled. Read more about each reverb setting in the sections below.
Reverb Energy Cap
To control how much energy is required for reverb to be at max volume, you can set an energy cap on each emitter. Read more: Reverb Energy Cap.
Echograms
An emitter's echogram must be set up correctly for reverb to sound correct. Read more: Echogram Refinement.
Access Reverb Results
Reverb results are exposed via the OnReverbUpdated callback on the World. This gives you a chance to create/update reverb effects before playing a sound.
var world = new World()
{
OnReverbUpdated = () =>
{
// Access reverb stats
Console.WriteLine(listener.RawReverb.ReturnedTotal);
Console.WriteLine(listener.ProcessedReverb.OutsidePercent);
Console.WriteLine(listener.EAX.DecayTime);
// Update reverb effects
// ...
}
};
world.onReverbUpdated = () => {
// Access reverb stats
console.log(listener.rawReverb.returnedTotal);
console.log(listener.processedReverb.outsidePercent);
console.log(listener.eax.decayTime);
// Update reverb effects
// ...
};
void OnReverbUpdated() {
// Access reverb stats
printf("%f\n", vaEmitterGetRawReverb(emitter)->returnedTotal);
printf("%f\n", vaEmitterGetProcessedReverb(emitter)->outsidePercent);
printf("%f\n", vaEmitterGetEAX(emitter)->decayTime);
// Update reverb effects
// ...
}
vaWorldSetOnReverbUpdatedCallback(ctx, OnReverbUpdated);
The values in the EAX object can be copied directly onto an EAX reverb effect in your engine.
If you wish to calculate these values yourself, see Custom EAX Formulas.
If you wish to calculate these values yourself, see Custom EAX Formulas.
Planned feature: calculate reverb properties for Godot, Unreal, FMOD, Wwise reverb systems
Raw Reverb
Emitter.RawReverb contains the raw results of raytracing, such as the total energy in all rays returning to the listener. Read more: RawReverb.
Processed Reverb
Emitter.ProcessedReverb contains the results of raytracing that have been processed into a more dev-friendly format. Read more: ProcessedReverb.
Grouped EAX
Every emitter has its own EAX object, but it's expensive to run many reverb effects in real time. The engine will group emitters with similar reverb properties together. These grouped EAX objects can be accessed in the world.GroupedEAX list.
Read more: Grouped EAX.
Directional Reverb
The direction and volume of each Grouped EAX can be calculated relative to an emitter.
To enable this, set relative reverb to true on the emitter that you would like to hear directional reverb from (usually just your main listener emitter):
var listener = new Emitter()
{
HasRelativeReverb = true,
};
const listener = va.Emitter_Create();
listener.hasRelativeReverb = true;
VAEmitter* listener = vaEmitterCreate();
vaEmitterSetHasRelativeReverb(listener, true);
Then access the relative direction and volume on each entry in world.GroupedEAX:
world.OnReverbUpdated = () =>
{
foreach (var eax in world.GroupedEAX)
{
Vector pan = eax.RelativeDirections[listener];
float gain = eax.RelativeGains[listener];
}
};
world.onReverbUpdated = () => {
for (const eax of world.groupedEAX) {
const pan = eax.relativeDirections(listener);
const gain = eax.relativeGains(listener);
}
};
void OnReverbUpdated()
{
const VAEAXReverb** grouped_eax = vaWorldGetGroupedEAX(ctx);
int count = vaWorldGetGroupedEAXCount(ctx);
for (int i = 0; i < count; i++)
{
VAVector* pan = vaEAXReverbGetRelativeDirection(grouped_eax[i], listener);
float* gain = vaEAXReverbGetRelativeGain(grouped_eax[i], listener);
}
}
Read more: Volume and Direction.
Troubleshooting
If this documentation isn't helping, please visit our support page.
First add vaudio.dll as a dependency to your project, as well as helpers to copy the dependencies and resource folder to the output directory:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<!-- Add vaudio.dll to your project -->
<ItemGroup>
<Reference Include="vaudio">
<HintPath>path\to\your\vaudio\folder\vaudio.dll</HintPath>
</Reference>
</ItemGroup>
</Project>
Then create a World and run the project:
using vaudio;
static void Main(string[] args)
{
var world = new World()
{
WorldSize = new(100),
RenderingEnabled = true,
}
while (true)
{
world.Update();
System.Threading.Thread.Sleep(16);
}
}
The first time you run this, you will likely face some errors. Below is a list of solutions for each error.
Native Libraries Failed to Load
When rendering is enabled, vaudio extracts native libraries (glfw3.dll, libSkiaSharp.dll, libHarfBuzzSharp.dll) to a temp folder on first run. If this fails, you will see an exception like:
IOException: Vercidium Audio could not extract 'glfw3.dll' to 'C:\Users\...\AppData\Local\Temp\vaudio\1.1.2\'.
Permission issue — the process does not have write access to the temp folder. Check that %TEMP% is writable by the current user.
Antivirus blocking — some AV software quarantines DLLs extracted to temp by unrecognised processes. Add an exclusion for %TEMP%\vaudio\ in your AV settings.
First, serve the wasm folder over HTTP — the .wasm binaries and .NET runtime cannot be loaded from a file:// URL. Use any local dev server:
npx serve .
Then initialise the .NET runtime and create the JS wrapper. Read more: Getting Started.
The first time you run this, you will likely face some errors. Below is a list of solutions for each error.
SharedArrayBuffer Not Available
Multi-threading requires SharedArrayBuffer, which browsers only expose on cross-origin isolated pages. You will see an error like:
SharedArrayBuffer is not defined
Your server must send these two HTTP headers on every response:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Most dev servers support custom headers in a config file. For example, with Vite (vite.config.js):
export default {
server: {
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
},
};
Module Type Error
If you see an error like:
Cannot use import statement in a non-module world
Make sure your package.json contains "type": "module". The wasm folder already includes this file — if you copy files into your own project, include it too.
Thread Count
If the browser freezes or raytracing appears to stall, the thread pool may be too small. The .NET runtime needs 6 threads of its own. Set pthreadPoolInitialSize to at least YOUR_THREAD_COUNT + 6:
const { getAssemblyExports, getConfig } = await dotnet
.withConfig({
jsThreadBlockingMode: "DangerousAllowBlockingWait",
pthreadPoolInitialSize: YOUR_THREAD_COUNT + 6,
pthreadPoolUnusedSize: 0,
})
.create();
Without jsThreadBlockingMode: "DangerousAllowBlockingWait", all calls into Web Assembly are asynchronous, which is more difficult to work with.
First, copy the shared library for your platform next to your executable:
| Platform | File |
|---|---|
| Windows | windows/vaudionative.dll |
| Linux | linux/libvaudionative.so |
| macOS | macos/libvaudionative.dylib |
| Android | android/<abi>/libvaudionative.so |
Then include the header and link against the import library:
#include "vaudio.h"
The first time you run this, you will likely face some errors. Below is a list of solutions for each error.
Missing Shared Library
On Windows, if you see:
The code execution cannot proceed because vaudionative.dll was not found.
Copy windows/vaudionative.dll to the same directory as your executable, or add its directory to PATH.
On Linux/macOS, if you see:
error while loading shared libraries: libvaudionative.so: cannot open shared object file
Add the library's directory to LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS):
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH # Linux
export DYLD_LIBRARY_PATH=/path/to/lib:$DYLD_LIBRARY_PATH # macOS
Linker Error (Windows / MSVC)
If the project compiles but you get unresolved external symbol errors at link time, make sure you are linking against windows/vaudionative.lib:
cl your_app.c /I path\to\include /link path\to\windows\vaudionative.lib
Linker Error (Linux / macOS)
Pass the library directory with -L and the library name with -l:
gcc your_app.c -Ipath/to/include -Lpath/to/linux -lvaudionative -o your_app
Visualisation
Visualisation rays are cast in random directions, and the position of each bounce can be used to render symbols/particles in your game.
To enable audio visualisation on an emitter, set these four settings:
var emitter = new Emitter()
{
VisualisationRayCount = 32,
VisualisationBounceCount = 3,
VisualisationUpdateFrequency = 500, // milliseconds
VisualisationCallback = (VisualisationData[] data) =>
{
for (int i = 0; i < data.Length; i++)
{
var position = data[i].position;
var normal = data[i].normal;
}
}
};
const emitter = va.Emitter_Create();
emitter.visualisationRayCount = 32;
emitter.visualisationBounceCount = 3;
emitter.visualisationUpdateFrequency = 500; // milliseconds
emitter.visualisationCallback = (data) =>
{
for (let i = 0; i < data.length; i++)
{
var position = data[i].position;
var normal = data[i].normal;
}
};
void VisualisationCallback(VAVisualisationData* data, int count)
{
for (var i = 0; i < count; i++)
{
var position = data[i].position;
var normal = data[i].normal;
}
}
void Initialise()
{
VAEmitter* emitter = vaEmitterCreate();
vaEmitterSetVisualisationRayCount(emitter, 32);
vaEmitterSetVisualisationBounceCount(emitter, 3);
vaEmitterSetVisualisationUpdateFrequency(emitter, 500); // milliseconds
vaEmitterSetVisualisationCallback(emitter, VisualisationCallback);
}
VisualisationUpdateFrequencyvisualisationUpdateFrequencyvaEmitterSetVisualisationUpdateFrequency() controls how often visualisation rays should be cast.
VisualisationCallbackvisualisationCallbackThe callback passed to vaEmitterSetVisualisationCallback() will be invoked on the main thread when raytracing completes.
It's up to you to render shapes/symbols/etc in your game. The simplest approach is to copy the position and normal data directly to an instance buffer, similar to a particle system.
Worlds
A world is composed of:
- 3D Primitives like spheres, prisms, etc
- 3D Emitters that cast rays and can be discovered by other emitters
- Materials that affect how rays lose energy
- Many more configurable settings
Settings can be customised at any time, either when creating a world or at runtime:
const world = vaudio.World_Create();
world.position = { x: 0, y: 0, z: 0 };
world.size = { x: 100, y: 100, z: 100 };
VAWorld* ctx = vaWorldCreate();
vaWorldSetPosition(ctx, vaVectorCreate(0, 0, 0));
vaWorldSetSize(ctx, vaVectorCreate(100, 100, 100));
The full list of fields and methods is available here: World.
Air Absorption
Customise humidity, temperature and pressure settings, or provide your own custom formula. Read more: Air Absorption.
world.AirAbsorption = new AirAbsorptionSettings()
{
HumidityPercent = 0.4f,
TemperatureCelsius = 26
};
const airAbsorption = vaudio.AirAbsorptionSettings_Create();
airAbsorption.humidityPercent = 0.4;
airAbsorption.temperatureCelsius = 26;
world.airAbsorption = airAbsorption;
VAAirAbsorptionSettings* airAbsorption = vaAirAbsorptionCreate();
vaAirAbsorptionSetHumidity(airAbsorption, 0.4f);
vaAirAbsorptionSetTemperature(airAbsorption, 26);
vaWorldSetAirAbsorption(ctx, airAbsorption);
Materials
Customise absorption, scattering and transmission values for existing materials, or create new materials. Read more: Materials.
var metal = world.GetMaterial(MaterialType.Metal);
metal.AbsorptionLF = 0.1f;
const metal = world.getMaterial(MaterialType.Metal);
metal.absorptionLF = 0.1;
vaWorldSetMaterialAbsorptionLF(ctx, VAMaterialMetal, 0.1f);
CustomEAXFormulas
This class allows overriding the default formulas for calculating EAX reverb properties. Read more: Custom EAX Formulas.
Dispose
To safely dispose a raytracing world, we need to wait for the background threads to finish. To do this, set the world.PendingShutdown to trueset the world.pendingShutdown field to truecall vaWorldSetPendingShutdown and continue calling world.Update()world.update()vaWorldUpdate. This flag indicates to the world that it should not submit any more work to the background threads.
Then check world.AreThreadsRunningworld.areThreadsRunningvaWorldGetThreadsRunning to determine when the world is safe to dispose.
World world;
public void Update()
{
if (world == null)
return;
// Tell the world we would like to shut it down
world.PendingShutdown = true;
world.Update();
// Dispose everything when threads are no longer running
if (world.PendingShutdown && !world.AreThreadsRunning)
{
world.Dispose();
world = null;
// Forget about emitters and prisms
listener = null;
prism = null;
}
}
let world;
function update()
{
if (world == null)
return;
// Tell the world we would like to shut it down
world.pendingShutdown = true;
world.update();
// Dispose everything when threads are no longer running
if (world.pendingShutdown && !world.areThreadsRunning)
{
world.dispose();
world = null;
// Forget about emitters and prisms
listener = null;
prism = null;
}
}
VAWorld* ctx;
bool shouldShutdown = true;
void update()
{
if (ctx == NULL)
return;
// Tell the world we would like to shut it down
if (shouldShutdown)
vaWorldSetPendingShutdown(ctx);
vaWorldUpdate(ctx);
// Dispose everything when threads are no longer running
if (shouldShutdown && !vaWorldGetThreadsRunning(ctx))
{
// Free the world
vaWorldFree(ctx);
// Free all your emitters/primitives
vaEmitterDestroy(listener);
vaPrismPrimitiveFree(prism);
ctx = NULL;
}
}
Echogram Refinement
Empty Bins
EAX properties can only be calculated correctly if the emitter's echogram contains enough data. Echograms are rendered in the debug window, and show how much energy is returning back to the emitter over time. The length of the echogram is determined by Emitter.MaxEchogramTimeemitter.maxEchogramTimevaEmitterSetMaxEchogramTime(), and the duration of each 'bin' (a bar in the image below) is controlled by Emitter.EchogramGranularityemitter.echogramGranularityvaEmitterSetEchogramGranularity().
Red bars indicates bins with zero energy, and they break the calculation of EAX properties such as decay time, diffusion, density and delay:

To ensure there are no gaps in your echograms, you can either:
- increase the echogram granularity, so there are fewer bins
- increase the amount of reverb rays, so more data is populated in the echogram
An echogram with higher granularity has fewer bins and is less likely to have gaps:

Emitter.EchogramGranularityemitter.echogramGranularityvaEmitterSetEchogramGranularity() defaults to 50 (milliseconds) - increase it to 60, 80, 100, etc. until your echogram no longer has any red gaps. Alternatively, increase the number of reverb rays until all bins are populated.
Max Duration
If you have a larger environment, or highly reflective materials, you may need to increase Emitter.MaxEchogramTimeemitter.maxEchogramTimevaEmitterSetMaxEchogramTime() to fully capture the reverb tail.
For example, for a large building with a reflective concrete material, an echogram with 1000ms worth of data looks like this:

The bars on the far right still contain energy, which means our echogram isn't long enough. I recommend increasing Emitter.MaxEchogramTimeemitter.maxEchogramTimevaEmitterSetMaxEchogramTime() until you see red bars at the end:

This means the echogram is now large enough to capture reverb for the 'worst case' scenario, i.e. the largest/most reflective environment in your game. This ensures all environments in your game will have correct reverb as well.
Summary
In the image below:
- the Main echogram has correct granularity (no gaps) and length (red bars are only at the end)
- the Rabbit echogram is too sparse (too many gaps). Solution: increase
Emitter.EchogramGranularityemitter.echogramGranularityvaEmitterSetEchogramGranularity()(cheap) orEmitter.ReverbRayCountemitter.reverbRayCountvaEmitterSetReverbRayCount()(more expensive) - the Speech echogram is too long. Solution: reduce
Emitter.MaxEchogramTimeemitter.maxEchogramTimevaEmitterSetMaxEchogramTime()

Grouped EAX
It's expensive to run many reverb effects in real time, so it's best to group emitters with similar reverb properties together.
Set MaximumGroupedEAXCountmaximumGroupedEAXCountvaWorldSetMaximumGroupedEAXCount() to the number of reverb effects your game has, and set AffectsGroupedEAXaffectsGroupedEAXvaEmitterSetAffectsGroupedEAX() to true on the emitters that should be grouped together. I recommend setting this to false on your listener emitter, and true on all other emitters.
const world = va.createWorld();
world.maximumGroupedEAXCount = 4;
const emitter = va.createEmitter();
emitter.affectsGroupedEAX = true;
world.addEmitter(emitter);
VAWorld* world = vaWorldCreate();
vaWorldSetMaximumGroupedEAXCount(world, 4);
VAEmitter* emitter = vaEmitterCreate();
vaEmitterSetAffectsGroupedEAX(emitter, true);
vaWorldAddEmitter(world, emitter);
Grouping Logic
All emitters with AffectsGroupedEAXaffectsGroupedEAXvaEmitterGetAffectsGroupedEAX() set to true will have their EAX objects iteratively combined together to produce N grouped EAX objects.
EAX objects are compared via their:
- Outside percent
- Returning percent
- EAX decay time
- EAX low-frequency gain
- EAX high-frequency gain
Accessing Grouped EAX
Grouped EAX objects can be accessed via World.GroupedEAXWorld.groupedEAXvaWorldGetGroupedEAX(). This list will be empty until raytracing has run at least once - I recommend accessing this list in the OnReverbUpdated callbackonReverbUpdated callbackcallback passed to vaWorldSetOnReverbUpdatedCallback():
List<ALReverbEffect> effects = [];
world.OnReverbUpdated = () =>
{
for (int i = 0; i < world.GroupedEAX.Count; i++)
{
var grouped = world.GroupedEAX[i];
// Copy properties to an OpenAL EAX reverb effect (or whichever reverb system your game uses)
effects[i].DecayTime = grouped.DecayTime;
effects[i].GainLF = grouped.GainLF;
// etc...
}
}
world.onReverbUpdated = () => {
for (let i = 0; i < world.groupedEAX.length; i++) {
const grouped = world.groupedEAX[i];
// Copy properties to an OpenAL EAX reverb effect (or whichever reverb system your game uses)
effects[i].decayTime = grouped.decayTime;
effects[i].gainLF = grouped.gainLF;
// etc...
}
};
void OnReverbUpdated()
{
int count = vaWorldGetGroupedEAXCount(world);
const VAEAXReverb** groupedEAX = vaWorldGetGroupedEAX(world);
for (int i = 0; i < count; i++)
{
const VAEAXReverb* grouped = groupedEAX[i];
// Copy properties to an OpenAL EAX reverb effect (or whichever reverb system your game uses)
effects[i].decayTime = grouped->decayTime;
effects[i].gainLF = grouped->gainLF;
// etc...
}
}
void Initialise()
{
vaWorldSetOnReverbUpdatedCallback(world, OnReverbUpdated);
}
When an emitter finishes raytracing, use its GroupedEAXIndexgroupedEAXIndexvaEmitterGetGroupedEAXIndex() to select the correct reverb effect to use for that emitter's sound playback:
emitter.OnRaytracedByAnotherEmitter = (Emitter other) =>
{
ALReverbEffect effect = effects[emitter.GroupedEAXIndex];
// OpenAL - apply the reverb effect to the source
AL.Source3i(sourceID, AL.AL_AUXILIARY_SEND_FILTER, effect.effectSlotID, 0, 0);
}
emitter.onRaytracedByAnotherEmitter = (other) => {
const effect = effects[emitter.groupedEAXIndex];
// WebAudio - apply the reverb effect to the source
// ...
};
void onRaytracedByAnotherEmitter(VAEmitter* source, VAEmitter* target)
{
int index = vaEmitterGetGroupedEAXIndex(target);
// Apply the reverb effect at index to the source
// ...
}
vaEmitterSetOnRaytracedByAnotherEmitterCallback(emitter, onRaytracedByAnotherEmitter);
Ensure you've set up a callback for World.OnReverbUpdatedWorld.onReverbUpdatedvaWorldSetOnReverbUpdatedCallback(), so that you can update your reverb effects before playing sounds in the Emitter.OnRaytracedByAnotherEmitter callbackEmitter.onRaytracedByAnotherEmitter callbackcallback passed to vaEmitterSetOnRaytracedByAnotherEmitterCallback
Volume and Direction
The volume and direction of an emitter's reverb is relative. For example, an emitter in the same room as your listener emitter should have clear reverb, but another emitter in another room should have quiet reverb.
To enable this, set Emitter.HasRelativeReverbEmitter.hasRelativeReverbvaEmitterSetHasRelativeReverb() to true on your listener emitter. The relative gain and relative direction of each grouped EAX will then be automatically calculated for your listener emitter.
Access the gain and direction like so:
var listener = new Emitter()
{
HasRelativeReverb = true,
};
world.OnReverbUpdated = () =>
{
for (int i = 0; i < world.GroupedEAX.Count; i++)
{
// Get the gain of this EAX slot relative to this listener
effects[i].EffectSlotGain = grouped.RelativeGains[listener];
// Get the direction of this EAX slot relative to this listener
Vector direction = grouped.RelativeDirections[listener];
effects[i].ReflectionsPan = direction;
effects[i].LateReverbPan = direction;
}
}
const listener = va.createEmitter();
listener.hasRelativeReverb = true;
world.onReverbUpdated = () => {
for (let i = 0; i < world.groupedEAX.length; i++) {
const grouped = world.groupedEAX[i];
// Get the gain of this EAX slot relative to this listener
effects[i].effectSlotGain = grouped.relativeGains(listener);
const direction = grouped.relativeDirections(listener);
effects[i].reflectionsPan = direction;
effects[i].lateReverbPan = direction;
}
};
void OnReverbUpdated()
{
int count = vaWorldGetGroupedEAXCount(world);
const VAEAXReverb** groupedEAX = vaWorldGetGroupedEAX(world);
for (int i = 0; i < count; i++)
{
const VAEAXReverb* grouped = groupedEAX[i];
// Get the gain of this EAX slot relative to this listener
float* gain = vaEAXReverGetRelativeGain(grouped, listener);
VAVector* direction = vaEAXReverGetRelativeDirection(grouped, listener);
// ERROR: gain/direction is not calculated for this emitter
if (gain == NULL || direction == NULL)
continue;
effects[i].effectSlotGain = *gain;
effects[i].reflectionsPan = *direction;
effects[i].lateReverbPan = *direction;
}
}
void Initialise()
{
VAWorld* world = vaWorldCreate();
vaWorldSetOnReverbUpdatedCallback(world, OnReverbUpdated);
VAEmitter* listener = vaEmitterCreate();
vaEmitterSetHasRelativeReverb(listener, true);
}
This gain is in the range 0.0 to 1.0, and should control the final output volume of that EAX reverb effect, (e.g. the effectSlotGain in OpenAL/EAX).
Do not apply a low pass filter between the audio source and the reverb effect in your game - the reverb effect should process raw audio data. Instead, the volume of the reverb effect's output should be controlled by the gain relative to your listener emitter. This allows reverb to be muffled on the other side of a wall.
The direction vector is not normalised - its magnitude indicates how strong the reverb directionality is. For example if the direction is (0, 0, 0), you'll hear reverb all around you, and if the pan vector is (0, 0, 1), you'll only hear reverb in that direction.
It's rare that the direction of all reverb rays will average to a pan vector of (0, 0, 0) (fully diffuse), so you can customise the minimum threshold that's required for reverb to be fully diffuse:
const listener = va.Emitter_Create();
listener.hasRelativeReverb = true;
listener.relativeReverbInnerThreshold = 0.5;
listener.relativeReverbOuterThreshold = 0.8;
world.addEmitter(listener);
VAEmitter* listener = vaEmitterCreate();
vaEmitterSetHasRelativeReverb(listener, true);
vaEmitterSetRelativeReverbInnerThreshold(listener, 0.5f);
vaEmitterSetRelativeReverbOuterThreshold(listener, 0.8f);
vaWorldAddEmitter(ctx, listener);
In the example above, pan will blend from fully directional at 0.8f magnitude, to fully diffuse at 0.5f magnitude. This means you don't need to be fully enclosed in a room to hear the reverb all around you.
Reverb Energy Cap
It's difficult to compute the maximum amount of reverb energy that can return to a player's ears ahead-of-time, since every environment is different. Previously the volume of reverb was controlled by the energy in all reverb rays that return to the emitter, and dividing it by emitter.ReverbRayCount * emitter.ReverbBounceCount.
However not all energy should have to return to the emitter in order for reverb to be at max volume. Especially with higher ReverbBounceCount values, rays will naturally travel further away from the emitter. But this means less energy can return to the emitter, and therefore the volume of reverb drops.
This is inaccurate - rather than comparing the returning energy against the total possible returning energy - we should compare it against a user-defined threshold. This gives you control over how much energy needs to return to the emitter for reverb to be at max volume.
In the code below, only 20% of energy is required for reverb to be at max volume. If reverb is too loud, you can increase this energy cap.
var listener = new Emitter()
{
ReverbRayCount = 128,
ReverbBounceCount = 64,
ReverbEnergyCap = 0.2f, // 20%
}
const emitter = va.Emitter_Create();
emitter.reverbRayCount = 128;
emitter.reverbBounceCount = 64;
emitter.reverbEnergyCap = 0.2; // 20%
VAEmitter* emitter = vaEmitterCreate();
vaEmitterSetReverbRayCount(emitter, 128);
vaEmitterSetReverbBounceCount(emitter, 64);
vaEmitterSetReverbEnergyCap(emitter, 0.2f); // 20%