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.

Dividing a complex Mozaic script into several scripts (and AUv3 instances): is this a good idea?

Hey everybody

I'm still trying to get my head around Mozaic and what's the best, most robust way of creating complex scripts.

Coming from a professional web developer background, I'm used to more powerful (higher level) languages than Mozaic. My last attempt to add some robustness to my scripts was the idea of creating automated regression tests using Mozaic's log:

https://forum.audiob.us/discussion/40464/poor-mans-regression-testing-and-debugging-with-mozaic-how-do-you-do-it#latest

As the log is limited to ~60-80 lines, the idea isn't as powerful as I thought.

But tonight I had the idea (I'm already dreaming of Mozaic and the possibilities it opens to me, it's kind of frightening, haha) of dividing my rather complex "spaghetti code" script into several scripts that would talk to each other using MIDI. This way I could create more modularised scripts that each provide functionality for a specific domain (e.g. user input handling, talking to AUM, talking to my looper, etc.). Having smaller chunks of code would make them obviously easier to read and extend.

So my question: is this a good idea? Is it sensible to have several Mozaic AUv3 instances running at the same time? Or would it become quickly somehow "unstable"?

What's the possibilities to communicate between the different scripts? Using plain MIDI notes? Or using Mozaic's global variables? Or using Sysex or something (I have no idea what's Sysex used for yet, just doing some name dropping here)?

Thanks for your inspirations.
Joshua

«1

Comments

  • I’ve considered it in relation to a sequencer script I’m working on, less for the reasons you state, more for the added interface options but I’m not sure I will or not yet as I’m in the middle of simplifying what I made over complex...

  • _ki_ki
    edited August 2020

    Script to script communication always introduces some kind of lag. Even though a trigger from a midi event happens in the same host time-slot, the instances are executed in some (host defined) order, so you don‘t kown which will act first. If you send midi out, it probably will arrive at the next host time-slot

    When using globals, there is also a lag due to the instance execution order. There is also no method other than busy waiting with a 1ms timer to see if things in a global variable changed

    .

    I think for some script ideas its usefull to split into smaller sub-script, each doing only part of a complex task if having all the features in one script would result in a too complex UI or if the base logic is incompatible.

    For instance note sustaining in combination with In-Order-ARP would have resulted in a much more complex note status handling and processing than when these two actions are handled in separate scripts, one managing note sustaining and the other script for the arping.

  • edited August 2020

    Thanks for your comments, guys. It strengthens my opinion that I indeed can enhance simplicity inside my spaghetti code by introducing a layer of separation with multiple instances of Mozaic.

    As I'm not doing very timely critical things so far, the lag inside script to script communication would not be a problem. I just need to be careful when sending MIDI messages between scripts to set up the routing properly (this is the price I have to pay for gaining the additional simplicity).

    Regarding the global variables: can they be arrays?

  • Bear in mind the global variables are accessible to other people’s scripts running at the same time as yours...

  • @TheOriginalPaulB said:
    Bear in mind the global variables are accessible to other people’s scripts running at the same time as yours...

    Yeah thanks, I'm aware of this.

  • edited August 2020

    I think some sort of convention for dynamically reserving a global variable so that scripts can avoid clashes would be useful. What form that convention should take, I’m not sure at this point.
    As to whether they’re arrays or not, try copying in an array and reading it again.

  • All my scripts that make use of global variables i set magic debug numbers in the initiating instance and these are monitored by the receivers and also the sender. In that way, i can detect communication problems and react to that. Quite often i use bi-directional communication between instances, but still there‘s only one instance setting up the magics.

  • edited August 2020

    Given that memory shouldn't be a problem, I could imagine that "wasting memory" e.g. by pre-defining large static arrays and cramming everything into one script in favor of better performance would work the best.
    Any communication between multiple Mozaic instances will only add delays for sure, not to speak of situations where one instance cannot proceed until it has received the answer of another.

  • Seems like using au events, midi notes or midi cc as ipc events would be better than spin loops or timers for coordinating multiple cooperating scripts.

  • @xor: What does "midi cc as ipc events" mean?

  • MIDI control codes as inter-process communication events?

  • edited August 2020

    I am also building quite complex script for my MIDI controller (and overall live setup) and I was also trying out to split the script into multiple Mozaic instances.
    You can definitely use globals for some communication. Just be aware of every other Mozaic instances to not conflict with yours :) For example, I am using one global to store currently active channel number. The problem is with some event handling. You can use MIDI, but I am afraid this would be overcomplicating. I'd rather go with organizing the code so you can better navigate within it, e.g. something like:

        // ***************
        // KNOB EVENTS
        // ***************
        @OnKnobTouch
        ...
    

    And then adding all knob change related custom functions below. Or any other way using comments that help you better navigate in the code.

    But we definitely need some way to better write code in Mozaic. I know it was never intended for such complex scripts, but when it simply works... why not?
    I was even thinking about writing language definition for e.g. VSCode and find some way to transfer script from desktop to Mozaic. Ideally if this was supported directly by Mozaic, via some live socket communication on local network, to avoid 3rd party servers. Also, syntax highlighting, static syntax checker, refactoring tools like variable renames, auto indentation etc etc would be useful, but this could have been solved by writing the language definition inside already existing IDE.
    Maybe in version 2.0 :smile:

  • wimwim
    edited August 2020

    @skrat said:
    I was even thinking about writing language definition for e.g. VSCode and find some way to transfer script from desktop to Mozaic. Ideally if this was supported directly by Mozaic, via some live socket communication on local network, to avoid 3rd party servers. Also, syntax highlighting, static syntax checker, refactoring tools like variable renames, auto indentation etc etc would be useful, but this could have been solved by writing the language definition inside already existing IDE.
    Maybe in version 2.0 :smile:

    Textastic is a really good editor for iOS and Mac. @_ki wrote a grammar for it and it works great. It's available on the patchstorage.com site.

    I write everything on the Mac and use simple copy/paste with handoff to dump it into Mozaic. If you don't have a Mac, then that's no use to you. But writing in iOS Textastic using a BT keyboard, then using a few quick keyboard combinations to copy/paste into Mozaic is almost as good.

  • I’ve got used to working in the app itself, the scrolling is a bit keen when selecting but otherwise it’s usable... what would be handy would be if the function calls were listed down the right hand side so I could navigate my own madness a little quicker... @brambos ? 🤔😀😬

  • @josh83 @TheOriginalPaulB right. Use the same mechanism you used to ping the second script to do something to send the result back to the first script. This way both scripts are event driven and neither is in a loop waiting for a global variable to change. In Mozaic there are at least four kinds of events you can use: midi notes, midi cc, sysex and au parameters. One script uses Mozaic functions to send the event and the other script uses OnXXX functions to receive them. If the data can be made to fit into that event’s payload, seems like sysex could always be made to work, then there’s no need for globals.

  • @xor said:
    @josh83 @TheOriginalPaulB right. Use the same mechanism you used to ping the second script to do something to send the result back to the first script. This way both scripts are event driven and neither is in a loop waiting for a global variable to change. In Mozaic there are at least four kinds of events you can use: midi notes, midi cc, sysex and au parameters. One script uses Mozaic functions to send the event and the other script uses OnXXX functions to receive them. If the data can be made to fit into that event’s payload, seems like sysex could always be made to work, then there’s no need for globals.

    The only thing about using sysex is there isn't an optional delay on the SendSysex command. Not that it would be needed in most cases, but the optional delay for other midi commands could be very useful for avoiding timer loops (provided not too many get queued up, which Bram says could be bad for performance).

  • @wim said:

    @skrat said:
    I was even thinking about writing language definition for e.g. VSCode and find some way to transfer script from desktop to Mozaic. Ideally if this was supported directly by Mozaic, via some live socket communication on local network, to avoid 3rd party servers. Also, syntax highlighting, static syntax checker, refactoring tools like variable renames, auto indentation etc etc would be useful, but this could have been solved by writing the language definition inside already existing IDE.
    Maybe in version 2.0 :smile:

    Textastic is a really good editor for iOS and Mac. @_ki wrote a grammar for it and it works great. It's available on the patchstorage.com site.

    I write everything on the Mac and use simple copy/paste with handoff to dump it into Mozaic. If you don't have a Mac, then that's no use to you. But writing in iOS Textastic using a BT keyboard, then using a few quick keyboard combinations to copy/paste into Mozaic is almost as good.

    Thanks, using Handoff is a great idea! Actually the first serious use case I can find for it :lol:
    Also thanks for pointing me to this Textastic language definiton. I can see it uses TextMate syntax files which should be usable also in VSCode. Textastic looks OK, but not sure if I'll use it that much, I'd still prefer coding on Macbook and on iPad just run and test the script. Writing on keyboard while laying on the couch is not really comfortable (you know I've tried it :lol: ) so anyway I need to sit behind the desk so why not use the big screen and all the benefits of a desktop.

    Of course, it would be perfect if such features are supported directly by Mozaic. But I can imagine Bram don't want to overcomplicate it, it's already pretty complex and putting actually a code editor into it could be a deep rabbit hole... Only if there is really some way to "sync code from desktop & run on ipad", that would be perfect. Map it on a hotkey and you could easily just write the code, hit the hotkey, see it running in Mozaic, test how it works (or see the log), update the code, run it again etc... Much more fluent than to always need to copy, paste or switch between apps etc..
    I'll also investigate some automation options on iOS, maybe that could help together with Handoff.
    Again, thanks a lot!

  • I used the language definition of Sublime 3 syntax for the Mozaic Language Package for Code Text Editors, not the one for TextMate. This allowed me to implement not only syntax highlighting, but full Mozaic language syntax parsing and error detection. (Sublime manages a stack of contexts with regular expressions used to branch into sub contexts - so could use these features to build a state maschine following the Mozaic language and detect if stuff isn‘t as expected)

  • I'm on vacation and will take a closer look at your comments when I'm home in 2 weeks. But for now, just a few things:

    Sysex seems to be a good way to communicate between scripts, much better than MIDI. As I'm not using Sysex for anything else so far, this keeps the inter script communication apart from MIDI, which is a good thing.

    As I read, theoretically anything can be sent through Sysex. So I hoped it might be possible somehow to maybe also send text strings or "key => value" pairs. But as far as I can see, Mozaic doesn't offer any possibility for encoding/decoding something like that. Right?

    I tried to load the Mozaic syntax highlighting into TextMate. While in fact I could create a new Bundle and load the file...

    ...it doesn't appear in the list of bundles, so I can't activate it. Anyone having an idea what's going on?

  • I’ll let you know in 2 weeks...

  • The Mozaic Monokai file only contains additonal color definitions.

    I already commented somewhere above that the syntax definition and parser uses the Sublime 3 syntax and not the Textmate syntax definition format (that is also supported by the Textastic editor)

    I used Sublimes syntax scheme, because it has more features and allowed to implement a full language parser with syntax error detection.

    .

    Is there anything in the language grammar folder ? Thats were the TextMate syntax defintion file would belong.

  • So I ended up with using Sublime text. As mentioned above, Sublime language definition is a bit different than Textmate, so you can't use it (easily) in VSCode. Not a big deal, Sublime text can be used virtually for free too, just occassionally nagging with a popup window.
    And want to thanks a lot to @_ki , it's really awesome and looking forward to use it for some longer coding session!

  • edited September 2020

    Hey guys

    I'm back from holidays and I'm in the process of splitting my spaghetti code into useful Mozaic instances. They are communicating with each other through sysex, and it looks something like this so far.

    The BlueBoardHandler instance receives the button presses from the BlueBoard and sends them to ModeSwitcher.

    @OnMidiNoteOn
      buttonDownTime = SystemTime // Time at which button was pressed down
      Log {Button }, MidiNote, { down @ }, buttonDownTime
    @End
    
    @OnMidiNoteOff
      buttonUpTime = SystemTime
      buttonPressedDuration = buttonUpTime - buttonDownTime
    
      If buttonPressedDuration >= TAP_TIME_1_BAR
        tapType = TAP_TYPE_1_BAR
      Elseif buttonPressedDuration >= TAP_TIME_1_BEAT
        tapType = TAP_TYPE_1_BEAT
      Else
        tapType = TAP_TYPE_IMMEDIATE
      Endif
    
      Log {Button }, MidiNote, { up   @ }, buttonUpTime, { => tapType: }, tapType
    
      data = [MOZ_INST_MODE_SWITCHER, MidiNote, tapType]
      SendSysex data, 3
    @End
    

    ModeSwitcher decides which mode is active (Setup, Step sequencer...). If a mode switch button combination is pressed (e.g. A+D), then the active mode is switched. All other received button presses are sent to the active mode's instance.

    @OnSysex
      ReceiveSysex data
    
      if data[0] = MOZ_INST_MODE_SWITCHER
        bbButton = data[1]
        tapType  = data[2]
    
        Log {Received button=}, bbButton, {, tapType=}, tapType
    
        Call @DispatchButtonPress
      endif
    @End
    

    The important thing here is that MOZ_INST_MODE_SWITCHER has the same value in both instances. I prepare those in an @InitSharedConstants function in each instance:

    @InitSharedConstants
      UNDEFINED = -1
    
      MOZ_INST_MODE_SWITCHER  = 1
    @End
    

    By sticking to such a protocol, sysex is pretty powerful and helps a lot to clean up my code.

    Any suggestion for improvement here, guys?

    @xor wrote:
    In Mozaic there are at least four kinds of events you can use: midi notes, midi cc, sysex and au parameters.

    Can you tell a little more about the au parameters? Where "are" they? Are they dependent on the loaded AUv3, or general ones? How can I interact with them?

    I'm happy to be back! :smiley: Have a nice weekend soon...!

  • Welcome back. Hope you’re refreshed, it’s apocalyptic orange here.

    Just ignore my misguided comments about AU parameters. Apparently hosts haven’t figured out how to allow AUv3s to discover and communicate with each other so the AU parameters are limited to host communications currently.

  • I'm going to make some Mozaic "Objects" that communicate using GLOBALS. It's a good idea.
    With proper documentation anyone can make another object that works with my objects.

    I'll have GLOBALS for BPM, Root, Scale, Swing, and maybe Strum/Arp...
    Objects for:
    sequencing
    chords
    randomization

    I think you can see the idea... something like the design philosophy of Unix pipes and shared ENV variables.

    Don't wait for me if you want to try this concept out and make your own little network of scripts that can be assembled. I thin the right delivery package will be a zip of all the object scripts together since they would then appear in a sub-directory in Mozaic.

  • _ki_ki
    edited September 2020

    @McD If you work on globals, please add a MAGIC number before your structures so that the script (and the ones using the values) can identify the integrety of the data. This common practice worked very in other programming areas like file-types, memory management, runtime debugging, interrupt handler hooking etc. It should also serve the Moazic script-to-script communication.

    .

    Have a look at the Migration Manager documentation, especially the scripts source - the variable MM_MAGIC_ID is the magic number, it is placed in global97[MM_MAGIC] and regulary checked if the value is still there. This allows to detect commication breaks by other scripts.

    I tend to use constants (like MM_MAGIC=0, MM_SCRIPT=1, ...) for all the offsets into the data structure. This prevent mixups that easily happen when only using direct numbers like global97[3| and it also simplifies changing the layout of the structure (when all acess is done using the constants, you don‘t need to change the script code at all when restructuring the layout)

    BTW: The scripts communicating through the manager snippet bring in their own magic constant (stored in the offset MM_SCRIPT) so not only the migration manager can be identified just by looking at the specific global var, but the manager can also identify if sender and receiver and only initiate the import action if both scripts using the manager snipet are the same (but in different script versions)

    The manager also showcases how to implement bi-directional, asynchronous communication, as the value at the MM_ACC offset is set to the value found at MM_TYPE by the receiver after processing the data. The sender waits another round (everything runs in a timer) if MM_ACC didn‘t change. And the receiver only picks up data, if the values at the offsets MM_ACC and MM_TYPE differ...

    .

    I applied the concept of ‚structures start with a magic‘ in all my Mozaic scripts using globals.

  • @_ki said:
    @McD If you work on globals, please add a MAGIC number before your structures
    I applied the concept of ‚structures start with a magic‘ in all my scripts using globals.

    Cool. I will hollow out one of your scripts and change the MAGIC NUMBER.
    I wonder if 42 is taken.

  • _ki_ki
    edited September 2020

    I mosty use hex constants with leetspeak. Using ‚normal‘ number range is bad practice, as they could be set by others. Better use something like 0x42424242 or 0x42004200 which is more uniqe and unlikely to be set by any other script.

    Migration Manager uses 0x15EEC0DE (I see code), the scripts using the manager are allowed to use up to three byte magic codes. The magic is also used and incremented during the communication to distinguish the individual packets - too large numbers may run into the problem that x+1 ends up as x+5 instead due to number resolution problems - thats why the snippet checks the scripts magic for these incrementing problems before hand)

  • _ki_ki
    edited September 2020

    For reference: https://en.wikipedia.org/wiki/Magic_number_(programming)
    The chapter ‚debug values’ contains a list of several hex values as inspiration.

  • @_ki said:
    I mosty use hex constants with leetspeak. Using ‚normal‘ number range is bad practice, as they could be set by others. Better use something like 0x42424242 or 0x42004200 which is more uniqe and unlikely to be set by any other script.

    Migration Manager uses 0x15EEC0DE (I see code), the scripts using the manager are allowed to use up to three byte magic codes. The magic is also used and incremented during the communication to distinguish the individual packets - too large numbers may run into the problem that x+1 ends up as x+5 instead due to number resolution problems - thats why the snippet checks the scripts magic for these incrementing problems before hand)

    OK. I'll consider words available using ABCDEF015

    0xDEAF DEAD
    0xFEED DADA
    0xAAAA AA42
    0xAE10 EE00
    0x24000042

Sign In or Register to comment.