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:
var world = new World()
{
RenderingEnabled = true,
};
// Update the camera
world.CameraPosition = new Vector(4, 10, 4);
world.CameraPitch = 0.0f;
world.CameraYaw = MathF.PI / 2;
world.FieldOfView = float.DegreesToRadians(50);
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 = 128 * 64 * 0.05,
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 { wrapVA, MaterialType } from './vaudio-wrapper.js';
// Set this to the number of threads you'll use
const YOUR_THREAD_COUNT = 4;
// The .NET runtime needs 6 threads. Without this, the browser may freeze
const DOTNET_THREAD_COUNT = 6;
// Initialise the .NET runtime
const { getAssemblyExports, getConfig } = await dotnet
.withDiagnosticTracing(false)
.withConfig({
jsThreadBlockingMode: "DangerousAllowBlockingWait", // Without this all calls to WASM are async
pthreadPoolInitialSize: YOUR_THREAD_COUNT + DOTNET_THREAD_COUNT,
pthreadPoolUnusedSize: 0,
})
.create();
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
// Wrap all Web Assembly calls into JS objects with get/setters for ease of use
const va = wrapVA(exports.VAudioExports);
// 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 = 128 * 64 * 0.05;
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, 128 * 64 * 0.05f);
vaEmitterSetOcclusionRayCount(listener, 512);
vaEmitterSetOcclusionBounceCount(listener, 8);
vaEmitterSetPermeationRayCount(listener, 128);
vaEmitterSetPermeationBounceCount(listener, 3);
vaEmitterSetAmbientOcclusionRayCount(listener, 512);
vaEmitterSetAmbientOcclusionBounceCount(listener, 8);
vaEmitterSetAmbientPermeationRayCount(listener, 128);
vaEmitterSetAmbientPermeationBounceCount(listener, 3);
int errorCode = vaWorldAddEmitter(world, listener);
assert(errorCode == 0);
// 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);
errorCode = vaWorldAddEmitter(world, source);
assert(errorCode == 0);
// 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;
}
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.
Godot Changelog
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
VercidiumAudionode:
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
SDK Changelog
C# - Version 1.2.0
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
Changes:
- Many methods and classes renamed and cleaned up.
RaytracingContextrenamed toWorld
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
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:
- 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
C# - Version 1.1.1
Fixes:
- Rays now collide with cones, cylinders, disks and half-spheres correctly
- Fixed
World.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
World.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
C# - Version 1.1.0
See Breaking Changes v1.1.0 for an overview of breaking changes and new features.
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
C# - 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
C# - 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
C# - Version 1.0.4
Changes:
Voice.filteris now initiallynull, to prevent accidentally accessing it before raytracing completes
Additions:
- New
World.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.
C# - 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
world.ListenerEAXReverbtoworld.ListenerEAX - Renamed
world.OutsideEAXReverbtoworld.OutsideEAX - Renamed
world.BlendedEAXReverbtoworld.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)
C# - 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
World.trailColourtoWorld.trailColor - Renamed
World.reverbColourtoWorld.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
C# - 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
CapsulePrimitive
A circular capsule with rounded ends.
public class CapsulePrimitive : Primitive
{
// Length of the cylindrical body between the hemispherical caps
public float length;
// Radius of the capsule
public float radius;
// Must only contain rotation and translation components, not scale
public Matrix transform;
}
interface CapsulePrimitive {
// Created via: va.CapsulePrimitive_Create()
// Length of the cylindrical body between the hemispherical caps
get length(); set length(v);
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// Radius of the capsule
get radius(); set radius(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
}
// Creates a new CapsulePrimitive with default values
VACapsulePrimitive* vaCapsulePrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaCapsulePrimitiveFree(VACapsulePrimitive* capsule);
// Radius of the capsule cylinder and end caps.
float vaCapsulePrimitiveGetRadius(const VACapsulePrimitive* capsule);
// Length of the capsule cylinder, excluding end caps.
float vaCapsulePrimitiveGetLength(const VACapsulePrimitive* capsule);
// World transform of the capsule.
const VAMatrix* vaCapsulePrimitiveGetTransform(const VACapsulePrimitive* capsule);
// Radius of the capsule cylinder and end caps.
void vaCapsulePrimitiveSetRadius(VACapsulePrimitive* capsule, float radius);
// Length of the capsule cylinder, excluding end caps.
void vaCapsulePrimitiveSetLength(VACapsulePrimitive* capsule, float length);
// Sets the world transform. Must not contain scale components.
void vaCapsulePrimitiveSetTransform(VACapsulePrimitive* capsule, const VAMatrix* transform);
// Material assigned to this primitive.
VAMaterialType vaCapsulePrimitiveGetMaterial(const VACapsulePrimitive* capsule);
// Material assigned to this primitive. Must not be VAMaterialAir.
void vaCapsulePrimitiveSetMaterial(VACapsulePrimitive* capsule, VAMaterialType material);
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.
ConePrimitive
A cone with a circular base.
interface ConePrimitive {
// Created via: va.ConePrimitive_Create()
// Height of the cone from base to apex
get height(); set height(v);
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// Radius of the cone base
get radius(); set radius(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
}
// Creates a new ConePrimitive with default values
VAConePrimitive* vaConePrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaConePrimitiveFree(VAConePrimitive* cone);
// Radius of the cone base.
float vaConePrimitiveGetRadius(const VAConePrimitive* cone);
// Height of the cone.
float vaConePrimitiveGetHeight(const VAConePrimitive* cone);
// World 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);
// Sets the world 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)
CylinderPrimitive
A cylinder.
interface CylinderPrimitive {
// Created via: va.CylinderPrimitive_Create()
// Length of the cylinder along its axis
get length(); set length(v);
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// Radius of the cylinder
get radius(); set radius(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
}
// Creates a new CylinderPrimitive with default values
VACylinderPrimitive* vaCylinderPrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaCylinderPrimitiveFree(VACylinderPrimitive* cylinder);
// Radius of the cylinder.
float vaCylinderPrimitiveGetRadius(const VACylinderPrimitive* cylinder);
// Length of the cylinder along its axis.
float vaCylinderPrimitiveGetLength(const VACylinderPrimitive* cylinder);
// World 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);
// Sets the world 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);
DiskPrimitive
A flat circular disk.
interface DiskPrimitive {
// Created via: va.DiskPrimitive_Create()
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// Radius of the disk
get radius(); set radius(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
}
// Creates a new DiskPrimitive with default values
VADiskPrimitive* vaDiskPrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaDiskPrimitiveFree(VADiskPrimitive* disk);
// Radius of the disk.
float vaDiskPrimitiveGetRadius(const VADiskPrimitive* disk);
// World transform of the disk.
const VAMatrix* vaDiskPrimitiveGetTransform(const VADiskPrimitive* disk);
// Radius of the disk.
void vaDiskPrimitiveSetRadius(VADiskPrimitive* disk, float radius);
// Sets the world 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);
HalfSpherePrimitive
Half of a sphere.
interface HalfSpherePrimitive {
// Created via: va.HalfSpherePrimitive_Create()
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// Radius of the hemisphere
get radius(); set radius(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
}
// Creates a new HalfSpherePrimitive with default values
VAHalfSpherePrimitive* vaHalfSpherePrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaHalfSpherePrimitiveFree(VAHalfSpherePrimitive* halfSphere);
// Radius of the half-sphere.
float vaHalfSpherePrimitiveGetRadius(VAHalfSpherePrimitive* halfSphere);
// World transform of the half-sphere.
const VAMatrix* vaHalfSpherePrimitiveGetTransform(VAHalfSpherePrimitive* halfSphere);
// Radius of the half-sphere.
void vaHalfSpherePrimitiveSetRadius(VAHalfSpherePrimitive* halfSphere, float radius);
// Sets the world 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.
TODO
public class MeshPrimitive : Primitive
{
// Whether sound can permeate through this mesh. Set to true for watertight meshes.
public bool supportsPermeation;
// Must only contain rotation and translation components, not scale
public Matrix transform;
// Create a mesh primitive from a list of vertices
public MeshPrimitive(MaterialType material, List<Vector> vertices, Vector minBounds, Vector maxBounds, Matrix transform);
// Create a mesh primitive from an array of vertices
public MeshPrimitive(MaterialType material, Vector[] vertices, Vector minBounds, Vector maxBounds, Matrix transform);
// Dispose the OpenGL vertex buffer (dev build only)
public void DisposeBuffer(bool alreadyCurrent = false);
}
VAMeshPrimitive* vaMeshPrimitiveCreate(VAMaterialType material, const VAVector* vertices, int vertexCount, VAVector minBounds, VAVector maxBounds, const VAMatrix* transform);
// Frees all resources owned by this primitive
void vaMeshPrimitiveFree(VAMeshPrimitive* primitive);
// World transform matrix for this mesh.
const VAMatrix* vaMeshPrimitiveGetTransform(const VAMeshPrimitive* primitive);
// Sets the world 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 vaMeshPrimitiveGetSupportsPermeation(const VAMeshPrimitive* primitive);
// Whether sound can permeate through this mesh. Set to true for watertight meshes.
void vaMeshPrimitiveSetSupportsPermeation(VAMeshPrimitive* primitive, bool supportsPermeation);
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#:supportsPermeation|C:mesh_primitive_set_supports_permeation() to true. If it is not enclosed (e.g. heightmap), permeation is not supported, and rays will lose energy based on the PlaneTransmissionLF/HF fields instead. See Materials.
PlanePrimitive
A flat rectangular plane.
interface PlanePrimitive {
// Created via: va.PlanePrimitive_Create()
// Height of the plane
get height(); set height(v);
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
// Width of the plane
get width(); set width(v);
}
// Creates a new PlanePrimitive with default values
VAPlanePrimitive* vaPlanePrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaPlanePrimitiveFree(VAPlanePrimitive* plane);
// Width of the plane.
float vaPlanePrimitiveGetWidth(VAPlanePrimitive* plane);
// Height of the plane.
float vaPlanePrimitiveGetHeight(VAPlanePrimitive* plane);
// World 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);
// Sets the world 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.
public abstract class Primitive
{
// Determines the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
public MaterialType material;
}
TODO
PrismPrimitive
A rectangular prism.
public class PrismPrimitive : Primitive
{
// Dimensions of the prism along each axis
public Vector size;
// Must only contain rotation and translation components, not scale
public Matrix transform;
}
interface PrismPrimitive {
// Created via: va.PrismPrimitive_Create()
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// Dimensions of the prism along each axis
get size(); set size(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
}
// Creates a new PrismPrimitive with default values
VAPrismPrimitive* vaPrismPrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaPrismPrimitiveFree(VAPrismPrimitive* prism);
// Width, height, and depth of the prism.
VAVector vaPrismPrimitiveGetSize(const VAPrismPrimitive* prism);
// World transform of the prism.
const VAMatrix* vaPrismPrimitiveGetTransform(const VAPrismPrimitive* prism);
// Width, height, and depth of the prism.
void vaPrismPrimitiveSetSize(VAPrismPrimitive* prism, VAVector size);
// Sets the world 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);
RectangularConePrimitive
A cone with a rectangle base.
public class RectangularConePrimitive : Primitive
{
// Height of the cone from base to apex
public float height;
// Length of the rectangular base
public float length;
// Must only contain rotation and translation components, not scale
public Matrix transform;
// Width of the rectangular base
public float width;
}
interface RectangularConePrimitive {
// Created via: va.RectangularConePrimitive_Create()
// Height of the cone from base to apex
get height(); set height(v);
// Length of the rectangular base
get length(); set length(v);
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
// Width of the rectangular base
get width(); set width(v);
}
// Creates a new RectangularConePrimitive with default values
VARectangularConePrimitive* vaRectangularConePrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaRectangularConePrimitiveFree(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 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);
// Sets the world 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).
SpherePrimitive
A sphere.
interface SpherePrimitive {
// Created via: va.SpherePrimitive_Create()
// Center position of the sphere in world space
get center(); set center(v);
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// Radius of the sphere
get radius(); set radius(v);
}
// Creates a new SpherePrimitive with default values
VASpherePrimitive* vaSpherePrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaSpherePrimitiveFree(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);
// Frees all resources owned by this primitive
void vaTrianglePrimitiveFree(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()
// The height of the cone
get height(); set height(v);
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// The radius of the triangular base
get radius(); set radius(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
}
// Creates a new TriangularConePrimitive with default values
VATriangularConePrimitive* vaTriangularConePrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaTriangularConePrimitiveFree(VATriangularConePrimitive* cone);
// Radius of the triangular cone base.
float vaTriangularConePrimitiveGetRadius(VATriangularConePrimitive* cone);
// Height of the triangular cone.
float vaTriangularConePrimitiveGetHeight(VATriangularConePrimitive* cone);
// World 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);
// Sets the world 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).
TriangularPrismPrimitive
A prism with a triangle base.
interface TriangularPrismPrimitive {
// Created via: va.TriangularPrismPrimitive_Create()
// Length of the prism along its axis
get length(); set length(v);
// Controls the amount of energy lost when rays bounce off this primitive, permeate through it, and scatter off it
get material(); set material(v);
// Circumradius of the triangular cross-section
get radius(); set radius(v);
// This transform must only contain rotation and translation components, not scale
get transform(); set transform(v);
}
// Creates a new TriangularPrismPrimitive with default values
VATriangularPrismPrimitive* vaTriangularPrismPrimitiveCreate(void);
// Frees all resources owned by this primitive
void vaTriangularPrismPrimitiveFree(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 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);
// Sets the world 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);
CustomEAXFormulas
Virtual class that can be overridden with custom formulas.
public class CustomEAXFormulas
{
// Calculates the EAX density parameter based on the number of energy peaks in the early echogram
public virtual float CalculateDensity();
// Calculates the EAX diffusion parameter based on how smoothly energy accumulates over time
public virtual float CalculateDiffusion();
// Calculates the low and high frequency gain values relative to a reference energy level
public 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
public virtual float CalculateLateReverbDelay();
// Calculates the gain for early reflections and late reverb by splitting the echogram at a transition point
public 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
public virtual float CalculateReflectionsDelay(float energyThreshold);
// Calculates the RT60 reverberation time in seconds for a given echogram using linear regression on the decay slope
public virtual float CalculateRT60(float[] echogram);
// Initialises the formula calculator with echogram data from a processed reverb result
public virtual void Initialise(ProcessedReverb processed, float totalReturningEnergyLF, float totalReturningEnergyHF, 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 totalReturningEnergyLF;
float totalReturningEnergyHF;
float totalReturningEnergyAverage;
float totalReturningEnergy;
float* echogramLF;
float* echogramHF;
float* echogramAverage;
int echogramLength;
float maxEnergy;
};
VACustomEAXFormulas* vaCustomEaxFormulasCreate(void);
void vaCustomEaxFormulasFree(VACustomEAXFormulas* formulas);
EAXConstants
EAX reverb constants from OpenAL.
public class EAXConstants
{
public const float AIR_ABSORPTION_GAIN_HF_MAX = 1.0f;
public const float AIR_ABSORPTION_GAIN_HF_MIN = 0.892f;
public const int DECAY_HF_LIMIT_MAX = 1;
public const int DECAY_HF_LIMIT_MIN = 0;
public const float DECAY_HF_RATIO_MAX = 2.0f;
public const float DECAY_HF_RATIO_MIN = 0.1f;
public const float DECAY_LF_RATIO_MAX = 2.0f;
public const float DECAY_LF_RATIO_MIN = 0.1f;
public const float DECAY_TIME_MAX = 20.0f;
public const float DECAY_TIME_MIN = 0.1f;
public const float DENSITY_MAX = 1.0f;
public const float DENSITY_MIN = 0.0f;
public const float DIFFUSION_MAX = 1.0f;
public const float DIFFUSION_MIN = 0.0f;
public const float ECHO_DEPTH_MAX = 1.0f;
public const float ECHO_DEPTH_MIN = 0.0f;
public const float ECHO_TIME_MAX = 0.25f;
public const float ECHO_TIME_MIN = 0.075f;
public const float GAIN_HF_MAX = 1.0f;
public const float GAIN_HF_MIN = 0.0f;
public const float GAIN_LF_MAX = 1.0f;
public const float GAIN_LF_MIN = 0.0f;
public const float GAIN_MAX = 1.0f;
public const float GAIN_MIN = 0.0f;
public const float HF_REFERENCE_MAX = 20000f;
public const float HF_REFERENCE_MIN = 1000f;
public const float LATE_REVERB_DELAY_MAX = 0.1f;
public const float LATE_REVERB_DELAY_MIN = 0.0f;
public const float LATE_REVERB_GAIN_MAX = 10.0f;
public const float LATE_REVERB_GAIN_MIN = 0.0f;
public const float LF_REFERENCE_MAX = 1000f;
public const float LF_REFERENCE_MIN = 20f;
public const float MODULATION_DEPTH_MAX = 1.0f;
public const float MODULATION_DEPTH_MIN = 0.0f;
public const float MODULATION_TIME_MAX = 4.0f;
public const float MODULATION_TIME_MIN = 0.04f;
public const float REFLECTIONS_DELAY_MAX = 0.3f;
public const float REFLECTIONS_DELAY_MIN = 0.0f;
public const float REFLECTIONS_GAIN_MAX = 3.16f;
public const float REFLECTIONS_GAIN_MIN = 0.0f;
public const float ROOM_ROLLOFF_FACTOR_MAX = 10.0f;
public const float ROOM_ROLLOFF_FACTOR_MIN = 0.0f;
public static Vector LATE_REVERB_PAN_MAX = new(1);
public static Vector LATE_REVERB_PAN_MIN = new(-1);
public static Vector REFLECTIONS_PAN_MAX = new(1);
public 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.
public class EAXReverb
{
// Linear gain applied per meter of distance for high-frequency air absorption (0.892–1)
public float AirAbsorptionGainHF;
// Whether to limit high-frequency decay time to the air absorption limit (0 or 1)
public int DecayHFLimit;
// Ratio of high-frequency decay time to mid-frequency decay time (0.1–2)
public float DecayHFRatio;
// Ratio of low-frequency decay time to mid-frequency decay time (0.1–2)
public float DecayLFRatio;
// Reverberation decay time at mid frequencies, in seconds (0.1–20)
public float DecayTime;
// Modal density of the late reverberation (0–1)
public float Density;
// Echo diffusion of the late reverberation (0–1)
public float Diffusion;
// Amplitude of the echo effect (0–1)
public float EchoDepth;
// Cycling time of the echo effect, in seconds (0.075–0.25)
public float EchoTime;
// Overall linear gain of the reverb (0–1)
public float Gain;
// High-frequency gain of the reverb (0–1)
public float GainHF;
// Low-frequency gain of the reverb (0–1)
public float GainLF;
// Reference frequency for high-frequency decay ratio, in Hz (1000–20000)
public float HFReference;
// Delay of late reverberation relative to early reflections, in seconds (0–0.1)
public float LateReverbDelay;
// Linear gain of late reverberation (0–10)
public float LateReverbGain;
// Reference frequency for low-frequency decay ratio, in Hz (20–1000)
public float LFReference;
// Amplitude of the modulation effect (0–1)
public float ModulationDepth;
// Cycling time of the modulation effect, in seconds (0.04–4)
public float ModulationTime;
// Delay before early reflections are heard, in seconds (0–0.3)
public float ReflectionsDelay;
// Linear gain of early reflections (0–3.16)
public float ReflectionsGain;
// The direction that both early reflections and late reverb should be heard from, relative to each emitters with HasRelativeReverb set to true
public Dictionary<Emitter, Vector> RelativeDirections;
// The gain for this reverb effect, relative to each emitter with HasRelativeReverb set to true
public Dictionary<Emitter, float> RelativeGains;
// Rolloff factor for reflected sound sources (0–10)
public float RoomRolloffFactor;
public override bool Equals(object obj);
public override int GetHashCode();
// Create an empty EAXReverb object
public static EAXReverb CreateEmpty();
}
interface EAXReverb {
// Linear gain applied per meter of distance for high-frequency air absorption (0.892–1)
get airAbsorptionGainHF();
// Whether to limit high-frequency decay time to the air absorption limit (0 or 1)
get decayHFLimit();
// Ratio of high-frequency decay time to mid-frequency decay time (0.1–2)
get decayHFRatio();
// Ratio of low-frequency decay time to mid-frequency decay time (0.1–2)
get decayLFRatio();
// Reverberation decay time at mid frequencies, in seconds (0.1–20)
get decayTime();
// Modal density of the late reverberation (0–1)
get density();
// Echo diffusion of the late reverberation (0–1)
get diffusion();
// Amplitude of the echo effect (0–1)
get echoDepth();
// Cycling time of the echo effect, in seconds (0.075–0.25)
get echoTime();
// Overall linear gain of the reverb (0–1)
get gain();
// High-frequency gain of the reverb (0–1)
get gainHF();
// Low-frequency gain of the reverb (0–1)
get gainLF();
// Reference frequency for high-frequency decay ratio, in Hz (1000–20000)
get hfReference();
// Delay of late reverberation relative to early reflections, in seconds (0–0.1)
get lateReverbDelay();
// Linear gain of late reverberation (0–10)
get lateReverbGain();
// Reference frequency for low-frequency decay ratio, in Hz (20–1000)
get lfReference();
// Amplitude of the modulation effect (0–1)
get modulationDepth();
// Cycling time of the modulation effect, in seconds (0.04–4)
get modulationTime();
// Delay before early reflections are heard, in seconds (0–0.3)
get reflectionsDelay();
// Linear gain of early reflections (0–3.16)
get reflectionsGain();
// Rolloff factor for reflected sound sources (0–10)
get roomRolloffFactor();
// Returns the reverb direction for the given emitter. Returns null if the emitter does not have hasRelativeReverb set to true
relativeDirections(emitter);
// Returns the effect slot gain for the given emitter. Returns -1 if the emitter does not have hasRelativeReverb set to true
relativeGains(emitter);
}
typedef struct VAEAXReverb
{
float outsidePercent;
float returningPercent;
float materialAbsorptionLF;
float materialAbsorptionHF;
float materialRoughness;
List* relativeDirections; /* Direction that reverb should be heard from, relative to each emitter with hasRelativeReverb=true. Access via vaEaxReverbGetRelativeDirection */
List* relativeGains; /* Gain for this reverb effect, relative to each emitter with hasRelativeReverb=true. Access via vaEaxReverbGetRelativeGain */
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;
// Returns NULL if no entry exists for this emitter.
VAVector* vaEaxReverbGetRelativeDirection(const VAEAXReverb* eax, const VAEmitter* emitter);
// Returns NULL if no entry exists for this emitter.
float* vaEaxReverbGetRelativeGain(const VAEAXReverb* eax, const VAEmitter* emitter);
EAXUtils
Helper functions for comparing EAX objects.
public static class EAXUtils
{
// Finds the candidate reverb preset most similar to the target
public static EAXReverb FindBestMatch(EAXReverb target, List<EAXReverb> candidates, out int index);
// Computes a similarity score between two reverb presets
public 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.
public class ProcessedReverb
{
// The average absorption of all surfaces that all rays hit.
public float MaterialAbsorption;
// The average high-frequency absorption of all surfaces that all rays hit.
public float MaterialAbsorptionHF;
// The average low-frequency absorption of all surfaces that all rays hit.
public float MaterialAbsorptionLF;
// The average roughness/scattering of all surfaces that all rays hit.
public float MaterialRoughness;
// Inverse of MaterialRoughness.
public 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.
public 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.
public float MeasuredDecayTimeLF;
// The percentage of energy that escaped outside (hit the world edge).
// Calculated as raw.OutsideTotal / (ReverbRayCount * ReverbBounceCount)
public float OutsidePercent;
// The percentage of energy that returned back to the emitter.
// Calculated as raw.ReturnedTotal / (ReverbRayCount * ReverbBounceCount)
public float ReturnedPercent;
}
interface ProcessedReverb {
// Average absorption of all surfaces that all rays hit
get materialAbsorption();
// Average high-frequency absorption of all surfaces that all rays hit
get materialAbsorptionHF();
// Average low-frequency absorption of all surfaces that all rays hit
get materialAbsorptionLF();
// Average roughness/scattering of all surfaces that all rays hit
get materialRoughness();
// Inverse of materialRoughness
get materialSmoothness();
// High-frequency reverberation decay time measured from the echogram, in seconds
get measuredDecayTimeHF();
// Low-frequency reverberation decay time measured from the echogram, in seconds
get measuredDecayTimeLF();
// Percentage of energy that escaped outside the world edge
get outsidePercent();
// Percentage of energy that returned back to the emitter
get returnedPercent();
}
typedef struct VAProcessedReverb
{
// percentages
float returningPercent; /* 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.
public 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)
public 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
public float OutsideTotal;
// The total number of times that a ray returned to the listener. Will be in the range 0.0 to ReverbRayCount * ReverbBounceCount
public 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.
public class AirAbsorptionSettings
{
// Custom air absorption formula for high frequencies. Takes distance (meters) and returns energy loss as a percentage
public Func<float, float> CustomFormulaHF;
// Custom formula for low frequency sound. Takes distance (meters) and returns energy loss as a percentage
public Func<float, float> CustomFormulaLF;
// Relative humidity as a percentage (0–1)
public float Humidity;
// Atmospheric pressure in Pascals
public float Pressure;
// Air temperature in degrees Celsius
public float Temperature;
public AirAbsorptionSettings();
}
interface AirAbsorptionSettings {
// Created via: va.AirAbsorptionSettings_Create()
// Relative humidity as a percentage (0–1)
get humidity(); set humidity(v);
// Atmospheric pressure in Pascals
get pressure(); set pressure(v);
// Air temperature in degrees Celsius
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
// Relative humidity as a percentage (0–1)
void vaAirAbsorptionSetHumidity(VAAirAbsorptionSettings* settings, float value);
// Air temperature in degrees Celsius
void vaAirAbsorptionSetTemperature(VAAirAbsorptionSettings* settings, float value);
// Atmospheric pressure in Pascals
void 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);
// Frees all resources owned by this settings object
void vaAirAbsorptionFree(VAAirAbsorptionSettings* settings);
Color
Determines the color of trails and primitives in the debug window. Used in the dev build only.
public struct Color
{
// Alpha channel
public byte A;
// Packed 32-bit ARGB value
public uint ARGB;
// Blue channel
public byte B;
// Green channel
public byte G;
// Red channel
public byte R;
// Packed 32-bit ABGR value
public uint Value;
// Creates a color from byte components. Alpha defaults to 255
public Color(byte red, byte green, byte blue, byte alpha = 255);
// Creates a color from another color with a alpha value
public Color(Color other, float alpha);
// Creates a color from a packed ABGR value
public Color(uint abgr);
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
// Returns a copy of this color with the specified normalized alpha
public Color WithAlpha(float alpha);
// Returns true if two colors differ
public static bool operator !=(Color a, Color b);
// Returns true if two colors have identical values
public static bool operator ==(Color a, Color b);
// Linearly interpolates between two colors
public 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.
public class Emitter
{
// Indicates whether this Emitter's EAX is blended to produced grouped EAX. Set this to false for listener emitters
public bool AffectsGroupedEAX = true;
// This object contains the LF and HF gain for ambient sounds, to be applied to a low pass filter
public 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.
// Parameters: (bool lowFrequency, int ambientOcclusionRayCount, int ambientPermeationRayCount, int ambientPermeationBounceCount, float ambientOcclusionEnergy, float ambientPermeationEnergy)
// Returns: float
public Func<bool, int, int, int, float, float, float> AmbientGainFormula;
// Number of bounces per ambient occlusion ray
public int AmbientOcclusionBounceCount;
// True if both AmbientOcclusionRayCount and AmbientOcclusionBounceCount are greater than zero
public bool AmbientOcclusionEnabled;
// The percentage of occlusion energy required for the emitter to be at full volume. Defaults to 15% of this emitter's AmbientOcclusionRayCount.
public float AmbientOcclusionEnergyCap = 0.15f;
// Number of ambient occlusion rays cast
public int AmbientOcclusionRayCount;
// Number of bounces per ambient permeation ray
public int AmbientPermeationBounceCount;
// The color of ambientPermeation rays in the debug window (dev build only)
public Color AmbientPermeationColor = Color.Yellow.WithAlpha(0.0f);
// True if both AmbientPermeationRayCount and AmbientPermeationBounceCount are greater than zero
public 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.
public float AmbientPermeationEnergyCap = 0.15f;
// Number of ambient permeation rays cast
public int AmbientPermeationRayCount;
// Whether this emitter casts rays. False if all ray counts and/or bounce counts are set to 0.
public bool CastsRays;
// Whether to clamp this emitter's position to the world bounds, to prevent it from going out of bounds
public bool ClampPosition;
// EAX reverb properties for the listener. Contains parameters compatible with EAX reverb effects
public EAXReverb EAX;
// The length (in milliseconds) of each entry in the echogram. Defaults to 50ms
public int EchogramGranularity = 50;
// 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.
// Parameters: (bool lowFrequency, int occlusionRayCount, int permeationRayCount, int permeationBounceCount, float occlusionEnergy, float permeationEnergy)
// Returns: float
public Func<bool, int, int, int, float, float, float> GainFormula;
// Index indicating which GroupedEAX reverb effect should be used for this emitter
public int GroupedEAXIndex = -1;
// Whether this emitter is used as a reference point for calculating relative reverb gain and direction
public bool HasRelativeReverb;
// True if this emitter hasn't cast its own rays yet.
public bool Initialising = true;
// A custom logging callback. Defaults to WriteLine()
public Action<string> LogCallback;
// A custom logging callback. Defaults to Error.WriteLine()
public Action<string> LogErrorCallback;
// How long (in milliseconds) the echogram records data for. Returning reverb rays after this period will be ignored. Defaults to 5000ms
public int MaxEchogramTime = 5000;
// The threshold below which permeation rays are cancelled to prevent unnecessary traversal. Clamped to minimum of 0
public float MinimumPermeationEnergy = 0.01f;
// User-defined name of this emitter
public string Name = string.Empty;
// Number of bounces per occlusion ray
public int OcclusionBounceCount;
// The color of occlusion rays in the debug window (dev build only)
public Color OcclusionColor = Color.Green.WithAlpha(0.0f);
// True if both OcclusionRayCount and OcclusionBounceCount are greater than zero
public bool OcclusionEnabled;
// The percentage of occlusion energy required for the emitter to be at full volume. Defaults to 15% of the other emitter's OcclusionRayCount.
public float OcclusionEnergyCap = 0.15f;
// Number of occlusion rays cast
public int OcclusionRayCount;
// Whether to only scatter rays around their yaw (no pitch or Y component)
public 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.
public Action<Emitter> OnRaytracedByAnotherEmitter;
// A callback that is invoked after this emitter casts its rays for the first time.
public Action OnRaytracingComplete;
// The percentage of ambient occlusion rays that reached the edge of the world. Ranges from 0.0 to 1.0
public float OutsidePercent;
// When defined, rays will be cast out of these positions rather than the default Position
public Vector[] OverridePositions;
// When defined, rays will be cast in these directions rather than the default spherical ray distribution
public Vector[] OverrideRayDirections;
// Number of bounces per permeation ray
public int PermeationBounceCount;
// The color of permeation rays in the debug window (dev build only)
public Color PermeationColor = Color.Orange.WithAlpha(0.0f);
// True if both PermeationRayCount and PermeationBounceCount are greater than zero
public bool PermeationEnabled;
// The percentage of permeation energy required for the emitter to be at full volume. Defaults to 15% of the other emitter's PermeationRayCount * PermeationBounceCount.
public float PermeationEnergyCap = 0.15f;
// Number of permeation rays cast
public int PermeationRayCount;
// The position of this Emitter. Can be a Vector3F or IPosition
public IPosition Position = Vector.Zero;
// Contains reverb data that has been transformed into more usable parameters
public ProcessedReverb ProcessedReverb;
// Whether to render each trail a different color (dev build only)
public bool RandomTrailColor;
// Raw reverb properties calculated directly from raytracing
public RawReverb RawReverb;
// 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. Clamped to minimum of 0.
public 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.
public int RefreshRayCount = 16;
// The lower bound of the relative reverb blend range
public float RelativeReverbInnerThreshold = 0.6f;
// The upper bound of the relative reverb blend range
public float RelativeReverbOuterThreshold = 0.8f;
// The number of emitters that are allocated ahead of time, to prevent runtime allocations. Clamped to minimum of 0
public int ReservedEmitterTargets;
// Number of bounces per reverb ray
public int ReverbBounceCount;
// The color of reverb rays in the debug window (dev build only)
public Color ReverbColor = Color.Cyan.WithAlpha(0.0f);
// True if both ReverbRayCount and ReverbBounceCount are greater than zero
public bool ReverbEnabled;
// The amount of returning energy required for reverb to be at maximum volume. Defaults to 100. Must adjust this setting if adjusting reverb ray and bounce counts.
public float ReverbEnergyCap = 100.0f;
// Number of reverb rays cast
public int ReverbRayCount;
// A seed used to randomise scattering vectors
public int ScatteringSeed;
// Number of bounces per trail
public int TrailBounceCount;
// The color of ray trails in the debug window (dev build only)
public Color TrailColor = Color.White.WithAlpha(0.2f);
// Number of trails this emitter will create
public int TrailCount;
// User-defined type for this emitter
public int Type;
// Number of times each visualisation ray bounces
public 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.
public Action<VisualisationData[]> VisualisationCallback;
// True if both VisualisationRayCount and VisualisationBounceCount are greater than zero
public bool VisualisationEnabled;
// Number of visualisation rays cast
public int VisualisationRayCount;
// How often - in milliseconds - to cast visualisation rays
public int VisualisationUpdateFrequency;
// Emitters outside the world bounds will not be raytraced. Set ClampPosition to true to keep this emitter inside the world bounds
public bool WithinWorldBounds;
public Emitter();
// Adds an emitter to this emitter's target list
public 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.
public 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
public bool HasRaytracedTarget(Emitter target);
// Returns whether the target emitter is in this emitter's target list
public bool HasTarget(Emitter target);
// Removes an emitter from this emitter's target list
public 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
public void ResetTrails();
// Validates this emitter's configuration
public void Validate();
}
interface Emitter {
// Created via: va.Emitter_Create()
// Whether this emitter's EAX is blended to produce grouped EAX. Set to false for listener emitters
get affectsGroupedEAX(); set affectsGroupedEAX(v);
// Number of bounces per ambient occlusion ray
get ambientOcclusionBounceCount(); set ambientOcclusionBounceCount(v);
// Percentage of occlusion energy required for ambient sounds to be at full volume
get ambientOcclusionEnergyCap(); set ambientOcclusionEnergyCap(v);
// Number of ambient occlusion rays cast
get ambientOcclusionRayCount(); set ambientOcclusionRayCount(v);
// Number of bounces per ambient permeation ray
get ambientPermeationBounceCount(); set ambientPermeationBounceCount(v);
// Percentage of permeation energy required for ambient sounds to be at full volume
get ambientPermeationEnergyCap(); set ambientPermeationEnergyCap(v);
// Number of ambient permeation rays cast
get ambientPermeationRayCount(); set ambientPermeationRayCount(v);
// Whether to clamp this emitter's position to the world bounds
get clampPosition(); set clampPosition(v);
// Length (in milliseconds) of each entry in the echogram. 10ms is recommended
get echogramGranularity(); set echogramGranularity(v);
// Index indicating which grouped EAX reverb effect should be used for this emitter
get groupedEAXIndex(); set groupedEAXIndex(v);
// Whether this emitter is used as a reference point for calculating relative reverb gain and direction
get hasRelativeReverb(); set hasRelativeReverb(v);
// True if this emitter hasn't cast its own rays yet
get initialising(); set initialising(v);
// How long (in milliseconds) the echogram records data for. Returning reverb rays after this period are ignored
get maxEchogramTime(); set maxEchogramTime(v);
// Threshold below which permeation rays are cancelled to prevent unnecessary traversal
get minimumPermeationEnergy(); set minimumPermeationEnergy(v);
// Number of bounces per occlusion ray
get occlusionBounceCount(); set occlusionBounceCount(v);
// Percentage of occlusion energy required for the emitter to be at full volume
get occlusionEnergyCap(); set occlusionEnergyCap(v);
// Number of occlusion rays cast
get occlusionRayCount(); set occlusionRayCount(v);
// Whether to only scatter rays around their yaw (no pitch or Y component)
get onlyScatterXZ(); set onlyScatterXZ(v);
// Percentage of ambient occlusion rays that reached the edge of the world (0.0 to 1.0)
get outsidePercent(); set outsidePercent(v);
// Number of bounces per permeation ray
get permeationBounceCount(); set permeationBounceCount(v);
// Percentage of permeation energy required for the emitter to be at full volume
get permeationEnergyCap(); set permeationEnergyCap(v);
// Number of permeation rays cast
get permeationRayCount(); set permeationRayCount(v);
// 3D position of this emitter
get position(); set position(v);
// A ray trail is re-created if an old bounce position is further than this distance from the new position
get refreshDistanceThreshold(); set refreshDistanceThreshold(v);
// Number of trails rebuilt from scratch each frame to prevent staleness when the listener moves
get refreshRayCount(); set refreshRayCount(v);
// Lower bound of the relative reverb blend range
get relativeReverbInnerThreshold(); set relativeReverbInnerThreshold(v);
// Upper bound of the relative reverb blend range
get relativeReverbOuterThreshold(); set relativeReverbOuterThreshold(v);
// Number of emitters allocated ahead of time to prevent runtime allocations
get reservedEmitterTargets(); set reservedEmitterTargets(v);
// Number of bounces per reverb ray
get reverbBounceCount(); set reverbBounceCount(v);
// Amount of returning energy required for reverb to be at maximum volume
get reverbEnergyCap(); set reverbEnergyCap(v);
// Number of reverb rays cast
get reverbRayCount(); set reverbRayCount(v);
// User-defined type for this emitter
get type(); set type(v);
// Number of times each visualisation ray bounces
get visualisationBounceCount(); set visualisationBounceCount(v);
// Number of visualisation rays cast
get visualisationRayCount(); set visualisationRayCount(v);
// How often (in milliseconds) to cast visualisation rays
get visualisationUpdateFrequency(); set visualisationUpdateFrequency(v);
// Low- and high-frequency gain for ambient sounds
get ambientFilter();
// True if both ambientOcclusionRayCount and ambientOcclusionBounceCount are greater than zero
get ambientOcclusionEnabled();
// True if both ambientPermeationRayCount and ambientPermeationBounceCount are greater than zero
get ambientPermeationEnabled();
// Whether this emitter casts rays. False if all ray counts and/or bounce counts are 0
get castsRays();
// EAX reverb properties for the listener, compatible with EAX reverb effects
get eax();
// True if both occlusionRayCount and occlusionBounceCount are greater than zero
get occlusionEnabled();
// True if both permeationRayCount and permeationBounceCount are greater than zero
get permeationEnabled();
// Reverb data transformed into more usable parameters
get processedReverb();
// Raw reverb properties calculated directly from raytracing
get rawReverb();
// True if both reverbRayCount and reverbBounceCount are greater than zero
get reverbEnabled();
// Number of bounces per trail
get trailBounceCount();
// Number of trails this emitter will create
get trailCount();
// True if both visualisationRayCount and visualisationBounceCount are greater than zero
get visualisationEnabled();
// Whether this emitter is within the world bounds. Emitters outside the world are not raytraced
get withinWorldBounds();
// When defined, rays are cast from these positions rather than the emitter's position
set overridePositions(v);
// When defined, rays are cast in these directions rather than the default spherical distribution
set overrideRayDirections(v);
// Invoked with each bounce produced by visualisation rays. Do not store the array outside the callback
set visualisationCallback(v);
// Invoked when another emitter raytraces this emitter for the first time
set onRaytracedByAnotherEmitter(fn);
// Invoked after this emitter casts its rays for the first time
set onRaytracingComplete(fn);
// Adds an emitter to this emitter's target list
addTarget(target);
// Returns an object containing the LF and HF gain for a target emitter. Only access when hasRaytracedTarget is true
getTargetFilter(target);
// Returns the current ray trails for this emitter
getWasmTrails(prev?);
// Returns whether a target emitter has been raytraced. If true, it is safe to check the target's filter via getTargetFilter
hasRaytracedTarget(target);
// Returns whether the target emitter is in this emitter's target list
hasTarget(target);
// Removes an emitter from this emitter's target list
removeTarget(target);
// Invalidates the ray cache — all memory is cleaned up and all rays are re-cast
resetTrails();
// Sets the position of this emitter
setPosition(x, y, z);
// Validates this emitter's configuration
validate();
}
// Creates a new emitter with default settings.
VAEmitter* vaEmitterCreate(void);
// Frees all resources owned by this emitter.
void 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);
// Only safe to call when vaEmitterHasRaytracedTarget() returns true.
VALowPassFilter* vaEmitterGetTargetFilter(VAEmitter* emitter, VAEmitter* target);
int vaEmitterAddTarget(VAEmitter* emitter, VAEmitter* target);
// Removes a previously added target from this emitter.
void vaEmitterRemoveTarget(VAEmitter* emitter, VAEmitter* target);
// Invalidates the ray cache; all rays will be re-cast on the next Update().
void vaEmitterResetTrails(VAEmitter* emitter);
// Number of reverb rays cast per frame.
void vaEmitterSetReverbRayCount(VAEmitter* emitter, int value);
// Maximum number of bounces per reverb ray.
void vaEmitterSetReverbBounceCount(VAEmitter* emitter, int value);
// Number of occlusion rays cast per frame.
void vaEmitterSetOcclusionRayCount(VAEmitter* emitter, int value);
// Maximum number of bounces per occlusion ray.
void vaEmitterSetOcclusionBounceCount(VAEmitter* emitter, int value);
// Number of permeation rays cast per frame.
void vaEmitterSetPermeationRayCount(VAEmitter* emitter, int value);
// Maximum number of bounces per permeation ray.
void vaEmitterSetPermeationBounceCount(VAEmitter* emitter, int value);
// Number of ambient occlusion rays cast per frame.
void vaEmitterSetAmbientOcclusionRayCount(VAEmitter* emitter, int value);
// Maximum number of bounces per ambient occlusion ray.
void vaEmitterSetAmbientOcclusionBounceCount(VAEmitter* emitter, int value);
// Number of ambient permeation rays cast per frame.
void vaEmitterSetAmbientPermeationRayCount(VAEmitter* emitter, int value);
// Maximum number of bounces per ambient permeation ray.
void vaEmitterSetAmbientPermeationBounceCount(VAEmitter* emitter, int value);
// Energy threshold below which permeation rays are cancelled to prevent unnecessary traversal.
void vaEmitterSetMinimumPermeationEnergy(VAEmitter* emitter, float value);
int vaEmitterSetReverbEnergyCap(VAEmitter* emitter, float value);
int vaEmitterSetOverridePositions(VAEmitter* emitter, VAVector* positions, int count);
int vaEmitterSetOverrideRayDirections(VAEmitter* emitter, VAVector* directions, int count);
int vaEmitterSetGainFormula(VAEmitter* emitter, VAEmitterGainFormula formula);
int vaEmitterSetAmbientGainFormula(VAEmitter* emitter, VAEmitterGainFormula formula);
// True if reverb ray count and bounce count are both greater than zero.
bool vaEmitterReverbEnabled(const VAEmitter* emitter);
// True if occlusion ray count and bounce count are both greater than zero.
bool vaEmitterOcclusionEnabled(const VAEmitter* emitter);
// True if permeation ray count and bounce count are both greater than zero.
bool vaEmitterPermeationEnabled(const VAEmitter* emitter);
// True if ambient occlusion ray count and bounce count are both greater than zero.
bool vaEmitterAmbientOcclusionEnabled(const VAEmitter* emitter);
// True if ambient permeation ray count and bounce count are both greater than zero.
bool vaEmitterAmbientPermeationEnabled(const VAEmitter* emitter);
// True if visualisation ray count and bounce count are both greater than zero.
bool vaEmitterVisualisationEnabled(const VAEmitter* emitter);
// False if all ray counts and/or bounce counts are set to 0.
bool vaEmitterCastsRays(const VAEmitter* emitter);
// Emitters outside the world bounds will not be raytraced; set clampPosition to true to keep them inside the world.
bool vaEmitterWithinWorldBounds(const VAEmitter* emitter);
// Invoked after this emitter casts its rays for the first time.
void vaEmitterSetOnRaytracingCompleteCallback(VAEmitter* emitter, VAEmitterOnRaytracingComplete callback);
// Invoked when another emitter raytraces this emitter for the first time.
void vaEmitterSetOnRaytracedByAnotherEmitterCallback(VAEmitter* emitter, VAEmitterOnRaytracedByAnotherEmitter 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);
// World-space position of this emitter.
VAVector vaEmitterGetPosition(const VAEmitter* emitter);
// When true, the emitter has not yet received its first raytracing result.
bool vaEmitterGetInitialising(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);
// Whether this emitter's EAX is blended into grouped EAX.
bool vaEmitterGetAffectsGroupedEax(const VAEmitter* emitter);
// Which grouped EAX reverb effect applies to this emitter (-1 = none).
void vaEmitterSetGroupedEaxIndex(VAEmitter* emitter, int value);
// Index into the world's grouped eax list (-1 = none).
int vaEmitterGetGroupedEaxIndex(const VAEmitter* emitter);
// Read-only percentage of energy in ambient occlusion rays that reached the edge of the world (0–1).
float vaEmitterGetOutsidePercent(const VAEmitter* emitter);
// Whether this emitter is used as a reference point for calculating relative reverb gain and direction.
void vaEmitterSetHasRelativeReverb(VAEmitter* emitter, bool value);
bool vaEmitterGetHasRelativeReverb(const VAEmitter* emitter);
// Lower bound of the relative reverb blend range.
void vaEmitterSetRelativeReverbInnerThreshold(VAEmitter* emitter, float value);
float vaEmitterGetRelativeReverbInnerThreshold(const VAEmitter* emitter);
// Upper bound of the relative reverb blend range.
void vaEmitterSetRelativeReverbOuterThreshold(VAEmitter* emitter, float value);
float vaEmitterGetRelativeReverbOuterThreshold(const VAEmitter* emitter);
// When true, position is clamped to world bounds to prevent 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.
void vaEmitterSetName(VAEmitter* emitter, const char* name);
// Display name for debugging and logging.
const char* vaEmitterGetName(const VAEmitter* emitter);
// User-defined integer tag for categorising emitters.
void vaEmitterSetType(VAEmitter* emitter, int value);
// User-defined integer tag for categorising emitters.
int vaEmitterGetType(const VAEmitter* emitter);
// Number of reverb rays cast per frame.
int vaEmitterGetReverbRayCount(const VAEmitter* emitter);
// Maximum number of bounces per reverb ray.
int vaEmitterGetReverbBounceCount(const VAEmitter* emitter);
// Number of occlusion rays cast per frame.
int vaEmitterGetOcclusionRayCount(const VAEmitter* emitter);
// Maximum number of bounces per occlusion ray.
int vaEmitterGetOcclusionBounceCount(const VAEmitter* emitter);
// Number of permeation rays cast per frame.
int vaEmitterGetPermeationRayCount(const VAEmitter* emitter);
// Maximum number of bounces per permeation ray.
int vaEmitterGetPermeationBounceCount(const VAEmitter* emitter);
// Number of ambient occlusion rays cast per frame.
int vaEmitterGetAmbientOcclusionRayCount(const VAEmitter* emitter);
// Maximum number of bounces per ambient occlusion ray.
int vaEmitterGetAmbientOcclusionBounceCount(const VAEmitter* emitter);
// Number of ambient permeation rays cast per frame.
int vaEmitterGetAmbientPermeationRayCount(const VAEmitter* emitter);
// Maximum number of bounces per ambient permeation ray.
int vaEmitterGetAmbientPermeationBounceCount(const VAEmitter* emitter);
// Number of visualisation rays cast per visualisation update.
int vaEmitterGetVisualisationRayCount(const VAEmitter* emitter);
// Number of visualisation rays cast per visualisation update.
void vaEmitterSetVisualisationRayCount(VAEmitter* emitter, int value);
// Maximum number of bounces per visualisation ray.
int vaEmitterGetVisualisationBounceCount(const VAEmitter* emitter);
// Maximum number of bounces per visualisation ray.
void vaEmitterSetVisualisationBounceCount(VAEmitter* emitter, int value);
// Interval in milliseconds between visualisation ray updates.
int vaEmitterGetVisualisationUpdateFrequency(const VAEmitter* emitter);
// Interval in milliseconds between visualisation ray updates.
void vaEmitterSetVisualisationUpdateFrequency(VAEmitter* emitter, int value);
// Total number of trails (reverb + occlusion + permeation) owned by this emitter.
int vaEmitterGetTrailCount(const VAEmitter* emitter);
// Total bounce budget per trail across all ray types.
int vaEmitterGetTrailBounceCount(const VAEmitter* emitter);
// How long (ms) the echogram records data. Returning reverb rays after this period are ignored. Defaults to 5000ms.
int vaEmitterGetMaxEchogramTime(const VAEmitter* emitter);
// How long (ms) the echogram records data. Returning reverb rays after this period are ignored. Defaults to 5000ms.
void vaEmitterSetMaxEchogramTime(VAEmitter* emitter, int value);
// Length (ms) of each entry in the echogram. Defaults to 50ms.
int vaEmitterGetEchogramGranularity(const VAEmitter* emitter);
// Length (ms) of each entry in the echogram. Defaults to 50ms.
void vaEmitterSetEchogramGranularity(VAEmitter* emitter, int value);
// Number of trails rebuilt from scratch each frame to prevent staleness when the emitter moves. Minimum 0.
int vaEmitterGetRefreshRayCount(const VAEmitter* emitter);
// Number of trails rebuilt from scratch each frame to prevent staleness when the emitter moves. Minimum 0.
void vaEmitterSetRefreshRayCount(VAEmitter* emitter, int value);
// A trail is re-created when an old bounce position is this far from the new position. Minimum 0.
float vaEmitterGetRefreshDistanceThreshold(const VAEmitter* emitter);
// A trail is re-created when an old bounce position is this far from the new position. Minimum 0.
void vaEmitterSetRefreshDistanceThreshold(VAEmitter* emitter, float value);
// Amount of returning energy required for reverb to be at maximum volume. Defaults to 100.
float vaEmitterGetReverbEnergyCap(const VAEmitter* emitter);
// Percentage of occlusion energy required for the emitter to be at full volume. Defaults to 15% of occlusionRayCount.
float vaEmitterGetOcclusionEnergyCap(const VAEmitter* emitter);
int vaEmitterSetOcclusionEnergyCap(VAEmitter* emitter, float value);
// Percentage of permeation energy required for the emitter to be at full volume. Defaults to 15% of permeationRayCount * permeationBounceCount.
float vaEmitterGetPermeationEnergyCap(const VAEmitter* emitter);
int vaEmitterSetPermeationEnergyCap(VAEmitter* emitter, float value);
// Percentage of ambient occlusion energy required for the emitter to be at full volume.
float vaEmitterGetAmbientOcclusionEnergyCap(const VAEmitter* emitter);
int vaEmitterSetAmbientOcclusionEnergyCap(VAEmitter* emitter, float value);
// Percentage of ambient permeation energy required for the emitter to be at full volume.
float vaEmitterGetAmbientPermeationEnergyCap(const VAEmitter* emitter);
int vaEmitterSetAmbientPermeationEnergyCap(VAEmitter* emitter, float value);
// Number of target emitter slots pre-allocated. Minimum 0.
int vaEmitterGetReservedEmitterCount(const VAEmitter* emitter);
// Number of target emitter slots pre-allocated. Minimum 0.
void vaEmitterSetReservedEmitterCount(VAEmitter* emitter, int value);
// Energy threshold below which permeation rays are cancelled to prevent unnecessary traversal.
float vaEmitterGetMinimumPermeationEnergy(const VAEmitter* emitter);
// Raw reverb results; only valid after raytracing has run at least once.
VARawReverb* vaEmitterGetRawReverb(const VAEmitter* emitter);
// Processed reverb results; only valid after raytracing has run at least once.
VAProcessedReverb* vaEmitterGetProcessedReverb(const VAEmitter* emitter);
// EAX reverb results; only valid after raytracing has run at least once.
VAEAXReverb* vaEmitterGetEAX(const VAEmitter* emitter);
// LF and HF gain for ambient sounds to apply to a low pass filter.
VALowPassFilter* vaEmitterGetAmbientFilter(const VAEmitter* emitter);
FuncPosition
Interface for setting the position of an emitter.
Invoked when RaytracingContext.Update() is called.
public class FuncPosition : IPosition
{
public FuncPosition(Func<Vector> position);
public Vector GetPosition();
}
Not available in JS or C.
IPosition
Interface for setting the position of an emitter.
Invoked when RaytracingContext.Update() is called.
public interface IPosition
{
// Returns a 3D position
public Vector GetPosition();
}
Not available in JS or C.
LowPassFilter
Contains low-frequency and high-frequency gains.
public class LowPassFilter
{
// High-frequency gain in the range 0.0 to 1.0
public float GainHF;
// Low-frequency gain in the range 0.0 to 1.0
public float GainLF;
}
interface LowPassFilter {
// Created via: va.LowPassFilter_Create()
// High-frequency gain in the range 0.0 to 1.0
get gainHF(); set gainHF(v);
// Low-frequency gain in the range 0.0 to 1.0
get gainLF(); set gainLF(v);
}
typedef struct VALowPassFilter
{
// Low-frequency gain in the range 0.0 to 1.0
float gainLF;
// High-frequency gain in the range 0.0 to 1.0
float gainHF;
} VALowPassFilter;
MaterialProperties
Stores properties for a single material.
Read more: Materials.
public class MaterialProperties
{
// Percentage of high-frequency energy that is lost on each bounce (0.0 to 1.0).
public float AbsorptionHF;
// Percentage of low-frequency energy that is lost on each bounce (0.0 to 1.0).
public 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
public 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
public 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
public float Scattering;
// High-frequency transmission in decibels per meter (0.0 or greater).
public float TransmissionHF;
// Low-frequency transmission in decibels per meter (0.0 or greater).
public float TransmissionLF;
// Create a MaterialProperties with all values set to 0
public MaterialProperties();
// Create a MaterialProperties with all values provided
public MaterialProperties(float absorptionLF, float absorptionHF, float scattering, float transmissionLF, float transmissionHF, float planeTransmissionLF, float planeTransmissionHF);
// Create a MaterialProperties with the same values as another
public MaterialProperties(MaterialProperties prop);
// Copy all properties from another MaterialProperties
public void Update(MaterialProperties prop);
// Update all properties
public 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 metersToBlock meters through a material
public static float TransmissionForThickness(float metersToBlock);
}
interface MaterialProperties {
// Created via: va.MaterialProperties_Create()
// Also: va.MaterialProperties_CreateWith(absorptionLF, absorptionHF, scattering, transmissionLF, transmissionHF, planeTransmissionLF, planeTransmissionHF)
// Percentage of high-frequency energy lost on each bounce (0.0 to 1.0)
get absorptionHF(); set absorptionHF(v);
// Percentage of low-frequency energy lost on each bounce (0.0 to 1.0)
get absorptionLF(); set absorptionLF(v);
// Percentage of high-frequency energy lost when a ray passes through a flat plane, disk, triangle, or non-watertight mesh (0.0 to 1.0)
get planeTransmissionHF(); set planeTransmissionHF(v);
// Percentage of low-frequency energy lost when a ray passes through a flat plane, disk, triangle, or non-watertight mesh (0.0 to 1.0)
get planeTransmissionLF(); set planeTransmissionLF(v);
// Scattering strength (0.0 to 1.0), where 0.0 has no scattering and 1.0 scatters up to 90 degrees from the reflected direction
get scattering(); set scattering(v);
// High-frequency transmission in dB/m (0.0 or greater)
get transmissionHF(); set transmissionHF(v);
// Low-frequency transmission in dB/m (0.0 or greater)
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.
public 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 (0.5m) | 120 (0.25m) | Link |
| Cloth | 0.72 | 0.91 | 0.7 | 10 (3m) | 60 (0.5m) | Link |
| Concrete | 0.31 | 0.55 | 0.4 | 50 (0.6m) | 100 (0.3m) | Link |
| Concrete Polished | 0.06 | 0.12 | 0.1 | 50 (0.6m) | 100 (0.3m) | Link |
| Dirt | 0.48 | 0.88 | 0.5 | 20 (1.5m) | 75 (0.4m) | Link |
| Glass | 0.08 | 0.12 | 0.05 | 150 (0.2m) | 600 (0.05m) | |
| Grass | 0.71 | 0.93 | 0.8 | 7.5 (4m) | 37.5 (0.8m) | Link |
| Gravel | 0.06 | 0.12 | 0.7 | 30 (1m) | 100 (0.3m) | Link |
| Gyprock | 0.04 | 0.08 | 0.05 | 75 (0.4m) | 200 (0.15m) | Link |
| Ice | 0.01 | 0.03 | 0.01 | 200 (0.15m) | 375 (0.08m) | |
| Leaf | 0.71 | 0.91 | 0.8 | 6 (5m) | 30 (1m) | Link |
| Marble | 0.06 | 0.12 | 0.03 | 60 (0.5m) | 150 (0.2m) | Link |
| Metal | 0.05 | 0.02 | 0.01 | 300 (0.1m) | 600 (0.05m) | Link |
| Mud | 0.56 | 0.82 | 0.5 | 25 (1.2m) | 85.71 (0.35m) | Link |
| Rock | 0.31 | 0.51 | 0.4 | 50 (0.6m) | 100 (0.3m) | Link |
| Sand | 0.65 | 0.92 | 0.7 | 15 (2m) | 60 (0.5m) | Link |
| Snow | 0.65 | 0.92 | 0.7 | 10 (3m) | 50 (0.6m) | Link |
| Tile | 0.06 | 0.12 | 0.03 | 60 (0.5m) | 150 (0.2m) | Link |
| Tree | 0.22 | 0.46 | 0.3 | 20 (1.5m) | 75 (0.4m) | Link |
| Water | 0.06 | 0.12 | 0.1 | 60 (0.5m) | 150 (0.2m) | |
| Wood Indoor | 0.12 | 0.3 | 0.1 | 75 (0.4m) | 200 (0.15m) | Link |
| Wood Outdoor | 0.16 | 0.37 | 0.2 | 60 (0.5m) | 150 (0.2m) | Link |
Matrix
Struct for a 4x4 matrix.
public struct Matrix
{
public float M11;
public float M12;
public float M13;
public float M14;
public float M21;
public float M22;
public float M23;
public float M24;
public float M31;
public float M32;
public float M33;
public float M34;
public float M41;
public float M42;
public float M43;
public float M44;
public override bool Equals(object obj);
public override int GetHashCode();
// Returns the inverse of this matrix
public Matrix Inverse();
// Returns true if all elements are exactly equal to those in other
public bool IsEqual(Matrix other);
// Converts this matrix to a Matrix4x4
public System.Numerics.Matrix4x4 ToNumerics();
// Returns true if a does not equal b
public static bool operator !=(Matrix a, Matrix b);
// The identity matrix
public 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
public static bool operator ==(Matrix a, Matrix b);
// Create a rotation matrix from a Quaternion
public static Matrix CreateFromQuaternion(System.Numerics.Quaternion q);
// Create a transposed rotation matrix from a Quaternion
public static Matrix CreateFromQuaternionTransposed(System.Numerics.Quaternion q);
public static Matrix CreateOrthoProjection(float left, float top, float right, float bottom, float near, float far);
// Create a rotation matrix around the X axis
public static Matrix CreateRotationX(float rads);
// Create a rotation matrix around the Y axis
public static Matrix CreateRotationY(float rads);
// Create a rotation matrix around the Z axis
public static Matrix CreateRotationZ(float rads);
// Create a scale matrix. Note scale matrices can only be set on MeshPrimitive and not any other primitive
public 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
public static Matrix CreateScale(Vector v);
// Create a scale matrix. Note scale matrices can only be set on MeshPrimitive and not any other primitive
public static Matrix CreateScale(float scaleFactor);
// Create a translation matrix
public static Matrix CreateTranslation(System.Numerics.Vector3 vec);
// Create a translation matrix
public static Matrix CreateTranslation(Vector vec);
// Create a translation matrix
public static Matrix CreateTranslation(float x, float y, float z);
// Multiply two matrices together
public 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);
ThreadStatistics
Helper class for retrieving and caching the number of threads available.
public static class ThreadStatistics
{
// The number of threads available for background tasks, excluding the main thread
public static int BackgroundThreadCount;
// The minimum number of threads in the thread pool
public 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
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
Struct for a 3D vector.
public struct Vector : IPosition
{
// Gets or sets a component by index (0=X, 1=Y, 2=Z)
public float this[int index];
// The length of this vector
public float Magnitude;
// Returns a normalized copy of this vector
public Vector Normalized;
// The X component
public float X;
// The Y component
public float Y;
// The Z component
public float Z;
// Creates a vector with all components set to v
public Vector(float v);
// Creates a vector with the specified components
public Vector(float x, float y, float z);
// Creates a vector from a Vector3
public Vector(System.Numerics.Vector3 v);
public override bool Equals(object obj);
public override int GetHashCode();
// IPosition implementation, returns itself
public Vector GetPosition();
// Returns true if any component of this vector is greater than the corresponding component in the other vector
public 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
public bool GreaterEqualAny(Vector v);
// Returns true if all components of this vector equal those of other
public bool IsEqual(Vector other);
// Returns true if any component of this vector is less than the corresponding component in the other vector
public 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
public bool LessEqualAny(Vector v);
// Normalizes this vector in place
public void Normalize();
// Converts to Vector3
public System.Numerics.Vector3 ToNumerics();
public override string ToString();
// Returns true if a does not equal b
public static bool operator !=(Vector a, Vector b);
// Returns true if all components of a are greater than or equal to those of b
public static bool operator >=(Vector a, Vector b);
// A vector with all components set to MaxValue
public static Vector MAX = new(float.MaxValue);
// A vector with all components set to MinValue
public static Vector MIN = new(float.MinValue);
// A vector with all components set to one
public static Vector One = new(1);
// Returns true if all components of a are less than or equal to those of b
public static bool operator <=(Vector a, Vector b);
// Returns true if a equals b
public static bool operator ==(Vector a, Vector b);
// A unit vector pointing upward (0, 1, 0)
public static Vector Up = new(0, 1, 0);
// A vector with all components set to zero
public static Vector Zero = new(0);
// Returns true if all components of a are greater than those of b
public static bool operator >(Vector a, Vector b);
// Returns the cross product of a and b
public static Vector Cross(Vector a, Vector b);
// Returns the dot product of a and b
public static float Dot(Vector a, Vector b);
// Creates a direction vector from pitch and yaw angles in radians
public static Vector FromPitchYaw(float pitch, float yaw);
// Returns the component-wise maximum of a and b
public static Vector GetMax(Vector a, Vector b);
// Returns the component-wise minimum of a and b
public static Vector GetMin(Vector a, Vector b);
// Interpolates from one vector to another
public static Vector Lerp(Vector a, Vector b, float c);
// Returns true if all components of a are less than those of b
public static bool operator <(Vector a, Vector b);
// Reflects direction about the normal
public static Vector Reflect(Vector direction, Vector normal);
// Transforms a normal vector by a matrix, ignoring translation
public static Vector TransformNormal(Vector a, Matrix b);
// Scales a by scalar b
public static Vector operator *(float b, Vector a);
// Scales a by scalar b
public static Vector operator *(Vector a, float b);
// Transforms a vector by a matrix, including translation
public static Vector operator *(Vector a, Matrix b);
// Returns the component-wise product of a and b
public static Vector operator *(Vector a, Vector b);
// Returns the component-wise sum of a and b
public static Vector operator +(Vector a, Vector b);
// Returns the negation of a
public static Vector operator -(Vector a);
// Returns the component-wise difference of a and b
public static Vector operator -(Vector a, Vector b);
// Divides scalar b by each component of a
public static Vector operator /(float b, Vector a);
// Divides a by scalar b
public static Vector operator /(Vector a, float b);
// Returns the component-wise quotient of a and b
public 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);
// Scales a by scalar
VAVector vaVectorMultiplyScalar(VAVector a, float scalar);
// Divides a by scalar
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);
// Gets a component by 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 v
VAVector vaVectorNormalize(VAVector v);
// Creates a direction vector from pitch and yaw angles in radians
VAVector vaVectorFromPitchYaw(float pitch, float yaw);
// Transforms a point by a matrix, including translation (w=1)
VAVector vaVectorMultiplyMatrix(VAVector a, const VAMatrix* b);
// Transforms a normal vector by a matrix, ignoring translation, and normalizes the result
VAVector vaVectorTransformNormal(VAVector a, const VAMatrix* b);
// Returns true if any component of vec is NaN or infinity
bool vaVectorIsNanOrInfinity(VAVector vec);
// Reflects direction about normal
VAVector vaVectorReflect(VAVector direction, VAVector normal);
VisualisationData
Struct that contains position and normal data.
public struct VisualisationData
{
// The world-space normal of the ray bounce. Guaranteed to be normalised
public Vector normal;
// The world-space position of the ray bounce
public Vector position;
}
interface VisualisationData {
// Created via: va.VisualisationData_Create()
// The world-space normal of the ray bounce. Guaranteed to be normalised
get normal(); set normal(v);
// The world-space position of the ray bounce
get position(); set position(v);
}
World
Contains emitters, primitives, materials and many customisable settings. Manages its own raytracing and multithreading.
public class World
{
// Air absorption settings. Set to null to disable air absorption.
public 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.
public float AnalysisTime;
// True while background raytracing threads are still running.
// After setting PendingShutdown, continue calling Update until this is false, then call Dispose.
public bool AreThreadsRunning;
// The pitch of the camera in the debug window.
public float CameraPitch;
// The position of the camera in the debug window.
public Vector CameraPosition;
// The yaw of the camera in the debug window.
public float CameraYaw;
// Custom formulas for calculating EAX properties (diffusion, density, etc). Set to null to use default formulas.
public CustomEAXFormulas CustomEAXFormulas;
// Whether Emitters outside the world have 0 occlusion/permeation energy (true) or maximum energy (false).
public bool EmittersOutsideTheWorldAreMuffled;
// The epsilon value used for ray offsets, world bounds clamping and line-of-sight tests. Defaults to 0.01f
public float Epsilon;
// The field of view (in radians) of the camera in the debug window.
public float FieldOfView;
// List of grouped EAX reverb properties for all emitters. Contains parameters compatible with EAX reverb effects.
public List<EAXReverb> GroupedEAX = [];
// Inverse speed of sound in seconds per meter. Defaults to 1.0f / 343.0f. Affects reverb calculation
public float InverseSpeedOfSound;
// A custom logging callback. Defaults to WriteLine()
public Action<string> LogCallback;
// Whether to log memory allocation warnings
public 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.
public float MainThreadTime;
// The maximum bounds of the world (Position + Size)
public Vector MaxBounds;
// The maximum amount of threads that can run in parallel for this world
public int MaximumConcurrencyLevel;
// The maximum number of grouped EAX reverb properties created for all emitters. Higher values increase accuracy but are more expensive to run.
public int MaximumGroupedEAXCount = 2;
// Gets meters per world unit. Affects air absorption and reverb calculation.
public float MetersPerUnit = 1;
// Callback that is invoked when keys are pressed
public Action<KeyEvent> OnKeyEvent;
// This is invoked after EAX reverb results are updated, and before emitter.OnRaytracingComplete callbacks are invoked. This gives you a chance to update your EAX effects, so they can be applied to an emitter in it's OnRaytracingComplete callback.
public 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.
public 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.
public 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.
public float PreparationTime;
// The percentage of rays that were reused from previous frames, rather than being cast this frame. Higher values indicate better performance.
public float RayCachePercent;
// The number of rays cast this frame.
public 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.
public float RaytracingTime;
// High-frequency reference (Hz) for air absorption, reverb, and material scattering
public float ReferenceFrequencyHF = 4000;
// Low-frequency reference (Hz) for air absorption, reverb, and material scattering
public float ReferenceFrequencyLF = 300;
// Whether to render the raytracing scene in a separate window. Only one world can have rendering enabled
public bool RenderingEnabled;
// Returns true when raytracing has run at least once, and it is safe to access reverb objects
public bool ReverbCalculated;
// The size of the world.
// Emitters outside the world will not be raytraced, and Primitives that are fully outside these bounds will be ignored
public Vector Size;
// The total number of rays that could potentially be cast each frame, including trail rays, reverb rays, occlusion rays, permeation rays and visualisation rays
public int TotalPossibleRayCount;
// The number of work items to split trails across for load balancing. A higher workItemCount helps evenly distribute work across all threads.
public int WorkItemCount = 128;
// 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.
public bool WorldIsIndoors;
// Create a new world
public World();
// Add an Emitter to the world.
// This method is thread-safe and will not affect the current raytracing threads.
public Emitter AddEmitter(Emitter emitter);
// Add a new material to this world
public 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.
public void AddPrimitive(Primitive primitive);
// Waits for background thread to complete, then disposes everything.
// After calling this method, this world cannot be reused.
public void Dispose();
// Get properties for a specific material.
public MaterialProperties GetMaterial(MaterialType type);
// Gets the debug rendering color for a specific material type
public Color GetMaterialColor(MaterialType type);
// Returns true if a material exists
public bool HasMaterial(MaterialType type);
// Returns true if a key is pressed in the debug window.
public 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.
public 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.
public void RemovePrimitive(Primitive primitive);
// Sets the debug rendering color for a specific material type
public 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. The actual raytracing runs on background threads.
// Calling this more frequently (multiple times per frame) is safe and can reduce latency for emitter updates.
public 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).
public void Wait();
}
interface World {
// Created via: va.World_Create()
// Air absorption settings. Set to null to disable air absorption
get airAbsorption(); set airAbsorption(v);
// Whether emitters outside the world have 0 occlusion/permeation energy (true) or maximum energy (false)
get emittersOutsideTheWorldAreMuffled(); set emittersOutsideTheWorldAreMuffled(v);
// Epsilon value for ray offsets, world bounds clamping and line-of-sight tests. Defaults to 0.01
get epsilon(); set epsilon(v);
// Whether to expose trail data for rendering
get exposeTrails(); set exposeTrails(v);
// Inverse speed of sound in seconds per meter. Defaults to 1/343. Affects reverb calculation
get inverseSpeedOfSound(); set inverseSpeedOfSound(v);
// Whether to log memory allocation warnings
get logMemoryAllocationWarnings(); set logMemoryAllocationWarnings(v);
// Maximum bounds of the world (position + size)
get maxBounds(); set maxBounds(v);
// Maximum number of threads that can run in parallel for this world
get maximumConcurrencyLevel(); set maximumConcurrencyLevel(v);
// Maximum number of grouped EAX reverb properties. Higher values increase accuracy but cost more
get maximumGroupedEAXCount(); set maximumGroupedEAXCount(v);
// Meters per world unit. Affects air absorption and reverb calculation
get metersPerUnit(); set metersPerUnit(v);
// When set to true, update() stops submitting work to background threads. Poll areThreadsRunning until false, then call destroy()
get pendingShutdown(); set pendingShutdown(v);
// Minimum bounds of the world. Emitters and primitives outside these bounds are ignored
get position(); set position(v);
// High-frequency reference in Hz for air absorption, reverb, and material scattering
get referenceFrequencyHF(); set referenceFrequencyHF(v);
// Low-frequency reference in Hz for air absorption, reverb, and material scattering
get referenceFrequencyLF(); set referenceFrequencyLF(v);
// Size of the world. Emitters and primitives fully outside these bounds are ignored
get size(); set size(v);
// Number of work items to split trails across for load balancing
get workItemCount(); set workItemCount(v);
// Whether the entire world is considered indoors or outdoors. Affects reverb ray behaviour at world bounds
get worldIsIndoors(); set worldIsIndoors(v);
// Average time (in milliseconds) spent in the analysis thread calculating reverb properties
get analysisTime();
// True while background raytracing threads are still running. Poll this after setting pendingShutdown, then call destroy() when false
get areThreadsRunning();
// Averaged EAX reverb objects for all emitters with affectsGroupedEAX set to true
get groupedEAX();
// Average time (in milliseconds) spent by update() on the main thread
get mainThreadTime();
// Average time (in milliseconds) spent in the preparation thread updating the BVH
get preparationTime();
// Average real-world elapsed time (in milliseconds) for raytracing threads to complete
get raytracingTime();
// True when raytracing has run at least once and reverb objects are safe to access
get reverbCalculated();
// Invoked after EAX reverb results are updated, before onRaytracingComplete callbacks
set onReverbUpdated(fn);
// Adds an emitter to the raytracing scene
addEmitter(e);
// Adds or replaces a material in this world
addMaterial(materialType, properties);
// Adds a 3D primitive to the raytracing scene
addPrimitive(p);
// Returns the number of background threads used by this world
getBackgroundThreadCount();
// Returns the acoustic properties for a specific material
getMaterial(materialType);
// Returns the total number of threads used by this world
getThreadCount();
// Returns true if a material exists in this world
hasMaterial(materialType);
// Removes an emitter from the raytracing scene
removeEmitter(e);
// Removes a 3D primitive from the raytracing scene
removePrimitive(p);
// Updates the raytracing simulation. Call regularly to process results and submit new work
update();
// Blocks until all background raytracing threads complete and handles the results
wait();
}
// Creates a new world
VAWorld* vaWorldCreate();
// Waits for background thread to complete, then frees all resources. After calling this method, this world cannot be reused.
void vaWorldFree(VAWorld* world);
void vaWorldUpdate(VAWorld* world);
// 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 vaWorldWait(VAWorld* world);
void vaWorldSetPendingShutdown(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).
int vaWorldGetGroupedEAXCount(const VAWorld* world);
// Primitive management — use the macros below, not these directly
void vaWorldAddPrimitive_(VAWorld* world, void* primitive);
void 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);
// Set the world position. Returns VA_SUCCESS, or VA_INVALID_VALUE if the new value is NaN or Infinity.
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 be ignored.
VAVector vaWorldGetSize(const VAWorld* world);
// Set the world size. Returns VA_SUCCESS, or VA_INVALID_VALUE if the new value is NaN, Infinity or less than or equal to (0, 0, 0).
VAResult vaWorldSetSize(VAWorld* world, VAVector size);
// The maximum bounds of the world (Position + Size).
VAVector vaWorldGetMaxBounds(const VAWorld* world);
// Set the max bounds of the world. Returns VA_SUCCESS, or VA_INVALID_VALUE if the new value is NaN, Infinity or less than or equal to (0, 0, 0).
VAResult vaWorldSetMaxBounds(VAWorld* world, VAVector maxBounds);
// Number of emitters in this world
int vaWorldGetEmitterCount(const VAWorld* world);
// Total number of rays that could potentially be cast each frame across all emitters.
int vaWorldGetTotalPossibleRayCount(const VAWorld* world);
// Percentage of rays reused from previous frames rather than cast this frame. Higher values indicate better performance.
float vaWorldGetRayCachePercent(const VAWorld* world);
// The maximum number of grouped EAX reverb properties created for all emitters. Higher values increase accuracy but are more expensive to run. Must be >= 2. Returns VA_SUCCESS, or VA_OUT_OF_RANGE if value is less than 2.
VAResult vaWorldSetMaximumGroupedEAXCount(VAWorld* world, int value);
// The number of work items to split trails across for load balancing. A higher value helps evenly distribute work across all threads. Must be >= 1. Returns VA_SUCCESS, or VA_OUT_OF_RANGE if value is less than 1.
VAResult vaWorldSetWorkItemCount(VAWorld* world, int value);
// The maximum amount of threads that can run in parallel for this world. Must be >= 1. Returns VA_SUCCESS, or VA_OUT_OF_RANGE if value is less than 1.
VAResult vaWorldSetMaximumConcurrencyLevel(VAWorld* world, int value);
// Gets meters per world unit. Affects air absorption and reverb calculation. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if <= 0.
VAResult vaWorldSetMetersPerUnit(VAWorld* world, float value);
// Inverse speed of sound in seconds per meter. Defaults to 1.0f / 343.0f. Affects reverb calculation. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if <= 0.
VAResult vaWorldSetInverseSpeedOfSound(VAWorld* world, float value);
// Low-frequency reference (Hz) for air absorption, reverb, and material scattering. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if <= 0.
VAResult vaWorldSetReferenceFrequencyLF(VAWorld* world, float value);
// High-frequency reference (Hz) for air absorption, reverb, and material scattering. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if <= 0.
VAResult vaWorldSetReferenceFrequencyHF(VAWorld* world, float value);
// Air absorption settings
void* vaWorldGetAirAbsorption(VAWorld* world);
// Air absorption settings. Pass NULL to disable air absorption. Returns VA_SUCCESS, or VA_INVALID_VALUE if validation fails.
VAResult vaWorldSetAirAbsorption(VAWorld* world, VAAirAbsorptionSettings* value);
// Relative humidity as a percentage (0–1).
void vaWorldSetAirAbsorptionHumidity(VAWorld* world, float value);
// Air temperature in degrees Celsius.
void vaWorldSetAirAbsorptionTemperature(VAWorld* world, float value);
// Atmospheric pressure in Pascals.
void vaWorldSetAirAbsorptionPressure(VAWorld* world, float value);
// Custom LF air absorption formula. Takes distance in meters, returns energy loss as a percentage. NULL to use default.
void vaWorldSetAirAbsorptionCustomFormulaLF(VAWorld* world, VAAirAbsorptionCustomFormula value);
// Custom HF air absorption formula. Takes distance in meters, returns energy loss as a percentage. NULL to use default.
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);
// The maximum number of grouped EAX reverb properties created for all emitters.
int vaWorldGetMaximumGroupedEAXCount(const VAWorld* world);
// The number of work items to split trails across for load balancing.
int vaWorldGetWorkItemCount(const VAWorld* world);
// The maximum amount of threads that can run in parallel for this world.
int vaWorldGetMaximumConcurrencyLevel(const VAWorld* world);
// Whether the system is running in single-threaded mode.
bool vaWorldGetSingleThreaded(const VAWorld* world);
// Whether the system is running in single-threaded mode.
void vaWorldSetSingleThreaded(VAWorld* world, bool value);
// Gets meters per world unit. Affects air absorption and reverb calculation.
float vaWorldGetMetersPerUnit(const VAWorld* world);
// Inverse speed of sound in seconds per meter. Defaults to 1.0f / 343.0f. Affects reverb calculation.
float vaWorldGetInverseSpeedOfSound(const VAWorld* world);
// Low-frequency reference (Hz) for air absorption, reverb, and material scattering.
float vaWorldGetReferenceFrequencyLF(const VAWorld* world);
// High-frequency reference (Hz) for air absorption, reverb, and material scattering.
float vaWorldGetReferenceFrequencyHF(const VAWorld* world);
// Whether emitters outside the world have 0 occlusion/permeation energy (true) or maximum energy (false).
bool vaWorldGetEmittersOutsideTheWorldAreMuffled(const VAWorld* world);
// TODO
float vaWorldGetEpsilon(const VAWorld* world);
// TODO
void vaWorldSetEpsilon(VAWorld* world, float value);
// Add a new material to this world.
void vaWorldCreateMaterial(VAWorld* world, int materialId);
// Gets the percentage of low-frequency energy that is lost when a ray bounces off this material, in the range 0.0 to 1.0
float vaWorldGetMaterialAbsorptionLF(const VAWorld* world, int materialId);
// Gets the percentage of high-frequency energy that is lost when a ray bounces off this material, in the range 0.0 to 1.0
float vaWorldGetMaterialAbsorptionHF(const VAWorld* world, int materialId);
// Gets the scattering strength when a ray bounces off this material, in the range 0.0 to 1.0 (0.0 = no scattering, 1.0 skews the ray reflection direction by up to 90 degrees)
float vaWorldGetMaterialScattering(const VAWorld* world, int materialId);
// Gets how much low-frequency energy is lost (in dB/meter) when a permeation ray passes through this primitive, in the range 0.0 or greater
float vaWorldGetMaterialTransmissionLF(const VAWorld* world, int materialId);
// Gets how much high-frequency energy is lost (in dB/meter) when a permeation ray passes through this primitive, in the range 0.0 or greater
float vaWorldGetMaterialTransmissionHF(const VAWorld* world, int materialId);
// Gets the percentage of low-frequency energy that is lost when a permeation ray passes through a flat VAPlanePrimitive, VADiskPrimitive, VATrianglePrimitive, or non-watertight VAMeshPrimitive, in the range 0.0 to 1.0
float vaWorldGetMaterialPlaneTransmissionLF(const VAWorld* world, int materialId);
// Gets the percentage of high-frequency energy that is lost when a permeation ray passes through a flat VAPlanePrimitive, VADiskPrimitive, VATrianglePrimitive, or non-watertight VAMeshPrimitive, in the range 0.0 to 1.0
float vaWorldGetMaterialPlaneTransmissionHF(const VAWorld* world, int materialId);
// Updates the percentage of low-frequency energy that is lost when a ray bounces off this material. Must be in the range 0.0 to 1.0. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if < 0.0 or > 1.0.
VAResult vaWorldSetMaterialAbsorptionLF(VAWorld* world, int materialId, float value);
// Updates the percentage of high-frequency energy that is lost when a ray bounces off this material. Must be in the range 0.0 to 1.0. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if < 0.0 or > 1.0.
VAResult vaWorldSetMaterialAbsorptionHF(VAWorld* world, int materialId, float value);
// Updates the scattering strength (0.0 = no scattering, 1.0 skews the ray reflection direction by up to 90 degrees). Must be in the range 0.0 to 1.0. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if < 0.0 or > 1.0.
VAResult vaWorldSetMaterialScattering(VAWorld* world, int materialId, float value);
// Updates how much low-frequency energy is lost (in dB/meter) when a permeation ray passes through this primitive. Must be 0.0 or greater. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if < 0.0.
VAResult vaWorldSetMaterialTransmissionLF(VAWorld* world, int materialId, float value);
// Updates how much high-frequency energy is lost (in dB/meter) when a permeation ray passes through this primitive. Must be 0.0 or greater. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if < 0.0.
VAResult vaWorldSetMaterialTransmissionHF(VAWorld* world, int materialId, float value);
// Updates the percentage of low-frequency energy that is lost when a permeation ray passes through a flat VAPlanePrimitive, VADiskPrimitive, VATrianglePrimitive, or non-watertight VAMeshPrimitive. Must be in the range 0.0 to 1.0. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if < 0.0.
VAResult vaWorldSetMaterialPlaneTransmissionLF(VAWorld* world, int materialId, float value);
// Updates the percentage of high-frequency energy that is lost when a permeation ray passes through a flat VAPlanePrimitive, VADiskPrimitive, VATrianglePrimitive, or non-watertight VAMeshPrimitive. Must be in the range 0.0 to 1.0. Returns VA_SUCCESS, VA_INVALID_VALUE if NaN or Infinity, or VA_OUT_OF_RANGE if < 0.0.
VAResult vaWorldSetMaterialPlaneTransmissionHF(VAWorld* world, int materialId, float value);
// Average time (ms) spent by vaWorldUpdate() on the main thread
double vaWorldGetMainThreadTime(VAWorld* world);
// Average time (ms) spent in background raytracing threads
double vaWorldGetRaytracingTime(VAWorld* world);
// Average time (ms) spent in the preparation thread
double vaWorldGetPreparationTime(VAWorld* world);
// Average time (ms) spent in the analysis thread
double vaWorldGetAnalysisTime(VAWorld* world);
VAResult vaWorldAddEmitter(VAWorld* world, VAEmitter* emitter);
// Remove an emitter from the world. Thread-safe. This emitter's OnRaytracingComplete callback will not be invoked.
void vaWorldRemoveEmitter(VAWorld* world, VAEmitter* emitter);
// Returns true when raytracing has run at least once, and it is safe to access reverb objects.
bool vaWorldGetReverbCalculated(const VAWorld* world);
// Invoked after EAX reverb results are updated, and before emitter OnRaytracingComplete callbacks are invoked.
void vaWorldSetOnReverbUpdatedCallback(VAWorld* world, void (*callback)(void));
// A custom logging callback.
void vaWorldSetLogCallback(VAWorld* world, VALogCallback callback);
// Whether to log memory allocation warnings.
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.
VAVector vaWorldCalculateListenerRelativePan(VAVector worldVector, float listenerPitch, float listenerYaw);
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.
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
TODO - clear ray cache when changing these 4 world settings?
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);
// TODO - comment says 'max' bounces. should just say bounces
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:
var emitter = new Emitter()
{
Name = "Player",
Position = new Vector(10),
ReverbRayCount = 128,
ReverbBounceCount = 64,
};
world.AddEmitter(emitter);
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:
public class Enemy : IPosition
{
public Vector position;
// Implement the interface
public Vector GetPosition() => position;
}
Then use the enemy directly as the emitter's position
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:
void Update()
{
var emitter1 = new Emitter();
var emitter2 = new Emitter();
var emitter3 = new Emitter();
world.AddEmitter(emitter1);
world.AddEmitter(emitter2);
world.AddEmitter(emitter3);
// The 3 emitters will be raytraced together
world.Update();
}
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 Android, Mac, Windows, Linux
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
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:
<PropertyGroup>
<!-- Replace this with the path to your vaudio SDK -->
<VAudioDir>path\to\your\vaudio\folder</VAudioDir>
</PropertyGroup>
<!-- Add vaudio.dll to your project -->
<ItemGroup>
<Reference Include="vaudio">
<HintPath>$(VAudioDir)\vaudio.dll</HintPath>
</Reference>
</ItemGroup>
<!-- Copy dev build dependencies. If using the production build, you can delete this block -->
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<!-- Copy dependencies to the build directory -->
<Copy SourceFiles="$(VAudioDir)\glfw3.dll" DestinationFolder="$(OutDir)" />
<Copy SourceFiles="$(VAudioDir)\libSkiaSharp.dll" DestinationFolder="$(ProjectDir)" />
<Copy SourceFiles="$(VAudioDir)\libHarfBuzzSharp.dll" DestinationFolder="$(ProjectDir)" />
</Target>
JavaScript / Web Assembly Setup
The JS SDK contains a vaudio-wrapper.js file and a _framework folder:
- the
_frameworkfolder contains the .wasm Web Assembly binaries and JS .NET runtime - the JS wrapper provides a nicer JS API, over the 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 { wrapVA } from './vaudio-wrapper.js';
// Set this to the number of threads you'll use
const YOUR_THREAD_COUNT = 4;
// The .NET runtime needs 6 threads. Without this, the browser may freeze
const DOTNET_THREAD_COUNT = 6;
// Initialise the .NET runtime
const { getAssemblyExports, getConfig } = await dotnet
.withDiagnosticTracing(false)
.withConfig({
jsThreadBlockingMode: "DangerousAllowBlockingWait", // Without this all calls to WASM are async
pthreadPoolInitialSize: YOUR_THREAD_COUNT + DOTNET_THREAD_COUNT,
pthreadPoolUnusedSize: 0,
})
.create();
const config = getConfig();
const exports = await getAssemblyExports(config.mainAssemblyName);
// Wrap all Web Assembly calls into JS objects with get/setters for ease of use
const va = wrapVA(exports.VAudioExports);
C Setup
The C SDK package contains:
include/vaudio.h— single header with all declarationswindows/vaudionative.dll+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 -Ipath/to/include -Lpath/to/linux -lvaudionative -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 will store emitters, primitives and materials. Read more: Worlds.
var world = new World()
{
WorldSize = new Vector(100),
RenderingEnabled = true,
};
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 it
transform = Matrix.CreateRotationX(MathF.PI / 4) *
Matrix.CreateTranslation(30, 30, 30)
};
world.AddPrimitive(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(30, 30, 30)
);
world.addPrimitive(prism);
#include <math.h> // For M_PI
VAPrismPrimitive* prism = vaPrismPrimitiveCreate();
vaPrismPrimitiveSetMaterial(prism, VAMaterialConcrete);
vaPrismPrimitiveSetSize(prism, vaVectorCreate(15.0f, 15.0f, 15.0f));
VAMatrix rotation = vaMatrixCreateRotationX(M_PI / 4);
VAMatrix translate = vaMatrixCreateTranslation(30, 30, 30);
VAMatrix transform = vaMatrixMultiply(&rotation, &translate);
vaPrismPrimitiveSetTransform(prism, &transform);
vaWorldAddPrimitive(ctx, prism);
Emitters
Create a listener emitter that casts occlusion rays. Read more: Emitters.
var listener = new Emitter()
{
Position = new Vector(4),
OcclusionRayCount = 256,
OcclusionBounceCount = 8,
};
world.AddEmitter(listener);
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);
int errorCode = vaWorldAddEmitter(ctx, listener);
assert(errorCode == 0);
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));
int errorCode = vaWorldAddEmitter(ctx, target);
assert(errorCode == 0);
// 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.
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 = 32 * 64 * 0.05;
listener.reverbRayCount = 32;
listener.reverbBounceCount = 64;
listener.reverbEnergyCap = 32 * 64 * 0.05;
vaEmitterSetReverbRayCount(listener, 32);
vaEmitterSetReverbBounceCount(listener, 64);
vaEmitterSetReverbEnergyCap(listener, 32 * 64 * 0.05);
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.
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
import { MaterialType } from 'vaudio-wrapper.js';
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 cause the ray cache to be cleared and 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, 4, 7, 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.5f, 0.6f, 0.7f, 4, 7, 0.1f, 0.2f);
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, 4);
vaWorldSetMaterialTransmissionHF(world, MATERIAL_ALIEN, 7);
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 are defined as decibels per meter, which is a tricky unit to work with. There's a transmission->thickness helper function that I recommend using instead:
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.TransmissionForThickness(20);
// A ray will lose all high-frequency energy after travelling 5 meters through a concrete primitive
concrete.TransmissionHF = MaterialProperties.TransmissionForThickness(5);
import { transmissionForThickness, 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 = transmissionForThickness(20);
// A ray loses all high-frequency energy after travelling 5 meters through a concrete primitive
concrete.transmissionHF = transmissionForThickness(5);
// A ray loses all low-frequency energy after travelling 20 meters through a concrete primitive
vaWorldSetMaterialTransmissionLF(world, VAMaterialConcrete, materialPropertiesTransmissionForThickness(20));
// A ray loses all high-frequency energy after travelling 5 meters through a concrete primitive
vaWorldSetMaterialTransmissionHF(world, VAMaterialConcrete, materialPropertiesTransmissionForThickness(5));
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:
- The value must not be
NaNorInfinity - All
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).
var listener = new Emitter()
{
OcclusionRayCount = 1024,
OcclusionBounceCount = 8,
PermeationRayCount = 128,
PermeationBounceCount = 3,
};
world.AddEmitter(listener);
var target = new Emitter();
world.AddEmitter(target);
// Enable muffling
listener.AddTarget(target);
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:
var target = new Emitter();
target.OnRaytracedByAnotherEmitter = (Emitter Other) => ...
// This is invoked after OnRaytracedByAnotherEmitter()
target.OnRaytracingComplete = () =>
{
world.RemoveEmitter(target);
};
world.AddEmitter(target);
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.
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 = 32 * 64 * 0.05,
// 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 = 32 * 64 * 0.05;
// 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, 32 * 64 * 0.05f);
// 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
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.
var world = new World()
{
MaximumGroupedEAXCount = 4,
};
var emitter = new Emitter()
{
AffectsGroupedEAX = true,
};
world.AddEmitter(emitter);
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:
Emitter listener;
void Initialise()
{
listener = new Emitter()
{
HasRelativeReverb = true,
RelativeReverbInnerThreshold = 0.5f,
RelativeReverbOuterThreshold = 0.8f,
};
world.AddEmitter(listener);
}
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 = 128 * 64 * 0.2f, // 20%
}
const emitter = va.Emitter_Create();
emitter.reverbRayCount = 128;
emitter.reverbBounceCount = 64;
emitter.reverbEnergyCap = 128 * 64 * 0.2; // 20%
VAEmitter* emitter = vaEmitterCreate();
vaEmitterSetReverbRayCount(emitter, 128);
vaEmitterSetReverbBounceCount(emitter, 64);
vaEmitterSetReverbEnergyCap(emitter, 128 * 64 * 0.2f); // 20%