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*

1246789

Comments

  • It was pointed out in the other thread that this whole block of code:

    @OnMidiNoteOn
    
    if counter = 0
    chordnote[0] = MIDIByte2 
    endif 
    
    if counter = 1
    chordnote[1] = MIDIByte2
    endif
    
    if counter = 2
    chordnote[2] = MIDIByte2
    endif
    
    if counter = 3
    chordnote[3] = MIDIByte2
    endif
    
    Inc counter
    
    @End 
    

    Could be reduced to this:

    @OnMidiNoteOn
      chord[counter] = MIDINote
      Inc counter
    @End
    
  • The principle I want to outline is this....

    • Mozaic script receives midi notes from a user played chord.

    • Then by some method, the script "records" the played notes of the chord into an Array.

    • The script then applies some form of algorithm to the chord data.

    • The script then outputs a new set of Midi notes in some creative way, based upon the notes of the user chord input.

  • I'd like to back up a step and go forward with the starting point being a single note: what I call a "One Finger" input.

    From a single note we can make simple script example to:

    1. play a chord or a polyphonic voiced structure using the input note as a "root" tone
    2. play a sequence of notes in relation to the input
    3. start a rhythm pattern of notes using the DAW's Metronome or Mozaic's Timer functions
    4. run a random pattern based upon some pre-selected scale (Mozaic has functions for a whole slew of scale types)

    and many more as people propose ideas.

    Chord detection is a lot more advanced and more likely to confuse and loose a lot of non-coding readers but we can pursue
    many options with a request & code conversation.

  • edited March 2020

    The issues I am studying....

    ...Scripting logic that can be used to determine which played notes belong to a chord...

    My initial idea was to use the timer function for this... The concept being that any notes played within a given interval of a specified time will be treated as belonging to one specific chord.

    But in the other thread @hes introduced the idea of using logic to "identify" a chord by it's note spacings. And this gives rise to a different method for identifying when a user plays a specific chord.

    ...The next concept is about using an algorithm to parse the chord note input, and designing a script that makes some sort of creative output based upon the notes of the input chord.

    This requires that the script at some point senses completion of the chord input stage. Then logic tells the script to begin sending creatively calculated Midi output notes according what the input chord was.

  • edited March 2020

    After thinking about ways to use "chord input" in Mozaic. It occurred to me that hand played chords will have a NoteOn input sequence dependent on the timing which fingers "trigger" the notes of a chord.

    The order that chord notes are recognized by Mozaic, will not always be in an ascending order. But perhaps there's a way to use logic in Mozaic to reorganize the notes of a chord, and put them into an ascending order into an Array.

    That way there is consistency in how the "creative output" phase of the script would process incoming sets of chord notes.

  • @McD said:
    I'd like to back up a step and go forward with the starting point being a single note: what I call a "One Finger" input.

    From a single note we can make simple script example to:

    1. play a chord or a polyphonic voiced structure using the input note as a "root" tone
    2. play a sequence of notes in relation to the input
    3. start a rhythm pattern of notes using the DAW's Metronome or Mozaic's Timer functions
    4. run a random pattern based upon some pre-selected scale (Mozaic has functions for a whole slew of scale types)

    and many more as people propose ideas.

    Chord detection is a lot more advanced and more likely to confuse and loose a lot of non-coding readers but we can pursue
    many options with a request & code conversation.

    Sorry, I was writing out ideas and missed your post.

  • OK. Let’s tackle a “Chord Detection“.

    I would use this approach:

    1. Add new MIDI notes to an array (until some settable timer expires... start with 1 second for example)
    2. After 1 second analyse the notes for a chord type
    3. I would pre-process the notes to a Note Name value from 0 = C to 11 = B using Note%12

    That’s the modulo operator that divides a variable by a number and returns the remainder.

    60%12 returns 0 for a C and 48, 72, etc do too. They are all C’s in octaves.
    59%12 returns 11 for a B

    The chord types detected will be a product of the number of
    2. notes provided
    3. Intervals between notes

    Please post the easiest to understand and not the most complex solutions you can think of.
    Use as Few commands as possibles and we’ll optimize the solutions later.

  • @McD said:
    OK. Let’s tackle a “Chord Detection“.

    I would use this approach:

    1. Add new MIDI notes to an array (until some settable timer expires... start with 1 second for example)
    2. After 1 second analyse the notes for a chord type
    3. I would pre-process the notes to a Note Name value from 0 = C to 11 = B using Note%12

    That’s the modulo operator that divides a variable by a number and returns the remainder.

    60%12 returns 0 for a C and 48, 72, etc do too. They are all C’s in octaves.
    59%12 returns 11 for a B

    The chord types detected will be a product of the number of
    2. notes provided
    3. Intervals between notes

    Please post the easiest to understand and not the most complex solutions you can think of.
    Use as Few commands as possibles and we’ll optimize the solutions later.

    I'm beginning to understand this modulo thing.
    I see the usefulness of it.
    I have to think on it a bit.
    Thanks, McD

  • @horsetrainer said:

    The order that chord notes are recognized by Mozaic, will not always be in an ascending order. But perhaps there's a way to use logic in Mozaic to reorganize the notes of a chord, and put them into an ascending order into an Array.

    Yep. If you take a look at the code for the SmartChordBass Mozaic project, you'll find that it calls a sorting procedure each time a note is received, which arranges them in order in an array so (eventually) the chord can be identified.

  • McDMcD
    edited March 2020

    Here's my start on a Mozaic Script for the chord input. It always take a lot of trial and error for me to get something working. This is tricky. At this point it puts input notes into an Array and some some timer code that counts up to 6 seconds before resetting the chord index back to zero.

    @OnLoad
      interval1 = 0
      interval2 = 0
      count = 0
      time = 0
      notein = 0
      cindex = 0
      c= [0, 4, 7]
      Log{Chord: }, c[0], c[1], c[2]
      SetTimerInterval 1000
      StartTimer
    @End
    
    @OnMidiInput
      if MIDICommand = 144
        c[cindex] = MIDINote % 12
        Log {Note }, MIDINote, { cindex: }, cindex
        Log{Chord: }, c[0], { }, c[1], { }, c[2]
        Inc cindex
      endif  
    @End
    
    @OnTimer
     Inc count
     Inc time
     Log{Timer: }, time, { Count: }, count
     if count = 5
       count = 0
       interval1 = c[1] - c[0]
       interval2 = c[2] - c[1]
       // Log{I1: }, interval1, { I2: }, interval2, { cindex: }, cindex
       cindex = 0
     endif  
    @End
    
    @Description
    
     A simple chord detection script
     Play notes in 1 second interval
     The chord name will be printed on the log
    
    @End
    
  • @hes said:
    Yep. If you take a look at the code for the SmartChordBass Mozaic project, you'll find that it calls a sorting procedure each time a note is received, which arranges them in order in an array so (eventually) the chord can be identified.

    This was my first attempt to write something that uses the Mozaic Timer.

    Yes the next step for me is to sort the input notes. But I will create a solution because it's just fun to work through to a solution.

    After sorting then there's a need to do matching of note combinations for the 11 root options for:
    Major
    Minor
    7th
    ...

    I'll probably quit there and work on some simpler "One Finger" scripts that have less complexity and general use cases.

  • I made a script that can take four random played notes, then play them back in order from lowest note to highest note.

    Please critique my script.
    How can it be made more efficient?

    :)

    @OnLoad
    
        SEQ = 0
        counter = 0
        FillArray chordnote, -1, 10
        FillArray NoteOrder, -1, 10
    @End
    
    @OnMidiNoteOn
    
        chordnote[counter] = MIDIByte2 
        Inc counter
    
        if counter = 4
        counter = 0
        SEQ = 0
    
    Call @OrderFind
    
        endif
    
    @End
    
    
    @OrderFind
    
        For Calc = 36 to 96
    
        if  Calc = chordnote[0]
        NoteOrder[SEQ] = chordnote[0]
        Inc SEQ
        endif
    
        if chordnote[1] = Calc
        NoteOrder[SEQ] = chordnote[1]
        Inc SEQ
        endif
    
        if chordnote[2] = Calc
        NoteOrder[SEQ] = chordnote[2]
        Inc SEQ
        endif
    
        if chordnote[3] = Calc
        NoteOrder[SEQ] = chordnote[3]
        Inc SEQ
        endif
    
        if Calc = 96
    Call @Noteplay
        endif
    
        endfor 
    
    @End
    
    
    @NotePlay
    
        SendMIDINoteOn 1 , NoteOrder[0] , MIDIByte3 
    
        SendMIDINoteOn 1 , NoteOrder[1] , MIDIbyte3 , 1000
    
        SendMIDINoteOn 1 , NoteOrder[2] , MIDIbyte3 , 2000
    
        SendMIDINoteOn 1 ,  NoteOrder[3] , MIDIbyte3 , 3000
    
    
        SendMIDINoteOff 1 , NoteOrder[0] , MIDIbyte3 , 1000
    
        SendMIDINoteOff 1 , NoteOrder[1] , MIDIbyte3 , 2000
    
        SendMIDINoteOff 1 ,  NoteOrder[2] , MIDIbyte3 , 3000
    
        SendMIDINoteOff 1 ,  NoteOrder[3] , MIDIbyte3 , 4000
    
    @End
    
  • edited March 2020

    @horsetrainer Looks good! Here are some ideas for improving the efficiency.

    @OrderFind 
        for Calc = 36 to 96
            for i = 0 to 3
                if Calc = chordnote[i]
                    NoteOrder[SEQ] = chordnote[i]
                    Inc SEQ
                endif
            endfor
        endfor
        Call @Noteplay
     @End
    
    @NotePlay
        for i = 0 to 3
            SendMidiNoteOn 1, NoteOrder[i], MIDIByte3, (i * 1000)
            SendMidiNoteOff 1, NoteOrder[i], MIDIByte3, ((i * 1000) + 1000)
        endfor
    @End
    
    
  • McDMcD
    edited March 2020

    Great question for teaching coding science. Your solution requires 240 comparisons to finish.

    Bubble Sort... for 4 numbers I can't think of a simpler way.

    Compare 2 slots and swap if the higher slot has a lower value.

    1. Compare Slot 1 to Slots 2-4 in order and then 1 will have the lowest (or highest optionally) number.
    2. Compare 2 to 3-4 and 2 will have the lowest number (NOTE equal numbers don't need to be swapped to save time).
    3. Compare 3 to 4 and swap if needed.

    6 comparisons in this process... there's an optimization that reduces to 5 comparisons max... any ideas? Please describe.

    240 vs 6 if tests. You can also get clever and use loops to do Steps 1 then Step 2 then end with 3.

    Using:
    Log{NOTES:}, chordnote[0], { }, chordnote[1], { }, chordnote[2], { }, chordnote[3], { }
    Is really helpful to test your code after swapping and see the bubbling of HIGH to the last and LOW to the head.

    Sorting numbers is a classic computer programming problem. For huge sorts programmers try clever strategies to remove "typing" or get a faster runtime solution... a lot of financial apps involve massive sort to rate products by sales, likes, etc.

  • @Skyblazer said:
    @horsetrainer Looks good! Here are some ideas for improving the efficiency.

    @OrderFind 
        for Calc = 36 to 96
            for i = 0 to 3
                if Calc = chordnote[i]
                    NoteOrder[SEQ] = chordnote[i]
                    Inc SEQ
                endif
            endfor
        endfor
        Call @Noteplay
     @End
    
    @NotePlay
        for i = 0 to 3
            SendMidiNoteOn 1, NoteOrder[i], MIDIByte3, (i * 1000)
            SendMidiNoteOff 1, NoteOrder[i], MIDIByte3, ((i * 1000) + 1000)
        endfor
    @End
    
    

    Cool! Thanks Skyblazer! ;)

    That's exactly the sort of technique I was looking for.
    If I recall correctly, I think these are called "nested loops".

    I need to learn more about nested loops.

  • @McD said:
    Great question for teaching coding science. Your solution requires 240 comparisons to finish.

    Bubble Sort... for 4 numbers I can't think of a simpler way.

    Compare 2 slots and swap if the higher slot has a lower value.

    1. Compare Slot 1 to Slots 2-4 in order and then 1 will have the lowest (or highest optionally) number.
    2. Compare 2 to 3-4 and 2 will have the lowest number (NOTE equal numbers don't need to be swapped to save time).
    3. Compare 3 to 4 and swap if needed.

    6 comparisons in this process... there's an optimization that reduces to 5 comparisons max... any ideas? Please describe.

    240 vs 6 if tests. You can also get clever and use loops to do Steps 1 then Step 2 then end with 3.

    Using:
    Log{NOTES:}, chordnote[0], { }, chordnote[1], { }, chordnote[2], { }, chordnote[3], { }
    Is really helpful to test your code after swapping and see the bubbling of HIGH to the last and LOW to the head.

    Sorting numbers is a classic computer programming problem. For huge sorts programmers try clever strategies to remove "typing" or get a faster runtime solution... a lot of financial apps involve massive sort to rate products by sales, likes, etc.

    I was thinking about this concept last night, but didn't know it was called "bubble sort". The solution I came up with was easier for me to understand, so I went with that instead.

    I certainly get your point about 240 comparisons... Hence why I'm looking for alternative methods.

    Question....

    It seems most of the calculations Mozaic is capable of being scripted to do, already exist in other coding languages... Is this assumption somewhat correct?

    If I wanted to read tutorials for another type of scripting language.... in order to better understand how to script in Mozaic.

    Which scripting language would be most similar to Mozaic, for the purpose of learning about techniques that could be "translated" over to the Mozaic language?

    Thanks. :)

  • _ki_ki
    edited March 2020

    @horsetrainer
    The nested loop in OrderFind will take too much time, the inner loop runs 60*4 = 240 times. This leads to CPU spikes and therefore crackles if the synth also use some performance.

    In my Smart Chord Bass i used a single loop insert sort (if i remember correctly) - running for a maximum 1+2+3+4 = 10 inner loop runs for 4 notes (the code works for any number of notes btw). There are tons of sort algorythms, you need to choose something that runs well with low number of elements.

    Algorythms are normally teached indendant of programming languages as these are just concepts.

    You try to search for online tutorials about 'algorithms and data structures'. C, C#, C++, Java, JavaScript, Python (and many more procedural computer languages) all share the base functionalities of conditional statements, loops, variables, functions and data-structures.

    But in Mozaic there are just global variables of integer type and global functions without parameters or return value. There are also no data structures except the arrays. Nearly all algorithms need to be heavily adapted to these conditions before they can be used.

    I have to think about were one could find this latter knowledge (like unfolding n-dimensional arrays into 1d arrays, how do implement structure like behavior into arrays, how to do lists with head/tail pointers in 1d arrays, searching without nested loops etc). I know all these tricks from heart, but can't remember where in my career i had to learn all these things :) Perhaps it results from programming in assembler where you don't have data structures but only a big block of memory and some array pointers ?

    When i teached computer science at the university (25 years ago - woohoo i'm getting really old) , we used an quite unknown functional programming language with totally different syntax and methodology - there were no loops, no ifs, no variables... just operations on lists. This was done on purpose, to slow down those who already had programming experience to focus them on learning to understand algorithms itself. The students couldn't google solutions or use stack-overflow :)

  • @horsetrainer said:
    Which scripting language would be most similar to Mozaic, for the purpose of learning about techniques that could be "translated" over to the Mozaic language?

    I'm not sure I'd send you off on that tangent yet when you're kicking up a storm making working scripts.

    Here's why.. Mozaic is severely constrained to allow the executive to keep up with processing MIDI and NOT dropping any events. While most modern languages have an excess of execution engine capacity with Intel CPU's and they offer amazing tools.

    So generic tutorials will teach optimal features and not be event oriented. If it is event oriented it will be really hard to master. There's probably some robot control languages that have simple event engines. @Brambos has given use a great "pocket fisherman" type of tool for musical event tasks.

    As you pick a new problem, or consider new features you tackle a new solution space. So, you're doing the best thing possible to learn to think like a programmer:

    Pick something to design towards... and the target can certainly be moved at any point like drawing on a canvas. As you think of new features you'll be off trying to find a solution to provide that benefit.

    @Brambos describes how he elects to create a new product as Frankenstein'ing one of his current apps. Ripping it apart and
    re-building it from some existing parts and making new ones for the eventual "monster" he unleashes oil the market.

    Mozaic really is so different from anything else ha has made and I'm concerned didn't sell enough but I'm happy with it as is.
    I like it because we can make our own "black box" scripts and it's so much fun to solve the problems. It's pure hobbiest hacking in the workshop... making stuff.

    Programmer's by the way like to learn new languages all the time just to force their brain to think in new ways. @_Ki can use
    Mozaic is ways most of us are not prepared to unravel: fast, elegant tools. He is also very candid that he's not so great with the music theory side of the problems but still loves to make music like everyone else here.

  • @_ki said:
    @horsetrainer
    The nested loop in OrderFind will take too much time, the inner loop runs 60*4 = 240 times. This leads to CPU spikes and therefore crackles if the synth also use some performance.

    In my Smart Chord Bass i used a single loop insert sort (if i remember correctly) - running for a maximum 1+2+3+4 = 10 inner loop runs for 4 notes (the code works for any number of notes btw). There are tons of sort algorythms, you need to choose something that runs well with low number of elements.

    Does that mean the step by step version I came up with is more efficient than the nested version?

    The version below only loops 60 times.... Is this correct?

    @OrderFind
    
        For Calc = 36 to 96
    
        if  Calc = chordnote[0]
        NoteOrder[SEQ] = chordnote[0]
        Inc SEQ
        endif
    
        if chordnote[1] = Calc
        NoteOrder[SEQ] = chordnote[1]
        Inc SEQ
        endif
    
        if chordnote[2] = Calc
        NoteOrder[SEQ] = chordnote[2]
        Inc SEQ
        endif
    
        if chordnote[3] = Calc
        NoteOrder[SEQ] = chordnote[3]
        Inc SEQ
        endif
    
        if Calc = 96
    Call @Noteplay
        endif
    
        endfor 
    
    @End
    
  • edited March 2020

    Here's my attempt at bubble sort in Mozaic. I'm not sure if it's the most efficient, so I'm curious to know if it could be improved in any way, or if a different sorting algorithm would be slightly faster, or slightly less code. Maybe I'll try insertion sort next.

    @OnLoad
    
    Sorted = False
    Array = [0, 67, 2, 45, 4, 80, 6, 127]
    PointsInArray = 7
    TempCopy = 0
    
    while Sorted = False
        Sorted = True
        For i = 0 to PointsInArray
            If Array[i] > Array[i + 1]
                Sorted = False 
                TempCopy = Array[i]
                Array[i] = Array[i + 1]
                Array[i + 1] = TempCopy
            Endif
        Endfor
    Endwhile
    
    for i = 0 to PointsInArray
        Log Array[i]
    endfor
    
    @End
    
  • @_ki

    I looked at your "Smart Chord Bass" script last night and found it hard to understand.
    I'll take another look at it and see if I can find the section that sorts the note order, and try to understand how you made it work.

  • _ki_ki
    edited March 2020

    Does that mean the step by step version I came up with is more efficient than the nested version?
    The version below only loops 60 times.... Is this correct?

    • It only loops 60 times, so there are 60 'end-loop' checks, the inner statements produce 240 'IF' checks with 240 indexed variables accesses.

    • The nested loop variant has 240 'end-loop' checks, but the IF checks and variable access numbers stays at 240.

    In my experience its the IF count and variable access with index that takes up a lot more time than the 'loop-end' test. So the total difference between both variants is only minimal.

    .

    But i didn't yet time Mozaic's for loops... The time returned by the SystemTime function is not updated during the events, only set at the start of each Mozaic event, taking the SystemTime before a test-loop and also after doing the loop therefore doesn't work.

    BTW i've editied my upper response with more info about 'why general infos about algorythms won't really help for programming in Mozaic'

  • McDMcD
    edited March 2020

    @horsetrainer said:
    Does that mean the step by step version I came up with is more efficient than the nested version?

    The version below only loops 60 times.... Is this correct?

    How about 9 loops?

    // Count the number of  loops with in this code.
    
    chordnote = [100, 80, 20, 60]
    
    For outloops = 0 to 2
      For index = 1 to 3
        Log {NOTES: }, chordnote[0], { }, chordnote[1], { }, chordnote[2], { }, chordnote[3], { }
        if chordnote[outloops] < chordnote[index]
          temp = chordnote[outloops]
          chordnote[outloops] = chordnote[index]
          chordnote[outloops] = temp      
        endif
      endfor
    endfor
    

    It's tricky to follow. I haven't run it yet to see if I have a mistake.

    EDIT: I fucked it up. This works:

    Call @Mysort

    @MySort
    chordnote = [50, 80, 20, 10]
    for outloops = 0 to 2
    for index = (outloops + 1) to 3
    Log {OUT: }, outloops, { INNER: }, index, { NOTES: }, chordnote[0], { }, chordnote[1], { }, chordnote[2], { }, chordnote[3], { }
    if chordnote[outloops] > chordnote[index]
    var = chordnote[index]
    chordnote[index] = chordnote[outloops]
    chordnote[outloops] = var
    endif
    endfor
    endfor
    Log {OUT: }, outloops, { INNER: }, index, { NOTES: }, chordnote[0], { }, chordnote[1], { }, chordnote[2], { }, chordnote[3], { }

    @End

    It can work for arrays of any size (up to 1024) and the loop ends to use a variable:

     for outloops = 0 to (num_of_notes - 1) 
        for index = (outloops + 1) to num_of_notes
    
  • @McD said:

    @horsetrainer said:
    Does that mean the step by step version I came up with is more efficient than the nested version?

    The version below only loops 60 times.... Is this correct?

    How about 9 loops?

    // Count the number of  loops with in this code.
    
    chordnote = [100, 80, 20, 60]
    
    For outloops = 0 to 2
      For index = 1 to 3
        Log {NOTES: }, chordnote[0], { }, chordnote[1], { }, chordnote[2], { }, chordnote[3], { }
        if chordnote[outloops] < chordnote[index]
          temp = chordnote[outloops]
          chordnote[outloops] = chordnote[index]
          chordnote[outloops] = temp      
        endif
      endfor
    endfor
    

    It's tricky to follow. I haven't run it yet to see if I have a mistake.

    EDIT: I fucked it up. This works:

    Call @Mysort

    @MySort
    chordnote = [50, 80, 20, 10]
    for outloops = 0 to 2
    for index = (outloops + 1) to 3
    Log {OUT: }, outloops, { INNER: }, index, { NOTES: }, chordnote[0], { }, chordnote[1], { }, chordnote[2], { }, chordnote[3], { }
    if chordnote[outloops] > chordnote[index]
    var = chordnote[index]
    chordnote[index] = chordnote[outloops]
    chordnote[outloops] = var
    endif
    endfor
    endfor
    Log {OUT: }, outloops, { INNER: }, index, { NOTES: }, chordnote[0], { }, chordnote[1], { }, chordnote[2], { }, chordnote[3], { }

    @End

    It can work for arrays of any size (up to 1024) and the loop ends to use a variable:

     for outloops = 0 to (num_of_notes - 1) 
        for index = (outloops + 1) to num_of_notes
    

    Amazing! :smile:

    Thank you for making this. This is a great piece of coding work.

    I just have to study it for a while until I have these concepts mastered.

    I used this to replace the "sort code" I put in my earlier script, and it works perfectly!

  • In SmartChordBass i have separated the sorting from the note storage. These are stored by their index as in your script, but there is an additional sortedIdx[] array containg a list of indices pointing into the notes (and in SCB also velocity array) . And its just this index list that gets sorted not the initial note or velocity array. This makes it a bit more complicated, but i think i had reasons to do it that way :)

    .

    At start (for the first note) the sortedIDx[0] is set to 0 (in SortActiveNotesStart). This means that note[0] is lowest - which is always correct.

    When a new note arrives in OnMidiNoteOn (and its not the first or chord-detection wasn't canceled due to timeout or note-offs received in the meantime) SortActiveNotesAddCurrent is called .

    @OnMidiNoteOn  // Excerpt from SmartChordBass
    
      note[numActive] = MIDINote
      velo[numActive] = MIDIVelocity
      Inc numActive
    
      if numActive = 1
        sortedIdx[0]  = 0
      else 
         Call @SortActiveNotesAddCurrent 
      endif
    @End
    

    numActive is the number of current notes, 1 for the first note, 2 for the second active etc and the sort event function is only called if there are at least two notes (numActive will be >= 2)

    This function runs through all stored notes, from back to begin and moves the current sortIdx values one slot further as long as the new note is smaler than the note referenced via the sortedIdx ( using note[ sortedIdx[i] ] - double indirection). In that way, the new index is 'inserted' into the correct slot. There are some special treatments for reaching the start.

    @SortActiveNotesAddCurrent 
    
      found = NO
      i = numActive -2  
      repeat
        nxt = i +1
        sortedIdx[nxt] = sortedIdx[i]
        if MIDINnote > note[ sortedIdx[i] ]
          found = YES
          Inc i
        elseif i = 0
          found = YES
        else
          Dec i
        endif
      until found
    
      sortedIdx[i] = numActive -1
    @End
    

    But how does this work ?

    Here an example for the notes 60,72,50, 55 arriving in this order.

    For the first note 60 with velo 75

    note[]      = [60]
    velo[]      = [75]
    sortedIdx[] = [0]
    numActive= 1
    

    there is no sorting loop runing.

    .

    For the second note 72 with velo 77 the sorting loop starts at (numActive-2) = 0 and at first copied sortedIdx[1] = sortedIdx[0] // => [0,0] but since the 72 is larger, the loop terminates and the sortedIdx[1] get overwritten by the new note's index (1)
    resulting in

    note[]      = [60,72]
    velo[]      = [75,77]
    sortedIdx[] = [0,1]
    numActive= 2
    

    The sortedIdx array means that the note at index 0 is the smallest, and the next smallest is found at index 1.

    .

    .

    For the third note 50 with velo 80 the sorting loop start at index 1, first moves the sortedIdx[2] = sortedIdx[1] // => [0,1,1] one slot to the right, but since 50 is less than the current 72, the repeat-until loop continues with the previous index (0) and copies sortedIdx[1] = sortedIdx[0] // => [0,0,1].
    Note 50 is still less than the note 60 referenced by sortedIdx[0], so the next if checks if we arrived at the array start which is the case - so the loop terminates and sortedIdx[0] is overwritten with the new note's index (2)
    resulting in

    note[]      = [60,72,50]
    velo[]      = [75,77,80]
    sortedIdx[] = [2,0,1]
    numActive= 3
    

    The sortedIdx array means that the note at index 2 is the smallest, and the next higher is found at index 0 and the biggest is at index 1.

    .

    For the forth note 55 with velo 72 sorting loop starts at index 2, and moves sortedIdx[3] = sortedIdx[2] // => [2,0,1,1] . Note 55 is less than the 72 referenced by sortedIdx[2] and we do a second round moving sortedIdx[2] = sortedIdx[1] // => [2,0,0,1]. In this round for index 1 the 60 referenced by sortedIdx[1] is larger than the new 55 which will get note index 3.
    In the third round with index 0 the 50 referenced by sortedIdx[0] is smaller and the loop is terminated replacing sortedIdx[1] with note index 3 (What carefully - we were handling index 0, but if the number found is smaller, the index to be manipulated is incremented before terminating the loop ! )

    note[]      = [60,72,50,55]
    velo[]      = [75,77,80,72]
    sortedIdx[] = [2,3,0,1]
    numActive= 4
    

    The sortedIdx states that the note at idx 2 is smallest, followed by the one at index 3, then the one at index 0 and the highest and last is at index 1
    .

    TL;DR:
    Array sorting with indirection - I didn't say it would be easy to follow :)

  • @McD Does this algorithm have an official name, other than @MySort?

  • @Skyblazer said:
    @McD Does this algorithm have an official name, other than @MySort?

    No. It's just a name I elected to use for a "User Events (Subroutines)"

    Call @Any_Name_Function
    
    @Any_Name_Function
    ... any algorhythm
    @End
    

    The extra effort in Mozaic involves remembering Variable Names that allow the Function to modify variables and arrays.

    Of course, programmers would like @Brambos to add function parameters but that would also take his executive code engine off the task of managing MIDI processing in realtime to set up memory structures for a Function. These memory structures are temporary and get serviced by a pop on/off stack. It's not a trivial task and we can just use exiting variables and not need to
    request copies or additional memory management processing.

    When "real programmers" wrote machine code they also had to be really careful about using memory wisely.

    At least @Brambos gave us some Global Variables to really scale up a network of scripts and have shared control "knobs".

  • @_ki said:
    In SmartChordBass i have separated the sorting from the note storage. These are stored by their index as in your script, but there is an additional sortedIdx[] array containg a list of indices pointing into the notes (and in SCB also velocity array) . And its just this index list that gets sorted not the initial note or velocity array. This makes it a bit more complicated, but i think i had reasons to do it that way :)

    .

    At start (for the first note) the sortedIDx[0] is set to 0 (in SortActiveNotesStart). This means that note[0] is lowest - which is always correct.

    When a new note arrives in OnMidiNoteOn (and its not the first or chord-detection wasn't canceled due to timeout or note-offs received in the meantime) SortActiveNotesAddCurrent is called .

    @OnMidiNoteOn  // Excerpt from SmartChordBass
    
      note[numActive] = MIDINote
      velo[numActive] = MIDIVelocity
      Inc numActive
    
      if numActive = 1
        sortedIdx[0]  = 0
      else 
         Call @SortActiveNotesAddCurrent 
      endif
    @End
    

    numActive is the number of current notes, 1 for the first note, 2 for the second active etc and the sort event function is only called if there are at least two notes (numActive will be >= 2)

    This function runs through all stored notes, from back to begin and moves the current sortIdx values one slot further as long as the new note is smaler than the note referenced via the sortedIdx ( using note[ sortedIdx[i] ] - double indirection). In that way, the new index is 'inserted' into the correct slot. There are some special treatments for reaching the start.

    @SortActiveNotesAddCurrent 
    
      found = NO
      i = numActive -2  
      repeat
        nxt = i +1
        sortedIdx[nxt] = sortedIdx[i]
        if MIDINnote > note[ sortedIdx[i] ]
          found = YES
          Inc i
        elseif i = 0
          found = YES
        else
          Dec i
        endif
      until found
    
      sortedIdx[i] = numActive -1
    @End
    

    But how does this work ?

    Here an example for the notes 60,72,50, 55 arriving in this order.

    For the first note 60 with velo 75

    note[]      = [60]
    velo[]      = [75]
    sortedIdx[] = [0]
    numActive= 1
    

    there is no sorting loop runing.

    .

    For the second note 72 with velo 77 the sorting loop starts at (numActive-2) = 0 and at first copied sortedIdx[1] = sortedIdx[0] // => [0,0] but since the 72 is larger, the loop terminates and the sortedIdx[1] get overwritten by the new note's index (1)
    resulting in

    note[]      = [60,72]
    velo[]      = [75,77]
    sortedIdx[] = [0,1]
    numActive= 2
    

    The sortedIdx array means that the note at index 0 is the smallest, and the next smallest is found at index 1.

    .

    .

    For the third note 50 with velo 80 the sorting loop start at index 1, first moves the sortedIdx[2] = sortedIdx[1] // => [0,1,1] one slot to the right, but since 50 is less than the current 72, the repeat-until loop continues with the previous index (0) and copies sortedIdx[1] = sortedIdx[0] // => [0,0,1].
    Note 50 is still less than the note 60 referenced by sortedIdx[0], so the next if checks if we arrived at the array start which is the case - so the loop terminates and sortedIdx[0] is overwritten with the new note's index (2)
    resulting in

    note[]      = [60,72,50]
    velo[]      = [75,77,80]
    sortedIdx[] = [2,0,1]
    numActive= 3
    

    The sortedIdx array means that the note at index 2 is the smallest, and the next higher is found at index 0 and the biggest is at index 1.

    .

    For the forth note 55 with velo 72 sorting loop starts at index 2, and moves sortedIdx[3] = sortedIdx[2] // => [2,0,1,1] . Note 55 is less than the 72 referenced by sortedIdx[2] and we do a second round moving sortedIdx[2] = sortedIdx[1] // => [2,0,0,1]. In this round for index 1 the 60 referenced by sortedIdx[1] is larger than the new 55 which will get note index 3.
    In the third round with index 0 the 50 referenced by sortedIdx[0] is smaller and the loop is terminated replacing sortedIdx[1] with note index 3 (What carefully - we were handling index 0, but if the number found is smaller, the index to be manipulated is incremented before terminating the loop ! )

    note[]      = [60,72,50,55]
    velo[]      = [75,77,80,72]
    sortedIdx[] = [2,3,0,1]
    numActive= 4
    

    The sortedIdx states that the note at idx 2 is smallest, followed by the one at index 3, then the one at index 0 and the highest and last is at index 1
    .

    TL;DR:
    Array sorting with indirection - I didn't say it would be easy to follow :)

    Thanks @_ki
    This contains lots to learn from.

    A basic question though.....

    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"?

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

    @McD

    Perhaps for tutorial purposes, we should go over the range of things that can be placed in the Mozaic parameters for different functions?

    For example... Can I do this.... ?

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

  • McDMcD
    edited March 2020

    I'm thinking about chords and how to process/detect and manipulate them.

    If you only consider 3-note chord types for 11 root notes... that's 33 chords to Identify.

    TYPE | CHROMATIC SCALE POSITIONS  | MODAL SCALE | INTERVALS  
    Major                       0,4,7                                   1,3,5 (Major)          0,4,3
    Minor                       0,3,7                                   1,3,5 (Minor)         0,3,4
    Augmented             0,4,8                                  1,3,5 (Whole)          0,4,4
    

    If we apply the Modulo operator to the actual notes we can reduce any of the 33 possible chords a unique 3 note array.

    For C types they would match the CHROMATIC SCALE POSITIONS.

    We can create the intervals but if we can first figure out the Root note of the three notes.

    Augmented Chords are perfectly ambiguous with 4 steps between any 2 consecutive notes. So there are only
    12 distinct augmented chords that each have 2 clones.

    Once any note like 65 is Modulo'ed by 12 you get the remainder value of the key name note:
    C = 0
    C#/Db = 1
    D = 2
    D#/Eb = 3
    E = 4
    F = 5
    F#/Gb = 6
    G = 7
    G#/Ab = 8
    A = 9
    A#/Bb = 10
    B = 11

    I'll just show the "Modulo" numbers for the Major Chords to be ID'ed:
    C - 0,4,7
    Db = 1,5,8
    D = 2,6,9
    Eb = 3,7,10
    E = 4,8,11
    F = 0,5,9 - this is where the notes wrap around. If we detect the 0 to 5 (4th interval) we could code 0 to be 12 (add 12).
    alternate F = 5,9,12
    F# = 1,6,10 or 6,10,13
    G = 2,7,11, or 7,11,14
    Ab = 0,3,8 - if we detect the 0 to 3 jump we can roll up the bottom two notes to protect the intervals for detection.
    alternate Ab= 8,12,15
    A = 1,4,9 or 9,13,16
    Bb = 2,5,10 or 10,14,17
    B = 3,6,11 or 11,15,18

    That's as far as I've got thinking about faster chord detection. I suspect @_Ki has solved this already.

    Add the 4th note of the 7th, Minor 7th, Full Diminshed, Half Diminished and my brain needs a break.

    It all leads to making the smallest "matching table" possible to reduce the number of "ifs" required before we
    have coded audio dropouts (crackling) or worse. This is @_ki's end of the pool.

  • @_ki said:
    TL;DR:
    Array sorting with indirection - I didn't say it would be easy to follow :)

    You are so clever... that's what happens when to dedicate yourself to learning an art.

    For newbies... @_Ki has 3 arrays used as sequential lists:
    An Incoming Notes List: note[]
    An aligned list of Velocities: velo[]
    A List of the "Places in Line" for each Note/Velocity in the other Arrays: "sortedIdx"
    That "Sorting" list is a indirect reference to the other 2 lists.

    When he presses a new note he scans the "sortedIdx" for note numbers in order and compares each note until he finds the new notes place in line and slides all sortedIdx positions up that position by 1 spot in the list.

    Very cool idea. All Mozaic offers are Arrays of 1024 length and a big NoteState Locker that has 128 lockers in 16 columns to
    save the state of a given MIDINote. Probably most useful to save NoteOn details so you can later TurnNotes off but that's a guess. I haven't tried to use it yet.

Sign In or Register to comment.