How to Build a TACT Product¶
Set up the TACT (The Articulation Control Thing) system for a multi-articulation instrument, from CSV configuration through to playback routing.
Prerequisites¶
- An IVLS product with multiple articulation sample groups mapped in Kontakt
- Python 3.13+ (for the CSV importer)
- Understanding of flows and nodes
Steps¶
1. Create a TACT config CSV¶
Define your articulations in a CSV file named tact-config.csv. The importer reads specific labeled rows to build the articulation database.
, Articulations
, SUS_NORMAL, STACCATO, SPICCATO, PIZZICATO, TREMOLO, LEGATO_BOW, RELEASE
, Names
, "Sustain", "Staccato", "Spiccato", "Pizzicato", "Tremolo", "Legato Bow", "Release"
, Symbols
, TRUE, FALSE
, Values
, 1, 0
, Meta
Group Index, 0, 1, 4, 8, 11, 12, 24
Group Count, 1, 1, 1, 1, 1, 2, 1
Vel Layers, 1, 3, 2, 3, 1, 1, 1
Num RR, 1, 4, 4, 3, 1, 6, 1
Offset Range,0, 0, 0, 0, 0, 0, 0
Legato State,FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE
, EndKey rows: Group Index maps to the Kontakt group, Group Count is how many consecutive groups the artic uses, Vel Layers and Num RR define the velocity/round-robin matrix. The Symbols and Values rows let you use symbolic names (like TRUE/FALSE) in the metadata rows.
2. Run the CSV importer¶
The importer reads the CSV, generates KSP artic ID constants and saves the metadata NKA:
The script walks the current directory for tact-config.csv files and produces two outputs:
- A KSP file (e.g.
tkystrings-tact-db.ksp) containing thetact.csv.ICB()macro with artic ID constants - An NKA metadata file (e.g.
_tkystrings_meta.nka) with per-articulation parameter data
The generated KSP looks like:
{ DO NOT EDIT. RE-IMPORT USING PYTHON FOR CHANGES }
macro tact.csv.ICB()
family tact
const artic_id
SUS_NORMAL
STACCATO
SPICCATO
PIZZICATO
TREMOLO
LEGATO_BOW
RELEASE
end const
declare !artic_names[TACT_CONFIG.NUM_ARTICS]
end family
end macro3. Create the TACT configuration file¶
Create a config file that imports the generated DB, sets dimensions, and defines the tact.config.ICB() and tact.config.Functions() macros:
import "my-product/my-tact-db.ksp"
define TACT_CONFIG.MAIN_SLOT := 0
define TACT_CONFIG.NUM_ARTICS := 7
define TACT_CONFIG.NUM_KS := 128
define TACT_CONFIG.USE_PAGINATOR := TRUE
define TACT_CONFIG.VISIBLE_ARTICS := 7
define TACT_CONFIG.TOTAL_TABLES := 1
macro tact.config.ICB()
tact.csv.ICB()
family tact
{ Keyswitch assignment array -- maps MIDI notes to artic IDs }
declare ks_set[] := (0, 1, 2, 3, 4, 5, 6)
end family
{ Load metadata NKA }
declare my_meta[TACT_CONFIG.NUM_ARTICS, TACT.NUM_META_PARAMS]
declare !my_artic_names[TACT_CONFIG.NUM_ARTICS]
load_array(_my_meta, 2)
load_array(!my_artic_names, 2)
end macro
macro tact.config.Functions()
function tact.config.init_meta()
util.array.copy(!my_artic_names, !tact.artic_names)
util.array.copy(_my_meta, tact._meta)
end function
end macro
4. Import TACT and assemble¶
In a TACT assembly file, set up any custom library hooks and import the TACT node system:
{ Define mic count and group offset for multi-mic instruments }
define MICS := 3
define MIC_OFFSET := 50
{ Custom TACT hooks -- required even if empty }
function tact.lib.update_volume(artic)
{ Apply volume to additional groups beyond the standard set }
end function
function tact.lib.custom_cb(ctrl, artic)
{ React to custom TACT parameter changes }
end function
import "_TACT3/node-import.ksp"5. Add TACT.NodeBundle to IVLS_NODES¶
Register the TACT node bundle in your product assembly:
define IVLS_NODES += ..., ...
TACT.NodeBundle, ...
MyProduct.Features, ...
...6. Register TACT nodes in your evaluation flow¶
The critical flow pattern for TACT playback is: evaluate which articulation is active, apply fallbacks, then route to sound playback. Here is the standard pattern from Tokyo Scoring Strings:
node MyProduct.Flows:
cb Flows:
define FLOWS += my.flows.start, my.flows.play_sound
{ Articulation evaluation chain }
ivls.register_node(my.flows.start, TACT.Modify.EvalArtics)
ivls.register_node(my.flows.start, TACT.Modify.FallbackArtics)
ivls.register_node(my.flows.start, Stl.Pedals)
ivls.register_node(my.flows.start, MyProduct.Divert.PlaybackParse)
{ Sound playback chain }
ivls.register_node(my.flows.play_sound, Stl.RoundRobins)
ivls.register_node(my.flows.play_sound, TACT.Modify.TriggerArticVolume)
ivls.register_node(my.flows.play_sound, TACT.Sys.Modify.Dynamics)
ivls.register_node(my.flows.play_sound, TACT.Sys.SetADSR)
ivls.register_node(my.flows.play_sound, Stl.PlayEvent)
end nodeTACT.Modify.EvalArticsreads the current keyswitch state and setsVoice[self].tact.main_articTACT.Modify.FallbackArticsresolves fallback articulations when the primary is disabledTACT.Sys.Modify.Dynamicshandles velocity layer selectionTACT.Sys.SetADSRapplies per-articulation envelope settingsTACT.Modify.TriggerArticVolumeapplies per-articulation volume from the TACT panel
7. Implement TACT voice fields¶
TACT uses voice fields to track articulation state through the flow. These are created automatically by the TACT node bundle. Your Spawn and PlayEvent nodes use tact.play_artic_modulable() to create voices for specific articulations:
node MyProduct.Spawn:
cb NoteOn:
{ TACT sets Voice[self].tact.main_artic after EvalArtics runs }
{ Spawn a voice for the active articulation }
declare new_vo := tact.play_artic_modulable(self, Voice[self].tact.main_artic, my.flows.play_sound)
ivls.play(new_vo)
end nodetact.play_artic_modulable() creates a new formal voice, sets the TACT articulation fields, and routes it to the specified flow.
8. Load NKA at init¶
TACT metadata is loaded from NKAs during the tact.config.ICB() macro execution, which runs at init. Make sure your NKA files are in the expected Resources/data/ directory. The tact.config.init_meta() function copies the loaded data into TACT's internal arrays.
Verify¶
- Play your instrument -- the default articulation should sound
- Press keyswitch notes -- the articulation should change and the TACT UI should reflect the active artic
- Check that per-articulation volume, ADSR, and offset controls work from the TACT panel
- Confirm that round robins cycle correctly per articulation
Further reading¶
- TACT module reference for the complete API
- Guide: TACT for conceptual background on articulation management
- Stl.PlayEvent module reference for the event playback template