Skip to main content

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 with end 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

exit

stop_wait()

on async_complete

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

Load/Save Commands

set_voice_limit()

save_midi_file()

mf_insert_file()

mf_set_buffer_size()

mf_reset()

set_engine_par()

set_zone_par()

set_loop_par()

set_sample()

purge_group()

load_ir_sample()

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

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

set_controller()

ignore_controller

Events and MIDI: %CC[], $CC_NUM, $VCC_PITCH_BEND, $VCC_MONO_AT

on init

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 2

  • Creator 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

make_persistent()

read_persistent_var()

on persistence_changed

on listener

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

set_listener()

change_listener_par()

Callbacks and UI: $NI_SIGNAL_TYPE, $NI_SONG_POSITION

on note

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

on release

ignore_event()

set_event_par()

get_event_par()

Events and MIDI: $EVENT_NOTE, $EVENT_VELOCITY, $EVENT_ID

on note_controller

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

set_note_controller()

ignore_controller

Events and MIDI: $VNC_PITCH_BEND

on persistence_changed

on persistence_changed

Executed after the on init callback, or whenever a snapshot has been loaded.

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 init

read_persistent_var()

set_snapshot_type()

on pgs_changed

on pgs_changed

Executed whenever any pgs_set_key_val() command is executed in any script slot.

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

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

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

on note

ignore_event()

get_event_par(): $EVENT_PAR_REL_VELOCITY

on rpn/nrpn

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

on controller

set_rpn()/set_nrpn()

msb()

lsb()

Events and MIDI: $RPN_ADDRESS, $RPN_VALUE

on ui_control

on ui_control (<ui-widget-name>)

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

on ui_controls

on ui_update

Callbacks And UI: $NI_UI_ID

Control Parameters: $CONTROL_PAR_CUSTOM_ID, $CONTROL_PAR_TYPE

on ui_controls

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_controlscallback.

See Also

on ui_control

User-defined Functions

Callbacks And UI: $NI_UI_ID

Control Parameters: $CONTROL_PAR_CUSTOM_ID, $CONTROL_PAR_TYPE

on ui_update

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.

See Also

on ui_control

on ui_controls