TACT¶
A solo violin can play dozens of articulations -- sustained bowing, staccato, spiccato, pizzicato, legato transitions, harmonics. Each articulation uses different sample groups, different ADSR envelopes, different volume curves, different round-robin counts. Managing all of this by hand -- mapping keyswitches to group indices, configuring volume per articulation, setting up envelope parameters -- becomes unmanageable past a handful of articulations.
TACT (The Articulation Configuration Table) is a system for managing complex multi-articulation instruments. It defines all articulation metadata in external data files, loads it at init time, and provides nodes that evaluate, filter, and configure voices based on the current articulation.
TACT is not part of the core IVLS SDK. It is a product subsystem used in orchestral instruments like Tokyo Scoring Strings.
The Data Pipeline¶
TACT metadata follows a three-stage pipeline:
- CSV -- articulation properties are defined in spreadsheets during development: group counts, round-robin counts, volume defaults, envelope shapes, offset ranges, keyswitch assignments
- NKA -- the CSV data is compiled into Kontakt NKA (Kontakt Array) files, which are binary data arrays that load efficiently at runtime
- KSP -- at init time, TACT nodes load the NKA arrays and build the runtime lookup tables that voice processing uses
This separation means articulation configuration lives outside the code. You can adjust volume curves, add articulations, or change keyswitch mappings by editing the CSV and regenerating the NKA -- without touching any KSP.
The Evaluation Chain¶
When a voice enters the playback flow, it passes through a chain of TACT nodes that progressively determine which articulation to play and how to configure it. In Tokyo Scoring Strings, the main flow registers these nodes:
ivls.register_node(tky.flows.start, TACT.Modify.EvalArtics)
ivls.register_node(tky.flows.start, TACT.Modify.FallbackArtics)
ivls.register_node(tky.flows.play_sound, TACT.Modify.TriggerArticVolume)
ivls.register_node(tky.flows.play_sound, TACT.Sys.Modify.Dynamics)
ivls.register_node(tky.flows.play_sound, TACT.Sys.SetADSR)Each node in this chain has a specific role:
TACT.Modify.EvalArtics -- evaluates the current keyswitch state and writes the primary articulation (tact.main_artic), legato articulation (tact.legato_artic), and release articulation (tact.release_artic) onto the voice.
TACT.Modify.FallbackArtics -- checks whether the selected articulation is available (enabled, not purged). If not, it substitutes a fallback articulation. This ensures a voice always has a valid articulation to play.
TACT.Modify.TriggerArticVolume -- applies the articulation's volume setting from the TACT matrix. Different articulations often need different volume levels to balance properly against each other.
TACT.Sys.Modify.Dynamics -- applies dynamic layer processing. The dynamics system determines which velocity layer to use based on the voice's velocity and any dynamic modulation (CC expression control).
TACT.Sys.SetADSR -- configures the Kontakt ADSR envelope for the event based on the articulation's envelope parameters (attack time, decay, sustain level, release time). Articulation-specific overrides can replace any envelope parameter.
Group Management with find_group()¶
TACT does not use CSV row indices to identify groups. Instead, it uses find_group() to locate groups by name at init time. This means you can reorder groups in Kontakt without breaking the articulation mapping -- as long as the group names stay the same.
The TACT metadata stores group counts, round-robin counts, and other structural information per articulation. At runtime, TACT.GROUP_I(artic, index) looks up the actual Kontakt group index for a given articulation and sub-index.
Smart Attack¶
Some articulations benefit from being layered. A sustained bow stroke sounds more realistic if a short attack sample is briefly overlaid on top, simulating the initial bow contact.
Smart Attack is a TACT feature that velocity-triggers short articulation overlays on sustain notes:
cb NoteOn:
if Voice[self].tact.main_artic = tact.artic_id.SUS_NORMAL
declare pr := tky.smart_atk.get_profile_for_vel(Voice[self].vel)
if pr # -1
{ Create short overlay voice }
declare short_vo := tact.make_overlay_voice(self, ...)
Voice[short_vo].tact.main_artic := TKY.SMART(pr, ARTIC)
ivls.play.oneshot(short_vo, 1500 * 1000)
{ ... apply volume adjustments ... }
ivls.wait_ms(short_preroll)
{ The sustain voice continues after the overlay starts }
end if
end ifThe system checks velocity against a profile table. Higher velocities trigger staccatissimo overlays; moderate velocities trigger staccato overlays. The overlay is played as a oneshot with a fixed duration, and the sustain voice's attack envelope is adjusted to blend smoothly with the overlay's decay.
Adaptive Release¶
When a player releases a sustained note, the instrument needs to play a release sample that matches the amplitude of the sustain at the moment of release. If the sustain has been playing for several seconds and the room tone has decayed, a full-volume release sample will sound unnatural.
Adaptive Release uses DSP-analyzed volume data to match release samples to the sustain's current amplitude:
- During development, the sustain samples' volume envelopes are analyzed and stored as time-indexed volume arrays
- At release time, the system calculates how long the note has been playing and looks up the sustain's volume at that point
- The release sample's playback offset is set to start at the point in the release sample that matches the sustain's current volume
- A volume modulation offset is applied to fine-tune the level match
The volume data is loaded from NKA arrays and indexed by articulation, note, velocity layer, and round-robin. The tky.rls_vol.match_time_to_vol() function finds the closest time position in the volume data where the amplitude matches the target.
This produces releases that seamlessly continue the sustain's dynamic level, rather than abrupt jumps in volume at note-off.
TACT is a large system -- its full implementation spans dozens of nodes and thousands of lines of configuration data. This chapter covers the architectural patterns. The specific articulation configurations, keyswitch mappings, and envelope profiles are product-specific and live in the product's TACT CSV files.