josef
Converser
Posts: 19
|
Post by josef on Apr 2, 2018 19:21:13 GMT
Hi Nic, this is my first ruleset in Stream Byter and I have some questions now. I use the plugin in Midi Designer, so I am not directly a customer, but I hope, you will help me nevertheless. It has been a simple idea, but it became quite complicated: playing notes with a xy-pad. The x-controller (CC16) delivers values for note numbers, the y-controller (CC17) for velocity (and also for channel pressure). When touching the xy-pad, the controllers are also sending extra buttons on/off: CC16 activates toggle CC18 (I use the zero value for note off) CC17 activates toggle CC19, which I use for CC64, but only if yet another button (CC20) is on. If CC18 is manually set to on, the status does not change until it is again set manually to off. That means, that no note offs are created. But the main idea is, that CC17 always creates a note off for the previous note first, and then a note on for the current note. I could explain more, but I think, this is more than enough. Following the ruleset for this. It works, but if there is something strange, please tell me. IF M0 >= B0 IF M0 <= BD ASS G0 = M0 # CC, channel 1-14 MAT G1 = M0 - 30 # =8 Note Off MAT G2 = M0 - 20 # =9 Note On MAT G3 = M0 + 20 # =D Pressure END END IF M0 == G0 10 # CC16 ASS J1 = M2 # value for Note Nr. END IF M0 == G0 11 # CC17 ASS J2 = M2 # value for Velocity IF J1 != J0 # avoid repeated notes *(see also CC18) SND G1 J0 00 # Off for previous Note SND G2 J1 J2 # play current Note ASS J0 = J1 # previous Note END END IF M0 == G0 12 00 # CC18 SND G1 J0 00 # last note(s) Off ASS M0 = G1 J1 00 ASS J0 = 0 # *for this case (see CC17) END IF M0 == G0 15 # CC21 ASS JA = M2 # gate for Pressure END IF M0 == G0 11 # CC17 (=velocity) IF JA == 7F SND G3 J2 # Channel Pressure END END IF M0 == G0 14 # CC20 ASS JB = M2 # gate for Hold END IF M0 == G0 13 # CC19 IF JB == 7F ASS M0 = G0 40 M2 # Hold END END IF M0 == G0 14 00 ASS M0 = G0 40 00 # Hold Off END IF M0 == G0 16 00 # CC22 SND G3 00 # reset Pressure END B0-D 10-16 = XX XX +B # block CC16-22 on ch.1-14 My question now: I need 3 instances of the whole thing. I attached a text-file, because this is really long. xy-Keys_123.rtf (4.82 KB) In Midi Designer I have 3 pages which are completely identical, the difference is only the midi channel on which the controllers are sending. When accessing a page, CC12, channel16 is sent with the values 1,2,3 for pages 1,2,3. This is for identifying the page in the ruleset (first line). I have not found another solution than to give each page their own, unique variables (G and J). Do you see a way to assign these variables to another variable and combine the 3 pages into one? What is very important, the 3 pages have to be completely independent, it has to work also when all 3 are acting simultaneously. The ruleset works as it is, but I saw examples in the forum, where you offered solutions, where I would not even dream about it. Thank you very much! Josef
|
|
nic
Soapbox Supremo
Troublemaker
Press any key to continue
Posts: 2,011
|
Post by nic on Apr 3, 2018 9:26:36 GMT
Hi josef, OK, we're talking some complication here. This is my understanding of what you are doing now. CC16 to 20 on channels 1, 2 or 3. Let's say that the variables you are going to futz with are in arrays I, J or K (I=CH1, J=CH2, K=CH3) for each of the xy+button instances. You could have logic that copies the variables from each of those arrays to a 'working' array (L) at the start depending upon the MIDI channel, use L in your logic and when done, copy the working array back to I, J or K. That's a lot of logic and copying before/after each event. I would suggest a different approach using indirect variables. Assuming you need up to 16 variables per instance, we could divide one array (L in my examples) into 8 'sub-arrays' (so you could run up to 8 of your xy+button instances if you wanted) and use the MIDI channel to point into the correct sub array. Let's prepare to sub-divide array L into our 8 sections of 16 variables by incoming MIDI channel: # grab the channel into I0 MAT I0 = M0 & 0F
# work out index for first element # of the channelised section MAT I1 = I0 * 0FNow, when you want to work with array elements 1 to 16 of the current sub-array you index into the array using I1 as a starting point: # example 1, assign a value into 1st element # of current sub array ASS LI1 = 23and # example 2, checking element 3 # use I3 to index into sub-array element 3 MAT I3 = I1 + 2
# what is value of element 3 of current sub-array? IF LI3 == 0 # element 3 is zero for current instance END Now, you could 'preset' the indexes into any of the 16 sub-array variables at the top too instead of creating them as you need them: # setup subarray indices I1 to I8 MAT I2 = I1 + 1 MAT I3 = I1 + 2 MAT I4 = I1 + 3 MAT I5 = I1 + 4 MAT I6 = I1 + 5 MAT I7 = I1 + 6 MAT I8 = I1 + 7Regards, Nic.
|
|
josef
Converser
Posts: 19
|
Post by josef on Apr 3, 2018 14:24:41 GMT
Hi Nic,
thank you very much for your reply! At the moment I don't understand much, but I will work on it. Concerning one thing, I am not sure if we are talking about the same. The midi channel of my 3 instances is also variable. Instance 1,2,3 could be midi channel 2,3,8 or 5,7,14 or any combination between 1 and 14. But I think, your second approach takes this into account.
Thanks, Josef
|
|
nic
Soapbox Supremo
Troublemaker
Press any key to continue
Posts: 2,011
|
Post by nic on Apr 3, 2018 14:57:54 GMT
Hi josef , Actually, the mechanism I described will actually give you 16 subdivisions of 16 variables since a variable array has 256 values. I forgot that. So, it will handle up to 16 separate instances (and thus cover all your various instance channel possibilities). Really all we are doing is subdividing the 256 slot array logically into 16 sub-slots of 16 values. A bit like splitting a keyboard into octaves. Regards, Nic.
|
|
josef
Converser
Posts: 19
|
Post by josef on Apr 3, 2018 15:15:31 GMT
Hi Nic,
in the meantime I was thinking, that I could expand the 8 instances to 16, then the midi channel will choose the instance instead of a dedicated CC – I will not need this any more. The rest is still heavy to understand for me, but I go on.
Thanks, Josef
|
|
nic
Soapbox Supremo
Troublemaker
Press any key to continue
Posts: 2,011
|
Post by nic on Apr 3, 2018 16:38:14 GMT
Hi josef , Yes, that makes sense. If you can simplify it then that will always help. Let me explain in a little more detail, what I am suggesting and maybe it will fall into place. Originally you wanted to select which variable array to use per instance so you would need lots of IF clauses that would be the same but using different arrays (G, J and so on). What we can do is just use the one array for all instances (up to 16) and just the one bit of logic with the arrays *appearing* to be variable. In the Stream Byter an 'indirect variable' is just a way of using the value stored in one variable as the index of another. Here is a simple example: ASS L0 = 5 ASS L5 = 22 SET LB0 LL0This will display the number '22' in the Stream Byter block label because it uses the number stored in L0 to index into the L array, so since L0 == 5, LL0 resolves to L5 which then resolves to 22 (all in hex of course). You could think of it as L[L0] if you're familiar with programming languages. Thus we can use this technique to break down our L array into 16 separate blocks of 16, so logically instead of working with an array of 256 places we work instead with 16 smaller arrays of 16 places each. Instead of thinking the array is L00 to FF (decimal 0 to 255), we think of it as being more like a square table of 16 by 16 and we use an indirect variable to select which row/column we are working with. When you want to work with the first instance's variables (MIDI channel 1) then the 'real' array variables we are using are L00 to L0F (decimal 0 to 15). But instance 2's variables are maintained in L10 to L1F (decimal 16 to 31). We use the MIDI channel to figure out which row in our 16x16 table we are using with the indirect variable. As an example, let's just use array variable I1 (could be any array variable) as an index: If you wanted to refer to the first variable of the first instance (which is actually L0) then you would make sure that I1 is set to 0 and when you refer to LI1 then that works out to be L0 If you wanted to refer the the first variable of the second instance then you would set I1 to 10 (decimal 16) and then LI1 works out to be the value stored in L10 If you wanted to refer to the second variable of the second instance then you would set I1 to 11 and then LI1 works out to be the value stored in L11 The idea is that at the start of the rules when an event comes in, you pull out the MIDI channel of the message and figure out which row in our table you are working with and then setup one (or more) index variables in the I array to hold the 'pointers' into the full L array. That's what my first example does. Regards, Nic.
|
|
nic
Soapbox Supremo
Troublemaker
Press any key to continue
Posts: 2,011
|
Post by nic on Apr 3, 2018 17:20:51 GMT
And here's a real world implementation that does the note stuff. CC16 (x) is note and CC17 (y) is velocity - note that we require the Y value to be sent first by Midi Designer so the first note has a velocity! (although we could default to say 64 if no current velocity exists)
# josef's xy instances IF M0 >= B0 IF M0 <= BF # we have a CC of some sort
# grab the channel into I0 MAT I0 = M0 & 0F
# get first element of correct sub-array MAT I1 = I0 * 0F # handle CC 16 (x/note) IF M1 == 10 # send note off for current note on # (1st time round will send note 0 off) MAT I2 = 80 + I0 SND I2 LI1 00
# update current note in 1st element # of correct sub-array from CC value ASS LI1 = M2
# send note on with velocity stored # in 2nd element of correct sub-array MAT I2 = 90 + I0 MAT I1 = I1 + 1 SND I2 M2 LI1 END
# handle CC 17 (y/note) IF M1 == 11 # save CC value as current velocity # in 2nd element of correct sub-array MAT I1 = I1 +1 ASS LI1 = M2 END
END END
Regards, Nic.
|
|
josef
Converser
Posts: 19
|
Post by josef on Apr 3, 2018 18:28:57 GMT
Hi Nic,
great, thank you, it works! Only, Midi Designer sends the X value first. That means, that velocity is always the previous value of CC17. So I have to rearrange the order of your ruleset. But I think I can manage this.
Best regards! Josef
|
|
nic
Soapbox Supremo
Troublemaker
Press any key to continue
Posts: 2,011
|
Post by nic on Apr 3, 2018 19:07:49 GMT
Hi josef , In that case, yes, you need to change the way the rules work: All you probably need to do is move the SND of the note on to happen inside the CC17 clause instead. I think that would do it. Regards, Nic.
|
|
josef
Converser
Posts: 19
|
Post by josef on Apr 3, 2018 19:44:45 GMT
Hi Nic,
that did not work, I guess because in both the CC16 and CC17 clauses there is this assignment: ASS LI1 = M2
Regards, Josef
|
|
nic
Soapbox Supremo
Troublemaker
Press any key to continue
Posts: 2,011
|
Post by nic on Apr 3, 2018 20:06:27 GMT
Hi josef, Yes, more tweaks than just moving the send would be needed: # josef's xy instances IF M0 >= B0 IF M0 <= BF # we have a CC of some sort
# grab the channel into I0 MAT I0 = M0 & 0F
# get first element of correct sub-array # which will hold current note MAT I1 = I0 * 0F # handle CC 16 (x/note) IF M1 == 10 # send note off for current note on # (1st time round will send note 0 off) MAT I2 = 80 + I0 SND I2 LI1 00
# update current note in 1st element # of correct sub-array from CC value ASS LI1 = M2 END
# handle CC 17 (y/note) IF M1 == 11 # send note on stored in element 1 # of correct sub-array with current # CC velocity MAT I2 = 90 + I0 SND I2 LI1 M2 END
END ENDBut this will have another problem - when you release the xy pad, if y was not 0 then the note will continue sounding. You probably need a 3rd CC to sound the current note off or a button in MD to send all notes off. Note, we now only ever need to remember the current note as the velocity value is used immediately and we don't need it after that. Regards, Nic.
|
|
josef
Converser
Posts: 19
|
Post by josef on Apr 3, 2018 21:30:10 GMT
Hi Nic,
yes, for this case I have a button. But there are two other problems: If I slide the y-direction without changing the x-direction (I will do that, because I will also assign channel pressure to y), then each single change of the value will generate a note, like a machine gun. For this reason I had a rule, that notes are generated only if the y-value is different from the previous one. And when releasing the xy pad, 0 is sent into the array (just to send something in that is different), just in case, if I want to play a note with the same velocity the next time. I think, I can reconstruct that. But what is strange, if I slide through the xy pad, none of the notes get note offs. I see the note offs in the midi monitor, but the notes are all hanging. I will continue tomorrow. Thanks a lot, that you are helping me so much! Josef
|
|
josef
Converser
Posts: 19
|
Post by josef on Apr 4, 2018 19:00:49 GMT
Hi Nic,
finally I got it running. It took me a while to understand what you mean when talking about elements. I am still not sure if I fully understand what element 1 and 2 and so on are. I decided that I can assign to it whatever I want. It works for me at least. So I translated my original ruleset (because it works absolutely reliable) to the new logic you provided. Maybe I am abusing your ideas, but still, it works, hurra! Here it is:
# josef's xy instances IF M0 >= B0 IF M0 <= BF # we have a CC of some sort
# grab the channel into I0 MAT I0 = M0 & 0F
# get first element of correct sub-array which will hold current note MAT I1 = I0 * 0F
# handle CC 16 (x/note) IF M1 == 10 # update current note in 4th element of correct sub-array from CC value MAT I4 = I1 + 3 ASS LI4 = M2 END
# handle CC 21 (gate for Pressure) IF M1 == 15 MAT I6 = I1 + 5 ASS LI6 = M2 END
# handle CC 17 (y/note + pressure) IF M1 == 11 IF LI4 != LI3 # avoid repeated notes *(see also CC18) # send note off for current note on (1st time round will send note 0 off) MAT I2 = 80 + I0 SND I2 LI3 00
# send note on stored in element 1 (?) of correct sub-array with current CC velocity MAT I2 = 90 + I0 SND I2 LI4 M2 MAT I3 = I1 + 2 ASS LI3 = LI4 # previous Note END
IF LI6 == 7F MAT I7 = D0 + I0 # Channel Pressure SND I7 M2 END END
# handle CC 18 (last note(s) Off) IF M1 == 12 00 MAT I2 = 80 + I0 SND I2 LI3 00 SND I2 LI4 00 ASS LI3 = 0 # *for this case (see CC17) END
# handle CC 20 (gate for Hold) IF M1 == 14 MAT I5 = I1 + 4 ASS LI5 = M2 END # handle CC 19 (Hold) IF M1 == 13 IF LI5 == 7F ASS M1 = 40 M2 END END IF M1 == 14 00 ASS M1 = 40 00 END # reset Pressure CC 22 IF M1 == 16 00 SND I7 00 END END END
Best regards! Josef
|
|
josef
Converser
Posts: 19
|
Post by josef on Apr 4, 2018 19:50:44 GMT
oh oh, I have been glad about the result too early. The values are not stored in the appropriate instances. If I press a button (CC 18, 19, 20, 21, 22), it is for all instances, not only for the instance to which the midi channel should point to.
Regards, Josef
|
|
nic
Soapbox Supremo
Troublemaker
Press any key to continue
Posts: 2,011
|
Post by nic on Apr 5, 2018 10:57:04 GMT
Hi josef, I think you're mixing normal and indirect variables. I've reformatted your rules a little so it is more clear when you are using indirect variables (LI3 to LI6) and when you are just using a normal variable temporarily (I now use J0 for this). Also your final rule for CC22 would send probably invalid MIDI. I've fixed this too. Give this a try. # josef's xy instances MAT I0 = M0 & F0 IF I0 == B0 # we have a CC of some sort # grab the channel into I0 MAT I0 = M0 & 0F
# setup all pointers into sub-array MAT I1 = I0 * 0F # unused MAT I2 = I0 + 1 # unused MAT I3 = I0 + 2 # previous note MAT I4 = I0 + 3 # current note MAT I5 = I0 + 4 # hold MAT I6 = I0 + 5 # current CC21/gate
# handle CC 16 (x/note) IF M1 == 10 ASS LI4 = M2 END
# handle CC 21 (gate) IF M1 == 15 ASS LI6 = M2 END
# handle CC 17 (y/note + pressure) IF M1 == 11 IF LI4 != LI3 # avoid repeated notes *(see also CC18) # send note off for current note on # (1st time round will send note 0 off) MAT J0 = 80 + I0 SND J0 LI3 00
# send note on current CC velocity MAT J0 = 90 + I0 SND J0 LI4 M2
# update previous note ASS LI3 = LI4 END
# send channel pressure if gate on IF LI6 == 7F MAT J0 = D0 + I0 SND J0 M2 END END
# handle CC 18 (last note(s) Off) IF M1 == 12 00 MAT J0 = 80 + I0 SND J0 LI3 00 SND J0 LI4 00 ASS LI3 = 0 # reset previous note END
# handle CC 20 (gate for Hold) IF M1 == 14 ASS LI5 = M2 END
# handle CC 19 (Hold) IF M1 == 13 IF LI5 == 7F ASS M1 = 40 M2 END END
IF M1 == 14 00 ASS M1 = 40 00 END
# reset Pressure CC 22 IF M1 == 16 00 MAT J0 = D0 + I0 SND J0 0 END
END
Regards, Nic.
|
|