Callbacks
General Information
A callback is a section within a script that is being "called" (i.e. executed) at certain times.
All callbacks start with
on <callback-name>
and end withend on
.Callbacks can be stopped by using the
exit
command.Each callback has a unique ID number which can be retrieved with
$NI_CALLBACK_ID
.You can query which callback triggered a function with
$NI_CALLBACK_TYPE
and the corresponding built-in constants.You can query which UI widget triggered a UI callback with
$NI_UI_ID
and the corresponding built-in constants.
Examples
function show_callback_type() if ($NI_CALLBACK_TYPE = $NI_CB_TYPE_NOTE) message("Function was called from note callback!") end if if ($NI_CALLBACK_TYPE = $NI_CB_TYPE_CONTROLLER) message("Function was called from controller callback!") end if end function on note call show_callback_type() end on on controller call show_callback_type() end on
Query the callback type in a function.
See Also
on async_complete
|
---|
Async complete callback, triggered after the execution of any command that is executed asynchronously, for example various loading or saving related commands. |
Remarks
To resolve synchronization issues, the commands listed in the "See Also" section below return unique IDs when being used.
Upon completion of the command’s action, the
on async_complete
callback gets triggered and the built-in variable$NI_ASYNC_ID
is updated with the ID of the command that triggered the callback.If the command was completed successfully (for example if the file was found and successfully loaded), the internal value of
$NI_ASYNC_EXIT_STATUS
is set to 1, otherwise it is 0.
Examples
on init declare $load_midi_file_id declare ui_button $load_midi_file end on on ui_control ($load_midi_file) $load_midi_file_id := load_midi_file(<midi-file-path>) while ($load_midi_file_id # -1) wait(1) end while message("MIDI file loaded!") end on on async_complete if ($NI_ASYNC_ID = $load_midi_file_id) $load_midi_file_id := -1 end if end on
Example that pauses the ui_control callback until the file is loaded.
See Also
Music Information Retrieval: MIR Commands
Built-in Variables and Constants: $NI_ASYNC_EXIT_STATUS
, $NI_ASYNC_ID
Module Types and Subtypes: $ENGINE_PAR_EFFECT_TYPE
, $ENGINE_PAR_EFFECT_SUBTYPE
on controller
|
---|
MIDI controller callback, executed whenever a MIDI CC, Pitch Bend or Channel Pressure message is received. |
Examples
on controller if (in_range($CC_NUM, 0, 127)) message("CC Number: " & $CC_NUM & " - Value: " & %CC[$CC_NUM]) else if ($CC_NUM = $VCC_PITCH_BEND) message("Pitch Bend - Value: " & %CC[$CC_NUM]) end if if ($CC_NUM = $VCC_MONO_AT) message("Channel Pressure - Value: " & %CC[$CC_NUM]) end if end if end on
Query MIDI CC, Pitch Bend and Channel Pressure data.
See Also
Events and MIDI: %CC[]
, $CC_NUM
, $VCC_PITCH_BEND
, $VCC_MONO_AT
on init
|
---|
Initialization callback, executed when the script was successfully compiled without warnings or errors. |
Remarks
The on init
callback will be executed when:
The "Apply" button is clicked in Script Editor.
Script preset or an instrument are loaded.
Kontakt's audio engine is restarted by clicking the "Restart Engine" button in the Monitor > Engine tab, or "!" button in Kontakt's header.
Snapshot is loaded with
set_snapshot_type()
set to 0 or 2Creator Tools is connected to a Kontakt instance, and GUI Designer's performance view file is resaved. In this case, the scripts are reinitialized automatically in order to update the performance view with most recent changes.
Examples
on init declare ui_button $Sync declare ui_menu $Time add_menu_item($Time, "16th", 0) add_menu_item($Time, "8th", 1) $Sync := 0 { sync is off by default, so hide menu } move_control($Time, 0, 0) move_control($Sync, 1, 1) make_persistent($Sync) make_persistent($Time) read_persistent_var ($Sync) if ($Sync = 1) move_control($time, 2, 1) else move_control($Time, 0, 0) end if end on on ui_control ($Sync) if ($Sync = 1) move_control($Time, 2, 1) else move_control($Time, 0, 0) end if end on
init callback with read_persistent_var().
on init declare ui_button $Sync declare ui_menu $Time move_control($Sync, 1, 1) add_menu_item($Time, "16th", 0) add_menu_item($Time, "8th", 1) make_persistent($Sync) make_persistent($Time) end on function show_menu() if ($Sync = 1) move_control($Time, 2, 1) else move_control($Time, 0, 0) end if end function on persistence_changed call show_menu() end on on ui_control ($Sync) call show_menu() end on
The same script functionality, now with persistence_changed callback. This is the preferred method to use, especially when utilizing snapshots!
See Also
on listener
|
---|
Listener callback, executed at definable time intervals or whenever a transport command is received. |
Remarks
The listener callback is executed at time intervals defined with the
set_listener()
command. It can also react to the host's transport start and stop commands. This makes it the ideal callback for anything tempo-synced, like sequencers, arpeggiators, MIDI file players etc.In some situations (like tempo changes within the host), ticks can be occasionally left out.
Examples
on init declare ui_knob $Test (0, 99, 1) declare $direction declare $tick_counter set_listener($NI_SIGNAL_TIMER_MS, 10000) end on on listener if ($NI_SIGNAL_TYPE = $NI_SIGNAL_TIMER_MS) if ($direction = 0) inc($tick_counter) else dec($tick_counter) end if $Test := $tick_counter if ($tick_counter = 99) $direction := 1 end if if ($tick_counter = 0) $direction := 0 end if end if end on
Not useful as such, but nice to look at.
See Also
Callbacks and UI: $NI_SIGNAL_TYPE
, $NI_SONG_POSITION
on note
|
---|
Note callback, executed whenever a MIDI Note On message is received. |
Examples
on note message("Note Number: " & $EVENT_NOTE & " - Velocity: " & $EVENT_VELOCITY) end on
Query note properties.
See Also
Events and MIDI: $EVENT_NOTE
, $EVENT_VELOCITY
, $EVENT_ID
on note_controller
|
---|
MIDI 2.0 per-note controller callback, executed whenever a MIDI 2.0 Registered Per-Note Controller, MIDI 2.0 Assignable Per-Note Controller, or MIDI 2.0 Per-Note Pitch Bend message is received. |
Remarks
Currently, these messages can only be generated internally by KSP in one script slot, then acted upon in another script slot. Kontakt does not yet receive MIDI 2.0 messages from the outside.
Examples
on init set_ui_height(3) declare $i declare %black[5] := (1, 3, 6, 8, 10) declare %key_id[128] declare ui_label $L (1, 1) declare ui_table %Active[61] (6, 1, 1) declare ui_table %BG[61] (6, 1, 1) declare ui_table %KB[61] (6, 1, -8191) set_control_par(get_ui_id(%KB), $CONTROL_PAR_HIDE, $HIDE_PART_BG .or. $HIDE_PART_VALUE) set_control_par(get_ui_id(%KB), $CONTROL_PAR_WIDTH, 555) set_control_par(get_ui_id(%KB), $CONTROL_PAR_HEIGHT, 110) set_control_par(get_ui_id(%BG), $CONTROL_PAR_HIDE, $HIDE_PART_BG) set_control_par(get_ui_id(%BG), $CONTROL_PAR_WIDTH, 555) set_control_par(get_ui_id(%BG), $CONTROL_PAR_HEIGHT, 110) set_control_par(get_ui_id(%BG), $CONTROL_PAR_BAR_COLOR, 0777777H) set_control_par(get_ui_id(%Active), $CONTROL_PAR_WIDTH, 555) set_control_par(get_ui_id(%Active), $CONTROL_PAR_HEIGHT, 110) set_control_par(get_ui_id(%Active), $CONTROL_PAR_BAR_COLOR, 0AAAAAAH) set_control_par(get_ui_id($L), $CONTROL_PAR_HIDE, $HIDE_PART_BG) set_control_par(get_ui_id($L), $CONTROL_PAR_WIDTH, 560) set_control_par(get_ui_id($L), $CONTROL_PAR_FONT_TYPE, 19) set_text($L, "C1 " & ... "C2 " & ... "C3 " & ... "C4 " & ... "C5 " & ... "C6") while ($i < num_elements(%BG)) if (search(%black, $i mod 12) # -1) %BG[$i] := 1 end if inc($i) end while move_control_px($L, 55, 0) move_control_px(%BG, 62, 15) move_control_px(%Active, 62, 15) move_control_px(%KB, 62, 15) end on on note { make sure we only have one event per key } if (event_status(%key_id[$EVENT_NOTE]) = $EVENT_STATUS_NOTE_QUEUE) fade_out(%key_id[$EVENT_NOTE], 1000, 1) end if %key_id[$EVENT_NOTE] := $EVENT_ID set_note_controller($VNC_PITCH_BEND, $EVENT_NOTE, %KB[$EVENT_NOTE - 36]) if (in_range($EVENT_NOTE, 36, 96)) %Active[$EVENT_NOTE - 36] := 1 end if end on on release if (in_range($EVENT_NOTE, 36, 96)) %Active[$EVENT_NOTE - 36] := 0 end if end on on ui_control (%KB) set_note_controller($VNC_PITCH_BEND, 36 + $NI_CONTROL_PAR_IDX, %KB[$NI_CONTROL_PAR_IDX]) end on
on init declare const $BEND_RANGE := 2 declare $i declare %events[128] end on on note %events[$EVENT_NOTE] := $EVENT_ID end on on note_controller if ($NC_NUM = $VNC_PITCH_BEND and in_range($NC_NOTE, 36, 96)) change_tune(%events[$NC_NOTE], int(real($NC_VALUE) * 12.208522) * $BEND_RANGE, 0) end if end on
An example of MIDI 2.0 Per-Note Pitch Bend communication between two script slots.
See Also
Events and MIDI: $VNC_PITCH_BEND
on persistence_changed
|
---|
Executed after the |
Remarks
This callback is called whenever the persistent variables change in an instrument, i.e. it is always executed after the
on init
callback, and/or upon loading a snapshot.
Examples
on init set_snapshot_type(1) { init callback not executed upon snapshot loading } reset_ksp_timer declare $init_flag { 1 if init callback has been executed, 0 otherwise } $init_flag := 1 declare ui_label $label (2, 2) set_text($label, "Init callback " & $KSP_TIMER) end on function add_text() add_text_line($label, "Persistence changed callback " & $KSP_TIMER) end function on persistence_changed if ($init_flag = 1) { instrument has been loaded } call add_text() else { snapshot has been loaded } set_text($label, "Snapshot loaded!") end if $init_flag := 0 end on
Query if a snapshot or instrument has been loaded. This also demonstrates the ability to call functions upon initialization, i.e. the persistence callback acts as an extension to the init callback.
See Also
on pgs_changed
|
---|
Executed whenever any |
Remarks
PGS stands for Program Global Storage and is a means of communication between script slots. See the chapter on PGS for more details.
Examples
on init pgs_create_key(FIRST_KEY, 1) { defines a key with 1 element } pgs_create_key(NEXT_KEY, 128) { defines a key with 128 elements } declare ui_button $Push end on on ui_control($Push) pgs_set_key_val(FIRST_KEY, 0, 70 * $Push) pgs_set_key_val(NEXT_KEY, 0, 50 * $Push) pgs_set_key_val(NEXT_KEY, 127, 60 * $Push) end on
Pressing the button...
on init declare ui_knob $First (0, 100, 1) declare ui_table %Next[128] (5, 2, 100) end on on pgs_changed { checks if FIRST_KEY and NEXT_KEY have been declared } if (pgs_key_exists(FIRST_KEY) and pgs_key_exists(NEXT_KEY)) $First := pgs_get_key_val(FIRST_KEY, 0) %Next[0] := pgs_get_key_val(NEXT_KEY, 0) %Next[127] := pgs_get_key_val(NEXT_KEY, 127) end if end on
…will change the controls in this example, regardless of the script slot order.
See Also
PGS: pgs_create_key()
, pgs_set_key_val()
, pgs_get_key_val()
on poly_at
|
---|
Polyphonic aftertouch callback, executed whenever a MIDI Polyphonic Aftertouch message is received. |
Examples
on init declare %note_id[128] end on on note %note_id[$EVENT_NOTE] := $EVENT_ID end on on poly_at change_tune(%note_id[$POLY_AT_NUM], %POLY_AT[$POLY_AT_NUM] * 1000, 0) end on
A simple poly aftertouch to pitch implementation.
See Also
Events and MIDI: %POLY_AT[]
, $POLY_AT_NUM
, $VCC_MONO_AT
on release
|
---|
Release callback, executed whenever a MIDI Note Off message is received. |
Examples
on init declare polyphonic $new_id end on on release wait(1000) $new_id := play_note($EVENT_NOTE, $EVENT_VELOCITY, 0, 100000) change_vol($new_id, -24000, 1) end on
Creating an artificial release triggered noise.
See Also
get_event_par(): $EVENT_PAR_REL_VELOCITY
on rpn/nrpn
|
---|
RPN and NRPN callbacks, executed whenever a MIDI RPN or NRPN (registered/non-registered parameter number) message is received. |
Examples
on rpn select ($RPN_ADDRESS) case 0 message("Pitch Bend Sensitivity" & " - Value: " & $RPN_VALUE) case 1 message("Fine Tuning" & " - Value: " & $RPN_VALUE) case 2 message("Coarse Tuning" & " - Value: " & $RPN_VALUE) end select end on
Query standard RPN messages.
See Also
Events and MIDI: $RPN_ADDRESS
, $RPN_VALUE
on ui_control
|
---|
UI callback, executed whenever the user interacts with a particular UI widget. |
Examples
on init declare ui_knob $Knob (0, 100, 1) declare ui_button $Button declare ui_switch $Switch declare ui_table %Table[10] (2, 2, 100) declare ui_menu $Menu declare ui_value_edit $VEdit (0, 127, 1) declare ui_slider $Slider (0, 100) add_menu_item($Menu, "Entry 1", 0) add_menu_item($Menu, "Entry 2", 1) end on on ui_control ($Knob) message("Knob" & " (" & $ENGINE_UPTIME & ")") end on on ui_control ($Button) message("Button" & " (" & $ENGINE_UPTIME & ")") end on on ui_control ($Switch) message("Switch" & " (" & $ENGINE_UPTIME & ")") end on on ui_control (%Table) message("Table" & " (" & $ENGINE_UPTIME & ")") end on on ui_control ($Menu) message("Menu" & " (" & $ENGINE_UPTIME & ")") end on on ui_control ($VEdit) message("Value Edit" & " (" & $ENGINE_UPTIME & ")") end on on ui_control ($Slider) message("Slider" & " (" & $ENGINE_UPTIME & ")") end on
Various UI controls and their corresponding UI callbacks.
See Also
Callbacks And UI: $NI_UI_ID
Control Parameters: $CONTROL_PAR_CUSTOM_ID
, $CONTROL_PAR_TYPE
on ui_controls
|
---|
Global UI callback, executed whenever the user interacts with any particular UI widget. |
Remarks
When interacting with a particular UI widget, this callback will always be executed first, then the individually declared
on ui_control
callback (if it exists).Intended use of this callback type is to provide a more general approach of dealing with UI widgets, and also allowing for a sort of separation between the UI widgets (the Controller) and actual parameter data (the Model), according to MVC methodology (where UI widget covers the Controller and View parts). This results in UI widgets generally not needing to be made persistent, instead all parameter data could be held in a single persistent array (or two, in case of mixing integer and real values in the Model). As a consequence of not requiring persistence, variable names of UI widgets - and even widget types themselves - can change without affecting the state of the data in the script.
Examples
on init message("") set_snapshot_type(3) declare const $NUM_PARAMS := 4 declare const $FILT_SLOT := 0 declare $i declare $param_idx declare @str { this array actually holds our parameter values, not the widgets, and it's the only persistent variable! } declare %params[$NUM_PARAMS] := (1000000, 0, 630000, 500000) make_persistent(%params) declare const $FILT_CUT := 0 declare const $FILT_RES := 1 declare const $OUT_VOL := 2 declare const $OUT_PAN := 3 declare ui_slider $Cut (0, 1000000) declare ui_slider $Res (0, 1000000) declare ui_slider $Vol (0, 1000000) declare ui_slider $Pan (0, 1000000) { link between parameter indices and UI IDs } declare %pidx_to_uid[$NUM_PARAMS] %pidx_to_uid[$FILT_CUT] := get_ui_id($Cut) %pidx_to_uid[$FILT_RES] := get_ui_id($Res) %pidx_to_uid[$OUT_VOL] := get_ui_id($Vol) %pidx_to_uid[$OUT_PAN] := get_ui_id($Pan) { this is a quick example of course, but you can easily see how this can be used to scale up to larger instruments, since UI IDs can get out of order, but this doesn't matter when $CONTROL_PAR_CUSTOM_ID holds the "pointer" to the actual parameter index } $i := 0 while ($i < $NUM_PARAMS) set_control_par(%pidx_to_uid[$i], $CONTROL_PAR_CUSTOM_ID, $i) { set the default values for first time script apply case, optionally } set_control_par(%pidx_to_uid[$i], $CONTROL_PAR_VALUE, %params[$i]) inc($i) end while { set up a filter in group 1 so that we have something to work with } set_engine_par($ENGINE_PAR_EFFECT_TYPE, $EFFECT_TYPE_FILTER, 0, $FILT_SLOT, -1) end on function SetParam() select ($param_idx) case $FILT_CUT set_engine_par($ENGINE_PAR_CUTOFF, %params[$param_idx], 0, $FILT_SLOT, -1) case $FILT_RES set_engine_par($ENGINE_PAR_RESONANCE, %params[$param_idx], 0, $FILT_SLOT, -1) case $OUT_VOL set_engine_par($ENGINE_PAR_VOLUME, %params[$param_idx], 0, -1, -1) case $OUT_PAN set_engine_par($ENGINE_PAR_PAN, %params[$param_idx], 0, -1, -1) end select end function function SetLabel() select ($param_idx) case $FILT_CUT @str := get_engine_par_disp($ENGINE_PAR_CUTOFF, 0, $FILT_SLOT, -1) & " Hz" case $FILT_RES @str := get_engine_par_disp($ENGINE_PAR_RESONANCE, 0, $FILT_SLOT, -1) & " %" case $OUT_VOL @str := get_engine_par_disp($ENGINE_PAR_VOLUME, 0, -1, -1) & " dB" case $OUT_PAN @str := get_engine_par_disp($ENGINE_PAR_PAN, 0, -1, -1) end select set_control_par_str(%pidx_to_uid[$param_idx], $CONTROL_PAR_LABEL, @str) end function on persistence_changed { easy refreshing of parameter values and automation labels! } $i := 0 while ($i < $NUM_PARAMS) $param_idx := $i call SetParam() call SetLabel() set_control_par(%pidx_to_uid[$i], $CONTROL_PAR_VALUE, %params[$i]) inc($i) end while end on { that's all you need, no need to write individual callbacks anymore! } on ui_controls $param_idx := get_control_par($NI_UI_ID, $CONTROL_PAR_CUSTOM_ID) %params[$param_idx] := get_control_par($NI_UI_ID, $CONTROL_PAR_VALUE) call SetParam() call SetLabel() end on
A small showcase of benefits provided by the on ui_controls
callback.
See Also
Callbacks And UI: $NI_UI_ID
Control Parameters: $CONTROL_PAR_CUSTOM_ID
, $CONTROL_PAR_TYPE
on ui_update
|
---|
UI update callback, executed with every GUI change in Kontakt. |
Remarks
This callback can be executed very often in Kontakt, so use it with caution!
Examples
on init declare ui_knob $Volume (0, 1000000, 1) set_knob_unit($Volume, $KNOB_UNIT_DB) set_knob_defval($Volume, 630000) $Volume := get_engine_par($ENGINE_PAR_VOLUME, -1, -1, -1) set_knob_label($Volume, get_engine_par_disp($ENGINE_PAR_VOLUME, -1, -1, -1)) end on on ui_update $Volume := get_engine_par($ENGINE_PAR_VOLUME, -1, -1, -1) set_knob_label($Volume, get_engine_par_disp($ENGINE_PAR_VOLUME, -1, -1, -1)) end on on ui_control ($Volume) set_engine_par($ENGINE_PAR_VOLUME, $Volume, -1, -1, -1) set_knob_label($Volume, get_engine_par_disp($ENGINE_PAR_VOLUME, -1, -1, -1)) end on
Mirroring instrument volume with a KSP control.