Timer Repeat Delay Variable: Is this Dynamic? Can I change the value while in the Repeat Loop?

I have some Timers that cycle 16 times.
With each iteration, they check the status of a bit of a variable and compare it to the previous status of the variable.
If the status has changed, it updates the previous run state variable and outputs a MIDI CC
If the status has NOT changed, it Exit/Skips Outgoing Action (where Outgoing Action = CC Output)

Question:
If I define a variable for the Repeat Delay as say "Z1", can the value of Z1 change *during* the Timer Repeating?

As in (following my example above):
If the status has changed, Z1 is set to 2ms and it outputs a MIDI CC.
If the status has NOT changed, Z1 is set to 0ms and it Exit/Skips Outgoing Action

It's probably a moot point because the bottleneck in my system is not the Timers running, but this hit my head and it just seemed like a sexy thing to do.

Hi, once you set a repeating timer. It is set at the original delay rate. If you want to change the rate, you can restart the repeating timer at a different rate and it will automatically kill the last timer. 

Maybe it is better to start a 1 shot timer that sends a note and then starts another 1 shot timer at a different rate. Us a global variable to handle the final count.

 

Steve

 

Thanks for the response, Steve.

Question: If a Timer is set for 0ms Initial Delay; 0ms Repeat Delay, and since the BomeBox is basically a little Linux box, running a Timer for 16 iterations is effectively such a short amount of time it's inconsequential, no?

My Timers are basically testing bit by bit (only 16 bits, tho) across a Global Variable used as an Output Mask and comparing the status incurred by a button press and subsequent modification of said mask against a previous run state.

I don't have a situation that there would be NO output from a Timer (it would never seek to run if there is no change across all 16 Tracks). But as a Timer cycles through the 16 iterations I only want it to Delay any Output so the receiving device doesn't get overwhelmed.

So even if I move the TimerWithDelay part further up the chain and the Output portion is only called IF there is an Output, putting a Delay on the Output will not slow it down, but just Delay ALL the outputs.

I have:

PART A:[Proc Decides Output Necessary; Output:Do 0ms Timer "DO OUTPUT"] ->

PART B:[0ms Timer "DO OUTPUT" Calls 2 Timers 16x] ->

PART C1:1-[16x "OUTPUT CHANCE 0 MIDI CC" (Layer Level Control) Output Checks to see if run state for this bit has changed since the last time; if no change, exit; if change, output a MIDI CC]

PART C2:2-[16x "OUTPUT PYRAMIDI CC" (Group Level Control) Output Checks to see if run state for this bit has changed since last time; if no change, exit; if change, output a MIDI CC]

 

My Delays are currently at the Part B section so that Part C's iterations have a 2ms Delay between each.
If I add a Part D "Output" and pass a new Global over to it, and put a Delay on those outputs instead of the Timer, and if that Delay is the same for all outputs, then I'm just delaying the MIDI Bottleneck.

So, if I have a single Output routine Part D - the 16x iterations happen at Part C, and then call the Output Translator. If the Output Translator is not part of the Timer, but a result OF the Timer, I can change the value for Z1, no?

Example with 4 iterations:
Timer iteration 0 checks bit at index 0, sees this bit as the same as the last time it ran, so exit/skip
Timer iteration 1 checks bit at index 1, has new status for this bit compared to the previous status, so calls the Output routine, passes the Globals to it: ZA ZB ZC with Output Delay Z1 (Execute Outgoing Action after a delay).
Timer iteration 2 sees this bit as the same as the last time it ran, so exit/skip
Timer iteration 3 has a new status for this bit, so calls the Output routine, passes the Globals to it: Output ZA ZB ZC with Output Delay Z1.
...

Since the Timer is effectively running nearly instantaneously compared to the MIDI Data, ZA ZB ZC & Z1 are being changed BEFORE the first output is done because of the "Execute Outgoing Message After A Delay". However, these values are changed AFTER the Output Translator has received that data.
So can I just at the start of the Output Translator: oo=ZA pp=ZB qq=ZC xx=Z1 and then Output oo pp qq with Delay xx?

And since Z1 is created during the Timer and there is a counter, I can change the value of Z1 before it gets sent to the Output Translator and make each successive Z1 a little longer by [counter]*[Factor]

Should I sketch this out in MTPro speak?
I'm wondering about the sequence that MTPro completes each Translator and how it affects Global Variables, etc.

Hi,

In answer to your first question. I just did a repeating timer with a delay of 0ms of 128 note off messages. In the log they all showed the same time stamp which tells me that they all happened within a 1ms period (since that is the precision of the timestamp). They appear very fast if you have 0 delay (as fast as the processor can pedal).

 

No you know you cannot pass local variables to a timer so you have to use globals, You can, however in the timer rules, copy the globals to local variables and then delay the output of the local variable in the rules so your strategy might work.

However be careful that you read the global variables before any other translator can change them or you will have unpredictable results.

The local variables will be common to anything that triggers the same timer. If you retrigger a timer, the old timer goes away and a new one starts.

So maybe if you

Capture input into global variable and immediate send it to a timer, then grab the global immediately into a local variable (before another incoming trigger). You could, delay the output of the timer with the local variable still in-tact, again. I'm pretty sure if you use the same incoming trigger for both. The the rules will happen sequentially before the timers so it probably would not work.

 

 

 

I hope this answers your questions.

 

 

Yes, Steve.
Thank you for running that test.
I didn’t even think of running a test like that and I wasn’t even bright enough to conceive of what kind of test I’d run. But that clarifies a LOT.

Yeah: I know about transferring the globals to locals at the start of the translator. heh heh I do that because of re-using bits of code, as you suggested awhile back - so it’s easier to read the Globals into locals and then I can copy/paste.

So, I take it I’d have to make my Timer run with a Delay so the individual output routines have “time” to bounce the Global Values into locals? Or if the Timer runs effectively instantaneous, but each iteration calls the same output routine which sticks the Globals into Locals…? Is that just a potential pandoras box and I should step back slowly?

The issues I’m trying to resolve are characteristic of my script, I think. I don’t know if I can resolve this with playing technique or if I need to recode something.

I’m going to be hooking up a cell phone overhead my rig to record myself playing so I can try to see if the timing problems are me or the script getting ‘confused’ or doing things out of order.

Thanks again for your help, Steve!
Stay well!

The timing of the changing of the globals vs reading them back is crucial no matter how you look at it. I generally only let 1 translator change a global and them multiple translators read them. Sometimes I use a separate global as a "locked" or "busy" indicator. That way I can set it just before changing the global and then clear it right after, but even this is not perfect as the operation of lock/change/clear itself is not atomic. Also the reading translator might have to "poll" the lock variable periodically because there is no "blocking" that I'm aware of in MT Pro. Blocking would be a mechanism to let the translator sleep while waiting for the variable to be available for reading and then send a trigger back to the reading translator when it becomes available for reading again.

Right now the choices for a reading translator is "polling" or "spinning". Unfortunately spinning would consume a lot of processor resources so I would not recommend it.

Example of spinning

// Spin in loop until variable is available

// g0 is the lock and ga is the variable you want to read

// Warning consumes compute resources

Label "spin"

if g0!=0 then goto "spin"

// finish spinning and read global variable

pp=ga

--- Rather it would be better to have a timer lock the variable before writing and then trigger a timer when it is unlocked

Translator : Set Lock

Incoming: Whatever you want (Could be a 1 shot 0 delay timer)

Rules:

// Already locked if go==1 then exit rules, skip outgoing action

g0=1

Outgoing : one shot timer "ga locked" (if you want to notify other translators)

Translator - Something that changes ga

Translator

Clear Lock and notify

Incoming: Whatever you want

Rules ga=0

Outgoing: 1 shot timer "ga unlocked" (to trigger the read event)

 

Steve Caldwell
Bome Q and A Moderator and
Independent Bome Consultant/Specialist
bome@sniz.biz

 

Okay, I can totally use the variable locking. Thank you for sharing that technique.
Since I’m storing MIDI CCs and their values, which only use 7 bits, is there anything stopping me from using the 8th bit to indicate lock status?

Actually, and apologies for ‘thinking out loud’, but a 3 byte message is 24 bits, so I can actually use one variable to pass that info along to the Output Translators, and then I only need 16 Globals for one cycle using unique Globals in each case. I just need to put in a routine to say, “Hey, if iteration 7 then use ZG, if iteration 8 then use ZH…” etc.

Then for the Output Translators I can just have a counter that only increments if the Translator is going to send something out, multiplies that by a factor, puts that in a local, and the Execute After a Delay is that local. BOOM!

Spinning uses a lot of processor resources: a “Lot”? It doesn’t seem it consumers a lot because of complexity, but it’s because it’s constantly running?

I’ve learned so much - but always more to learn!
THANKS STEVE!

Yes, you can use the a bit on the lock variable for the lock and the rest for the values. You have 32 bits to work from so anything you are not using, you can use for something else but be sure only ONE translator does the manipulation.

Spinning is easy but keeps the processor running in continuous loop hence the drain on processor resource and why I don’t recommend this technique. Maybe in a future version of MT Pro, I can convince Florian for the need of atomic variables and/or blocking support but I think there are probably other things of higher priority right now. (like MIDI 2.0, OSC Support, more global variables with meaningful names. One thing that is probably good about this Corona virus is that Florian actually may have more time for development since I think he is stuck at home now too with “shelter in place” or whatever you want to call it.

Steve Caldwell
Bome Q and A Moderator and
Independent Bome Consultant/Specialist
bome@sniz.biz

Thanks Steve!
Excellent!

If I had known when I started how much use I’d get out of ‘packed variables’ I would have learned that technique long ago.

I’ve been using them extensively for my meta controls where one variable packs in CC destination, hi value, lo value, and a signed offset so I can have a bank of CC’s react to one incoming control.

So, yeah: great info on locking variables and BRIL to be able to lock any variable that is less than 2147483647 or so.

I know I’ve been getting ahead on my coding/playing/practicing, but it’s a distraction for me.
Apologies for all my questions and posts - I realise they’re not the standard Q&A fare.

Well, since they are posted on this forum, there is value and others can look in as well so no issue with posting here on the Q&A.