|
Post by midijam on Feb 6, 2022 17:15:14 GMT
Hi, can K0 take random number from K1? I cannot comment boutique code CHORD ROTATOR: ASS K0 = 7 # fixed interval (positive) ASS K1 = 8 5 7 1 # rotating intervals (negative) For best resolute would be random that NOT repeat 7 7 7 1, but 7 8 1 5(always new). Thinking, if CC knob would be better, to click on midi foot-switch and moving numbers from K1 ti K0(and randomize then later CC), Here is full code: audeonic.boards.net/thread/585/chord-rotator-la-michael-brecker # rotator # map incoming MIDI notes to rotating chords
IF LOAD ASS L0 = 4 # number of rotating intervals ASS K0 = 7 # fixed interval (positive) ASS K1 = 8 5 7 1 # rotating intervals (negative) ASS G0 = 1 # current rotation SET LB0 G0 +D END
# blanket convert all note on+vel0 to off 9X XX 00 = 8X
MAT I0 = M0 & E0
IF I0 == 80 # is this a note on/off IF M0 >= 90 # if this is note on ASS GM1 = G0 # remember rotation for this note SET LB1 M1 +D MAT G0 = G0 + 1 # increment rotation IF G0 > L0 ASS G0 = 1 END SET LB0 G0 +D END MAT J0 = M1 + K0 # fixed interval ASS J1 = GM1 MAT J1 = M1 - KJ1 # rotating interval SND M0 J0 M2 SND M0 J1 M2 END
|
|
|
Post by uncledave on Feb 6, 2022 19:28:01 GMT
Hi. What exactly is your question? This script does this, but you wish it would do this?
|
|
|
Post by midijam on Feb 6, 2022 19:44:25 GMT
HI uncledave, K0 is fixed interval, I mean, now play 8 5 7 1(K1), and If change in code 1 7 5 8, change rotating. And works really good, that I would like that would to this computer instead of me, becouse are same 4 numbers.  I mean this code always start with adding 7(K0), so I would like to randomize this numbers that K0 takes K1, that I don't need to push 3 x same key to rotate from 8 to 7 (8571), ideal would be that would be random as If I wrote in code 1 7 5 8 . I mean not looking for random 1 1 1 3. If you understand 24 total possibilities have computer if are 4 numbers(L0) 8 5 7 1(K1): for each number we have 6 possibilities, if start with 8: 8571, 8517, 8751, 8715, 8157, 8175 etc...6x4 is 24, I would like that computer randomise 24 chances, that would be element of surprise. Now changing intervals with pressing key adding 8 then 5 then 7 then 1 and again 8....5 7 1
In short: I would like that when I open streambyter in midifire that would be surprise for ear.. for example 1758... Now always play 8571(K1) and again 8571 instead 7185 for example(Also would be great that computer would take all 24 chances random, not repeating 8571 10x in all 24 chances that have, would be fine surprise that would play just once 8571 in 24 circles/chances.)
|
|
|
Post by uncledave on Feb 7, 2022 2:40:42 GMT
This script adds two notes to each played note. One is the fifth above (+7), the other is one of 4 intervals below, taken in rotation from a list.
Can you describe how you would like that to change, without using terms like K0, K1, etc.? Because I cannot understand what you mean by "randomize the numbers that K0 takes K1". Those are just variables in the script. Do you want to change the +7 above the note, or only the sequence of intervals below the note?
I do understand that the number of permutations of 4 items taken 4 at a time is 4! = 24. Do you want to run through those permutations in random order, without repeats? How often do you want the permutation to change? Every 4 notes? Every day?
I think you could get close with random draws without replacement. That would produce a new permutation each time, so no 1 1 1 3. And the probability of repeating the same sequence would be pretty small (1/24).
The script has to remember the notes it sent to handle the note off. Right now it just saves the permutation index. I guess it still can, only the index now varies randomly, instead of simply cycling. That should be OK then..
|
|
|
Post by midijam on Feb 7, 2022 12:38:20 GMT
Hi, my 1st thought was, that would be easier that K01 would get numbers from K1 random to sound different and we don't need then to change K1, because this would maybe do K0, because K0 have inside (7), K1 also have 7 inside 8 5 (7) 1. So not sure how would sound better, be easier... I just know
I would like that computer takes random Line of 24 chances if L0 is 4, Random line means: 1st line is: 8571 2nd line is: 8517 etc... 24 line is: 7185
and that each line is random played "just once time in 24 chances". For example: 8571(K1) will computer take at random at 18 place for example and then after 24 at 46 place and after 48 at 55 place for "example of random".
|
|
|
Post by uncledave on Feb 7, 2022 18:28:59 GMT
Hi, my 1st thought was, that would be easier that K01 would get numbers from K1 random to sound different and we don't need then to change K1, because this would maybe do K0, because K0 have inside (7), K1 also have 7 inside 8 5 (7) 1. So not sure how would sound better, be easier... I just know That script adds the fifth above the input note, and one of the 4 intervals below the played note. 7 halfsteps down actually corresponds to 5 halfsteps up; the meaning is different when going down. You still are not making sense when using K0 and K1 in a sentence. Please try to say what you mean in musical terms. Do you actually want to select the up interval randomly as well as the down interval? It would not be easier to do this because we'd need to remember two different notes in order to handle note off. Right now the script just remembers the loop (or slot) index. We can continue to do this when we switch to a random slot index. I've generated the permutations, so let me describe what I believe you are asking for: [/em] random permutation * etc. * this continues until all 24 permutations have been played once, then loops to a new random choice [/ul] Is that correct? By the way, we won't actually change the list of intervals (K1), we'll just index them differently. The current program indexes them in order; we'll be guided by the permutations.
|
|
|
Post by uncledave on Feb 7, 2022 19:51:50 GMT
Here's an interim version. It just picks a random permutation, not guaranteed to be different. I'll work on that later.
One question I have concerns the original script, with its 8 5 7 1 negative offsets. Is the "1" correct there? It's just a semitone down from the played note. So it's like a Maj7, but you don't normally have the root played right next to it. Should it be at least "2", which would give a Dom7. They sound pretty bad to me.
You need to be very careful with the Generate function because it's recursive. It calls itself 3 times in order to enumerate each possible permutation.
This looks like a lot of code, but the Generator only runs once. The permutations are saved with any preset, because we have "+P" when initializing the L array. Only UnpackPerm and the original script are active at runtime.
#RandomRotator # map incoming MIDI notes to rotating chords # cycles through all possible permutations of 4 intervals before # repeating. This seems effectively random, while avoiding # repeated effects.
IF LOAD Alias 4 numVars ASS K0 = 7 # fixed interval (positive) ASS K1 = 8 5 7 1 # rotating intervals (negative)
# the following generates the permutations # working variables, shared by all levels # J00 is storage index Alias J01 bitsTaken # bitmap of taken positions Alias J02 newResult # result being built, 3 bits for each value Alias J03 stackPointer Ass J00 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# stack of variables local to each recursion level Alias 10 stackBase Ass J10 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Ass stackPointer = stackBase
# permutations of 0..3 are stored in this array # each perm consists of 4 2-bit integers packed in a word Ass L00 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +P Ass L10 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +P
Alias D0 bitBase # bitmap values (powers of 2) Ass KD0 = 1 2 4 8 10 20 40 80 100 200 400 800 1000 2000 4000 8000
# recursive subroutine to generate permutations Sub Generate theDepth # note: JI01 and JI02 are private to this recursion level Ass I01 = stackPointer Mat JI01 = theDepth - 1 # next depth Mat I02 = I01 + 1 Ass JI02 = 0 # loop counter While JI02 < numVars Mat I03 = bitBase + JI02 Mat I04 = bitsTaken & KI03 If I04 == 0 # this bit is free Mat bitsTaken = bitsTaken ^ KI03 # take this bit Mat newResult = newResult * 4 Mat newResult = newResult + JI02 # add to result If JI01 == 0 # reached the bottom, store result Ass LJ0 = newResult Log Stored newResult Mat J0 = J0 + 1 Ass JI02 = numVars # break loop Else Mat stackPointer = stackPointer + 2 # push stack Ass I05 = JI01 # must do this to avoid corruption Generate I05 # recursive call to next level Mat stackPointer = stackPointer - 2 # pop stack Ass I01 = stackPointer # restore indices Mat I02 = I01 + 1 Mat I03 = bitBase + JI02 End # if bottom Mat bitsTaken = bitsTaken ^ KI03 # release this bit Mat newResult = newResult / 4 End # if free Mat JI02 = JI02 + 1 End # loop End
# unpacks selected perm into J0-J3. Values are 1..4. Sub UnpackPerm theSlot Ass I10 = theSlot Ass I11 = LI10 Set LB0 I11 Log Slot theSlot +d Log Perm I11 Ass I10 = numVars While I10 > 0 Mat I10 = I10 - 1 Mat I12 = I11 & 3 Mat JI10 = I12 + 1 Mat I11 = I11 / 4 End End
If L00 == 0 Generate numVars Log Count J00 End
Ass J04 = 3 Set name ROTPER END
IF MT < A0 # is this a note on/off IF MT == 90 # if this is note on Mat J04 = J04 + 1 # cycle the values in the permutation Mat J04 = J04 % numVars If J04 == 0 # need to select new perm here UnpackPerm R$23 # just pick a random one for now End ASS GM1 = JJ04 # remember rotation for this note SET LB0 J04 +D SET LB1 GM1 +D END # remaining steps for both Note On and Note Off MAT I00 = M1 + K0 # fixed interval ASS I01 = GM1 # this recalls the saved index value for the note MAT I01 = M1 - KI01 # rotating interval SND M0 I00 M2 SND M0 I01 M2 END
|
|
|
Post by midijam on Feb 8, 2022 1:00:23 GMT
Wau, really looks massive code.  Yes, I think you randomised OK! Is hard to monitor to see if repeat after 24, but I think is totally OK also from your explanation  I played now, works. Not sure what would you like to add? If 1st sound bad, leave it and add also 2 3 4  instead of 1 7 8  haha, just kidding, but I like cecil taylor and tone clusters www.youtube.com/watch?v=EstPgi4eMe4 I am using this script on every note in midifire, (all c notes I have in midi ch1.. all B in midi ch12) and each channel will get at least 2 script and I will switch between scripts with REMOTE midi footswitch in midifire. I tested now and works with footswitch.  Yeah 1st time tried remote in midifire and works perfect for changing between two scripts. Thanks 100x thanks UNCLEDAVE!!! Really fine to play with and test intervals. I like that principle of code is still the same with adding intervals by user how wish.
|
|
|
Post by uncledave on Feb 8, 2022 1:19:57 GMT
Now, you do realize that this script just selects a random one of the generated permutations. It does not iterate through them exhaustively. I feel that that is probably enough, since there are only the four offsets.
Here's a simpler version, what I proposed initially. It just generates a random permutation by random draws without replacement, so it will always give ( 8 5 7 1 ) in some order. Then it generates another random permutation. So this is basically what my first version does right now, but it doesn't have to create and save the permutations.
The random draws without replacement is the tricky part. Picking the first one is simple, then you have to pick from the remaining 3, then the remaining 2, then the last one is trivial. So we need a data organization that lets us make those picks correctly.
Try this and see what you think.
#RandomPerm # map incoming MIDI notes to rotating chords # generates a random permutation for each group of 4 notes # prob of repeating the same sequence is 1/24.
IF LOAD Set name RANPER Alias 4 numVars ASS K0 = 7 # fixed interval (positive) ASS K1 = 8 5 7 1 # rotating intervals (negative)
Alias D0 bitBase # bitmap values (powers of 2) Ass KD0 = 1 2 4 8 10 20 40 80 100 200 400 800 1000 2000 4000 8000
Alias J00 stepIndex # loop counter Alias J01 bitsTaken # records offsets already used Alias J02 aPerm # the perm in hex, for logging Ass J00 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# returns number of free bit to take, 0..3 Sub SelectIndex result theStep Mat I21 = numVars - theStep # I21 is 4, 3, 2, 1 for each step If I21 > 1 Mat I21 = I21 * $99 Mat result = RI21 / $100 # random value in the range Else # skip random draw when only one remaining free bit Ass result = 0 End End
# returns slot index on 0..3 Sub SelectEvent result theStep Ass result = 0 SelectIndex I11 theStep # random free slot number, 0..3 # we're looking for the I11'th free bit Ass I12 = 0 While I12 < numVars Mat I10 = I12 + bitBase # I10 indexes the bit constants Mat I13 = bitsTaken & KI10 # test each bit If I13 == 0 # this bit is free If I11 == 0 Mat bitsTaken = bitsTaken ^ KI10 # take this bit # result is the slot where this bit was found Ass result = I12 Ass I12 = numVars # break the loop Else Mat I11 = I11 - 1 # count down End End # iterate over the slots Mat I12 = I12 + 1 End End
# initialize index so we start at zero Mat stepIndex = numVars - 1 End # Initialization ———————————————————————————
IF MT < A0 # is this a note on/off IF MT == 90 # if this is note on Mat stepIndex = stepIndex + 1 # cycle the values in the permutation Mat stepIndex = stepIndex % numVars If stepIndex == 0 # starting a new frame of 4 notes Log Perm aPerm # record the permutation just completed Ass bitsTaken = 0 # mark all bits free Ass aPerm = 0 End # returns 0..3, index of offset SelectEvent I00 stepIndex # select rotation for this note Mat aPerm = 4 * aPerm Mat aPerm = aPerm + I00 # record index value for log Mat GM1 = I00 + 1 # index in K is 1-based SET LB0 stepIndex +D SET LB1 GM1 +D END # remaining steps for both Note On and Note Off MAT I00 = M1 + K0 # fixed interval ASS I01 = GM1 # this recalls the saved index value for the note MAT I01 = M1 - KI01 # select the offset from the list SND M0 I00 M2 SND M0 I01 M2 END
|
|
|
Post by midijam on Feb 8, 2022 11:47:18 GMT
Awesome, you optimised it and documented it really well. Can I ask you what I should add here to get hold notes?
80 XX XX = XX +B
I tried this message block “note-OFF”, but synth strange react to just “note ON” I mean I am looking for long notes till I push new note, but seems complicated to me after block them I would need again note OFF to previous one....I found one script in boutique code from Nic but need cc messages to hold or release notes…so I am lost for now, how to get good working "hold notes" in setup.
|
|
|
Post by uncledave on Feb 8, 2022 12:30:53 GMT
This will do it. Assumes you play only one note at a time. It will not send note off for the last not played. If you're using transport controls (play/pause), you could use them to do this. Add +D500 to the snd note off to create a slight overlap between notes, a faux legato. I have not tested this, but it ought to work.
#HoldNote # this only works when playing single notes (monophonic) # this will not send note off for the last note If load Alias J0 savedNote Ass J0 = 80 # not a valid note End
If MT == 80 # ignore note off Block End
If MT == 90 # handle note on If savedNote < 80 Mat I0 = 80 + MC # note off on same channel Snd I0 savedNote 0 # send note off for previous note End Ass savedNote = M1 # remember this note End
|
|
|
Post by midijam on Feb 8, 2022 12:56:49 GMT
Thanks, not works smooth yet, problem is(looking at midi monitor): I am pressing same monophonic key(for random chord script) and this script I think: send note OFF with note on at the same time to same note? So I think that now is even more complicated to me, because I play the same key more times which get note OFF and ON at same time, I did not think on this before, my apologies.
Maybe is trick, that note-off should be send before note on. I am looking in event monitor.
|
|
|
Post by uncledave on Feb 8, 2022 19:02:40 GMT
Yes, I noticed that also. It's a problem with the way SB orders the messages. Also, that script does not handle chords. Turns out that is relatively easy to do. This version handles both those problems. I've also added two ways to stop the last notes: tapping Play/Pause in the host, and sending CC64 (sustain) off. You could adapt that for a convenient button that you may have.
A chord is recognized as notes struck within 500 ms (chordDelay) in the script. It works for me striking notes together or in rapid sequence. You could make this time very small to disable it, or simply not set the chordFlag if you don't want chords.
#HoldChord # Notes are sustained until other notes are played. # Notes struck in a short time will be treated as a chord and # sustained together. We cannot tell from the keyboard when # sustained notes should end. Presently using Play/Pause and # CC64 (sustain) off. Could just use a long delay after the note # was played, but how long?
# There's one little catch in case a new note is the same as one # that was being sustained. The Note Off messages sent to end # the previous chord are sent (by StreamByter) after the new # NoteOn message, causing the new note to be chopped. This # script delays the first Note On by a few ms, so the messages # will reach the synth in the correct order.
If load Set Name HCHORD Alias $500 chordDelay # time for chord notes, ms
# J0 holds the count, followed by the notes Alias J00 noteCount Alias J10 chordFlag Alias J11 holdChannel # channel used by notes Ass J00 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Ass J10 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Ass K0 = chordDelay
# note that the count is set to zero by the loop Sub AllNotesOff theChannel Mat I10 = 80 + theChannel While J0 >= 1 Snd I10 JJ0 0 Mat J0 = J0 - 1 End End
Sub ResetAll theChannel Mat I10 = B0 + theChannel Snd I10 $120 0 # all notes off Ass noteCount = 0 End
Sub SaveNote theNote Ass I10 = J0 Ass I11 = 1 # ignore duplicate notes While I10 >= 1 If JI10 == theNote Ass I11 = 0 End Mat I10 = I10 - 1 End If I11 == 1 Mat J0 = J0 + 1 Ass JJ0 = theNote End End End # Initialization ———————————————————————————
If MT == 80 # ignore note off Ass holdChannel = MC Block End
If MT == 90 # handle note on Ass holdChannel = MC Ass I01 = M1 # save the note If chordFlag == 0 # not in a chord If noteCount > 0 AllNotesOff MC # delay first note to handle possible double strike Snd M0 M1 M2 +D10 Block End Ass chordFlag = 1 # this note is start of possible chord Snd F0 7D 01 10 F7 +I +DK0 # start chord timer End SaveNote I01 End
If MT == B0 # use sustain Off (CC64) to silence notes If M1 == $64 0 ResetAll holdChannel Block End End
If M0 == F0 7D 01 # handle system messages If M3 == 10 # chord has timed out. Next note will kill the others. Ass chordFlag = 0 End If M3 >= 7A # use Play/Pause to silence notes ResetAll holdChannel End End
|
|
|
Post by uncledave on Feb 8, 2022 19:05:52 GMT
Could you please start a new thread for this question? It might be useful to someone, but they'll never find it here because of the funny title. If you post the question, I'll post my answer.
|
|