コールバックとは、スクリプトの中で特定のタイミングに「呼び出される」(つまり実行される)部分のことです。
すべてのコールバックは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
関数の中でコールバックの種類を調べます。
|
|---|
非同期完了コールバックです。非同期に実行されるコマンド、例えば各種のロードやセーブに関連するコマンドの実行が完了した後にトリガーされます。 |
同期の問題を解決するため、下の「関連項目」に挙げたコマンドは、使用したときに固有の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コールバックを一時停止させる例です。
ミュージックインフォメーションリトリーバル: MIRコマンド
ビルトイン変数と定数: $NI_ASYNC_EXIT_STATUS, $NI_ASYNC_ID
モジュールの種類とサブタイプ: $ENGINE_PAR_EFFECT_TYPE, $ENGINE_PAR_EFFECT_SUBTYPE
|
|---|
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のデータを調べます。
イベントとMIDI: %CC[], $CC_NUM, $VCC_PITCH_BEND, $VCC_MONO_AT
|
|---|
初期化コールバックです。スクリプトが警告もエラーも出さずに正常にコンパイルされたときに実行されます。 |
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を利用する場合は、こちらの書き方が推奨されます。
|
|---|
リスナーコールバックです。設定可能な時間間隔ごと、またはトランスポートコマンドを受信するたびに実行されます。 |
リスナーコールバックは、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
実用的ではありませんが、見ていて楽しい例です。
コールバックとUI: $NI_SIGNAL_TYPE, $NI_SONG_POSITION
|
|---|
ノートコールバックです。MIDI Note Onメッセージを受信するたびに実行されます。 |
on note
message("Note Number: " & $EVENT_NOTE & " - Velocity: " & $EVENT_VELOCITY)
end on
ノートのプロパティを調べます。
イベントとMIDI: $EVENT_NOTE, $EVENT_VELOCITY, $EVENT_ID
|
|---|
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をやり取りする例です。
イベントとMIDI: $VNC_PITCH_BEND
|
|---|
|
このコールバックは、インストゥルメント内の永続変数が変化するたびに呼び出されます。つまり、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コールバックの拡張として機能します。
|
|---|
いずれかのスクリプトスロットで |
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()
|
|---|
ポリフォニックアフタータッチコールバックです。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
|
|---|
リリースコールバックです。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
人工的なリリーストリガーノイズを作ります。
get_event_par(): $EVENT_PAR_REL_VELOCITY
|
|---|
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メッセージを調べます。
イベントとMIDI: $RPN_ADDRESS, $RPN_VALUE
|
|---|
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コールバックの例です。
コールバックとUI: $NI_UI_ID
コントロールパラメーター: $CONTROL_PAR_CUSTOM_ID, $CONTROL_PAR_TYPE
|
|---|
グローバル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コールバックがもたらす利点を、簡単に示した例です。
コールバックとUI: $NI_UI_ID
コントロールパラメーター: $CONTROL_PAR_CUSTOM_ID, $CONTROL_PAR_TYPE
|
|---|
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のコントロールに連動させます。
参照元情報:Callbacks
https://docs.native-instruments.com/online-guides/ksp-manual/en/callbacks