Loopy Pro: Create music, your way.

What is Loopy Pro?Loopy Pro is a powerful, flexible, and intuitive live looper, sampler, clip launcher and DAW for iPhone and iPad. At its core, it allows you to record and layer sounds in real-time to create complex musical arrangements. But it doesn’t stop there—Loopy Pro offers advanced tools to customize your workflow, build dynamic performance setups, and create a seamless connection between instruments, effects, and external gear.

Use it for live looping, sequencing, arranging, mixing, and much more. Whether you're a live performer, a producer, or just experimenting with sound, Loopy Pro helps you take control of your creative process.

Download on the App Store

Loopy Pro is your all-in-one musical toolkit. Try it for free today.

App that divides incoming midi

Does anybody know of any apps that will let me divide incoming midi in real time? I can record the midi in ATOM2 and then afterwards half the playback speed, but Im looking for something that will do it in real time.

Any ideas?

«1

Comments

  • Couple quick thoughts…
    So mainly when recording midi into atom 2?

    In what manners are you interested in dividing midi up? Ie. By instrument? By Octaves? By Randomness, etc…?

    Are you trying to divide the midi up on to individual midi channel's?

    there are ways to separate/filter midi, but it depends on how you want to filter or split it up

    I think Mozaic has a few scripts that might be very helpful. I can’t recall the specific names of the free scripts but they are on Patchstorage.com

    Maybe Streambyter, but again, with a script that divides midi.

    If I recall correctly the MIDI Tools app by the same developer as Atom 2 might be able to help you. I’d have to double check that one.

    look into these as options.

  • I believe the OP wants to slo.o..o...w the MIDI down. That's difficult in real time because you need a buffer large enough for the whole stream, so you can send the notes more slowly. Recording it and playing back at half speed is easy, because you've buffered the entire stream first.

  • wimwim
    edited April 20

    That's the way I understood it too.

    There isn't anything real-time like that to my knowledge. @uncledave hit the nail on the head about the need for a buffer.

    I did do an experimental midi recorder in Mozaic that in theory could be adapted for something like that, but I abandoned that project as impractical. It was just pushing the limits of Mozaic too far.

  • I think I could do it in StreamByter. I once made a script that delayed drum hits for up to a bar, different user-selected delay time for each note. But that had a fixed, known delay time for each note. Cutting the speed in half means a different, longer delay time for each subsequent note, and the buffer keeps filling as long as the stream continues. Might be able to slow down a few seconds of MIDI input. The required buffer size depends a lot on the data, whether it's percussion or legato strings. I guess the good news is that the notes come out in order, where they didn't in the prior project, so I had to scan the entire buffer on every MIDI clock.

  • @Crano . You will not be able to slow a long MIDI stream in real-time. What sort of content are you looking at (how many notes per bar), and how long a stream would you hope to process? The maximum duration would likely be a few seconds. If we slow to half speed, the buffer will still be holding half the input data when the input stops, and the buffer we could access would likely be less than 1000 events

  • @wim . I just had a crazy idea. StreamByter has the ability to send a delayed message. Could we just take each input note, calculate the delay (tricky), and send the note on with a delay? That moves the buffer from our hands to the MIDI queuing system. There's bound to be a limit, but it could be interesting to try.

  • wimwim
    edited April 20

    @uncledave said:
    @wim . I just had a crazy idea. StreamByter has the ability to send a delayed message. Could we just take each input note, calculate the delay (tricky), and send the note on with a delay? That moves the buffer from our hands to the MIDI queuing system. There's bound to be a limit, but it could be interesting to try.

    That's a very good idea. Mozaic could do that easily too. However, Bram let me know that cueing up a lot of delayed messages in Mozaic could bring it to its knees. It could work for limited numbers of incoming messages though. It would probably have to be limited to just notes, not CC's and pitch bend though. Probably some counter would be wise to keep from going over a reasonable number of queued messages, which could get real complicated.

    Calculating the delay in ms is easy if you know the BPM.
    [edit] Actually the sentence above is wrong now that I think about it. The time between the current and last received messages has to be measured, then multiplied by the slowdown factor. BPM doesn't come into it.

  • edited April 20

    @wim said:
    That's the way I understood it too.

    There isn't anything real-time like that to my knowledge. @uncledave hit the nail on the head about the need for a buffer.

    Well, at least the OP didn't ask about making it faster in real-time 🤪 (we had someone ask something like that some time ago and actually had to explain why that is impossible...)

  • I’ve just tried this in dRambo going along with what @uncledave was suggesting.

    There are delay modules inserted into the midi stream for pitch, gate and velocity.
    The knob changes the delay time and there are two AN kick drums
    so that you can hear what the delay is doing alongside the original.
    It’s quite cool actually.
    Thanks guys.

  • wimwim
    edited April 20

    @Gravitas said:
    I’ve just tried this in dRambo going along with what @uncledave was suggesting.

    There are delay modules inserted into the midi stream for pitch, gate and velocity.
    The knob changes the delay time and there are two AN kick drums
    so that you can hear what the delay is doing alongside the original.
    It’s quite cool actually.
    Thanks guys.

    Wait doesn't that just delay each note, but keep the space between the messages the same?

    I think to slow things down you would have to calculate the time between the last message and the current message, then multiply that by the slowdown factor.

    Or am I missing something?

  • @SevenSystems said:
    Well, at least the OP didn't ask about making it faster in real-time 🤪 (we had someone ask something like that some time ago and actually had to explain why that is impossible...)

    Humm ... however in this dividing case, you could actually apply some negative swing ... without even needing to figure out time travel. 🤔

  • wimwim
    edited April 20

    My prototype Mozaic script is totally not working.
    I've clearly not thought this through well enough. 😂

  • @wim said:

    @SevenSystems said:
    Well, at least the OP didn't ask about making it faster in real-time 🤪 (we had someone ask something like that some time ago and actually had to explain why that is impossible...)

    Humm ... however in this dividing case, you could actually apply some negative swing ... without even needing to figure out time travel. 🤔

    😁

  • My StreamByter sample actually works! It measures the time since Play, and delays each note on/off by that time (for half speed). Other ratios TBD. Initially, I was doubling the time, but that gave a 1/3 speed, surprise! It can only delay up to about 60 seconds, because the delay time is ms in a 16-bit variable. It handled a minute of 8th notes (from ZOA) at 120 BPM without stumbling, so that's 240 note on and 240 note off events.

    I guess I could handle longer delays in 60-second stages, using delayed internal messages, until the time got close enough. SB has some 32-bit registers, so doing the arithmetic is no problem. Passing the 32-bit values through a SysEx would be a bit of a chore, though; need 5 bytes of 7 bits each.

    (Long time ago, I used a computer with 36-bit words and 7-bit ACSII. Lots of fun packing characters into a word using Fortran. Why Fortran? Because the only alternative was assembler!)

  • @Crano . If you're at all interested in these goings-on, please answer my previous questions. My current sample shows that something can be done, but I need to know how you'd use it.

  • @wim said:
    I think to slow things down you would have to calculate the time between the last message and the current message, then multiply that by the slowdown factor.

    Or am I missing something?

    I believe you need to delay each event by the total time since Play (for half speed). I see it as mapping between two timelines, one twice as long as the other. So it's the cumulative time that matters, not just the local relative time. The last event on the short line is mapped to the end of the longer line.

  • @wim

    Yeah, my thing definitely just delays the signal but it’s more of a starting point than anything else
    because once you’ve separated the three signals we can do whatever we like with them.
    I’m going to give it more thought in regards to actually dividing the incoming rhythm into half time
    or whatever time but doing it in real-time I think will inevitably lead to the outgoing rhythm being delayed
    however I’m hoping someone is going to prove me wrong.
    Shift register anyone?
    @uncledave @rs2000 ??

  • @Gravitas said:
    @wim

    Yeah, my thing definitely just delays the signal but it’s more of a starting point than anything else
    because once you’ve separated the three signals we can do whatever we like with them.
    I’m going to give it more thought in regards to actually dividing the incoming rhythm into half time
    or whatever time but doing it in real-time I think will inevitably lead to the outgoing rhythm being delayed
    however I’m hoping someone is going to prove me wrong.
    Shift register anyone?
    @uncledave @rs2000 ??

    I think this will be tough in Drambo because the incoming notes keep piling up, and you need to store them in a very elastic buffer.

  • @uncledave

    I think this will be tough in Drambo because the incoming notes keep piling up, and you need to store them in a very elastic buffer.

    Agreed, however I remembered we have the Sequencer module which we can record incoming
    midi with and then half the speed using that and we can mix between the incoming midi
    and the midi recorded or imported in by the Sequencer module.

  • @SevenSystems said:

    @wim said:
    That's the way I understood it too.

    There isn't anything real-time like that to my knowledge. @uncledave hit the nail on the head about the need for a buffer.

    Well, at least the OP didn't ask about making it faster in real-time 🤪 (we had someone ask something like that some time ago and actually had to explain why that is impossible...)

    Love this comment. But all we need is an AI that can accurately predict, what you're about to play. I'm sure we'll get there;)

  • @uncledave said:

    @wim said:
    I think to slow things down you would have to calculate the time between the last message and the current message, then multiply that by the slowdown factor.

    Or am I missing something?

    I believe you need to delay each event by the total time since Play (for half speed). I see it as mapping between two timelines, one twice as long as the other. So it's the cumulative time that matters, not just the local relative time. The last event on the short line is mapped to the end of the longer line.

    Yeh, I didn't think that one through well at all.

    I'm probably not going to pursue it anyway. I don't feel like Mozaic is an especially good vehicle for this. The basic coding wouldn't be that hard, but doing things in such a way that it wouldn't bog down, or need to be very limited to play it safe, just doesn't feel like it would lead to a very satisfactory outcome.

  • My experiment with a “Mozaic MIDI Recorder” script that would launch note on Playback using
    the delay option for Note On and Note off hit a Mozaic limit of 256 simultaneous outstanding events.

    I think it should be possible to:

    1. Save the “systemtime” as “Time0” when the first note on comes in and pass it through.
    2. Save the details of only Note On/Off events in parallel arrays:

    Channel
    Event type (Note On, Note Off) These are combined in MidiByte1
    MidiByte2 (Note Value)
    MidiBtye3 (Velocity)

    1. As events arrive request the current SystemTime (Time) and calculate Time-Time0 as “HalfDelay”.
    2. Forward the relevant Note events immediately as MidiByte1, MidiByte2, MidiByte3, HalfDelay*2.

    This will break when there are 256 outstanding events yet to be processed by Mozaic’s runtime code.

    I wonder when a StreamByter implementation might fail as Notes being held back start to queue up?

  • edited April 21

    @McD . I suspect StreamByter might fail at the same point, 256 events queued. That might be the available queue length. My 60-second test of eighth notes @120 BPM peaked at 240 events, just under. I'll try a longer run and see if it fails.

    Edit: Well, it passes the 256 event limit, ran to 493 events queued over 60 seconds of input..

  • edited April 21

    Wow thanks a lot for all the thoughts/input guys! I just realized that multiplying the rests in between midi notes might be a better description.

    I thought there might be an app that i overlooked that does this. But if its as complicated as it sounds I might be better off recording and half the total length afterwards.

    Feeling a bit reluctant now to explain why I asked it (because there might be more obvious solutions which I overlooked), but I came to the idea because of Harmony Bloom. I didnt see any options to slow down the sequence while synced to the host without reducing the amount of notes. And sometimes I like the results but wanted it to be half the speed. So I could also try settling with unsyncing and adjusting to ear.

    @uncledave said:
    @Crano . You will not be able to slow a long MIDI stream in real-time. What sort of content are you looking at (how many notes per bar), and how long a stream would you hope to process? The maximum duration would likely be a few seconds. If we slow to half speed, the buffer will still be holding half the input data when the input stops, and the buffer we could access would likely be less than 1000 events

    Thank you. I dont have an exact answer to your questions. But my elaboration above might provide some answers. I wasnt really looking for a custom solution, but rather to use some tool that was already developed. I would just try out what I have to work with.

  • Well, here is what I've got. It starts delaying on transport Run, and stops on Stop. It can handle 3 minutes of 16ths at 120 bpm, with a factor of 2 (hald speed). After 4 minutes, it goes bananas; probably runs out of buffers, and starts sending internal messages in the output stream. It only processes note on/off messages, so it's no good for MPE. I'd be wary of trying to handle CCs or Pitch Bends, because they can generate a lot of messages.

    Instead of starting on Run, we could make it reset automatically after a period of quiet, maybe 10 seconds. That way you could send MIDI for a while, stop the MIDI and listen, then start again and simply carry on.

    One thing we cannot do is stop the delayed output. It is already queued and will be sent. Of course, you can mute the receiving instrument.

    #DelayMIDI
    
    # Delays MIDI note messages received while transport is running,
    # changing the speed by a factor.
    
    # The delay factor gives the length of the output sequence relative
    # to the length of the input, multiplied by 10. So, 20 means the output
    # is double the length of the input, or half-speed; 15 means the output 
    # is 50% longer, or 2/3 speed.
    
    # This can handle 3 min of 16th notes at 120 bpm, factor of 2x, max 
    # pending events 1440 (half the total). It fails at 4 min, max pending
    # events 1918. This on iPad 6, iPadOS 17.4.1.
    
    # Load StreamByter in a MIDI slot. Paste this in and Install Rules.
    # You can Save it for later reuse (hamburger menu in lower right). 
    # Tap the magnifier to see the actual input and output messages. 
    # Tap the "globe" icon in lower right to switch between controls 
    # and edit screen.
    
    If load
    Set Name DelayM
    
    Alias T00 Timer         # measures time since previous event
    Alias P0F totalTime     # accumulates time since start
    Alias J00 runFlag       # set when running
    Alias J01 totalCount        # number of msgs processed, info only
    Alias K00 scaleFactor   # internal time multiplier
    Alias $60000 maxDelay   # maximum delay is 60 seconds
    
    # useful initialization
    Ass J00 = 0 0 0 0   0 0 0 0 
    Ass P00 = 0 0 0 0   0 0 0 0   0 0 0 0   0 0 0 0 
    Ass K00 = 0 0 0 0   0 0 0 0 +P
    Ass scaleFactor = $10
    
    # Screen Controllers ———————————————————————————
    Alias Q0 theDelayFactor
    Alias 0 indDelayFactor
    Set Q0 DelayFact_x10 $11 $50
    Set Q1 +H
    Set Q2 +H
    Set Q3 +H
    
    Set Q4 +H
    Set Q5 +H
    Set Q6 +H
    Set Q7 +H
    
    # Initial values for sliders. Actual values saved in any preset.
    Ass Q0 = $20 0 0 0   0 0 0 0 +P
    # Ass Q8 = 0 0 0 0   0 0 0 0 +P
    
    Set Sliderdisplay 1
    # End Screen Controllers —————————————————————————
    
    # this internal SysEx message uses the arbitrary index 23. Use for delays.
    define repeatEvent F0 7D 23
    
    # unpack 32-bit value into 5 bytes of L starting at pFirst, MSB first
    # bytes have max 7 data bits, for MIDI compatibility
    Sub Unpack32 pValue pFirst
       ass PF0 = pValue
       mat I30 = pFirst + 5
       while I30 > pFirst
          mat I30 = I30 - 1
          mat LI30 = PF0 & 7F       # mask off LSB
          mat PF0 = PF0 / 80            # shift remainder right
       end
    End
    
    # assemble 32-bit pValue from 5 M bytes starting at pFirst, MSB first
    Sub Pack32 pValue pFirst
       ass PF0 = 0
       ass I30 = pFirst
       mat I31 = pFirst + 5
       while I30 < I31
          mat PF0 = 80 * PF0            # shift result left
          mat PF0 = PF0 | MI30      # OR in next byte
          mat I30 = I30 + 1
       end
       ass pValue = PF0
    End
    
    # convert the total time into the delay for this event
    Sub GetDelay pDelay pTime
       mat P20 = pTime * scaleFactor
       mat P20 = P20 + 5        # round to nearest ms
       mat pDelay = P20 / $10
    End
    
    # delay value must be 16-bit, <64K
    # if required delay is too large, we send an internal message with
    # maximum delay, containing the MIDI data and time remaining.
    # maxDelay is actually 60 seconds for easier testing.
    Sub SendDelay pDelay
       if pDelay > maxDelay
          # here when delay is too large
          mat P10 = pDelay - maxDelay       # calc delay remaining
          Unpack32 P10 0        # convert remaining delay to bytes at L00
          ass I10 = maxDelay
          # send internal SysEx containing MIDI data and delay value
          send repeatEvent 2 M0 M1 M2 L00 L01 L02 L03 L04 F7 +I +DI10
       else
        # here when delay is <= maximum
          ass I10 = pDelay
          snd M0 M1 M2 +DI10        # send delayed MIDI message
       end
    End
    
    End # Initialization ———————————————————————————
    
    # handle note on and off, any channel
    If MT < A0
       if runFlag > 0
          mat totalTime = totalTime + Timer # time since Run
          GetDelay P00 totalTime                # delay for this event
          SendDelay P00                         # send it
          mat totalCount = totalCount + 1
          set LB0 P00 +D        # labels show last delay and message count
          set LB1 totalCount +D
          block                 # block original message
          exit                  # skip rest of code, for speed
       end
    End
    
    # handle internal messages
    If M0 == repeatEvent
       if M3 == 2
          # here with long delay message
          Pack32 P00 7      # restore remaining delay time
          ass M0 = M4           # shift message data to normal positions
          ass M1 = M5
          ass M2 = M6
          SendDelay P00     # handle same as original note
          block
          exit                  # quit early for speed
       end
       if M3 == 1
          # here with delayed stop message
          ass runFlag = 0
       end
       block
    End 
    
    If M0 == F0 7D 01       # handle system events
       if M3 == 7A              # run
          ass runFlag = 1
          ass I00 = Timer       # clear the timer
          ass totalTime = 0
          ass totalCount = 0
       end
       if M3 == 7C          # stop
          # delay stop since last note may come after
          send repeatEvent 1 F7 +I +D200
       end
       if M3 == indDelayFactor      # user moved slider
          mat scaleFactor = theDelayFactor - $10
       end
    End
    
    If MT == B0
       if M1 >= $120   # Zoa sends these annoying CCs
          block
       end
    End
    
  • @tyslothrop1 said:

    @SevenSystems said:

    @wim said:
    That's the way I understood it too.

    There isn't anything real-time like that to my knowledge. @uncledave hit the nail on the head about the need for a buffer.

    Well, at least the OP didn't ask about making it faster in real-time 🤪 (we had someone ask something like that some time ago and actually had to explain why that is impossible...)

    Love this comment. But all we need is an AI that can accurately predict, what you're about to play. I'm sure we'll get there;)

    Looking at the likes of suno.ai, we're probably already there alright 😂 but I'd have better uses for a time machine, like going into the future, getting the feckin' lottery numbers, and then back and fill out my ticket 🙄

  • @wim . You can see in my post how I cracked the 60-second delay limit by sending a delayed SysEx back around. Implemented the pack/unpack between 32-bit int and 7-bit byte, so the time can be sent in a MIDI message. It looks like it runs out of buffers somewhere between 1400 and 1900 pending events, then it goes completely crazy.

    I think we lose some precision calculating the total time since start by accumulating all the intervals between notes. Can maybe reset the time using a longer timer, read every 60 seconds or so.

  • @SevenSystems said:

    @tyslothrop1 said:

    @SevenSystems said:

    @wim said:
    That's the way I understood it too.

    There isn't anything real-time like that to my knowledge. @uncledave hit the nail on the head about the need for a buffer.

    Well, at least the OP didn't ask about making it faster in real-time 🤪 (we had someone ask something like that some time ago and actually had to explain why that is impossible...)

    Love this comment. But all we need is an AI that can accurately predict, what you're about to play. I'm sure we'll get there;)

    Looking at the likes of suno.ai, we're probably already there alright 😂 but I'd have better uses for a time machine, like going into the future, getting the feckin' lottery numbers, and then back and fill out my ticket 🙄

    I guess, I'd rather stay in a world with hoverboards, than go back with that boring sports almanac:)

  • edited April 22

    Imagine @GeertBevin's MIDI recorder with a separate playhead, playing the recorded buffer with adjustable (slower) speed 😊

  • This updated StreamByter script resets the delay after a suitable interval between notes. It ignores transport Run and Stop commands.

    #DelayMIDIAuto
    
    # Delays MIDI note messages, changing the speed by a factor.
    # Restarts after a long enough gap between input notes.
    # Note that delayed notes cannot be cancelled.
    
    # The delay factor gives the length of the output sequence relative
    # to the length of the input, multiplied by 10. So, 20 means the output
    # is double the length of the input, or half-speed; 15 means the output 
    # is 50% longer, or 2/3 speed.
    
    # Can adjust the size of the interval between notes that triggers reset.
    
    # This can handle 3 min of 16th notes at 120 bpm, factor of 2x, max 
    # pending events 1440 (half the total). It fails at 4 min, max pending
    # events 1918. This on iPad 6, iPadOS 17.4.1.
    
    # Load StreamByter in a MIDI slot. Paste this in and Install Rules.
    # You can Save it for later reuse (hamburger menu in lower right). 
    # Tap the magnifier to see the actual input and output messages. 
    # Tap the "globe" icon in lower right to switch between controls 
    # and edit screen.
    
    If load
    Set Name DelayM
    
    Alias T00 Timer         # measures time since previous event
    Alias P0F totalTime     # accumulates time since start
    Alias J00 runFlag       # set when running
    Alias J01 totalCount        # number of msgs processed, info only
    Alias K00 scaleFactor   # internal time multiplier
    Alias K01 maxInterval
    Alias $60000 maxDelay   # maximum delay is 60 seconds
    
    # useful initialization
    Ass J00 = 0 0 0 0   0 0 0 0 
    Ass P00 = 0 0 0 0   0 0 0 0   0 0 0 0   0 0 0 0 
    Ass K00 = 0 0 0 0   0 0 0 0 +P
    Ass scaleFactor = $10
    Ass maxInterval = $5000
    
    # Screen Controllers ———————————————————————————
    Alias Q0 theDelayFactor
    Alias Q1 theMaxInt
    Alias 0 indDelayFactor
    Alias 1 indMaxInt
    Set Q0 DelayFact_x10 $11 $50
    Set Q1 Max_Int_sec 1 $10
    Set Q2 +H
    Set Q3 +H
    
    Set Q4 +H
    Set Q5 +H
    Set Q6 +H
    Set Q7 +H
    
    # Initial values for sliders. Actual values saved in any preset.
    Ass Q0 = $20 5 0 0   0 0 0 0 +P
    
    Set Sliderdisplay 1
    # End Screen Controllers —————————————————————————
    
    # this internal SysEx message uses the arbitrary index 23. Use for delays.
    define repeatEvent F0 7D 23
    
    # unpack 32-bit value into 5 bytes of L starting at pFirst, MSB first
    # bytes have max 7 data bits, for MIDI compatibility
    Sub Unpack32 pValue pFirst
       ass PF0 = pValue
       mat I30 = pFirst + 5         # start at LSB and work back
       while I30 > pFirst
          mat I30 = I30 - 1
          mat LI30 = PF0 & 7F       # mask off LSB
          mat PF0 = PF0 / 80            # shift remainder right
       end
    End
    
    # assemble 32-bit pValue from 5 M bytes starting at pFirst, MSB first
    Sub Pack32 pResult pFirst
       ass PF0 = 0
       ass I30 = pFirst
       mat I31 = pFirst + 5
       while I30 < I31
          mat PF0 = 80 * PF0            # shift result left
          mat PF0 = PF0 | MI30      # OR in next byte
          mat I30 = I30 + 1
       end
       ass pResult = PF0                # return result
    End
    
    # convert the total time into the delay for this event
    Sub GetDelay pDelay pTime
       mat P20 = pTime * scaleFactor
       mat P20 = P20 + 5        # round to nearest ms
       mat pDelay = P20 / $10
    End
    
    # send delayed note message
    # delay value must be 16-bit, <64K
    # if required delay is too large, we send an internal message with
    # maximum delay, containing the MIDI data and time remaining.
    # maxDelay is actually 60 seconds for easier testing.
    Sub SendDelay pDelay
       if pDelay > maxDelay
          # here when delay is too large
          mat P10 = pDelay - maxDelay       # calc delay remaining
          Unpack32 P10 0        # convert remaining delay to bytes at L00
          ass I10 = maxDelay
          # send internal SysEx containing MIDI data and delay value
          send repeatEvent 2 M0 M1 M2 L00 L01 L02 L03 L04 F7 +I +DI10
       else
        # here when delay is <= maximum
          if pDelay > 0
             ass I10 = pDelay
             snd M0 M1 M2 +DI10     # send delayed MIDI message
          else
             snd M0 M1 M2
          end
       end
    End
    
    # restart the delay process
    Sub Reset
       ass runFlag = 1
       ass I00 = Timer      # clear the timer
       ass totalTime = 0        # clear totals
       ass totalCount = 0
    End
    
    End # Initialization ———————————————————————————
    
    # handle note on and off, any channel
    If MT < A0
       if runFlag == 0
          Reset
       else
          ass I00 = Timer
          if I00 >= maxInterval
             Reset
          else
             mat totalTime = totalTime + I00        # time since Run
          end
       end
       GetDelay P00 totalTime                   # delay for this event
       SendDelay P00                            # send it
       mat totalCount = totalCount + 1
       set LB0 P00 +D       # labels show last delay and message count
       set LB1 totalCount +D
       block                    # block original message
       exit                     # skip rest of code, for speed
    End
    
    # handle internal messages
    If M0 == repeatEvent
       if M3 == 2
          # here with long delay message
          Pack32 P00 7      # restore remaining delay time
          ass M0 = M4           # shift message data to normal positions
          ass M1 = M5
          ass M2 = M6
          SendDelay P00     # handle same as original note
          block
          exit                  # quit early for speed
       end
       if M3 == 1
          # here with delayed stop message
          ass runFlag = 0
       end
       block
    End 
    
    If M0 == F0 7D 01       # handle system events
       if M3 == indDelayFactor      # user moved slider
          mat scaleFactor = theDelayFactor - $10
       end
       if M3 == indMaxInt
          mat maxInterval = $1000 * theMaxInt
       end
       # Run and Stop are disabled for testing
       if M3 == 7F          # run 7A
          Reset
       end
       if M3 == 7F          # stop 7C
          # delay stop since last note may come after
          send repeatEvent 1 F7 +I +D200
       end
    End
    
    If MT == B0
       if M1 >= $120   # Zoa sends these annoying CCs
          block
       end
    End
    
Sign In or Register to comment.