Stl.Modulation and Fades¶
The fades system provides timed volume modulation for IVLS voices. It is built from Stl.Modulation (base data and the fire function), Fades (the convenience API and flow registration), and the internal playback nodes FadeOut and FadeIn.
Why This Exists¶
Fading sounds in and out in Kontakt is not trivial when you need the fade to: - Interact with other modulation (CC, LFO, VMC modbits) - Work on step-sequenced sounds whose event IDs may be recycled - Compose with other simultaneous fades on the same voice - Remain cancellable if the source voice dies early
Building this on top of VMC modbits and IVLS voices makes all of those things automatic.
Key Concepts¶
The Modulation Voice Model¶
ivls.fire_modulation creates a new voice that travels its own flow (FadeOut or FadeIn), asynchronously from the voice being faded:
function ivls.fire_modulation(vo, time_ms, kill, curve, mod_flow) -> mod_vo
mod_vo := Voice.copy(vo)
Voice[mod_vo].mod_time := time_ms
Voice[mod_vo].mod_target := vo
Voice[mod_vo].mod_kill_on_end := kill
Voice[mod_vo].mod_curve := curve
ivls.reflow_voice(mod_vo, mod_flow)
end functionThe modulation voice is an independent IVLS voice. Calling ivls.play(mod_vo) launches the ramp asynchronously. Releasing mod_vo externally cancels the ramp early.
Fade Functions¶
declare mod_vo := fades.out(vo, time_ms, kill) // linear fade out
declare mod_vo := fades.in(vo, time_ms) // linear fade in
declare mod_vo := fades.curve_out(vo, time_ms, curve, kill) // shaped fade out
declare mod_vo := fades.curve_in(vo, time_ms, curve) // shaped fade inCall ivls.play(mod_vo) to start the ramp. The kill parameter controls whether the target voice is released when the fade completes.
The Curve Parameter¶
The curve parameter shapes the exponential ramp. 0 is linear. Positive values produce a convex curve (faster at start, slower at end). Negative values produce a concave curve. Range is -100 to 100.
FadeOut Internals¶
FadeOut allocates a MULT volume modbit, attaches it to the target voice, and ramps from 10000 (100%) to 0 over mod_time milliseconds in 5 ms steps:
declare mb := Modbit.local_modbit(ivls.mod_par.VOLUME, ivls.mod_type.MULT)
Modbit.add_to_voice(mb, target)
while path_on and (t < mod_time) and check_ref(Voice, target)
voice_mod.float_calc := math.curve_exp(float(curve), 1.0 - (float(t)/float(mod_time)), 0, 10000)
Modbit.set(mb, int(voice_mod.float_calc))
t := t + 5
ivls.wait_ms(5)
end while
Modbit.set(mb, 0)
if kill_on_end = YES
ivls.release_voice(target)
end ifThe loop exits early if the modulation voice is released (path_on becomes false) or if the target voice no longer exists (check_ref fails).
Connection to VMC Modbits¶
Modbit.add_to_voice(mb, target) registers the modbit against the target voice. Multiple modbits combine: MULT modbits multiply together. A fade-out modbit and a sustain-pedal modbit on the same voice both apply and combine multiplicatively.
This is what makes VMC fades compose with other modulation — they are just modbits in the tree, subject to the same combination rules as everything else.
Equal-Power Crossfade¶
For legato crossfades, simultaneous fade-out and fade-in produce constant perceived loudness when the curves are complementary:
fades.curve_out(src_vo, crossfade_ms, out_curve, YES) // fade out + kill source
fades.curve_in(dest_vo, crossfade_ms, in_curve) // fade in destinationTokyo Scoring computes the companion curve via tky.legato.calculate_equal_power_companion_curve(), which produces a sine-based complement ensuring out² + in² = 1 at every point. The out-curve varies by dynamic layer and vibrato state.
VMC Fade vs. Native fade_out()¶
Native fade_out() |
VMC fade | |
|---|---|---|
| Cost | Very cheap | More expensive |
| VMC visibility | None | Full |
| Works for step-seq sounds | No | Yes |
| Composes with other modbits | No | Yes |
Use native fade_out() for simple cheap attenuation when you do not need composability. Use VMC fades for crossfades, for step-sequenced sounds, and whenever the modulation must interact with other systems.
Including the Fade System¶
define Stl.Fades := Fades, FadeOut, FadeInAdd this to the product's node composition list. Fades declares the two flows and registers FadeOut and FadeIn into them during cb Flows.
Connections to Other Parts of IVLS¶
Fades are built entirely on vmc modbits. stl-play-event calls VMC.register_sound_voice which makes the target voice responsive to modbit updates from fades. stl-legato uses fades.curve_out and fades.curve_in for equal-power crossfades. stl-portamento uses the same modbit infrastructure for the TUNE ADD modbit but does not use the fade API.
Patterns and Caveats¶
- After calling
fades.out(vo, ...), always callivls.play(mod_vo)on the returned voice. The ramp does not start until the modulation voice is played. - The 5 ms step granularity means very short fades (< 10 ms) may not complete smoothly. For sub-10 ms transitions, consider a single
Modbit.setcall instead. - Multiple simultaneous fades on the same voice combine multiplicatively. If a voice has both a fade-out modbit (dropping to 0%) and a CC modbit (at 80%), the combined result is 0%. Plan modbit interactions carefully.
kill = YESreleases the target voice after the fade completes. If the target voice was already released externally before the fade finishes, thecheck_ref(Voice, target)guard in the loop will exit cleanly.
Related¶
- vmc — the modbit system that fades are built on
- stl-legato — uses fades.curve_out / fades.curve_in for crossfades
- stl-play-event — VMC.register_sound_voice enables modbit updates on playing sounds