{ Contact: support@robertinventor.com Web page: http://robertinventor.com/ftswiki/High_numbered_keyswitches_retuning Copyright (c) 2013 Robert Walker This software is provided as-is, without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. This is the ZLib license suitable for free software and also for open source software and commmercial use https://en.wikipedia.org/wiki/Zlib_License } {TO CONFIGURE If instrument has keyswitches then set the variables declared at the start of the "on init" handler TO MERGE WITH EXISTING SCRIPTS * Add the code from the on init, on note, on release and on controller handlers to your own handlers * Copy all the other routines into your code. * You may want to reposition the $vksr_ResetTo12EQ control All variables and routines start vskr_ so chance of variable collision is low If you do get a variable collision, simply change the prefix with search and replace of "vskr_" in this code before you do the merge } on init set_script_title("Keyswitch Retuning") declare $vksr_instrument_has_keyswitches_below :=0 { If instrument has keyswitches below its range. set this to lowest playable note, defaults to 0 } declare $vksr_instrument_has_keyswitches_above :=128 { If instrument has keyswitches above its range, set this to highest playable note, defaults to 128 } declare $vksr_keyswitchv2_retuning_byte_position:=0 { gets set to e.g. 4 for 4 byte retuning - gets decremented as data is received, retunes when reaches 0 } declare $vksr_keyswitch_instruction:=0 { used for "running status" } declare $vksr_keyswitchv2_extra_fine_pitch_bend_byte_position := 0 declare $vksr_midi_tuning_enabled:=0 declare %vksr_pitch_bend_for_output_note[128] declare %vksr_output_note[128] declare %vksr_output_note_at_note_on[128] declare %vksr_is_active[128] declare %vksr_note_id[128] declare $vksr_EVENT_NOTE:=0 declare $vksr_EVENT_VELOCITY:=0 declare $vksr_EVENT_ID:=0 declare $vskr_CC_NUM:=0 declare $vksr_input_note := 0 declare $vksr_next_output_note := 0 declare $vksr_next_pitch_bend_coarse := 0 declare $vksr_next_pitch_bend_fine := 0 declare $vksr_next_pitch_bend_extra_fine := 0 declare $vksr_pitch_bend_combined := 0 declare $vksr_note_shift := 0 declare $vksr_input_pitch_bend_0_at := 8192 declare $vksr_input_pitch_steps_in_semitone := 16384 { Use full pitch bend range to adjust from -50 to 50 cents } declare $vksr_cc119_was:=0 declare $vksr_result:= 0 { these next are local temporary variables except get syntax error messages in Kontakt 5 if I try to declare them within a function so declaring them here as globals, should be okay since midi is serial } declare $vksr_ready_to_retune := 0 declare $vksr_parsed :=0 declare $vksr_i := 0 declare $vskr_data_value:=0 declare ui_button $vksr_ResetTo12EQ $vksr_ResetTo12EQ:=1 set_text ($vksr_ResetTo12EQ,"Reset to 12-equal") make_persistent (%vksr_pitch_bend_for_output_note) make_persistent (%vksr_output_note) declare $vksr_normal_group { normal groups } {INLINED vksr_reset_to_12et} $vksr_i := 0 while ($vksr_i<128) %vksr_is_active[$vksr_i] := 0 %vksr_note_id[$vksr_i] := 0 %vksr_output_note_at_note_on[$vksr_i] := $vksr_i { This has no effect if these are set to make_persistent } %vksr_pitch_bend_for_output_note[$vksr_i] := 0 %vksr_output_note[$vksr_i] := $vksr_i $vksr_i := $vksr_i+1 end while {END vksr_reset_to_12et} end on { on init } function vksr_reset_to_12et $vksr_i := 0 while ($vksr_i<128) %vksr_is_active[$vksr_i] := 0 %vksr_note_id[$vksr_i] := 0 %vksr_pitch_bend_for_output_note[$vksr_i] := 0 %vksr_output_note[$vksr_i] := $vksr_i %vksr_output_note_at_note_on[$vksr_i] := $vksr_i $vksr_i := $vksr_i+1 end while $vksr_next_pitch_bend_extra_fine:= 0 $vksr_keyswitch_instruction:=0 $vksr_keyswitchv2_retuning_byte_position:= 0 $vksr_ResetTo12EQ:=1 end function on ui_control ($vksr_ResetTo12EQ) $vksr_ResetTo12EQ := 1 {press the "reset to 12 et" button} { This button cant be unpressed. Idea is to show as pressed on init, or if reset to 12 et either through pressing this button or through midi in event.. gets unpressed when you change the tuning of one of the notes. - note, if you adjust the notes individually to 12-et then wont show it as pressed } call vksr_reset_to_12et end on { on ui_control ($vksr_ResetTo12EQ) } function vksr_retune_currently_active_note { If $vksr_input_note is currently active, retune it immediately } if (%vksr_is_active[$vksr_input_note] # 0) if (%vksr_output_note_at_note_on[$vksr_input_note]=%vksr_output_note[$vksr_input_note]) change_tune(%vksr_note_id[$vksr_input_note],%vksr_pitch_bend_for_output_note[$vksr_input_note],0) else $vksr_note_shift := %vksr_output_note[$vksr_input_note]-%vksr_output_note_at_note_on[$vksr_input_note] change_tune(%vksr_note_id[$vksr_input_note],%vksr_pitch_bend_for_output_note[$vksr_input_note]+($vksr_note_shift*100000),0) end if end if end function function vksr_process_tuning_keyswitches $vksr_ready_to_retune:=0 $vksr_parsed:=0 $vskr_data_value:=-1 if ($vksr_EVENT_NOTE=126) $vskr_data_value:=$vksr_EVENT_VELOCITY end if if ($vksr_EVENT_NOTE=127 and $vksr_EVENT_VELOCITY=127) $vskr_data_value:=0 end if if ($vksr_midi_tuning_enabled = 119) {enable_high_note_keyswitches_retuning_v2 } { CHECK FOR INSTRUCTIONS FOR 126, 127 METHOD - E.G. 127, 4 FOR COMPLETE RETUNING MESSAGE FOR A SINGLE NOTE } { 127, 126 = Reset to 12-et 127,4 = Retune instruction (I chose 4 because it reminds you how many data bytes follow) 126, input note 126, output note 126, coarse pitch bend 126, fine pitch bend 127, 127 = data value 0 } if ($vksr_EVENT_NOTE#127 and $vksr_EVENT_NOTE#126) { not recognized as instruction or data - so stop processing any instruction in progress } $vksr_keyswitch_instruction:=0 $vksr_keyswitchv2_retuning_byte_position:=0 end if { $vksr_EVENT_NOTE#127 and $vksr_EVENT_NOTE#126 } if ($vksr_EVENT_NOTE=127 and $vksr_EVENT_VELOCITY#127) { "Instructions - do 127, 127 for data 0 case separately } $vksr_keyswitch_instruction:=$vksr_EVENT_VELOCITY $vksr_keyswitchv2_retuning_byte_position:=0 if($vksr_keyswitch_instruction=11) { 127,11 = set extra fine pitch adjustment for next note to be retuned 126, e } $vksr_keyswitchv2_extra_fine_pitch_bend_byte_position := 1 $vksr_parsed:=1 end if { $vksr_keyswitch_instruction = 11 } if($vksr_keyswitch_instruction=126) { note 127, vel 126 = Reset to 12-et} call vksr_reset_to_12et $vksr_parsed:=1 end if if($vksr_keyswitch_instruction = 4) {4 byte Retune instruction } $vksr_keyswitchv2_retuning_byte_position:=4 $vksr_parsed:=1 end if if($vksr_keyswitch_instruction = 3) $vksr_keyswitchv2_retuning_byte_position:=3 $vksr_parsed:=1 end if if($vksr_keyswitch_instruction = 2) $vksr_keyswitchv2_retuning_byte_position:=2 $vksr_parsed:=1 end if if($vksr_keyswitch_instruction = 1) $vksr_keyswitchv2_retuning_byte_position:=1 $vksr_parsed:=1 end if if($vksr_keyswitch_instruction = 127) $vksr_parsed:=1 { data value zero, processed below } end if if($vksr_parsed# 0) $vksr_ResetTo12EQ:=0 end if if($vksr_parsed = 0) { not recognized as instruction or data - so stop processing any instruction in progress } $vksr_keyswitchv2_retuning_byte_position:=0 $vksr_keyswitch_instruction:=0 end if { $vksr_parsed = 0 } end if {$vksr_EVENT_NOTE=127 and $vksr_EVENT_VELOCITY#127} if($vksr_keyswitch_instruction = 100) { end tuning and switch off tuning method} $vksr_midi_tuning_enabled:=0 end if { CHECK FOR EXTRA FINE PITCH BEND FOR 126, 127 METHOD } if($vksr_keyswitchv2_extra_fine_pitch_bend_byte_position=1) if ($vskr_data_value>=0) $vksr_next_pitch_bend_extra_fine:=$vskr_data_value $vksr_ready_to_retune:=1 end if $vksr_keyswitchv2_extra_fine_pitch_bend_byte_position:=0 end if { $vksr_keyswitchv2_extra_fine_pitch_bend_byte_position=1 } { CHECK FOR DATA FOR 126, 127 METHOD - THIS WILL BE EITEHR 126, N OR 127, 127 FOR DATA VALUE 0 } if($vksr_keyswitchv2_retuning_byte_position#0) { Data byte for input note, output note, coarse or fine pitch bend depending on $vksr_keyswitchv2_retuning_byte_position } if ($vskr_data_value>=0) { non zero values as 126,value} if($vksr_keyswitchv2_retuning_byte_position= 4) {input note} $vksr_input_note := $vskr_data_value end if if($vksr_keyswitchv2_retuning_byte_position= 3) {output note} $vksr_next_output_note := $vskr_data_value end if if($vksr_keyswitchv2_retuning_byte_position= 2) $vksr_next_pitch_bend_coarse := $vskr_data_value $vksr_next_pitch_bend_extra_fine:=0 end if if($vksr_keyswitchv2_retuning_byte_position= 1) $vksr_next_pitch_bend_fine := $vskr_data_value $vksr_next_pitch_bend_extra_fine:=0 $vksr_next_pitch_bend_extra_fine:=0 end if { After data recieved - Decrement byte position} $vksr_keyswitchv2_retuning_byte_position:=$vksr_keyswitchv2_retuning_byte_position-1 { If tuning position is zero, then ready to retune } if ($vksr_keyswitchv2_retuning_byte_position = 0) $vksr_ready_to_retune:=1 { actual retuning done below } $vksr_keyswitchv2_retuning_byte_position:=$vksr_keyswitch_instruction { example 4 for 127, 4, can then keep resending the data in groups of 4 as input, output, fine, coarse dont need to keep resending the insruction as well } end if end if {$vksr_EVENT_NOTE=126 or ($vksr_EVENT_NOTE= 127 and $vksr_EVENT_VELOCITY= 127) } end if { $vksr_keyswitchv2_retuning_byte_position#0 } if($vksr_ready_to_retune#0) %vksr_output_note[$vksr_input_note] := $vksr_next_output_note { This is the simpler calculation of pitch bend in millicents from input pitch bend } $vksr_pitch_bend_combined := $vksr_next_pitch_bend_coarse*128+$vksr_next_pitch_bend_fine %vksr_pitch_bend_for_output_note[$vksr_input_note] := 1000*100*($vksr_pitch_bend_combined-$vksr_input_pitch_bend_0_at)/($vksr_input_pitch_steps_in_semitone) { This is the calculation to do to take accout of the extra fine byte for the pitch bend } $vksr_pitch_bend_combined := $vksr_next_pitch_bend_coarse*128+$vksr_next_pitch_bend_fine { Ideally would do this, but Kontakt values are only integers not floating point so would overflow: $vksr_pitch_bend_combined := ($vksr_next_pitch_bend_coarse*128+$vksr_next_pitch_bend_fine)*128+$vksr_next_pitch_bend_extra_fine %vksr_pitch_bend_for_output_note[$vksr_input_note] := 1000*100*($vksr_pitch_bend_combined-$vksr_input_pitch_bend_0_at*128)/($vksr_input_pitch_steps_in_semitone*128) } { So instead do this - which could be out by perhaps 1 millicent from the correct value: } %vksr_pitch_bend_for_output_note[$vksr_input_note] := 1000*100*($vksr_pitch_bend_combined-$vksr_input_pitch_bend_0_at)/($vksr_input_pitch_steps_in_semitone) %vksr_pitch_bend_for_output_note[$vksr_input_note] := %vksr_pitch_bend_for_output_note[$vksr_input_note] + (1000*100*$vksr_next_pitch_bend_extra_fine)/($vksr_input_pitch_steps_in_semitone*128) $vksr_next_pitch_bend_extra_fine := 0 { have used it, so reset now, dont want it to carry over to next note} call vksr_retune_currently_active_note end if { $vksr_ready_to_retune#0 } end if { $vksr_midi_tuning_enabled = 119 enable_high_note_keyswitches_retuning_v2} end function function vksr_retune_note_on %vksr_is_active[$vksr_EVENT_NOTE] := 1 { Record the output note now, to use to work out the total pitch bend later on using the change of output note since the note on } %vksr_output_note_at_note_on[$vksr_EVENT_NOTE] := %vksr_output_note[$vksr_EVENT_NOTE] {Check that the note is not a keyswitch after retuning - if it is then is "out of range silent" } if($vksr_instrument_has_keyswitches_below<=%vksr_output_note[$vksr_EVENT_NOTE] and %vksr_output_note[$vksr_EVENT_NOTE] <=$vksr_instrument_has_keyswitches_above) %vksr_note_id[$vksr_EVENT_NOTE] := play_note(%vksr_output_note[$vksr_EVENT_NOTE],$vksr_EVENT_VELOCITY,0,-1) change_tune( %vksr_note_id[$vksr_EVENT_NOTE],%vksr_pitch_bend_for_output_note[$vksr_EVENT_NOTE],0) {Make sure it has same pan positions and volume as before (as in KSP Reference Manual under ignore_event() } change_vol(%vksr_note_id[$vksr_EVENT_NOTE],get_event_par($vksr_EVENT_ID,$EVENT_PAR_VOLUME),1) change_pan(%vksr_note_id[$vksr_EVENT_NOTE],get_event_par($vksr_EVENT_ID,$EVENT_PAR_PAN),1) else message("OUT OF RANGE note - " & $vksr_EVENT_NOTE & " -> " ... & %vksr_output_note[$vksr_EVENT_NOTE] & " - IGNORED - INSTRUMENT RANGE " &$vksr_instrument_has_keyswitches_below ... & " to " & $vksr_instrument_has_keyswitches_above... ) end if end function function vksr_on_controller if($vskr_CC_NUM = 119) {on receipt of %vksr_CC[119], switches midi tuning on if %vksr_CC[119] = 119 and otherwise switches it off} $vksr_midi_tuning_enabled:=%CC[119] end if end function on note $vksr_EVENT_NOTE:=$EVENT_NOTE $vksr_EVENT_VELOCITY:=$EVENT_VELOCITY $vksr_EVENT_ID:=$EVENT_ID if ($vksr_midi_tuning_enabled = 119 and $EVENT_NOTE >=126 ) {enable_high_note_keyswitches_retuning_v2 or enable_high_note_keyswitches_retuning } ignore_event($EVENT_ID) call vksr_process_tuning_keyswitches else if($vksr_instrument_has_keyswitches_below<=$vksr_EVENT_NOTE and $vksr_EVENT_NOTE <=$vksr_instrument_has_keyswitches_above) ignore_event($EVENT_ID) call vksr_retune_note_on end if end if end on { on note } on release %vksr_is_active[$EVENT_NOTE] := 0 %vksr_note_id[$EVENT_NOTE] := 0 end on on controller $vskr_CC_NUM:=$CC_NUM call vksr_on_controller end on