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 I
script.
re: using math on button id vs using G.BTN.X
and G.BTN.Y
- both are good options, but using X
and 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.