excellent!! funny enough, i just finished my version. i prefer your solution, it’s more elegant, but i’ll post mine just as an example of another way to implement it.
#1 IF G.BTNV: SCRIPT 2; BREAK L 0 - PN.L 0 1: Z I; SCRIPT 5 #2 IF EQ PN.L 0 D: PN.RM 1 0 IF EQ PN.L 0 D: PN.RM 0 0 A 1 L 1 D: Z I; SCRIPT 3 PN.PUSH 0 G.BTNI; PN.PUSH 1 A B G.BTNI; SCRIPT 4 #3 IF EZ PN.L 0: BREAK C 0 T - PN.L 0 1 L 0 T: C || C EQ Z PN 1 I IF EZ C: A Z #4 Y - 7 G.BTN.Y B X N + * Y 5 G.BTN.X B JF.VOX A X V 8 #5 IF NE PN 0 Z G.BTNI: BREAK PN.RM 0 Z A PN.RM 1 Z JF.VOX A 0 0 #I G.BTX 0 1 0 1 1 0 3 1 15 8 JF.MODE 1 JF.SHIFT V -2 PN.L 0 0 PN.L 1 0 D 6
pattern bank 0 stores pressed button ids (so its length reflects how many buttons are currently pressed). pattern 1 stores the corresponding JF voice.
when a button is pressed script 2 gets executed (via script 1). if the length of pattern bank 0 is 6 this means all 6 notes are used, so we remove the top one (which would be the oldest one - we append new notes at the end). script 3 checks if a JF voice passed in
Z variable is available, and if it is we store it in
A. we then append the new note using
PN.PUSH. finally, script 4 triggers the JF voice (
A is the voice number and
B is the button id). one thing i like about extracting this into its own script is that you can easily change the keyboard layout here, or do some additional stuff, like sending a pulse to trigger an envelope for a filter.
script 5 will be called upon a button release - it checks if this button was used for any of the active notes and removes the corresponding note.
edit: modified the scene so it can be used for any number of voices - set
D to the desired number in
re: using math on button id vs using
G.BTN.Y - both are good options, but using
Y has a couple of advantages: if you change your button ids it will still work, and if you decide to change the number of rows / columns you don’t have to change your formula.