What's new

What's wrong with my ON_NOTE script...? Trigger NOTE_ID twice?

maxchristensenaudio

Observant Sound
In my instrument I have a keyboard split mode and two sample layers that can either both be triggered by the same note or be split up,
so the left layer only plays on the left side of the keyboard, the right only on the right.

Having the keyboard split has been the standard during development, but now that I want to add the option to disable the split mode I'm having a tough time making Kontakt trigger both sample layers for the same note and I think it has something to do with how I handle NOTE_ID

Currently what's happening when the split mode is OFF, is that the left-layer will play, but the right one won't.
If I switch the positions and have play_right_note() come first in the script, then the right layer will play and the left one will not.
Any ideas?

Here's the script:

Code:
on note
    disallow_group($ALL_GROUPS)
    if (btn_kb_split = 0)                 // If keyboard split mode is off

        note_offset := -24                     // Set the same startpoint for L & R for octave transposing
        if (btn_layers_mute_l = 1)            //If Waveplayer is on
            play_left_note()                     // only allow group of current sample L            
        end if    
        if (btn_layers_mute_r = 1)            //If Waveplayer is on
            play_right_note()                     // only allow group of current sample R
        end if    
    else 
        if ($EVENT_NOTE < 60)                //Left side of the keyboard                    
            if (btn_layers_mute_l = 1)            //If Waveplayer is on
                note_offset := -24                 // Set the note offset for kb split mode
                play_left_note()                 // only allow group of current sample L
            end if    
        else 
            if (btn_layers_mute_r = 1)            //If Waveplayer is on
                note_offset := -72                 // Set the note offset for kb split mode
                play_right_note()                 // only allow group of current sample R
            end if    
        end if 
    end if 
end on

    macro play_left_note
        set_event_par_arr(EVENT_ID, $EVENT_PAR_ALLOW_GROUP, 1, arr_last_layer_values[SAMPLE_L_IDX])

        change_note($EVENT_ID, $EVENT_NOTE + note_offset + (%arr_last_layer_values[6] * 12))     //-24

        // Save & display current sample playback position
        while ($NOTE_HELD = 1)        
            $play_pos_l := get_event_par($EVENT_ID,$EVENT_PAR_PLAY_POS)
            set_ui_wf_property(waveform_l, $UI_WF_PROP_PLAY_CURSOR, 0, $play_pos_l)
            wait (10000)
        end while    
    end macro

    macro play_right_note
        set_event_par_arr(EVENT_ID, $EVENT_PAR_ALLOW_GROUP, 1, arr_last_layer_values[SAMPLE_R_IDX])

        change_note($EVENT_ID, $EVENT_NOTE + note_offset + (%arr_last_layer_values[6 + NUM_LAYER_CONTROLS] * 12))         //-72

        // Save & display current sample playback position
        while ($NOTE_HELD = 1)        //save & display current sample playback position
            $play_pos_r := get_event_par($EVENT_ID,$EVENT_PAR_PLAY_POS)
            set_ui_wf_property(waveform_r, $UI_WF_PROP_PLAY_CURSOR, 0, $play_pos_r)
            wait (10000)
        end while        
    end macro
 
Always begin the “on note” callback with the command “ignore_event($EVENT_ID)”. This tells Kontakt not to automatically pipe your keypress into the sampler (as it would if there was no script).
 
Because it's a "change_note" in series, you're changing the same note once and then again. There's still only one note.

Better to ignore_event (like Nabeel says above), and then use a play_note for each part of your split/layer.
 
alright makes sense.
What do I do when I have two wavetable players though?
you set the wf property by using the EVENT_ID and if I ignore it and use 2 play_note commands instead then how do those translate to the wave player?
 
Put ignore event at the top of the note callback.

Then in your macro, where you currently have "change_note", make it a play note and assign it to an ID, which you will then use for waveform or anything else. ie.

Left_Note_ID := play_note(blah blah blah

Right_Note_ID := play_note (...
 
In general you shouldn't refresh ui_wavetable on each and every note played. You should only change them when loading a different wavetable, and in persistence_changed. There is no need to do it on every note event. I recommend having a lookup array that contains all the required zone IDs to properly display the waveform, then you also don't need to play any events to get the zone ID in order to show the waveform...
 
Ah yes I forgot you could assign ID events to the play_note command.

In the while loop I'm not updating the waveform display but the playback position.

The weird thing is, if I just put 2 play_note events next to each other then it works.
But if they are in the 2 separate if clauses then it will only play one of the two.
It's like the if is preventing the second play_note event to happen
 
Aha so if you update the playback position, you should use the listener callback. Especially if you have more than one display to update. Should be pretty simple and also saves you from using two consecutive waits.
 
I've been fiddling more with it and somethings absolutely wrong.
In split mode everything works just fine, but as soon as I took the if away that the left layer only triggers for notes below 60 and the right one for the ones above, then everything kind of breaks...

It really seems to have a hard time treating the 2 events as something separate.
- If I turn of the left layer, it will play the assigned sample for the right layer and vice versa
- If both samples play then the waveform display will always follow the shorter sample of the 2 and apply it to both
- it cross applies the octave select menu to both sides
- etc...


Here's some adjusted code:
I'll try to break it down beneath:

Code:
on note
    disallow_group($ALL_GROUPS)
    ignore_event(EVENT_ID)

    if (btn_layers_mute_l = 1)            //If Waveplayer is on
        allow_group(arr_last_layer_values[SAMPLE_L_IDX])
    end if   
    if (btn_layers_mute_r = 1)            //If Waveplayer is on
        allow_group(arr_last_layer_values[SAMPLE_R_IDX])
    end if   

    if (btn_kb_split = 0)                 // If keyboard split mode is off
        note_offset := -24                     // Set the same startpoint for L & R for octave transposing
        play_both_notes()                    // play notes and display playback position
    else
        if ($EVENT_NOTE < 60)                //Left side of the keyboard                   
            note_offset := -24                 // Set the note offset for kb split mode
            play_left_note()                 // only allow group of current sample L
        else
            message("alfhauklshu")
            note_offset := -72                 // Set the note offset for kb split mode
            play_right_note()                 // only allow group of current sample R
        end if
    end if
end on

    macro play_left_note
        left_note_ID := play_note($EVENT_NOTE + note_offset + %arr_last_layer_values[6] * 12, EVENT_VELOCITY, ...
            real_to_int((int_to_real(%arr_last_layer_values[8]) / 100.00) * int_to_real(get_zone_par(%ZONE_IDs_L[arr_last_layer_values[SAMPLE_L_IDX]], $ZONE_PAR_SAMPLE_END)) * ~tick_to_sec), -1)

        // Save & display current sample playback position
        while ($NOTE_HELD = 1)        
            $play_pos_l := get_event_par(left_note_ID,$EVENT_PAR_PLAY_POS)
            set_ui_wf_property(waveform_l, $UI_WF_PROP_PLAY_CURSOR, 0, $play_pos_l)
            wait (10000)
        end while    
    end macro

    macro play_right_note
        right_note_ID := play_note($EVENT_NOTE + note_offset + %arr_last_layer_values[6 + NUM_LAYER_CONTROLS] * 12, EVENT_VELOCITY, ...
            real_to_int((int_to_real(%arr_last_layer_values[8 + NUM_LAYER_CONTROLS]) / 100.00) * int_to_real(get_zone_par(%ZONE_IDs_R[arr_last_layer_values[SAMPLE_R_IDX] - NUM_LAYERS], $ZONE_PAR_SAMPLE_END)) * ~tick_to_sec), -1)

        // Save & display current sample playback position
        while ($NOTE_HELD = 1)        //save & display current sample playback position
            $play_pos_r := get_event_par(right_note_ID,$EVENT_PAR_PLAY_POS)
            set_ui_wf_property(waveform_r, $UI_WF_PROP_PLAY_CURSOR, 0, $play_pos_r)
            wait (10000)
        end while        
    end macro

    macro play_both_notes
        left_note_ID := play_note($EVENT_NOTE + note_offset + %arr_last_layer_values[6] * 12, EVENT_VELOCITY, ...
            real_to_int((int_to_real(%arr_last_layer_values[8]) / 100.00) * int_to_real(get_zone_par(%ZONE_IDs_L[arr_last_layer_values[SAMPLE_L_IDX]], $ZONE_PAR_SAMPLE_END)) * ~tick_to_sec), -1)

        right_note_ID := play_note($EVENT_NOTE + note_offset + %arr_last_layer_values[6 + NUM_LAYER_CONTROLS] * 12, EVENT_VELOCITY, ...
            real_to_int((int_to_real(%arr_last_layer_values[8 + NUM_LAYER_CONTROLS]) / 100.00) * int_to_real(get_zone_par(%ZONE_IDs_R[arr_last_layer_values[SAMPLE_R_IDX] - NUM_LAYERS], $ZONE_PAR_SAMPLE_END)) * ~tick_to_sec), -1)

        // Save & display current sample playback position
        while ($NOTE_HELD = 1)
            $play_pos_l := get_event_par(left_note_ID,$EVENT_PAR_PLAY_POS)            
            $play_pos_r := get_event_par(right_note_ID,$EVENT_PAR_PLAY_POS)
            set_ui_wf_property(waveform_l, $UI_WF_PROP_PLAY_CURSOR, 0, $play_pos_l)            
            set_ui_wf_property(waveform_r, $UI_WF_PROP_PLAY_CURSOR, 0, $play_pos_r)
            wait (10000)
        end while            
    end macro

EVENT_NOTE + note_offset + %arr_last_layer_values[6] * 12
= pressed note + pre set offset + octave menu (0-6) * octave

%arr_last_layer_values[8]) / 100.00) * %ZONE_IDs_L[arr_last_layer_values[SAMPLE_L_IDX]], $ZONE_PAR_SAMPLE_END)) * ~tick_to_sec)
= % of playback pos slider * length of sample

[6 + NUM_LAYER_CONTROLS], [8 + NUM_LAYER_CONTROLS]
= saves same values but for the right side layer player

%ZONE_IDs_R[arr_last_layer_values[SAMPLE_R_IDX] - NUM_LAYERS]
= Idx of Zone IDs does not match index of triggered group
Left side groups start at 0
Left side zone IDs start at 0
Right side groups start at 148
Right side Zone IDs start at 0
 
Oh and I had to implement this "play_both_notes" macro because it will NOT accept 2 play notes events if I write play_left_note and play_right_note right under each other.
In that case it will only play the macro that comes first.....
 
And a 3rd correction:
Even in split mode on it will still always play both sides, regardless of which note I'm playing.
Like it's completely ignoring the if (EVENT_NOTE < 60)

Maybe I do need to use set_control_par_arr after all.....?
 
Again, you should not be doing this wait() for each of the layers in the note callback. Do it in the listener - the code is the same except there's no while loop or wait. You just work directly on your event ID variables.
 
ok, tried the on listener method and was surprisingly easy to do, but the issue I've mentioned before is still here and I don't know what to do about it.

Here's a video showcasing the issue. Hopefully that will make it more clear.


And here is the current code:
Code:
on listener
    $play_pos_l := get_event_par(left_note_ID,$EVENT_PAR_PLAY_POS)           
    $play_pos_r := get_event_par(right_note_ID,$EVENT_PAR_PLAY_POS)
    set_ui_wf_property(waveform_l, $UI_WF_PROP_PLAY_CURSOR, 0, $play_pos_l)           
    set_ui_wf_property(waveform_r, $UI_WF_PROP_PLAY_CURSOR, 0, $play_pos_r)   
end on

on note
    disallow_group($ALL_GROUPS)
    ignore_event(EVENT_ID)

    if (btn_kb_split = 0)                 // If keyboard split mode is off
        note_offset := -24                     // Set the same startpoint for L & R for octave transposing
        play_both_notes()                    // play notes and display playback position
    else
        if ($EVENT_NOTE < 60)                //Left side of the keyboard                   
            note_offset := -24                 // Set the note offset for kb split mode
            play_left_note()                 // only allow group of current sample L
        else
            note_offset := -72                 // Set the note offset for kb split mode
            play_right_note()                 // only allow group of current sample R
        end if
    end if
end on

    macro play_left_note
        if (btn_layers_mute_l = 1)            //If Waveplayer is on
            allow_group(arr_last_layer_values[SAMPLE_L_IDX])
        end if   

        left_note_ID := play_note($EVENT_NOTE + note_offset + %arr_last_layer_values[6] * 12, EVENT_VELOCITY, ...
            real_to_int((int_to_real(%arr_last_layer_values[8]) / 100.00) * int_to_real(get_zone_par(%ZONE_IDs_L[arr_last_layer_values[SAMPLE_L_IDX]], $ZONE_PAR_SAMPLE_END)) * ~tick_to_sec), -1)
    end macro

    macro play_right_note
        if (btn_layers_mute_r = 1)            //If Waveplayer is on
            allow_group(arr_last_layer_values[SAMPLE_R_IDX])
        end if   

        right_note_ID := play_note($EVENT_NOTE + note_offset + %arr_last_layer_values[6 + NUM_LAYER_CONTROLS] * 12, EVENT_VELOCITY, ...
            real_to_int((int_to_real(%arr_last_layer_values[8 + NUM_LAYER_CONTROLS]) / 100.00) * int_to_real(get_zone_par(%ZONE_IDs_R[arr_last_layer_values[SAMPLE_R_IDX] - NUM_LAYERS], $ZONE_PAR_SAMPLE_END)) * ~tick_to_sec), -1)   
    end macro

    macro play_both_notes
        if (btn_layers_mute_l = 1)            //If Waveplayer is on
            allow_group(arr_last_layer_values[SAMPLE_L_IDX])
        end if           
        if (btn_layers_mute_r = 1)            //If Waveplayer is on
            allow_group(arr_last_layer_values[SAMPLE_R_IDX])
        end if   

        left_note_ID := play_note($EVENT_NOTE + note_offset + %arr_last_layer_values[6] * 12, EVENT_VELOCITY, ...
            real_to_int((int_to_real(%arr_last_layer_values[8]) / 100.00) * int_to_real(get_zone_par(%ZONE_IDs_L[arr_last_layer_values[SAMPLE_L_IDX]], $ZONE_PAR_SAMPLE_END)) * ~tick_to_sec), -1)

        right_note_ID := play_note($EVENT_NOTE + note_offset + %arr_last_layer_values[6 + NUM_LAYER_CONTROLS] * 12, EVENT_VELOCITY, ...
            real_to_int((int_to_real(%arr_last_layer_values[8 + NUM_LAYER_CONTROLS]) / 100.00) * int_to_real(get_zone_par(%ZONE_IDs_R[arr_last_layer_values[SAMPLE_R_IDX] - NUM_LAYERS], $ZONE_PAR_SAMPLE_END)) * ~tick_to_sec), -1)   
    end macro
 
Top Bottom