|
Post by uncledave on Jun 6, 2022 15:33:01 GMT
Ah ofcourse, good call I forgot about the second GUI. I see that the slider value go's from 0 to 1.00. I assume that's all we can visually get from it? So setting the delay timing will be a 'from the hearing' thing again or memorising the values. But in all honesty, I was pretty much doing it from the hearing in AUM SB instance anyways. But was just curious if those sliders can be set to show differently scaled values. In Drambo, you can use a bank of buttons set to integer values, then Scale by 1/N to get the values for the SB Qn control. You can label the buttons with the actual delay value.
|
|
|
Post by mo13 on Jun 7, 2022 19:19:10 GMT
Sorry for the offtopic here, would you mind giving me a head start with how to set such thing up? I realised that those sliders can then be P-locked in to individual clips which means that there would be no more need to turn to MF to manually put those preset values into an SB instance for every new scene load and fire them back to Drambo. These will be used for the hardware CV envelopes trigs. I also just found out that If I place a Delay FX unit before an Envelope Follower or an ADSR then the same effect is recreated which I can map to my software synth's, so it would be pretty neat to have these different approaches of Delays for different purposes. Edit: no more need for the above as I've labeled the scales on the controller.
|
|
|
Post by mo13 on Jun 9, 2022 12:04:15 GMT
uncledave , I have all the 4 envelope tracks setup and running stable in Drambo now, I first ran in to the following issue, while recording something on another track it would also record those note trigs on the Env. tracks as the recording happens for all the individual tracks that are set to 'Receive Midi Always' Luckily I just found a workaround for that by first converting the notes to CC's before they enter Drambo, afterwards placing another SB instance that converts those CC's back to notes before the TickDelayLarge script. Maybe it would be better so that your script responds to a set of CC's instead of notes? On another channel then the CC clock. There is still another SB instance after your script which merges the notes: As you mentioned that it would be better to modify the main script to do everything.
|
|
|
Post by mo13 on Jun 14, 2022 10:19:21 GMT
uncledave, would you mind showing me how to add note off's to the TickDelayLarge script? as I'm stuck at this: # do not add the Note Offs to the buffer unless you really want them # If MT < A0 # used for testing If MT == 90 # process Note On # you can modify incoming values here as needed Ass I00 = M1 M2 AddNote I00 I01 # this note and velocity added to buffer else # from here on I can't get it right IF MT == 80 Ass I02 = M1 M2 Addnote I02 I03 end End And would this be the only part where something needs to be changed? Or also in the Aliases of SendNoteCommand ?
|
|
|
Post by uncledave on Jun 14, 2022 12:41:38 GMT
uncledave , would you mind showing me how to add note off's to the TickDelayLarge script? as I'm stuck at this: # do not add the Note Offs to the buffer unless you really want them # If MT < A0 # used for testing If MT == 90 # process Note On # you can modify incoming values here as needed Ass I00 = M1 M2 AddNote I00 I01 # this note and velocity added to buffer else # from here on I can't get it right IF MT == 80 Ass I02 = M1 M2 Addnote I02 I03 end End And would this be the only part where something needs to be changed? Or also in the Aliases of SendNoteCommand ? The script does not save the command, only the note and its velocity. You could try using zero velocity. Just drop the Ass I02 and use Addnote M1 0
It should then send a note on with zero velocity, which is equivalent to note off. If the destination doesn't like that, we could change it to note off when it is sent. It would be quite a job to store the note on/off commands with the data, because the data are tightly packed to maximize buffer length. Incidentally, the only reason I copied M1 and M2 was to give you a place to modify them. It's not required otherwise.
|
|
|
Post by mo13 on Jun 14, 2022 13:30:01 GMT
Thanks. Yeah I was actually looking for how to pass the note off's themselves just like how they reach the script, as with this new Drambo and Quad Env generator which accepts CV velocity gates, I'd like to hold the envelope by a fader of my controller which opens up the entire envelope until I shorten the notes with it again, so that depends on the note off's. I mentioned this because at the start of building this script (page 1 here) you asked me why I was blocking them, but as I hear now that's a whole other scenario for this script.
|
|
|
Post by uncledave on Jun 14, 2022 14:33:07 GMT
Since you very definitely didn't need note off, I designed it out, in order to maximize the buffer size. I'll have a look to see if it can be restored.
Edit: It looks like it should be possible. It'll take a minute to build and test it. It will be optional, so you can use the same script either way.
|
|
|
Post by uncledave on Jun 14, 2022 16:36:16 GMT
Here it is, updated to handle Note Off as well as Note on. I changed to code to double the count, and subtract 2 for each tick. The Note Off flag is stored in the "1" bit of the count. Change enableNoteOff to 0 to stop saving Note Off messages. #TickDelayLarge
# Update for note off: # Only need 1 bit to distinguish off and on. Double delay time # and use the 1 bit to mark note off. Decrement count by 2 each tick, # stop when count drops below 2.
# This version uses the W array, which has 2048 unsigned 16-bit words, # allowing a buffer of 1024 notes. SB limits loops to 128 iterations, so # some trickery is required to handle large amounts of data.
# If you trigger reset after running, the log will show the maximum index # values during the run. Just adjust the Count/Reset slider up and back to 0.
# I have tested this by sending 6-note chords from my keyboard as fast # as I could, into a whole note delay at 120 bpm. The highest number of # active entries was 150, and the highest cell used was 340, corresponding # to 170 total entries. This was delaying both Note On and Note Off # messages.
# Slider 7 displays the number of active items in the buffer.
If load Alias 9C sendNoteCommand # change channel as needed Alias 1 enableNoteOff # change to zero to block Note Off Alias $0 testOffset # should be 0 for normal use Alias $1024 maxCount # max number of entries Alias 1 debugLevel # set to monitor buffer count # turn debugLevel off for max performance
Set Name Delay # Screen Controllers ——————————————————————————— Alias 6 maxNote Alias 7 maxSlider Alias $11 maxSelect # Note that the Qi are published as AUv3 parameters (scaled 0..127) # so you could control them externally that way. Or, of course, you # could implement CCs to adjust them directly. Set Q0 BD 0 maxSelect Set Q1 HH 0 maxSelect Set Q2 SD 0 maxSelect Set Q3 RD 0 maxSelect Set Q4 CL 0 maxSelect Set Q5 TM 0 maxSelect Set Q6 Master_Select 0 maxSelect # manually set theCount to zero to reset buffer Alias Q7 theCount Alias 7 indCount Set Q7 Count/Reset 0 maxCount
Ass Q0 = 0 0 0 0 0 0 0 0 +P Set SLIDER_DISPLAY 1 # End Screen Controllers —————————————————————————
# Delays: Whole 96, Half dot 72, Half 48, Qtr dot 36, Qtr 24, Qtr3 16. # 8th dot 18, 8th 12, 8th3 8, 16th dot 9, 16th 6. # this tedious method is the only way to convert a number into an # arbitrary string Sub SayTickDelay pTicks Set LB1 pTicks +D If pTicks == 0 Set LB0 S0 End If pTicks == $96 Set LB0 SWhole End If pTicks == $72 Set LB0 SDotted_Half End If pTicks == $48 Set LB0 SHalf End If pTicks == $36 Set LB0 SDotted_Qtr End If pTicks == $24 Set LB0 SQtr End If pTicks == $16 Set LB0 SQtr_Triplet End If pTicks == $18 Set LB0 SDotted_8th End If pTicks == $12 Set LB0 S8th End If pTicks == $8 Set LB0 S8th_Triplet End If pTicks == $9 Set LB0 SDotted_16th End If pTicks == $6 Set LB0 S16th End End
# delay values in clock ticks, 24 ppqn (pulses per quarter note) # array indices 0 to maxSelect Ass K0 = 0 6 8 9 $12 $16 $18 $24 $36 $48 $72 $96
# returns the total delay for the note in pResult Sub GetDelay pResult pNote # Master delay Ass I20 = maxNote Ass I20 = QI20 # get Master slider value Ass pResult = KI20 # index the table of ticks Mat I20 = pNote - testOffset # allows using normal notes to test If I20 < maxNote # add delay for the note Ass I20 = QI20 Mat pResult = pResult + KI20 End End
# W000..W800 is a buffer of 1024 2-word entries (800 is $2048). # Each entry contains the delay (counting down) and the note # and its velocity, packed into one word. Times are counted down # on each tick, and each note is sent when its count reaches 0. # Notes are added to the first free slot.
# buffer parameters Alias 2 blockSize # number of words in each entry # control values in IFx registers Alias IF0 firstFree # position of first free slot Alias IF1 currCount # number of active entries Alias IF2 maxPos # last array index+1 Alias IF3 currMax # position of entry off the end Alias IF4 countMax # highest count seen Alias IF5 maxMax # highest buffer position used Ass IF0 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Mat maxPos = maxCount * blockSize
# All displayed count values are now doubled. Values are odd if # they are for Note Off.
# return the first free slot. This will normally be firstFree, unless # multiple notes arrive between ticks. Sub FindFreeSlot pResult Ass pResult = firstFree Ass I20 = firstFree If WI20 > 0 Ass pResult = maxPos # assume failure Mat I20 = I20 + blockSize # advance, since first item is taken # search for free entry While I20 < maxPos Mat I21 = maxPos - I20 # number of cells remaining If I21 > 7F # limit to 127 Ass I21 = 7F End While I21 > 0 # this inner loop limited to 127 steps Mat I21 = I21 - 1 If WI20 == 0 Ass pResult = I20 Ass I20 = maxPos # break loops on success Ass I21 = 0 End Mat I20 = I20 + blockSize # advance to next block End # inner loop End # while End End
# adds the given note to the queue, with the appropriate delay time # pMode is 90 for on, 80 for off Sub AddNote pMode pNote pVel GetDelay I11 pNote If I11 > 0 # skip if delay is 0 Mat I11 = 2 * I11 # double to allow on/off flag If pMode == 80 Mat I11 = I11 | 1 # add flag for note off End FindFreeSlot I10 If I10 < maxPos # only add if space available If currMax == I10 # update maximum when extending buffer Mat currMax = currMax + blockSize If maxMax < currMax # remember highwater level Ass maxMax = currMax End End Set LB0 pNote +D # show note and delay when adding Set LB1 I11 +D # displayed delay value is now double # new entry fills position I10 and I10+1 of W Ass WI10 = I11 # delay in ticks Mat I10 = I10 + 1 # Note and velocity packed in one word Mat WI10 = pNote * 80 # note value Mat WI10 = WI10 + pVel # velocity Mat currCount = currCount + 1 If countMax < currCount # remember max number of active items Ass countMax = currCount End If debugLevel > 0 Ass theCount = currCount # If currCount > 7F # Log Add_Count currCount +D # End End Else Set LB1 SFull Log Buffer_Full # log msg appears in monitor End End End
# sends the current note. pWhere is index of the count. Sub SendDelayedNote pWhere Ass I20 = pWhere # access count value, now 1 or zero Mat I23 = WI20 & 1 # test note off flag If I23 == 1 Mat I23 = sendNoteCommand ^ 10 # change 90 to 80 for note off Else Ass I23 = sendNoteCommand End Mat I20 = I20 + 1 # note in next word Mat I21 = WI20 / 80 # unpack note and velocity Mat I22 = WI20 & 7F Send I23 I21 I22 End
# decrement the count for each active entry, sending notes # when their delay expires. Called for each clock tick. Sub UpdateTicks Ass I10 = 0 Ass I11 = currCount Ass I12 = 0 Ass I14 = 0 Ass firstFree = currMax # loop over active buffer entries # note that I11 only changes for active entries, so inactive # entries off the end are ignored automatically While I11 > 0 # this double loop is required in case of more than 128 entries. # SB loops are limited to 128 iterations. Ass I13 = 7F While I13 > 0 Mat I13 = I13 - 1 If WI10 > 0 # skip notes already sent Mat I11 = I11 - 1 # decrement active counter If I11 <= 0 # passing last active breaks both loops Ass I13 = 0 End # counting down by 2s, since value was doubled Mat WI10 = WI10 - 2 # decrease tick count If WI10 <= 1 # first time count below 2 SendDelayedNote I10 Ass WI10 = 0 # clear the count Mat currCount = currCount - 1 Ass I12 = debugLevel End End # I14 is zero while we're looking for the first free position # then it's set very large, so this test always fails If WI10 == I14 Ass firstFree = I10 # save position of first free slot Ass I14 = FFFF # this will not happen again End Mat I10 = I10 + blockSize # advance through buffer End # inner while End # while
# remember position off the end Ass currMax = I10
# update count display if anything has changed If I12 > 0 Ass theCount = currCount # If currCount > 7F # Log Scan_Count currCount +D # End End End
# dump active entries for diagnosis # also shows peak levels seen since reset Sub DumpActive Ass I10 = 0 Log Dump_Count currCount +D Log countMax countMax +D Log maxMax maxMax +D Ass I11 = currCount While I11 > 0 Ass I14 = 7F While I14 > 0 Mat I14 = I14 - 1 If WI10 > 0 Mat I11 = I11 - 1 If I11 <= 0 Ass I14 = 0 End Mat I13 = I10 + 1 Mat I13 = WI13 / 80 Mat I12 = 100 * WI10 Mat I12 = I12 + I13 # log will show 2 hex nybbles with time and note Log Buffer I12 End Mat I10 = I10 + blockSize End End End
# maximum loop count is 128, so need this double logic. # two nested loops is good up to 16384. Sub ClearBuffer Ass I10 = 0 While I10 < maxPos Mat I11 = maxPos - I10 If I11 > 80 Ass I11 = 80 End # inner loop advances I10 by $128 or less. While I11 > 0 Ass WI10 = 0 Mat I10 = I10 + 1 Mat I11 = I11 - 1 End # inner loop End # while Log Last_Pos I10 +D End
# clear the buffer Sub ResetBuffer Set LB0 SReset DumpActive Ass theCount = 0 Ass firstFree = 0 Ass currCount = 0 Ass currMax = 0 Ass countMax = 0 Ass maxMax = 0 ClearBuffer End
ResetBuffer # initialize data on load
End # Initialization ———————————————————————————
If M0 == F8 # process clock first, since it is most frequent If currCount > 0 # check count first to save time UpdateTicks End Block Exit # exit here for quickest handling of F8 End
If MT < 10 # ignore this debugging code; it will never run If M1 == $48 # using low C to trigger diagnostic output If MT == 90 DumpActive End Block End End
If MT == 90 # process Note On # you can modify incoming values here as needed Ass I00 = M1 M2 AddNote MT I00 I01 # this note and velocity added to buffer Else If MT == 80 If enableNoteOff == 1 AddNote MT M1 M2 End End End
# these events happen when a control is changed If M0 == F0 7D 01 # handle control change If M3 < maxSlider # M3 is the index of the changed Qi # displays the delay based on the selection Ass I00 = QM3 SayTickDelay KI00 End If M3 == indCount # move slider and back to 0 to trigger reset If theCount == 0 ResetBuffer End End End
Block
|
|
|
Post by mo13 on Jun 14, 2022 17:57:01 GMT
Great thank you! For the try outs - AUM has the exact output which I was looking for with your unedited script (F8 clock and straight notes in) I'm trying to work out how this works for the Drambo setup as there it's sending a Note Off right away after Note On out of your script, even if the note is held longer. It must be something in my conversion because I'm converting those 90 XX and 80 XX to CC's first before they enter Drambo, and then converting those CC's back to Notes before they enter your script, this is so if I'm recording anything in Drambo then it won't record those incoming notes as the script tracks are set 'Receive MIDI always' and that's the only workaround I could think of. Another difference is the CC Clock. Still figuring out what this could be as we speak.
Edit: Found it! an error on my side with another script that comes after yours, I overlooked something in the second line where I'm consolidating notes 00-05 from the Notes Off into a single output.
|
|
|
Post by uncledave on Jun 17, 2022 13:11:11 GMT
Hi mo13 , As you know, I hate this idea of using multiple SB instances to layer patches on a working script, when it could all be handled directly. Every instance has to add some CPU overhead, just due to the host switching between them. So, I'd like to bring everything inboard. Here are the changes I know about: Are there any other variations we should implement? These will all be controlled by sliders and buttons on the second GUI page, so you'll be able to use the same script everywhere, just with different settings for the various uses. Edit: How are you sending the clock when using CCs for the notes? Edit: I've got it all except the CCs. Waiting for your input. Cheers, Dave
|
|
|
Post by mo13 on Jun 18, 2022 9:02:54 GMT
Thanks for thinking along for these extra's uncledave! I'm actually away from the device which has all of that set up until monday, but these are the things that I know from top of my head: Yes, this would be Ch.1 here for the Env.generator by I can see myself using this script for other purposes if there is a slider with channel selection, if that's how you envision it? Again, here it's 4 single notes starting from $48-51, but if there is a slider for that then it doesn't matter. That would be handy as well. That's clear, yeah I had something like: 90 XX XX = B0 01 XX 80 XX XX = B0 02 XX The notes are being encoded in an SB instance in AUM on iPad X going as CC's into Drambo on iPad Y. This is something I'd have to look in to when I'm back at the iPad Y on monday but I don't think there are any other major changes. Just by a clock generator into a CC gen. @ Drambo, outputting B0 00, so we have 3 CC's in total.
|
|
|
Post by mo13 on Jun 18, 2022 10:29:06 GMT
Edit: These should be on a different channel, to pass all the notes/CC's first, what I did here was incorrect:
90 XX XX = B0 01 XX 80 XX XX = B0 02 XX Should be:
90 00-05 XX = B1 XX XX 80 00-05 XX = B2 XX XX And then the clock will be:
B0 00
|
|
|
Post by uncledave on Jun 18, 2022 10:55:44 GMT
That's clear, yeah I had something like: 90 XX XX = B0 01 XX 80 XX XX = B0 02 XX The notes are being encoded in an SB instance in AUM on iPad X going as CC's into Drambo on iPad Y. That would hide the note value and send the velocity for all notes on CC# 1, all the note offs on CC# 2. I suspect it may be more like 90 XX XX = B0 01 XX2 80 XX XX = B0 02 XX2 (sends the notes, drops velocities), or maybe 90 XX XX = B0 XX XX 80 XX XX = B0 XX 00 (drops note off velocity), or even 90 XX XX = B0 XX XX 80 XX XX = B1 XX XX (keeps velocities, different channel for note off). I'll wait until you check the actual configuration. Need to know the exact encoding, not just "using CC", since we need to fudge the CC into notes. Need to be sure that doesn't conflict with one of the encoded notes. A different channel might be safest. Actually, if you're using a clock generator, it just follows the selected tempo (BPM). SB has direct access to the tempo. We could make a much simpler script that just sends delayed notes (using +D), with the delay time calculated from the selected number of ticks. That would use much less DSP, since it would not need to update the note counts on every tick. Could be an option to this script, just an add-on. Edit: Your edit crossed my post. So you're using my third alternative, different channel for Note Off. What do you think about optionally using the BPM and a simple delay?
|
|
|
Post by mo13 on Jun 18, 2022 11:20:12 GMT
Yeah the alternative sounds good too as I've already seen some small to medium DSP meter bumps in Drambo when putting automation on the faders which are mapped to your script's sliders, also got a few Mozaic ones in there now that perform slightly heavier duties, so that's a welcome thing for sure!
the exact encoding would be what I wrote in the edit, (if that's what you mean) the CC's that your script would respond to instead of notes would be B1 00-05 for On / B2 00-05 for Off. I didn't know about the direct acces to tempo yet, I think. In that case it could be B0 and B1 instead, if we don't need that clock generator at all.
|
|
|
Post by uncledave on Jun 18, 2022 16:50:14 GMT
Right. No reason to change the note CC#s, though. Using CCs and BPM will both be options, so you can pick the configuration for any setup. Once you save that configuration in a Drambo project or AUM session, it will continue working that way, of course.
|
|