|
Post by mo13 on Apr 14, 2022 12:03:40 GMT
this will come in most handy, thanks!
Using it now on all 4 values as a starting point, so it clears out all the delays when you engage a new part without manually have to write the rules on each part to which previous delays need to be cleared, so it only opens the sliders that need to be used, first I thought those songparts's Snd rules would need a +D10 but this works just fine:
#sceneClearout
If load Ass I00 = 0 While I00 < 7 Send dlay KI00 0th Mat I00 = I00 + 1 End Snd dlay BD 2nd #engages the first delay of a new scene End
#generalClearout
IF M0 == B1 22 Ass I00 = 0 While I00 < 7 Send dlay KI00 0th Mat I00 = I00 + 1 End #songparts IF M2 == 00 #pt_1 snd dlay BD 2nd end IF M2 == 01 #pt_2 Snd dlay BD 5th Snd dlay hh 2nd Snd dlay ride4 2nd Snd dlay sd 0th End IF M2 == 02 #pt_3 Snd dlay sd 2nd End IF M2 == 03 #pt_4 Snd dlay ride4 2nd End End Block And ofcourse also handy now just leaving out the part that has no delays at all.
[edit] still need to work on those if load rules, as now it's not always clearing out the previous block upon scene switch.
|
|
|
Post by uncledave on Apr 14, 2022 13:59:00 GMT
Now, you could get really clever and create a Sub for this:
#sceneClearout
If load Sub ResetAll Ass I00 = 0 While I00 < 7 Send dlay KI00 0th Mat I00 = I00 + 1 End End
ResetAll Snd dlay BD 2nd #engages the first delay of a new scene End
#generalClearout
IF M0 == B1 22
ResetAll #songparts IF M2 == 00 #pt_1 snd dlay BD 2nd end IF M2 == 01 #pt_2 Snd dlay BD 5th Snd dlay hh 2nd Snd dlay ride4 2nd Snd dlay sd 0th End IF M2 == 02 #pt_3 Snd dlay sd 2nd End IF M2 == 03 #pt_4 Snd dlay ride4 2nd End End Block This is why we use Subs, to save implementing repeated operations. This way, you only have to get it right once, and use it multiple times. It's especially important if the method needs to be changed, since there's only one place to update. Otherwise, you need to hunt for all the occurrences of the same algorithm.
|
|
|
Post by mo13 on Apr 14, 2022 14:20:53 GMT
Nice, not sure if you saw my [edit] in the previous post? It catches the next #sceneClearout now about 80% of the time, maybe it might have something to do with that my controller automatically fires a song part aka B1 22 00 upon a scene switch (but I suspect that it doesn't always come through at the same time, seems like sometimes before or after If Load. I'm already using these rules in another script to avoid double program changes for synths:
#block B0 22 00 once
if load assign L0 = 0 end if M0 == B1 22 00 if L0 == 0 block assign L0 = 1 end end Still figuring out if this is the issue here.
This is a tough one to trace, the 80% of the time clearance still keeps happening, maybe the ResetAll sub should only happen once and not be recalled. And everything afterwards going through a new songpart clearSub?
|
|
|
Post by uncledave on Apr 14, 2022 18:15:19 GMT
If Load only happens when you load a saved scene, or AUM session. If your controller is triggering a scene load and sending a message, the message will only be seen if the scene is loaded before it arrives. That's really up to the host, not SB.
Can you check whether the messages from If Load are actually sent? That would likely require another app outside AUM.
I don't really know anything about switching scenes on the fly, but it seems to me like some delay would be needed before you can expect the loaded program to work normally. You might need to send a delayed internal SysEx message to trigger events after the scene is loaded.
|
|
|
Post by uncledave on Apr 14, 2022 18:22:24 GMT
OK, here is the large buffer version of my TickDelay script. It can handle up to 1024 entries. Coding was easy, but testing was hard, because I ran into SB's limiting of loops to 128 iterations. It seems to work now, and I've hammered it pretty hard. It includes some new diagnostic tools.
#TickDelayLarge
# 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 as needed 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 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
# 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 Sub AddNote pNote pVel GetDelay I11 pNote If I11 > 0 # skip if delay is 0 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 # 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 Mat I20 = pWhere + 1 # note in next word Mat I21 = WI20 / 80 # unpack note and velocity Mat I22 = WI20 & 7F Send sendNoteCommand I21 I22 If testOffset > 0 # generate Note Off for testing Mat I22 = sendNoteCommand ^ 10 # convert note on to off Send I22 I21 0 +D500 End 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 Mat WI10 = WI10 - 1 # decrease tick count If WI10 <= 0 # first time count reaches 0 Ass WI10 = 0 SendDelayedNote I10 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
# 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 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 Apr 14, 2022 18:35:58 GMT
great! will be on it in a sec, this what comes through now when it normally 'catchess' the preset (and I guess the If Load) on a new scene switch:  and this is what happens @what I mentioned 80% chance when it doesn't go through:  Still don't know what to do best here, if it's an idea to create a dummy SB instance that always engages it's own if Load first and then sends for instance B1 23 to the If Load of the presets script, and with a slight delay, so it will always arrive. or maybe you can see something that i'm missing here?
|
|
|
Post by mo13 on Apr 14, 2022 18:56:31 GMT
Don't think it can get any more reliable then this! just tested again with 96 notes on 150 BPM, this time I set FLAM on all of them so the note stream doubled or likeley quadrupled, Count never gos over 15 with these extreme settings. with my normal presets the count never gos over 2 now, ha! another milestone Dave!
|
|
|
Post by uncledave on Apr 14, 2022 18:56:45 GMT
I need to study your output to understand it. But here's the delayed message trick I thought might help:
#DelayedSysExSample
If load define myMessage F0 7D 37 F7 Send myMessage +I +D1000 End
# this should not happen until the scene is loaded If M0 == myMessage Log Message_Received # do whatever you like here End Remember that the SB monitor is not enabled until you open it, so you cannot rely on it to see startup events.
|
|
|
Post by uncledave on Apr 14, 2022 19:00:23 GMT
Don't think it can get any more reliable then this! just tested again with 96 notes on 150 BPM, this time I set FLAM on all of them so the note stream doubled or likeley quadrupled, Count never gos over 15 with these extreme settings. with my normal presets the count never gos over 2 now, ha! another milestone Dave! That's because you're using a very short delay. The serious test is to use whole note delay, or even two whole notes (master+specific); though that's not likely musically appropriate.
|
|
|
Post by mo13 on Apr 14, 2022 19:05:44 GMT
right, all whole notes incl. Master + Flamed now, max.count. 69, still very good right?
|
|
|
Post by mo13 on Apr 14, 2022 19:30:54 GMT
I routed it to an MF virtual port back to AUM so the startup output should always be on that monitor, and as I also engage the new scene by my controller with SB monitor already open @aum.
not too sure how to use 'myMessage' code, do I make an Alias for myMessage? and where should I exactly put it, on top of the existing presets script or a saparate SB instance?
btw If a put in on a saparate SB instance in MF, unconnected to anything, and I press install rules, then my virtual main MidiFire port is receiving the SysEx msg.
|
|
|
Post by uncledave on Apr 14, 2022 21:40:13 GMT
You need to Block it in the receiver.
The idea is that, if you want to do some initialization al little bit after a scene is loaded, you can send that internal message, and put the actions in the message receiver. So you could add that to the script that sets the delays. You would put the Send in If Load of the presets script, and the receiver in the body, after the End of If Load. Then move the Sends from If Load in the message receiver. This might make things more repeatable.
But really, I don't have a clear picture of what's going on. Which scripts are in MidiFire, which are in AUM? Are you recalling a MidiFire scene using a controller message? Are you loading an AUM Session also, or not?
|
|
|
Post by mo13 on Apr 15, 2022 8:46:23 GMT
My presets script is in MF and getting reloaded on scene switch, your delays script is sitting statically in AUM and is receiving those preset CC values from virtual MF port. The workaround for now is doing this @mf:
If load Sub ResetAll Ass I00 = 0 While I00 < 7 Send dlay KI00 0th Mat I00 = I00 + 1 End End ResetAll Snd dlay BD 0th #these are engaging the first preset of a new scene Snd dlay hh 1st Snd dlay sd 1st Snd dlay ride4 0th Snd dlay cl 0th Snd dlay tm 0th End
what happens now is if I don't put those 0th to clear out the rest of the preset, the active sliders of the newly loaded scene aka hh 1st and sd 1st always do come through, but the clearout of the rest of the sliders is happening 80% of the time, when the clearout doesn't happen then the sliders from the previous preset come along for the newly loaded one. So somewhere there is timing fluctuation, I don't mind doing it this way now as well though as it's 100% coming through this way.
|
|
|
Post by uncledave on Apr 15, 2022 11:42:27 GMT
So, you could use the delayed internal message trick to ensure the delay sends occur after the scene is fully loaded. And since you're using a CC to trigger these events, you can just send the CC to yourself. I believe the problem is that the code in If Load runs as soon as SB is loaded, before the rest of the scene has been initialized. Using the delayed message lets MF complete loading the scene. It then runs SB because there is a pending message. By then, the ports are set up, and the messages will be sent correctly. Notice that, because we're using one of the expected CC messages, there is no "special" code in the event handlers to handle this initial case.
#DelayedInitialization
If load # other initialization here Set Name EnvDelays
Alias B7 dlay
#delays
Alias $50 BD Alias $51 hh Alias $52 sd Alias $53 ride4 Alias $54 cl Alias $55 tm Alias $56 master
#timeSigs
Alias $0 0th Alias $15 1st Alias $25 2nd Alias $35 3rd Alias $45 4th Alias $56 5th Alias $65 6th Alias $76 7th Alias $88 8th Alias $100 9th Alias $115 10th Alias $127 11th
Ass K0 = $50 $51 $52 $53 $54 $55 $56
Sub ResetAll Ass I00 = 0 While I00 < 7 Send dlay KI00 0th Mat I00 = I00 + 1 End End
Send B1 22 00 +I +D500 # trigger part 1, delayed End # load
IF M0 == B1 22 IF M2 == 00 #pt_1 ResetAll Snd dlay hh 1st Snd dlay sd 1st End IF M2 == 01 #pt_2 ResetAll Snd dlay BD 5th Snd dlay hh 2nd Snd dlay ride4 2nd End # and so forth... End Block
|
|
|
Post by mo13 on Apr 15, 2022 12:40:15 GMT
working thanks! I changed the +D to 5 instead of 500 and it's still catches it every time, so I guess it's like you say a matter of having a pending message and now it always comes after a new scene initialization no matter what. could you please elaborate on # other initialization here ? as I didn't use it, or that's just there to put anything else in the future?
|
|