Single Note to Homophonic Harmony/Chord Trigger

    Mar 14 2021 | 2:48 am
    Hello my friends,
    I need help building a patch. Here's the run-down:
    I need to be able to flexibly program single notes on my keyboard to different programmable destinations. Sometimes a key will trigger as a single note, sometimes a chord, sometimes several single notes on different midi channels, sometimes nothing at all.
    Here is how I imagine the patch functioning:
    I open a default version of the patch. No key triggers anything. I turn on "learn mode". I select a key to map by touching a key on my midi controller and then play either the note/chord I would like that key to trigger. The note/chord is stored in that key and touching the keyboard again selects the next key to trigger etc... Turning the patch off "learn mode" allows all the notes to be played as triggers with the unmapped keys completely blank.
    The purpose of this software is to be able to map chords I can't physically play to a single note and map homophonic harmonies (sent to multiple monophonic VSTs on separate channels) up and down a scale.
    Right now I am achieving this with several different instances of Mainstage's Chord Trigger or Xfer's Cthulhu open simultaneously with each one sending a different midi channel. Honestly, I love Mainstage's Chord Trigger and it's simple interface. If I could program a patch with that exact same workflow, GUI, and functionality, but also have the depth of Max at my disposal for future experiments, I would be a very happy man.
    Thanks for your help in advance. This is a style of playing I've been developing for about 5 years now and I've come to rely on it. If you can help me develop this idea it will change how I play for the rest of my life. I'm open and appreciative of all ideas and resources. I'm new to max but willing to put in the work and very excited to learn.
    Looking forward to hearing from you all! Sam

    • Mar 14 2021 | 9:18 am
      actually easy to do. You just need a menu or some buttons which route midi input to either normal midi through, learn mode for trigger key, learn mode for items to execute with trigger key, and trigger execute mode. Sounds like a bit more work to learn the chords, but in case of a need
      to repeat or modify the entry, sequence learn trigger > learn items would not be very effective. I would use coll to store assigned items for each key. depends a bit on if you want to store learned velocities as well, or use velocity of the trigger key for all notes in the chord . You then store trigger maps and recall them as needed. Midi channel routing could simply be done by routing sorted items in the trigger list to consecutive channels, or if it is internal Max thing to individual outputs targeting different plugins. Although it could get more complicated if you play several trigger keys which execute several chords , exceeding 16 midi channels.
      Why this midi channelising ? and monophonic plugins ? I guess they would all play same sound ...
    • Mar 20 2021 | 2:15 am
      Source Audio,
      Excuse the late reply. Your answer gave me plenty to study. I think Coll is exactly what I need. Thank you so much for leading me to it! I started a patch that allows you to set the index number, the note contents, and then send it as a single message into the Coll but I've gotten stuck. It works the first time I send it, but any following messages don't save in the Coll when I bang them. Any idea why this is?
      This has me very excited for the possibilities!
    • Mar 20 2021 | 9:20 am
      Here is a patch that fixes all problems with the procedure. I am a bit short of time to give you really detailed explanation, but in few words - main role plays ggate with 3 outlets 0 = play the captured chords 1 = set trigger note 2 = capture notes into chord list Few objects had to be used to avoid repeated notes and capture chords using only note on etc. in play mode coll output gets itered and paired with current velocity for midi output. lower kslider show notes played. top kslider is set to mode 2 (touchscreen), lower one to mode 1 (polyphonic) jit.cellblock shows captured trigger/chords.
    • Mar 22 2021 | 1:44 am
      Wow! It's gorgeous! Seriously, thank you. This didn't look easy to make and it is going to change the way I make music on a daily basis. To be honest, I don't fully understand the patch, but I think spending some quality time with it and studying the objects will illuminate me. I'll keep you filled in with how I use it creatively. I still have some ideas for additional functionality that I'd like to experiment with.
      Thanks again! Sam
    • Mar 26 2021 | 3:56 am
      Hello again,
      I spent some time with the patch and learned loads. Thanks again for your help. I tried to add the functionality of assigning different midi channels per Coll entry and while it seemed to be working for a bit, I broke it somehow. My plan was to embed an integer as the first entry for each indexed chord and then use that number to route the pitches to different midi channels, iterating, packing, and flushing as you had done. The Coll seems to be storing the numbers as expected, but looking through a midi monitor showed unexpected MIDI channels and sometimes I only received note-off messages.
      Lastly, is it possible to store multiple Coll entries with the same index number? For example, I might like 40 to trigger a notes on several different midi channels. To do this I would store several different Coll entries; each entry having its own midi routing number, and then trigger them all simultaneously. However, it seems that every time I add a new Coll entry on an existing index, it is overwritten.
      Again, I'm so very grateful for your energy and direction. This system is really important for my playing style and I'm very much looking forward to streamlining my workflow and expanding my palette with Max. Thanks again for your help! Sam
    • Mar 26 2021 | 9:19 am
      coll index can't be stored twice, it must be unique number or symbol. If you want to store channel together with the chord for each trigger note, it could be added at the end of the list, like : 22, 36 38 44 47 52 1; 23, 39 44 48 52 56 2; index as trigger note number, last item is midi channel. when that gets output, you would use zl ecils 1 to set last list item as midi channel for midi output. This is one option :
      Had to use midiformat , midiflush and midiout because it is more efficient than 16 noteout objects. --------- In case you need single midi output objects for whatever reason, like if you use different midi devices per channel one could also set midichannel as first item in the list, and user route 1 2 3 .. 16 to send rest of the list to single noteout objects
    • Mar 26 2021 | 12:04 pm
      P.S. regarding question about using same trigger note to play different chords to different midi channels ... One option would be to use trigger list with multiple midi channels assigned, if you can avoid using note numbers 1 - 16 to play notes. entry could look like this :
      22, 1 36 40 43 46 2 52 55 58 62; trigger note 22 plays notes 36 40 43 46 to midi channel 1, notes 36 40 43 46 to midi channel 2. output of the coll must be itered and sent to route 1 2 3 4 5 6 ... 16 Only you need to organise list entry for this purpose. 1 set trigger, 2 set midi channel, 3 append notes , 4 set next midi channel, 5 append notes etc than store the list.
      You know that you can write this entries into coll by typing, just respect the rule : index / comma / data / semicolon at end.
    • Apr 05 2021 | 6:10 pm
      Finally I was able to wrap my head around some of these concepts and scale the patch up to 16 MIDI channels. Thank you so much for your suggestions. The Coll seems to be storing and retrieving the data perfectly, however, I got stuck trying to split and route the list output into separate midi objects. I explored zl list, split, slice, regexp, iter, and route and while all of them seemed close, I couldn't figure out how to split and route data into the 16 different midi outs.
      I've attached my patch. If would be forever grateful for a nudge in the right direction on how to separate the outputs.
      As always, thank you so much for your time, energy, and expertise!
      Best, Sam
    • Apr 06 2021 | 12:51 pm
      I tried to capture few notes for 1 trigger, and coll received multiple I guess midi channels to play the captured notes ---- ??? Is that what you are trying ? If yes, than capturing should at least be done more flexible, not that setting 8 means that 1 2 3 4 5 6 7 8 must play all notes. You should add channels like you add notes to the list.
      Then if you split output of a coll line to route 1 - 16 separately out to enable or disable midi objects for that trigger, it might work, but routing or gating of channels must be done very efficient. Here is one patch with 4x4 matrix to set midi channels, and routing of midi channels, you have to try if it is good enough. just connect noteout objects to 16 outlets instedad of that buttons.
      router object gets cleared on each coll output first, then stored channels get activated and notes can pass. How efficient this is depends also on triggers polyphony and speed
    • Apr 06 2021 | 1:57 pm
      Thanks for your effort! I have never seen a matrix like that. It is definitely cleaner than my 16 buttons! And it's possible that I'm not understanding your patch, but it seems that I can only pass a single saved chord across all 16 channels. I need each channel to have complete control over the notes played. For example, if the trigger note is C2, I would like to be able to set a C major chord on Midi Channel 1, a Bb on Midi Channel 2, a C major chord in a different inversion in midi channel 3, etc. Currently in my patch, I select one of the 16 midi channel buttons, record what chord/notes I want, select the next channel, record what notes and chords I want, and so forth until I have all the information I want in my message boxes and I save into the Coll. Honestly, I'm happy with how this functions on my patch. However, what I don't know how to do, is split up the Coll into different outputs. For example if I had stored:
      68 1 64 66 68 2 54 72 85 3 21 34 78
      How do I break that message up into
      64 66 68 -> Midi Channel 1 54 72 85 - > Midi Channel 2 21 34 78 -> Midi Channel 3
      And then retrieve them and play them in real time.
      Thanks again! Sam
    • Apr 06 2021 | 3:46 pm
      output from coll like in the patch I posted should work no matter if you have several midi channels inside or not. BUT you should not sort list before storing into coll. you must keep midi channel as leading number, like in the example 68, 1 64 66 68 2 54 72 85 3 21 34 78;
    • Apr 06 2021 | 4:05 pm
      Forgive my ignorance, but I'm not understanding. Let's say I wanted to program the number passage stated earlier.
      68, 1 64 66 68 2 54 72 85 3 21 34 78;
      To attempt this, I would select "68" for my trigger note and channel 1 on the Midi matrix. I would then capture "64 66 68". Now I want to assign 54 72 85 to Midi Channel 2 and 21 34 78 to Midi channel 3. What would my next steps be before storing into the Coll?
      Thanks in advance! Sam
    • Apr 06 2021 | 4:21 pm
      You said you are happy with the way you input stuff into coll, so keep it that way. Use only output section of the patch i posted. If you want to have simpler way of inputing lines into coll, than remove matrix and use single number to select / insert midi channel. only don't wipe the list but add to it.
    • Apr 06 2021 | 5:08 pm
      Ah, I see. I gave it a try using the output with my Coll system and unfortunately I'm not getting any messages out of the router when I send my Coll data. Is this user error on my part? Here is how I set it up.
      Thanks! Sam
    • Apr 06 2021 | 5:45 pm
      yes it is user error. you did not connect anything. look into my last patch and connect router 16 outlets to 16 noteout objects
    • Apr 07 2021 | 7:23 am
      I also produced "user error" . In my last patch, router is set to open group of midi channels expected at the beginning of the trigger list, like : 22, 1 2 3 44 47 50 53; which would play notes 44 47 50 53 on midi channels 1 2 3. But in list like this 22, 1 48 52 77 2 60 64 67; it will not work properly, because midi channel 1 would play all 6 notes. for that, one would use gate instead of router. Here is patch doing exactly that, also modified midi channel/notes entry, and coll storage.
      You set trigger, than select midi channel and just proceed adding notes. Storing the buffer inserts midi channel /notes list. repeatedly selecting midi channel - notes and storing adds to the same trigger list, as many times as needed. In the middle delete Trigger button is included, select trigger number and hit that button. Selecting a row in the cellblock sets selection number too. -------------- There is another option in case you also want to send same note list to several midi channels. In that case one would make several groups of midi channels / notes. Example : 22, 1 2 3 44 47 50 4 57 60 63 5 6 67 70 73 10 35 37 14 15 16 82; Splitting that list would be a bit more demanding than just using single midi channel but would ease a lot entry of trigger lists. What do you think about that idea ?
    • Apr 08 2021 | 2:20 pm
      INCREDIBLE! It works beautifully. I can't tell you how excited/relieved I am to have this thing functional. Thank you so much!
      As far as sending the same notes across different channels: it sounds like an interesting idea but I don't think it's necessary. I'm happy to set each midi channel individually. I tested it in and it was playing nice and snappy and consuming less than 3% of my CPU. AMAZING! Now I'm going to map the transport/data buttons and each midi channel buttons on my midi controller and I think it will be very easy to store entries. Also, I'm going to study and see if I can't figure out how to use pattrstorage to allow recallable presets for a song/song basis.
      Hopefully I can get you a demo of it in action creatively soon. I can't thank you enough!
    • Apr 08 2021 | 2:48 pm
      I am really glad this works fine for you. In the meantime I prepared that alternative patch. Could you test it too ?
      This one has textedit object to write list of needed midi channels. The thing is, one could for example set channels 1 2 3 to play 40 44 48 and then channel 1 to also play 52 58 30, 1 2 3 40 44 48 1 52 58 6 66 77 88; etc -------- I would sugest another thing, to make live performance really as snappy as possible, separate capturing into coll from real performing patch. Like this here :
      Bare bones midi input and output. ----------- I am not a fan of pattr and such, allways used coll for all my storage needs, could you precise a bit what would be your storage needs ? like is one song one coll mapping enough so that one could recall 1 coll file per song ? You know you could also store and recall hundreds of coll files very fast from hd...
    • Apr 09 2021 | 4:21 am
      I tested your alternative patch. Unfortunately, it crashed Gig Performer. I think it was outputting too fast of a stream of Midi information.
      And I like your idea of having a lean, high-performance version of the patch. Would it be possible to disable aspects of the previous patch to make it more lightweight? A “record” or “perform” mode? It would nice to have both efficiency and flexibility.
      And good to know! As far as storage goes, I need to be able to recall a new coll for each song and be able to organize them in setlists and trigger song changes within Midi Designer (or Mira, possibly).
      As always, thanks for your hard work, attention, and expertise!
    • Apr 09 2021 | 8:23 am
      Strange that it produced any crash. I did run it on old 2008 MacBook , no problems at all. But I did not play much with, just run few tests to hardware synths. So you use Gig Performer plugin to host plugins inside Max ? Does it benefit in any way than to have plugs inserted directly into vst objects in max ? And what did you use to send 16 channels to it, for sure not noteout objects. Maybe there is the reason for the crash ... For store - recall options, are you going to build a standalone application for this ? One option would be to use dropfile and drag stored coll files in the right order to create and store show list. That list would populate umenu or coll with the file paths. You recall them by number or next messages, coming from any source, midi, OSC
      Or you can go complicated route using dict to pack all captured coll trigger sets, and then again place picked ones into new - show dict etc I would not bother with that. ------------ here is gate / single midi channel version of reduced player patch