KSPリファレンスマニュアル 2 - コールバック(Callbacks)

KSPリファレンスマニュアル 2 - コールバック(Callbacks)


一般情報

  • コールバックとは、スクリプトの中で特定のタイミングに「呼び出される」(つまり実行される)部分のことです。

  • すべてのコールバックはon <callback-name>で始まり、end onで終わります。

  • コールバックはexitコマンドで停止できます。

  • 各コールバックは固有のID番号を持っており、$NI_CALLBACK_IDで取得できます。

  • どのコールバックが関数を呼び出したかは、$NI_CALLBACK_TYPE対応するビルトイン定数で調べられます。

  • どのUIウィジェットがUIコールバックを引き起こしたかは、$NI_UI_ID対応するビルトイン定数で調べられます。

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

関数の中でコールバックの種類を調べます。

関連項目

exit

stop_wait()


on async_complete

on async_complete

非同期完了コールバックです。非同期に実行されるコマンド、例えば各種のロードやセーブに関連するコマンドの実行が完了した後にトリガーされます。

補足

  • 同期の問題を解決するため、下の「関連項目」に挙げたコマンドは、使用したときに固有のIDを返します。

  • コマンドの動作が完了するとon async_completeコールバックがトリガーされ、ビルトイン変数$NI_ASYNC_IDが、そのコールバックを引き起こしたコマンドのIDに更新されます。

  • コマンドが正常に完了した場合(例えばファイルが見つかり、正常にロードされた場合)、$NI_ASYNC_EXIT_STATUSの内部値は1に設定されます。そうでない場合は0になります。

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

ファイルのロードが終わるまでui_controlコールバックを一時停止させる例です。

関連項目

ロード/セーブコマンド

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()

ミュージックインフォメーションリトリーバル: MIRコマンド

ビルトイン変数と定数: $NI_ASYNC_EXIT_STATUS, $NI_ASYNC_ID

モジュールの種類とサブタイプ: $ENGINE_PAR_EFFECT_TYPE, $ENGINE_PAR_EFFECT_SUBTYPE


on controller

on controller

MIDIコントローラーコールバックです。MIDI CC、Pitch Bend、Channel Pressureのいずれかのメッセージを受信するたびに実行されます。

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

MIDI CC、Pitch Bend、Channel Pressureのデータを調べます。

関連項目

set_controller()

ignore_controller

イベントとMIDI: %CC[], $CC_NUM, $VCC_PITCH_BEND, $VCC_MONO_AT


on init

on init

初期化コールバックです。スクリプトが警告もエラーも出さずに正常にコンパイルされたときに実行されます。

補足

on initコールバックは、次のときに実行されます。

  • Script Editorで「Apply」ボタンをクリックしたとき。

  • スクリプトプリセットまたはインストゥルメントがロードされたとき。

  • Monitor > Engineタブの「Restart Engine」ボタン、またはKontaktのHeaderにある「!」ボタンをクリックして、Kontaktのオーディオエンジンを再起動したとき。

  • set_snapshot_type()0または2に設定した状態でSnapshotがロードされたとき

  • Creator ToolsがKontaktのインスタンスに接続されていて、GUI Designerのパフォーマンスビューファイルが保存し直されたとき。この場合、最新の変更をパフォーマンスビューに反映するため、スクリプトは自動的に再初期化されます。

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

read_persistent_var()を使ったinitコールバックです。

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

同じ動作を、今度はpersistence_changedコールバックで実現したものです。特にSnapshotを利用する場合は、こちらの書き方が推奨されます。

関連項目

make_persistent()

read_persistent_var()

on persistence_changed


on listener

on listener

リスナーコールバックです。設定可能な時間間隔ごと、またはトランスポートコマンドを受信するたびに実行されます。

補足

  • リスナーコールバックは、set_listener()コマンドで定義した時間間隔ごとに実行されます。ホストのトランスポートの開始/停止コマンドに反応させることもできます。そのため、シーケンサー、アルペジエーター、MIDIファイルプレーヤーなど、テンポに同期させるものを作るには最適なコールバックです。

  • 状況によっては(ホスト内でのテンポ変更など)、ティックが時折抜け落ちることがあります。

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

実用的ではありませんが、見ていて楽しい例です。

関連項目

set_listener()

change_listener_par()

コールバックとUI: $NI_SIGNAL_TYPE, $NI_SONG_POSITION


on note

on note

ノートコールバックです。MIDI Note Onメッセージを受信するたびに実行されます。

on note
    message("Note Number: " & $EVENT_NOTE & " - Velocity: " & $EVENT_VELOCITY)
end on

ノートのプロパティを調べます。

関連項目

on release

ignore_event()

set_event_par()

get_event_par()

イベントとMIDI: $EVENT_NOTE, $EVENT_VELOCITY, $EVENT_ID


on note_controller

on note_controller

MIDI 2.0のパーノートコントローラーコールバックです。MIDI 2.0 Registered Per-Note Controller、MIDI 2.0 Assignable Per-Note Controller、MIDI 2.0 Per-Note Pitch Bendのいずれかのメッセージを受信するたびに実行されます。

補足

  • 現時点では、これらのメッセージは1つのスクリプトスロット内でKSPが内部的に生成し、別のスクリプトスロットで処理することしかできません。Kontaktは外部からのMIDI 2.0メッセージをまだ受信しません。

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

2つのスクリプトスロットの間でMIDI 2.0 Per-Note Pitch Bendをやり取りする例です。

関連項目

set_note_controller()

ignore_controller

イベントとMIDI: $VNC_PITCH_BEND


on persistence_changed

on persistence_changed

on initコールバックの後、またはSnapshotがロードされたときに実行されます。

補足

  • このコールバックは、インストゥルメント内の永続変数が変化するたびに呼び出されます。つまり、on initコールバックの後、そしてSnapshotのロード時には必ず実行されます。

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

Snapshotとインストゥルメントのどちらがロードされたかを調べます。初期化時に関数を呼び出せることも示しています。つまり、persistenceコールバックはinitコールバックの拡張として機能します。

関連項目

on init

read_persistent_var()

set_snapshot_type()


on pgs_changed

on pgs_changed

いずれかのスクリプトスロットでpgs_set_key_val()コマンドが実行されるたびに実行されます。

補足

  • PGSはProgram Global Storageの略で、スクリプトスロット間で通信するための仕組みです。詳しくはPGSの章を参照してください。

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

このボタンを押すと…

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

…この例のコントロールが、スクリプトスロットの順序に関係なく変化します。

関連項目

PGS: pgs_create_key(), pgs_set_key_val(), pgs_get_key_val()


on poly_at

on poly_at

ポリフォニックアフタータッチコールバックです。MIDI Polyphonic Aftertouchメッセージを受信するたびに実行されます。

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

ポリアフタータッチをピッチに反映させるシンプルな実装例です。

関連項目

イベントとMIDI: %POLY_AT[], $POLY_AT_NUM, $VCC_MONO_AT


on release

on release

リリースコールバックです。MIDI Note Offメッセージを受信するたびに実行されます。

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

人工的なリリーストリガーノイズを作ります。

関連項目

on note

ignore_event()

get_event_par(): $EVENT_PAR_REL_VELOCITY


on rpn/nrpn

on rpn/nrpn

RPNおよびNRPNコールバックです。MIDIのRPNまたはNRPN(registered/non-registered parameter number)メッセージを受信するたびに実行されます。

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

標準的なRPNメッセージを調べます。

関連項目

on controller

set_rpn()/set_nrpn()

msb()

lsb()

イベントとMIDI: $RPN_ADDRESS, $RPN_VALUE


on ui_control

on ui_control (<ui-widget-name>)

UIコールバックです。ユーザーが特定のUIウィジェットを操作するたびに実行されます。

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

さまざまなUIコントロールと、それに対応するUIコールバックの例です。

関連項目

on ui_controls

on ui_update

コールバックとUI: $NI_UI_ID

コントロールパラメーター: $CONTROL_PAR_CUSTOM_ID, $CONTROL_PAR_TYPE


on ui_controls

on ui_controls

グローバルUIコールバックです。ユーザーがいずれかのUIウィジェットを操作するたびに実行されます。

補足

  • 特定のUIウィジェットを操作したとき、このコールバックが必ず最初に実行され、その後で個別に宣言されたon ui_controlコールバック(存在する場合)が実行されます。

  • このコールバックが想定している用途は、UIウィジェットをより汎用的に扱う手段を提供すること、そしてMVCの考え方(UIウィジェットがControllerとViewの部分を担います)に従って、UIウィジェット(Controller)と実際のパラメーターデータ(Model)とをある程度分離できるようにすることです。その結果、UIウィジェットは一般に永続化する必要がなくなり、代わりにすべてのパラメーターデータを1つの永続配列(Modelで整数と実数を混在させる場合は2つ)に保持できます。永続化が不要になることで、UIウィジェットの変数名、さらにはウィジェットの種類そのものを変更しても、スクリプト内のデータの状態に影響しなくなります。

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

on ui_controlsコールバックがもたらす利点を、簡単に示した例です。

関連項目

on ui_control

ユーザー定義関数

コールバックとUI: $NI_UI_ID

コントロールパラメーター: $CONTROL_PAR_CUSTOM_ID, $CONTROL_PAR_TYPE


on ui_update

on ui_update

UI更新コールバックです。Kontaktで何らかのGUIの変更があるたびに実行されます。

補足

  • このコールバックはKontaktの中で非常に頻繁に実行されることがあるため、使用には注意してください。

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

インストゥルメントのボリュームをKSPのコントロールに連動させます。

関連項目

on ui_control

on ui_controls


参照元情報:Callbacks
https://docs.native-instruments.com/online-guides/ksp-manual/en/callbacks