Audiobus: Use your music apps together.

What is Audiobus?Audiobus is an award-winning music app for iPhone and iPad which lets you use your other music apps together. Chain effects on your favourite synth, run the output of apps or Audio Units into an app like GarageBand or Loopy, or select a different audio interface output for each app. Route MIDI between apps — drive a synth from a MIDI sequencer, or add an arpeggiator to your MIDI keyboard — or sync with your external MIDI gear. And control your entire setup from a MIDI controller.

Download on the App Store

Audiobus is the app that makes the rest of your setup better.

Audiobus internal clock to StreamByter

Is it possible for a StreamByter script loaded in Audiobus to receive the MIDI clock generated by Audiobus? I tried to do this without success, detecting the F8 clock message. But I found a Mozaic script that can do this, using the @OnMetroPulse event (thanks @wim). That script sends a clock message which StreamByter receives with no problem. Using Mozaic like this is a viable solution, but seems unnecessary. I can also do it by having Audiobus send clock to the external FreEWI app and receiving it back, but this is very roundabout. Is there a trick that causes Audiobus to send clock to internal modules?

Comments

  • Nick of StreamByter has provided a possible answer. We can generate a periodic clock event by sending a delayed internal SysEx message to ourselves. This seems to work fine, and may be essentially what the Mozaic Metronome does. We just need to be clever with the delay time if it's not an integral number of ms. Here is my working sample implementation.

  • wimwim
    edited March 2022

    @uncledave said:
    Nick of StreamByter has provided a possible answer. We can generate a periodic clock event by sending a delayed internal SysEx message to ourselves. This seems to work fine, and may be essentially what the Mozaic Metronome does. We just need to be clever with the delay time if it's not an integral number of ms. Here is my working sample implementation.

    The Mozaic metronome is linked to the host clock. It doesn't run unless the host transport is running. So, it's as accurate as the host because it's using the same.

    The problem with trying to do midi clock with a timer is ms isn't fine grained enough to be accurate, especially at 24 ppqn. Even in Mozaic which has sub millisecond timer resolution and Arduino, which has even finer resolution, I've had to jump through hoops to correct every so often.

    It's kind of a fun challenge though.

  • edited March 2022

    @wim said:

    @uncledave said:
    Nick of StreamByter has provided a possible answer. We can generate a periodic clock event by sending a delayed internal SysEx message to ourselves. This seems to work fine, and may be essentially what the Mozaic Metronome does. We just need to be clever with the delay time if it's not an integral number of ms. Here is my working sample implementation.

    The Mozaic metronome is linked to the host clock. It doesn't run unless the host transport is running. So, it's as accurate as the host because it's using the same.

    The problem with trying to do midi clock with a timer is ms isn't fine grained enough to be accurate, especially at 24 ppqn. Even in Mozaic which has sub millisecond timer resolution and Arduino, which has even finer resolution, I've had to jump through hoops to correct every so often.

    It's kind of a fun challenge though.

    The StreamByter implementation also does not run unless the host transport is running. StreamByter provides Run and Stop events. Are you certain the Mozaic metronome is linked directly to the host clock?

    My implementation avoids the problem of integer ms resolution by periodically inserting one extra ms in the delay when needed. You basically accumulate the remainder from integer division and add one ms every time the total exceeds the divisor. This is a useful trick for implementing fractional steps on an integer grid.

    Edit: I ran the program in Audiobus for 30 minutes (93 bpm, 4 ppqn), along with an AR-909 pattern, with no detectable error. The program flashes each beat and bar on the StreamByter labels, and the flashes remain synced with the drums and the Audiobus beat ticker. So it appears to be reasonably stable.

    Edit 2: Ran for 85 minutes at 120 bpm, 24 ppqn. Still stable, right on the beat.

  • @uncledave : before I go down a StreamByter rabbit hole…

    I was just trying to implement a script that does the following: when a midi note is received, it sounds out a note. It then sends out another note X beats later and continues doing that until you turn it off.

    The technique I use is:

    • calculate the milliseconds per beat at current tempo
    • the first note is sent out, calculate the duration (in ms) till the next note needs to be played
    • when the note is sent, send an internal sysex message delayed to arrive 50 ms before the next note needs to be sent.
    • When that sysex is received, calculate the time until the note needs to be sent (by comparing time elapsed since last note)
    • Send out the note with the delay that I just calculated
    • send an internal sysex message with a delay calculated so that it comes in about 50 ms before the next note should be played
    • repeat previous 3 steps

    If the first note is sent when the host starts, the timing gradually drifts out of sync.

    Do I have to resort to your beautiful code or a similar strategy that is updating the sync on a more granular level?

    Am I correct that there is nothing like Mozaic’s host time variables and events?

  • @espiegel123 . Because you can only delay by an integer number of ms, you need to use the method I posted to keep stepping around the exact delay time. It's sometimes a fraction of an ms early, sometimes late, and periodically right on. That prevents it from drifting out of sync. This technique is not unusual, by the way. It's used in computer graphics to draw a diagonal line through a grid of pixels.

    If you fudge the clock that way, you shouldn't need this 50 ms slop factor. Just delay to,the note time and send the note. Remember, those delayed internal SysEx aren't going anywhere; StreamByter just inserts them in its input buffer, and hands them back to you at the correct time.

  • @uncledave said:
    @espiegel123 . Because you can only delay by an integer number of ms, you need to use the method I posted to keep stepping around the exact delay time. It's sometimes a fraction of an ms early, sometimes late, and periodically right on. That prevents it from drifting out of sync. This technique is not unusual, by the way. It's used in computer graphics to draw a diagonal line through a grid of pixels.

    If you fudge the clock that way, you shouldn't need this 50 ms slop factor. Just delay to,the note time and send the note. Remember, those delayed internal SysEx aren't going anywhere; StreamByter just inserts them in its input buffer, and hands them back to you at the correct time.

    Thanks. Dumb question. using your code, how you do something like "send an event 16 beats from now"?

  • @espiegel123 said:

    @uncledave said:
    @espiegel123 . Because you can only delay by an integer number of ms, you need to use the method I posted to keep stepping around the exact delay time. It's sometimes a fraction of an ms early, sometimes late, and periodically right on. That prevents it from drifting out of sync. This technique is not unusual, by the way. It's used in computer graphics to draw a diagonal line through a grid of pixels.

    If you fudge the clock that way, you shouldn't need this 50 ms slop factor. Just delay to,the note time and send the note. Remember, those delayed internal SysEx aren't going anywhere; StreamByter just inserts them in its input buffer, and hands them back to you at the correct time.

    Thanks. Dumb question. using your code, how you do something like "send an event 16 beats from now"?

    If you need to do it for a long time, without drift, you may need to think of creating a clock that ticks every 16 beats. Calculate the truncated delay in ms, create the accumulator, add the remainder each time, and add one ms to the delay each time the accumulator overflows. You may need to use one of the Pxx 32-bit variables to carry the accumulator. If it's possible to have multiple ones running simultaneously, you can encode the identity in the SysEx messages. Just make sure each message byte is only 7 bits.

  • @uncledave said:

    @espiegel123 said:

    @uncledave said:
    @espiegel123 . Because you can only delay by an integer number of ms, you need to use the method I posted to keep stepping around the exact delay time. It's sometimes a fraction of an ms early, sometimes late, and periodically right on. That prevents it from drifting out of sync. This technique is not unusual, by the way. It's used in computer graphics to draw a diagonal line through a grid of pixels.

    If you fudge the clock that way, you shouldn't need this 50 ms slop factor. Just delay to,the note time and send the note. Remember, those delayed internal SysEx aren't going anywhere; StreamByter just inserts them in its input buffer, and hands them back to you at the correct time.

    Thanks. Dumb question. using your code, how you do something like "send an event 16 beats from now"?

    If you need to do it for a long time, without drift, you may need to think of creating a clock that ticks every 16 beats. Calculate the truncated delay in ms, create the accumulator, add the remainder each time, and add one ms to the delay each time the accumulator overflows. You may need to use one of the Pxx 32-bit variables to carry the accumulator. If it's possible to have multiple ones running simultaneously, you can encode the identity in the SysEx messages. Just make sure each message byte is only 7 bits.

    Thanks to your example, I think I have it working... it took me a while to fully grok the modulo stuff.

    I was tearing my hair out for a while because of drift when I ran it...and then noticed that it was tight when when the iPad was at 48k and drifty at 44.1k. (I suspect this is related to general weirdness on iPad 6 and a few other devices that let you switch sample rate but are internally really working at 48k and faking 44.1k).

    Thanks again. If anyone is interested, I can clean up my code and post it.

Sign In or Register to comment.