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.

Learn to Program the Mozaic Workshop to create MIDI FX and Controllers *you could learn something*

123578

Comments

  • _ki_ki
    edited March 2020

    @horsetrainer said:
    What is This:

    if MIDINnote > note[ sortedIdx[i] ]

    Is this an Array named "note" that uses another Array named "sortedIdx[I]" to create an index (pointer) number for the Array named "note"?

    Yes, the inner expression (ie the output of SortedIdx[i] ) is used to compute the index used for the note array. Expressions can be nested ‚endless‘,

    If yes... I didn't know you could use an Array within the index (pointer) brackets [........] of a different Array.
    For example... Can I do this.... ?

    SendMIDINoteOn MIDINote1 , Some_Array[[Some_other_Array[Yet_another_Array[MIDINote 2]]] , MIDINote3

    Yes in priciple, but you have a syntax error in your example: the double [[ wont work.

    The index part of the array access allows for expression, so you can do math, call Mozaic functions that return a value or use the output of another array access:

    SendMIDINoteOn MIDINote1 , a[ 22 * b + c[ (Round Sin(d) * 50) ]] + 78 + e[ f[g * 4 +1] ]

    You have to be careful to use parenthesis when calling Mozaic functions, otherwise the called function doesn‘t know where its parameter list ends. All opened ( or [ have to be closed.
    My Code Text Editor language pack (for Textastic and Sublime) will detect all kinds of omission in such complex expressions and flag the error position.

    BTW: Complex expressions are allowed in IF comparisons, Loop-Start/End computations, as parameters for Mozaic commands and functions.

    Talking about optimizations: I often add intermediate variables containing parts of complex expression to aid in debugging and code readability. If the same expression is used at least twice its more efficient to store the result in a variable and use the variable instead of computing the complex expression several times.
    .
    If my observations of Mozaic are correct, indexed array access cost more than using a var without index - Writing a[0] is the same as a but the latter is executed faster by the Mozaic interpreter.
    Thats why i sometimes when i need to maybe access someArray[21] several times in a row without the chance that the value is modified by the algorithm, extract someArray[21] into another variable and use the faster access without indices in all needed comparisons and computations.
    .
    .
    In modern computer languages all this optimizing and re-ordering instructions etc is done by the compiler, or even in the cpu itself with branch prediction and instruction pre-fetch and execution... As cpus get faster and faster, code optimization is only taught on the fringe.

  • Some new insights on sorting 4 note numbers:

    I just scripted a ‚4 note sort‘ algorithm comparison script counting the number of indirect array access, number of comparisons and number of loop-end checks for various fixed input note arrays. This order also represents the cpu cost for the operations.

    With this script i found out that the SmartChordBass method is worse than expected .

    The indirection via sortedIdx[] adds tons of indirect array accesses, making it quite inefficient. I then did a version without this indirection and still in several cases my code is less efficient than you plain exchange sort.
    I guess I wasn't thorough enough with the preliminary considerations :#

    The test included several different ordered input note arrays, a pre-sorted and an inverse sorted input array as three counted numbers of the will differ depending on the input array.

    For the chord script i started in january i used a sorting network optimized for 4 notes from pages.ripco.net/~jgamble/nw.html which indeed has the least number of indirect accesses, constant number of 5 comparisons and no loop-checks at all.

    On that page, choose 4 elements, any of the algorithms (as they don’t differ for such few elements) and ‚SWAP‘ macros as output.
    Each SWAP means test if a[leftNum] > b[rightNum] then swap a[]s and b[]s content like in your code. For sorting four elements one of the several possible networks is:

     SWAP(0, 1);
     SWAP(2, 3);
     SWAP(0, 2);
     SWAP(1, 3);
     SWAP(1, 2);
    

    and the input is sorted. Some algorithms even compute orders with swaps that can be done in paralell.

  • McDMcD
    edited March 2020

    More thoughts on chord detection. It occurred to me that only 4 patterns of intervals need to be detected for a major chord.

    047 - this occurs 5 time with roots from 0 to 4 - C to E
    059 - this occurs 3 times with the 5th in root 3 times - F to G
    038 - this occurs 4 times with the 3rd in the root 4 times - Ab to B

    Take and 3 note chord and Modulo it into the 0-11 octave and roll the notes down to detect the C bottom pattern and you'll be able to match 047, 059, 038 for all major chords.

    This would be true for minors and augmented for a total of 9 shapes to detected for chord types to be detected. The interval used to lower the pattern to C = 0 would help name the chord root for
    C to B.

    That should be pretty easy to writein Mozaic (famous last words) to detect all 3 note chords.

    4 notes chords will work the same way with 4 shapes per type.

    NOTE: Using the modulo technique ignores duplicate notes in a chord voicing so the import chord note count is the number that are in the modulo'ed results and not the input.

  • _ki_ki
    edited March 2020

    @McD

    Edit: I think i misunderstood you (in my first unedited answer) after reading your post again. Your take the modolo 12 of the notes themselves and this results in 047, 059 and 038 ?

    .

    These numbers show up as intervals in my scripts: 0 = first note, second note is 4 semi—tones higher than first note and the third note is 7 semi-tones higher than the first.

    Using this ‚offset to lowest note’ or interval notation, the inversion 1 results in 059 and inversion 2 to 038 for its intervals. Thats interesting math isn‘t it ?

    .

    The SmartChordBass (and my other chord script) precomputes these intervals for 23 chord types, their 3 inversions and two voicings - its over 100 variants of chords - for all 12 root notes.

    .

    Since all the chord-intervals and variants are precomputed, it takes just an array lookup with the current used intervals to decide what chord that is. The detection return several variables pType 0..23 for the chord type like major, minor, dim7th, aug, mayor-minor etc and then pVariant 0..5 with 0 main-chord, 1 inversion1, etc, 4=voicing1, 5=voicing2.

    .

    The SmartChordBass script precomputes the root-offset, chord-type and the two inversions for a 26 base-chords for all 12 root notes. And detected any resulting ambiguous intervals - there are inversions of chords that are the same as other base chords, or augmented is always 0 4 8 in all inversion 0 8 8 for its voicings.

    If i would have more time, it would be cool to continue working on that second chord script to hand out the techniques used - but currently my spare-time is quite limited :(

  • @_ki said:
    Basically thats what SmartChordBass and my other chord script does - analyse the intervals for 23 chord types, their 3 inversions and two voicings - its over 100 variants of chords - for all 12 root notes.

    @McD
    You first 0 4 7 is the main mayor chord, 0 5 9 is the inversion 1 and 0 3 8 the second inversion. BTW voicing one of a mayor is 0 7 16 and voicing two 0 8 15.

    .

    Since all the chord-intervals and variants are precomputed, it takes just an array lookup with the current used intervals to decide what chord that is. The detection return several variables pType 0..23 for the chord type like major, minor, dim7th, aug, mayor-minor etc and then pVariant 0..5 with 0 main-chord, 1 inversion1, etc, 4=voicing1, 5=voicing2.

    .

    The SmartChordBass script only precomputes the root-offset, chord-type and the two inversions for a 26 base-chords for all 12 root notes. And detected any resulting ambiguous intervals - there are inversions of chords that are the same as other base chords, or augmented is always 0 4 8 in all inversion 0 8 8 for its voicings.

    If i would have more time, it would be cool to continue working on that second chord script to hand out the techniques used - but currently my spare-time is quite limited :(

    Thanks. I knew you had already solved the problem but I'm having a good time thinking it through.

    The idea of pattern matching for 2,3,4,5,6,7 notes chords all compressed between 0-11 using modulo is key.
    Then small transpositions to always match with a C in the pattern insures a minimal matching list required.

    This wasn't obvious to me but like you thinking through the data led to ways to automate the detection.

    I didn't think this would be a good way to teach beginners to code in Mozaic but there might be a plan here to bring
    more along to some mastery of Mozaic for mathematical analysis and programming techniques.

    The idea the an array value could be an index to a 2nd Harry is MINDBLOWING. I just never considered that and kept
    coding moving numbers into variables from arrays and then using that variable as an index. So, 2 commands are reduced to
    1. Readability will suffer and many will get lost with indirection but we could make the long and the shorter versions so the idea gets across and everyone get working code they could use an @ChordDetect functions in their own scripts.

    For teaching purposes we need to break down complexity into small bites and see if we can get more people to follow along
    and learn this fascinating science too. It's a lot like making music... there's a scale of skill levels.

  • _ki_ki
    edited March 2020

    I mis-understood what you wrote in my first answer that you commented and updated the whole thing while your were posting your answer.

    When i re-read what you wrote, i think you meant that you take the modulo 12 of the incoming notes and this then results in 047, 059 and 038 . They looked so familiar, but in SCB the numbers result from a different computation - the intervals to the lowest note played.

    .

    Solved = > Having put some more thought into it, its now clear to me what happens. By taking the modulo of the note-number you are indeed forming the inversions since notes that move into a higher octave are folded back to the same note but in the inital octave: Tada - inversion.

  • _ki_ki
    edited March 2020

    Your idea doesn‘t need the sorting of chord notes - for interval computation one needs them in raising note-order.

    If one doesn‘t need to distinguish between the inversion-types, your idea will lead to much simpler code, which is nice.

  • Solved = > Having put some more thought into it, its now clear to me what happens. By taking the modulo of the note-number you are indeed forming the inversions since notes that move into a higher octave are folded back to the same note but in the inital octave: Tada - inversion.

    Yes. I'm still working through detection but the next step would be to apply the input lowest note to determine inversions.
    Personally i care less about inversions but I do care about maintaining bass lines. Detecting baselines is pretty easy.

    After detecting chords and knowing the bassline it's fun to parse out potential voicings to make symphonic renderings
    of the detected chord progression. There are a lot of existing chord progression apps but most use closed voicing which
    are not as majestic and the big drop 2/drop 3 open voices that span 2 octaves. Drop the bass and octave or 2 and you have
    some great symphonic sounds. Split the sounds across multiple MIDI channels and you have orchestras and synth ensembles.

    One step at a time.

    I haven't even started to consider how to impose rhythms on the output to synthesize musical styles. But it should be
    similar... break it into small ideas with implementations based on tables (in arrays) and using the Metronome of Mozaic
    timer. This would also help people see what the guts of a DAW are like but written in the faster languages that run at
    the speed of the chip.

  • @McD I'm not too sure how I missed this over the past two months...Just saw it today (probably saw the top forums posts at just the right time) and am super excited to dive into all the exercises. Thank you taking the time to put these together!

  • @peanut_gallery said:
    @McD I'm not too sure how I missed this over the past two months...Just saw it today (probably saw the top forums posts at just the right time) and am super excited to dive into all the exercises. Thank you taking the time to put these together!

    It's a bit disjointed but there's a path here. Please ask questions to stay on track. Someone could probably organize this information effectively into other forms (videos, Wiki, PDF's) and make some coins. Teaching can pay some bills too. Not a bad work from home strategy.

  • ASSIGNMENT: Code and share a simple 3 note chord detector. Just "Log" the chord type at this point.

    Possible 3 note types derived from the stacked 3rd's and some 2nd or 4th intervals with M = Major 3rd, m = minor 3rd, sus = 4th, 2nd = whole tone, T = tritone, (3 whole tones), Power = 5th

    Major M + m
    Minor m + M
    Aug M + M
    Dim m + m
    Sus4 4th + 2nd
    Add2 2 + 4th
    BONUS 3 NOTE CHORD SKELETONS playing Roots + 3rd and 7th
    7th - M + T
    m7 - m + 5th
    M7 - M + 5th
    M6 - M + 4th
    m6 - m + T

    This could take some time but just keep adding chord types getting it to work and Log results for each type.
    Please post any progress you make so a casual reader can see how you go from simple to complex by making updates.

    Once this level is achieved the solution can be expanded to 4 notes chords but a lot of music sounds great using the
    SKELETONS. Many Jazz players do 3 note SKELETONS that drop the root and let the bass playing add that. They would
    play a C9 as 3rd, 7th, 9th using the best 3 notes to communicate that chord type. That's just more types to be matched.

  • edited March 2020

    @_ki
    @McD

    Thanks for sharing all those details about other "not readily apparent" methods for coding in Mozaic.

    Every "new" coding methodology learned, opens up so many more possibilities for thinking of new ways to make imagined midi Apps, into actual working scripts.

    I'm loving studying all these new concepts of coding. This is a lot of fun! :)

  • McDMcD
    edited March 2020

    @horsetrainer said:
    I'm loving studying all these new concepts of coding. This is a lot of fun! :)

    Not even close to sitting on top of a horse at gallop... but a lot safer. I only found myself on a horse at gallop once when we turned around at the summit of the trail to head back to the barn. I didn't know a lot but my balance and grip on the saddlehorn saved me.

    On the way up the hill he decided to jump a log rather than walk around it. Also very cool.

    After that one ride I figured... been there, done that. I cashed in my horse riding chips. Big winner.
    Best of all the ride was free: A sister-in-law's horse. I suspect she was trying to kill me. I showed her.

  • McDMcD
    edited March 2020

    ChordDetect script v0.2

    I changed the code to accept any number of input notes terminated by Note 48.
    Later this can be changed to a PAD or some other unique MIDU event.

    ChordShape will detect all inout notes and sort them.

    The Shift value indicates offset from C and indicates the "Bass Note".

    ChordShape[0] holds the number of notes input "MaxNotes" and [1] to [MaxNotes] stores the note numbers in the ChordShape array. Using MaxNotes later will allow on the right segment of the matching type table to be compared.

    NEXT STEP:
    Compare the ChordShape to known shapes in a table... start with Majors and
    keep building the table to match all types:
    Major M + m
    Minor m + M
    Aug M + M
    Dim m + m
    Sus4 4th + 2nd
    Add2 2 + 4th
    BONUS 3 NOTE CHORD SKELETONS playing Roots + 3rd and 7th
    7th - M + T
    m7 - m + 5th
    M7 - M + 5th
    M6 - M + 4th
    m6 - m + T

    @Onload
    
      MaxNotes = 0
      InputNoteCount = 0
      FillArray InputChord -1
      shift = 0
      index = 1
      // Log {MaxNotes = }, MaxNotes
    
    @End
    
    @OnMIDIInput
    
      if MIDICommand = 144
        if MIDINote = 48
           MaxNotes = InputNoteCount
           InputChord[0] = InputNoteCount
           InputNoteCount = 0
           Call @Sort
           Call @Detect
           Exit
        endif   
        Inc InputNoteCount
        InputChord[InputNoteCount] = MIDINote
      endif
    
    @End
    
    @Detect
    
      shift = 0
      for index = 0 to MaxNotes
        Log { InputChord: }, InputChord[index], { }
      endfor
      if (InputChord[0] % 12) > 0
        shift = InputChord[1] % 12
      endif
      index = 0
      for index = 1 to MaxNotes
        ChordShape[index] = (InputChord[index] - shift) % 12
        // Log { Shape: }, index, { Note: }, ChordShape[index]
        Log { Shift: }, shift, { ChordShape[}, index, {] = }, ChordShape[index], { Input = }, InputChord[index]
      endfor
    @End
    
    @Sort
    
      for outloop = 0 to MaxNotes - 1
        for inloop = 0 to MaxNotes - 1
          if InputChord[inloop] > InputChord[inloop+1]
            var = InputChord[inloop]
            InputChord[inloop] = InputChord[inloop+1]
            InputChord[inloop+1] = var
          endif
        endfor
      endfor
    
    @End
    
  • I added more code to locate Major and Minor matches. It only works for closed voices but not when you
    skip a note internally and play it up and octave.

    As before the note input terminates with a Note 48.

    NEXT VERSION SHOULD BE TIMER BASED STARTING WITH 5 SECOND TIMERS.

    @Onload
    
      MaxNotes = 0
      InputNoteCount = 0
      FillArray InputChord -1
      shift = 0
      index = 1
      // Log {MaxNotes = }, MaxNotes
      Match0 = [3,3,3,3,3,3]
      Match1 = [0,0,0,0,0,0]
      Match2 = [4,5,3,3,5,4]
      Match3 = [7,9,8,7,8,9]
    
      MaxNotes = 0
      InputNoteCount = 0
      FillArray InputChord -1
      shift = 0
      index = 1
      // Log {MaxNotes = }, MaxNotes
    
    @End
    
    @OnMIDIInput
    
      if MIDICommand = 144
        if MIDINote = 48
           MaxNotes = InputNoteCount
           InputChord[0] = InputNoteCount
           InputNoteCount = 0
           Call @Sort
           Call @Detect
           Call @Match
           Exit
        endif   
        Inc InputNoteCount
        InputChord[InputNoteCount] = MIDINote
      endif
    
    @End
    
    @Detect
    
      shift = 0
      for index = 0 to MaxNotes
        Log { InputChord: }, InputChord[index], { }
      endfor
      if (InputChord[0] % 12) > 0
        shift = InputChord[1] % 12
      endif
      index = 0
      for index = 1 to MaxNotes
        ChordShape[index] = (InputChord[index] - shift) % 12
        // Log { Shape: }, index, { Note: }, ChordShape[index]
        Log { Shift: }, shift, { ChordShape[}, index, {] = }, ChordShape[index], { Input = }, InputChord[index]
      endfor
    @End
    
    @Sort
    
      for outloop = 0 to MaxNotes - 1
        for inloop = 0 to MaxNotes - 1
          if InputChord[inloop] > InputChord[inloop+1]
            var = InputChord[inloop]
            InputChord[inloop] = InputChord[inloop+1]
            InputChord[inloop+1] = var
          endif
        endfor
      endfor
    
    @End
    
    @Match
    
      match = 0
      Log InputChord[0],Match0[0]
      Log ChordShape[1],Match1[0]  
      Log ChordShape[2],Match2[0]
      Log ChordShape[3],Match3[0]
      for index = 0 to 5
        if InputChord[0] = Match0[index]
          if ChordShape[1] = Match1[index]
            if ChordShape[2] = Match2[index]
              if ChordShape[3] = Match3[index]
                match = 1
                Log {Matched - Index = }, index
                Exit
              endif
            endif
          endif
        endif
      endfor
    
    @End  
    
  • @McD
    I'm finding your scripts great for learning.

    Looking forward to seeing how you implement Timer events in the next version.

    I think understanding Data handling and Data storage using Timer Events (And MetroPulse), will be very important for some of the script ideas I want to experiment with.

  • @horsetrainer said:
    I think understanding Data handling and Data storage using Timer Events (And MetroPulse), will be very important for some of the script ideas I want to experiment with.

    For anyone really interested in Chord Detection I think this application will fail to scale to be of much value since the matching table will becoming very large and it just requires punching in all the values into the tables for 3, 4, ,5 ,6 7 notes.

    I think I'll stop working on the Chord Detection and start making a simple Sequencer. For anyone really interested in Chord Detection I think this application will fail to scale to be of much value since the matching table will becoming very large and it just requires punching in all the values into the tables for 3, 4, ,5 ,6 7 notes.

    PROGRAM CONCEPTS: Enter 16 notes and it starts playing as a loop based on a timer.
    Then we can finally get to some knobs to speed up, slow down, transposing. reverse sequences,
    volume, # of echoes... maybe even a "Shepards Tone" Melodic Looper?

    ONE FINGER apps that are closer to the typical arps/sequencers on the classic keyboards.

    THEN MAKE a DAW METRONOME VERSION and apply knobs to control 2 to 3 instances.

    and get into how a script saves it's state so you can make multiple sequencers in AUM and
    when your replied the project you'll be able to continue with a work-in-progress.

  • @McD said:

    @horsetrainer said:
    I think understanding Data handling and Data storage using Timer Events (And MetroPulse), will be very important for some of the script ideas I want to experiment with.

    For anyone really interested in Chord Detection I think this application will fail to scale to be of much value since the matching table will becoming very large and it just requires punching in all the values into the tables for 3, 4, ,5 ,6 7 notes.

    I think I'll stop working on the Chord Detection and start making a simple Sequencer. For anyone really interested in Chord Detection I think this application will fail to scale to be of much value since the matching table will becoming very large and it just requires punching in all the values into the tables for 3, 4, ,5 ,6 7 notes.

    PROGRAM CONCEPTS: Enter 16 notes and it starts playing as a loop based on a timer.
    Then we can finally get to some knobs to speed up, slow down, transposing. reverse sequences,
    volume, # of echoes... maybe even a "Shepards Tone" Melodic Looper?

    ONE FINGER apps that are closer to the typical arps/sequencers on the classic keyboards.

    THEN MAKE a DAW METRONOME VERSION and apply knobs to control 2 to 3 instances.

    and get into how a script saves it's state so you can make multiple sequencers in AUM and
    when your replied the project you'll be able to continue with a work-in-progress.

    A simple looper script pretty much what I've been studying for the past couple of days.

    Though I think the Note sorting and Chord Detection techniques all teach important coding concepts that can be applied in many ways.

    I'm working on how to use MetroPulse's to record note play timing into Arrays. To make a simple loop recorder.

    The first version is to have one measure continuously loop. Playing keys in any "order", "note hold duration", "and note timing". I'm thinking separate arrays each containing "note order", "Velocity", "NoteOn Pulse Number", NoteOff Pulse Number.

    Once a counter variable = the number of MetroPulses for a full measure, the script loops back and repeats what was played in the first measure.

    When a loop with recorded notes is running... Pressing a "first key" of a new set of notes, ends the current playing loop and put the script back into record mode. While the script is recording it is also passing the live played notes thru so they can be heard while being played.

    The next version of the simple looper will have a knob for adjusting the number of measures the loop will contain.

    I think a script like this would be fun for live playing bass lines with one hand in real time. Then changing to a new bass pattern while the bass play remains in time.

    The right hand can then jam leads to the repeating bass patterns played by the left hand.

  • Neat stuff. Thanks @McD

    I suggest you point out the QuarterNote built-in variable.

    "Returns the duration (in milliseconds) of a quarter note, given the current tempo. A quarter note is the same as 1 beat."

    It seems more intuitive (and temporal) to use this when specifying a note duration than to use some hardcoded milliseconds.

  • McDMcD
    edited March 2020

    @horseman - sorry, just a quick post here. I haven't looked at the above comments yet.

    Here's a simple time-based looping script. Input notes until you use the "C2" (48) note to Start/Stop the Sequence from being sent. All thethe notes prior to pressing C2 will
    start being sent by the OnTimer event. The use of 48 is arbitrary and is the lowest note visible on the AUM keyboard. A better terminator should be implemented but that means more coding complexity. Pressing C2 again commands a StopTimer.

    This is as simpler a time based looper as there is. The SetTimerInterval determines the rate of playback. It also Logs the Notes output and their number in the sequence. At a fast rate that should be commented out, I'd expect. CAUTIOUS: It's also easy to submit more MIDI notes to an app than it can process and mayhem will result sometimes. Keep the DAW volume low.

    The "144's" are used to add a MIDINoteOn byte that gets added to the Channel Number.
    It's also used to only process the NotesON and ignore the NotesOff (bytevalue = 127).

    @Onload
    
      FillArray Sequence -1
      NoteCount = 0
      SeqCount = 0
    
    @End
    
    @OnMIDIInput
    
      if MIDICommand = 144
        if MIDINote = 48
           NumNotes = NoteCount
           SeqCount = 0
           SetTimerInterval 200
           StartTimer
        endif   
    
        Sequence[NoteCount] = MIDINote
        Inc NoteCount
    
      endif
    
    @End
    
    
    @OnTimer
    
      if SeqCount = NumNotes
        SeqCount = 0
      endif
    
      Log {Count: }, SeqCount, { Note: }, Sequence[SeqCount]
    
      SendMIDIOut 144 + 0, Sequence[SeqCount], 0
      SendMIDIOut 144 + 0, Sequence[SeqCount], 100
    
      Inc SeqCount
    @End
    
    @Description
    
     A simple sequencer - Input Notes and terminate with "C2" (48)
    
    @End
    
  • McDMcD
    edited March 2020

    @mojozart said:
    Neat stuff. Thanks @McD

    I suggest you point out the QuarterNote built-in variable.

    "Returns the duration (in milliseconds) of a quarter note, given the current tempo. A quarter note is the same as 1 beat."

    It seems more intuitive (and temporal) to use this when specifying a note duration than to use some hardcoded milliseconds.

    Excellent suggestion and tip. I didn't know this feature exists... I use the manual as a reference but don't read it unless I'm looking for new ideas to experiment with.

    This new sequencer example is the 2nd time I've tried to use the Timer features... before I always used the Metro(nome) option and changed BPM's in the DAW. I'm excited to be able to play with implementing speed, start and stop in the scripts Mozaic GUI because a lot of apps take off doing their own thing when the DAW Start is pressed but in AIM they sit idle and still play the MIDI events I feed them from Mozaic instances. This will allow me more control over a lot of Drumming apps in my projects.

    So accuracy in my scripts to the traditional BPM ideas while not be bound to the traditional time signature limits imposed by DAW's would be liberating for making complex polyrhythmic scripts.

    I think my next feature will be to increase and decrease the Timer value using notes 50 and 49 as a controller... then add a Knob and get those notes back for music and maybe use a PADs for Start/Stop and get note 48 back to be able to generate bassline sequences.

    It would be nice to use a pad to store the sequence into a backup array and have a PAD to cycle through stored sequences. They you can test out sequences and save those you like and recall them later using the AUM project state saving that works for Mozaic variables.
    Using different AUM project names allows the same setup to have different saved states.

  • edited March 2020

    This is my first Loop Sequencer Ver 0.3

    Please critique for better scripting methods. :)

    What it does.....

    It's a monophonic single-measure looper, and I made it for playing bass lines, but will repeat any notes.

    It works in AUM with the transport running. It will loop record one measure (4-bars) of keyboard play.

    It will then repeat whatever was played in the last measure over and over, until something else is played in the next measure, then it will repeat that.

    The chalange was to find a way put the script into record mode when a first new note is played.

    With this script It's possible to play something like two Bass Notes in the first bar, let it repeat and play some improv in the last three bars. But you have to re-record two (or whatever) Bass Notes in the next measure, to avoid a note-less next measure.

    Notes have to be within the measure to record. An early first note will be dropped.

    Trying to make this script "more forgiving of poor note timing" will be my next experiment.

    It has some bugs and it hangs notes once and awhile.

    @OnLoad
      SetMetroPPQN 16
      FillArray OnNotes, -1 , 70
      FillArray OffNotes, -1 , 70
      FillArray VelocityOn, 0 , 70
      FillArray VelocityOff, 0 , 70
      FillArray Channel, 0 , 70
    
      loopcount = 0
    
    @End 
    
    @OnMidiNoteOn
    
    if loopcount = 0
    for endnotes = 0 to 63
    SendMIDINoteOff Channel[endnotes] , OnNotes[endnotes] , 0
    endfor 
    
    FillArray OnNotes, -1 , 70
    FillArray OffNotes, -1 , 70
    FillArray VelocityOn, 0 , 70
    FillArray VelocityOff, 0 , 70
    FillArray Channel, 0 , 70
    
    loopcount = 1
    endif
    
     SendMIDIThru
     Channel[CurrentMetroPulse] = MIDIByte1 
     OnNotes[CurrentMetroPulse] = MIDIByte2 
     VelocityOn[CurrentMetroPulse] = MIDIByte3 
    
    @End
    
    @OnMidiNoteOff
    
     SendMIDIThru
     Channel[CurrentMetroPulse] = MIDIByte1 
     OffNotes[CurrentMetroPulse] = MIDIByte2 
     VelocityOff[CurrentMetroPulse] = MIDIByte3
    
    @End
    
    @OnMetroPulse
    
    if CurrentMetroPulse = 61
    loopcount = 0
    endif
    
    SendMIDINoteOn Channel[CurrentMetroPulse] , OnNotes[CurrentMetroPulse] , VelocityOn[CurrentMetroPulse]
    
    SendMIDINoteOff Channel[CurrentMetroPulse] , OffNotes[CurrentMetroPulse] , VelocityOff[CurrentMetroPulse]
    
    @End
    
  • _ki_ki
    edited March 2020

    @horsetrainer In your script you rely heavily on the (IIRC undocumented) fact the SendMIDINoteOn and SendMIDINoteOff does internal error checking and silently rejects commands when the note or channel is out of range.

    It would be better style if all your SendMIDINoteOn were guarded inside an if OnNotes[CurrentMetroPulse] >= 0 .... endif and all the SendMIDINoteOff inside if OnNotes[CurrentMetroPulse] >= 0 .... endif otherwise the reader also has to know this used loophole.
    This also makes it a lot clearer what is happening in the script and where the pauses come from.
    If SMNO would output an error message on ‚bad parameters‘ you would produce tons of them :)

    .

    You could improve the readability of your script if you apply strict indention rules. For each Event/if/loop statement indent the inner statements by a fixed amount of spaces (2 or 4) or tabs

    @MyEvent
      a = Random 0,10
      if a > 5 
        for i = 0 to 5
          b = b + a
        endfor
      endif
    @End
    

    This makes it easier to grasp the structure of the script as now the indentation level reflects the nesting structure of the script.

    .

    I would move the loopcount / clear thing into OnHostBar and count ‚bars‘ . If four are reached, clear rhe arrays and start the next round.
    You even could sync it a bit stronger by checking if HostBar % 4 = 0 which would restart every 4th hostbar and additionally work if the ‚jam‘ didn‘t start at bar 0.
    The OnHostBar event is fired before the OnMetroPulse or the OnMidiNoteOn/Off events of the same systemtime - even when clearing the buffers, you would still grap the notes on CurrentMetroPulse = 0

    Your variant with checking CurrentMetroPulse versus 61 (shouldn‘t that be 63 ???) basically does the same, but the code for reset etc is spread over several places in the script.

    .

    As the same code is used in OnLoad, i suggest moving the code needed for reset (i mean the FillArrays) into an own event (maybe called ResetSequencer ) which is then called in OnLoad and where the wrap is detected and handled.

    .

    Overall - cool idea for doing a quantized monophonic sequencer 👍🏻

  • @_ki said:
    @horsetrainer In your script you rely heavily on the (IIRC undocumented) fact the SendMIDINoteOn and SendMIDINoteOff does internal error checking and silently rejects commands when the note or channel is out of range.

    It would be better style if all your SendMIDINoteOn were guarded inside an if OnNotes[CurrentMetroPulse] >= 0 .... endif and all the SendMIDINoteOff inside if OnNotes[CurrentMetroPulse] >= 0 .... endif otherwise the reader also has to know this used loophole.
    This also makes it a lot clearer what is happening in the script and where the pauses come from.
    If SMNO would output an error message on ‚bad parameters‘ you would produce tons of them :)

    .

    You could improve the readability of your script if you apply strict indention rules. For each Event/if/loop statement indent the inner statements by a fixed amount of spaces (2 or 4) or tabs

    @MyEvent
      a = Random 0,10
      if a > 5 
        for i = 0 to 5
          b = b + a
        endfor
      endif
    @End
    

    This makes it easier to grasp the structure of the script as now the indentation level reflects the nesting structure of the script.

    .

    I would move the loopcount / clear thing into OnHostBar and count ‚bars‘ . If four are reached, clear rhe arrays and start the next round.
    You even could sync it a bit stronger by checking if HostBar % 4 = 0 which would restart every 4th hostbar and additionally work if the ‚jam‘ didn‘t start at bar 0.
    The OnHostBar event is fired before the OnMetroPulse or the OnMidiNoteOn/Off events of the same systemtime - even when clearing the buffers, you would still grap the notes on CurrentMetroPulse = 0

    Your variant with checking CurrentMetroPulse versus 61 (shouldn‘t that be 63 ???) basically does the same, but the code for reset etc is spread over several places in the script.

    .

    As the same code is used in OnLoad, i suggest moving the code needed for reset (i mean the FillArrays) into an own event (maybe called ResetSequencer ) which is then called in OnLoad and where the wrap is detected and handled.

    .

    Overall - cool idea for doing a quantized sequencer - it even copes with chords on the input 👍🏻

    Thanks for your extremely helpful critique _ki !

    I didn't even know there was a HostBar function. :)

    Your info gives me a lot of new things to study and experiment with. I like this coding stuff, it's like trying to solve a puzzle.

    I was wondering what the rules were for indentation...

    The reason I used 61 instead of 63 for turning on the "record mode" using CurrentMetroPulse, was to try to give the script a little forgiveness if the first note occurs sightly before the first bar (I tend to play slightly in front of the beat for some reason).

    Thanks again.

  • _ki_ki
    edited March 2020

    Hmm, one idea regarding the quatization. Maybe experiment with upping the metroPpqn by a factor and not storing or replaying at NoteOn[CurrentMetroPulse], but compute where to store stuff applying some grace period to do quantization.

    My idea is that the note on/off sampling happens uses a high resulution pulse counter, but storage and replay still uses the 1/16 grid. In the following rough example (i didn’t test any of that) notes arriving 1/64 before the correct position are moved to the next 1/16.

    @OnLoad
      ppqn = 16
      factor = 4
      grace = 1
      SetMetroPPQN ppqn * factor  // Real pulse runs 4 times the ppqn of 16
    @End
    
    
    // Later,  in the events when storing infos:
    
      quant = Div CurrentMetroPulse + grace, factor
      // This  moves ‚a bit to early notes‘ to the next 1/16
    
      // But if that was the last 1/16 or our bar, you need to wrap to position 0
     quant = quant % ppqn
    
     Channel[quant] = MIDIByte1 
     OnNotes[quant] = MIDIByte2 
     VelocityOn[quant] = MIDIByte3 
    
    
    // And in the OnMetroPulse replay only acts on 1/16th
    
    @OnMetroPulse
      If CurrentMetroPulse % factor = 0
        quant =   Div CurrentMetroPulse, factor
    
        If OnNotes[quant] >= 0
          SendMidiNoteOn Channel[quant],OnNotes[quant],VelocityOn[quant]
        Endif
    
        If OffNotes[quant] >= 0
          SendMidiNoteOff Channel[quant],OffNotes[quant],VelocityOff[quant]
        Endif
      Endif
    @End
    
  • @horsetrainer said to @_Ki:
    Your info gives me a lot of new things to study and experiment with.

    @_Ki's suggestions about error checking in is all new to me too. He's always great at sharing his knowledge
    when he's sees details to call out and someone interested in learning.

    Thanks for your extremely helpful critique _ki !

    Yes. Your replies and suggestions are always clearly detailed and useful for future reference.

    I like this coding stuff, it's like trying to solve a puzzle.

    Yes. It's fun for the right mindset that refuses to be put off by the frustrations of good puzzles.
    Some people seem to think "This makes me feel bad about myself" but it's never personal.
    It's like detective work and the culprit can often be found or you can throw away the change that
    your stumped on and start again from working code.

    For me working means "I hear music." I always think anyone is free to change my code since Mozaic
    runs on scripts we can pass around like viruses and someone else can stomp out my bugs too.

  • McDMcD
    edited March 2020

    I'm going to inject a stat in the thread so I can re-visit it later:

    1.8K views of this thread is reported.

    I'm just curious to see if more than ~5 people are following along.
    Since it was revived after going dormant 7 people have posted.

    I think this echoes a larger social issues... most people don't like math which means we probably have
    educational systems that teach them to avoid it.

    Most people don't relish exercise either but we do seem to have organized sports that teaches them
    there's potential wealth, sex and fame with enough disciplined effort to be good at exercising their
    physical bodies.

    The reality of social is that more could improve their lot in life by simply working as hard to train their
    mind with science education and be insured increased incomes. Now there's statistical evidence to support a strong correlation between income and sex if you can hit the upper income ranges.

    Anyway. 1.8K views.

    Maybe shorter threads with descriptive headlines in a series would be a better way to have someone pop in and want a "multi-track sequencer" in Mozaic and just take the code and have the projects in a zip on patchstorage. No. That's too much like work.

    We are spacial here (pun in tended).

    I'm amazed that more people don't see the magic of an app that can create apps.
    It's like a box of chocolates that has controls to create new chocolates without getting up
    or even buying supplies. I know... preaching to the choir. But still. Worth stating. It's so cool when the light goes on for someone curious and hopefully with some spare time.

  • @_ki said:
    Hmm, one idea regarding the quatization. Maybe experiment with upping the metroPpqn by a factor and not storing or replaying at NoteOn[CurrentMetroPulse], but compute where to store stuff applying some grace period to do quantization.

    My idea is that the note on/off sampling happens uses a high resulution pulse counter, but storage and replay still uses the 1/16 grid. In the following rough example (i didn’t test any of that) notes arriving 1/64 before the correct position are moved to the next 1/16.

    @OnLoad
      ppqn = 16
      factor = 4
      grace = 1
      SetMetroPPQN ppqn * factor  // Real pulse runs 4 times the ppqn of 16
    @End
    
    
    // Later,  in the events when storing infos:
    
      quant = Div CurrentMetroPulse + grace, factor
      // This  moves ‚a bit to early notes‘ to the next 1/16
    
      // But if that was the last 1/16 or our bar, you need to wrap to position 0
     quant = quant % ppqn
      
     Channel[quant] = MIDIByte1 
     OnNotes[quant] = MIDIByte2 
     VelocityOn[quant] = MIDIByte3 
    
    
    // And in the OnMetroPulse replay only acts on 1/16th
    
    @OnMetroPulse
      If CurrentMetroPulse % factor = 0
        quant =   Div CurrentMetroPulse, factor
    
        If OnNotes[quant] >= 0
          SendMidiNoteOn Channel[quant],OnNotes[quant],VelocityOn[quant]
        Endif
    
        If OffNotes[quant] >= 0
          SendMidiNoteOff Channel[quant],OffNotes[quant],VelocityOff[quant]
        Endif
      Endif
    @End
    

    I had to methodical use Google's calculation feature to figure out what a lot of these computations were doing. But I eventually figured it out.

    I like the way this math is used to hold a played note in a state of "waiting" until the next 1/16th interval occurs, then it allows the note to be stored into the note Array.

    Very cool stuff!

  • @McD said:

    @horsetrainer said to @_Ki:
    Your info gives me a lot of new things to study and experiment with.

    @_Ki's suggestions about error checking in is all new to me too. He's always great at sharing his knowledge
    when he's sees details to call out and someone interested in learning.

    Thanks for your extremely helpful critique _ki !

    Yes. Your replies and suggestions are always clearly detailed and useful for future reference.

    I like this coding stuff, it's like trying to solve a puzzle.

    Yes. It's fun for the right mindset that refuses to be put off by the frustrations of good puzzles.
    Some people seem to think "This makes me feel bad about myself" but it's never personal.
    It's like detective work and the culprit can often be found or you can throw away the change that
    your stumped on and start again from working code.

    For me working means "I hear music." I always think anyone is free to change my code since Mozaic
    runs on scripts we can pass around like viruses and someone else can stomp out my bugs too.

    The really cool thing I like about Mozaic. Is once you get a sense for how it processes code, and begin to memorize some of the primary Events, Functions, and math handling. It "opens the door" to the possibility of making your own Apps that might do things that may, or may not be possible, using a "commercial" AppStore Application.

    I have several future apps in mind that I want to try to build already.

    I like that as I learn more about Mozaic scripting, the possibility of the types of apps that might make, keeps expanding.

    It would be nice if more people were interested in this.

  • @horsetrainer said:

    @McD said:

    @horsetrainer said to @_Ki:
    Your info gives me a lot of new things to study and experiment with.

    @_Ki's suggestions about error checking in is all new to me too. He's always great at sharing his knowledge
    when he's sees details to call out and someone interested in learning.

    Thanks for your extremely helpful critique _ki !

    Yes. Your replies and suggestions are always clearly detailed and useful for future reference.

    I like this coding stuff, it's like trying to solve a puzzle.

    Yes. It's fun for the right mindset that refuses to be put off by the frustrations of good puzzles.
    Some people seem to think "This makes me feel bad about myself" but it's never personal.
    It's like detective work and the culprit can often be found or you can throw away the change that
    your stumped on and start again from working code.

    For me working means "I hear music." I always think anyone is free to change my code since Mozaic
    runs on scripts we can pass around like viruses and someone else can stomp out my bugs too.

    The really cool thing I like about Mozaic. Is once you get a sense for how it processes code, and begin to memorize some of the primary Events, Functions, and math handling. It "opens the door" to the possibility of making your own Apps that might do things that may, or may not be possible, using a "commercial" AppStore Application.

    I have several future apps in mind that I want to try to build already.

    I like that as I learn more about Mozaic scripting, the possibility of the types of apps that might make, keeps expanding.

    It would be nice if more people were interested in this.

    Yeah, what you say there resonates with my experiences with mozaic - I need to get back into as I’ve never had much fun programming stuff but this felt a lot more natural when I got into it a few weeks back... nearly done on a little work project so might have more time again soon 😁

Sign In or Register to comment.