patch for poly~ voice allocation to least-recent note

    Oct 15 2013 | 7:56 pm
    This is the start of a custom note allocator for poly~. I made this because, suppose there are sustained note and many short notes. After a number of short notes, the poly~ object truncates the sustained note, because it assigns poly resources round-robin, or in other words, cyclically.
    For example, say there are three voices in a poly~. When receiving new notes, the poly~ object seems to assign the incoming notes in cyclic, or round-robin, order…to voice #1, #2, #3, and back to number #1 . Now imagine the first note is still sustained when the fourth arrives. Even if the second and third notes have already been released, poly~ assigns the fourth note to instance 1 and cuts off the sustained note.
    This LRU-based algorithm instead assigns new notes to the voice with the oldest note which has already been turned off. What surprises me is, MAX has been around for– what–30 years now? And still there is no patch I know of which doesn’t assign new notes to poly instances in a cyclic style. Trying to make it in Max.
    This is more difficult than it appears, because after a mix of long and short notes, the order of voice assignment is no longer cyclic. So here is my current design, but I'm not quite sure how to package it to share?

    • Oct 15 2013 | 8:28 pm
      The four other main reasons this patch is so complicated are:
      (1) when receiving a note-off event, it doesn't know which voice could be playing the note. So it searches all playing voices for one with the same pitch as the note-off event.
      (2) The note-off event may need to be discarded, because the voice was already allocated to another note when poly~ was full.
      (3) When receiving a note on event while poly~ ifs full, it first sends a note-off event, with the pitch of the note being replaced, to the correct voice, before sending the note-on event.
      (4) It contains a full reset, which theoretically isn't necessary, but that's helpful if a note-off event is lost elsewhere in a design and there is a stuck note.
    • Oct 15 2013 | 8:36 pm
      I don't believe that's the case, at least as far as I remember it.
      Poly~ should allocate new notes to whatever voice is free, which is not necessarily cyclical. You should only see the truncation if the number of voices asked for exceeds the number of voices available, which can happen with long release times. The easy way to verify this is to crank up the number of voices, hold a note, and play lots of staccato notes over it. Poly~ will truncate the oldest note when it runs out of voices, but it shouldn't as long as there are notes free.
      (All of this is dependent on using thispoly~ in your poly~ patch and with the midinote rather than note message)
    • Oct 16 2013 | 2:06 am
      it probably depends on the design, but normally a running voice is only stolen when there are no more free voices.
      however, i also only use custom "target" based allocation solutions for a variety of reasons and i think this is the way to go. for algoritmic composition methods or for the emulation of physical instruments one might want to have total control over when and which voice is stolen, things like that are impossible using the "built-in" poly stuff.
      your patch above might be easier to realize when you build it around [coll] or mySQL, but i could be wrong. using zl reg means you dont see what you are doing, isnt that a bit annoying during programming? :)
    • Oct 16 2013 | 2:33 am
      That could be true in new designs, Peter. In the classic ADSR implementation, the more recent notes could be switched off but still making sound in the release phase. So the allocation is usually to the oldest released note.
    • Oct 16 2013 | 3:06 am
      It would be nice if Max had a "quick-release" feature like AU synth plugins have.
    • Oct 16 2013 | 3:55 am
      Here is a simple receiver from this allocator for the poly~ object. The allocator creates a list:
      {instance# midi_pitch midi_gate}
      The route modules then parse the message in each poly instance. It doesn't actually require the adsr~ object (it can be in GEN instead). An output from the GEN module can send a mute message to the thispoly~ object to reduce CPU, if desired.
    • Oct 17 2013 | 4:56 am
      It's pretty well done. So I made a little demo ensemble, and when I finish writing it up, I'll post it in toolbox and put a link here. Here's a screengrab. You may ned to maximize the window to see it properly :)
    • Oct 18 2013 | 9:27 pm