Skip to content

Adaptive Release

Adaptive release is a subsystem of tact that ensures release samples seamlessly match the sustain sample's amplitude at the moment of key release, regardless of how long the note was held. Without it, release samples sound too loud when triggered after a note that has already decayed significantly.

Why Adaptive Release Exists

A sustained string or pad sample naturally decays in volume over time. If you hold a note for two seconds, the sustain sample is noticeably quieter than at onset. When the key is released, the release sample — which starts at its own nominal volume — will be noticeably louder than the tail of the sustain. Adaptive release solves this by computing a volume offset that makes the release sample start at the correct amplitude to match wherever the sustain sample currently is.

Key Concepts

The DSP Analysis Pipeline

During product build, CreateReleaseArray.py analyses every release sample's volume-over-time envelope and writes the results into NKA arrays: vol_data[] and vol_index[]. This is a one-time offline analysis. At runtime, the engine only reads lookup tables — no DSP analysis happens during playback.

The tables store the dB level at regular time intervals for each sample. vol_index[] is an index structure that maps elapsed time to the closest position in vol_data[].

The Runtime Mechanism

At NoteOff, the engine looks up where in the sustain envelope it currently is, then offsets the release sample's volume to match:

// At NoteOff: find elapsed time since NoteOn
declare elapsed := ENGINE_UPTIME - Voice[self].note_on_time

// Look up the TIME position for a given target volume in the sustain envelope
declare vol_match := tky.rls_vol.match_time_to_vol(artic, target_vol, note, vel, rr)

// Apply a volume offset so the release sample starts at that level
Modbit.set(vol_offset_mb, vol_match)

// Play the release sample
ivls.play.oneshot(rel_vo, 1000000)

tky.rls_vol.match_time_to_vol(artic, target_vol, note, vel, rr) -> time takes 5 arguments and returns a TIME position. It does a lookup into the NKA-loaded vol_data[] array to find the time position that corresponds to a given target volume level — i.e., it finds where in the envelope a specific dB level occurs.

The volume offset is applied via a VMC modbit (vol_offset_mb) attached to the release voice. The vo_field tky.release.vol_offset stores the computed offset value on the release voice; it initializes to TKY.RLS_STANDARD_VOLUME_TARGET which is -21000 and is set at NoteOff time.

Normal vs. Adaptive Mode

Not all articulations use adaptive release. The per-artic flag in the TACT _meta.nka controls which mode applies:

Normal (shorts, pizzicato, staccato): fixed TKY.RLS_STANDARD_VOLUME_TARGET regardless of elapsed time. The release sample always plays at the same level.

Adaptive (long arcs, sustained bowing, any sample with significant natural decay): volume is computed from the sustain envelope position at the moment of release.

The distinction is driven by the _meta.nka flag, not hard-coded in the playback logic. Adding adaptive behavior to an articulation requires only a flag change in the source CSV and a CreateReleaseArray.py run.

Release Voice Isolation

The release voice is isolated from further modulation updates using ivls.isolate_vmc:

cb NoteOff:
    declare rel_vo := ivls.new_formal_voice(self)
    ivls.isolate_vmc(rel_vo)
    // Apply vol_offset_mb before isolation is complete
    ivls.reflow_voice(rel_vo, mf.release_flow)
    ivls.play(rel_vo)

After isolation, the release voice has its own root VMC and receives no further real-time modulation updates. The volume offset computed at NoteOff is baked in.

Connections to Other Parts of IVLS

Adaptive release is part of tact and uses: - The vmc modbit system to apply volume offsets to release voices - voices voice fields (tky.release.vol_offset) to carry the computed offset - NKA load at init time to make the vol_data[] and vol_index[] tables available

Patterns and Caveats

  • The CreateReleaseArray.py analysis must be re-run whenever sustain samples change. Stale tables will produce incorrect offset values.
  • The vol_offset_mb must be applied before the release voice is isolated and played. After ivls.isolate_vmc, the VMC is cut from parent updates.
  • The tky.release.vol_offset field initializes to TKY.RLS_STANDARD_VOLUME_TARGET (-21000). For normal-mode articulations (shorts, pizzicato, staccato), this fixed standard volume target is used regardless of elapsed time. For adaptive-mode articulations, the field is overwritten with the computed volume offset at NoteOff time.
  • Release voices should be isolated from further modulation. Do not skip ivls.isolate_vmc for release voices — CC movements after the key is released should not affect the release tail.
  • tact — adaptive release is part of the TACT articulation system
  • vmc — the modbit system used to apply the volume offset