desc:Dynamic poly/mono MIDI router
//tags: MIDI processing routing
//author: Tale

// Copyright (C) 2013-2017 Theo Niessink
// License: LGPL - http://www.gnu.org/licenses/lgpl.html

// This effect distributes incoming MIDI Note On/Off messages equally over
// the specified output channel range. Its typical use would be to convert a
// mono synth into a poly synth. To do this you insert it before multiple
// instances of the mono synth, each on a different MIDI channel.

slider1:0<0,16,1{Any,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Input Ch
slider2:0<0,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Output Ch Min
slider3:15<0,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Output Ch Max
slider4:0<0,1,1{Off,On}>Round-Robin

@init

// ch_buf = 0;
note_buf = 16;

@slider

in_ch  = max(0, min(16, slider1 |0)) - 1;
min_ch = max(0, min(15, slider2 |0));
max_ch = max(0, min(15, slider3 |0));

min_ch > max_ch ? (
  ch = min_ch;
  min_ch = max_ch;
  max_ch = ch;
);

rr_ch = slider4 >= 0.5 ? max(min_ch, min(max_ch, rr_ch)) : -1;

@block

while(midirecv(ofs, msg1, msg23) ? (

  in_ch < 0 || msg1 & 0x0F == in_ch ? (
    status = msg1 & 0xF0;
    msg2 = msg23 & 0x7F;
    msg3 = msg23 >> 8;

    // Note On
    status == 0x90 && msg3 ? (
      ch = note_buf[msg2];
      ch ? (
        ch -= 1;
      ) : rr_ch >= 0 ? (
        ch = rr_ch;
        rr_ch += 1;
        rr_ch > max_ch ? rr_ch = min_ch;

        ch_buf[ch] += 1;
        note_buf[msg2] = ch + 1;
        num_note += 1;
      ) : (
        ch = i = min_ch;
        n = ch_buf[ch];
        while(n && ((i += 1) <= max_ch) ? (
          m = ch_buf[i];
          m < n ? (
            n = m;
            ch = i;
          );
          1; // while
        ));

        ch_buf[ch] += 1;
        note_buf[msg2] = ch + 1;
        num_note += 1;
      );
      msg1 = status | ch;
    ) :

    // Note Off
    status == 0x80 || status == 0x90 ? (
      ch = note_buf[msg2];
      ch ? (
        ch -= 1;
        ch_buf[ch] -= 1;
        note_buf[msg2] = 0;
        num_note -= 1;
        msg1 = status | ch;
      ) : (
        msg1 = 0;
      );
    ) :

    // Polyphonic Key Pressure
    status == 0xA0 ? (
      ch = note_buf[msg2];
      msg1 = ch ? status | (ch - 1) : 0;
    ) :

    // Control Change, Program Change, Channel Pressure, Pitch Bend
    status >= 0xB0 && status <= 0xE0 ? (
      ch = min_ch;
      loop(max_ch - min_ch + 1,
        midisend(ofs, status | ch, msg23);
        ch += 1;
      );
      msg1 = 0;

      // All Notes Off
      status == 0xB0 && msg2 >= 123 && num_note ? (
        memset(ch_buf, 0, 16 + 128);
        num_note = 0;
      );
    );
  );

  msg1 ? midisend(ofs, msg1, msg23) : 1;
));
