desc:Midi MPE to single channel tags: MIDI processing routing author: Michael Schnell (mschnell@bschnell.de) version: 1.0 changelog: initial release donation: United Nations Foundation http://www.unfoundation.org/ about: ## Description This plugin combines the multiple channels in an MPE Midi stream to a single channel to allow for feeding a single polyphonic or monophonic instrument. All incoming midi messages are routed to a single channel. Up to three three realtime control messages are modified on the fly. All other messages are not modified. Each of the three message types can be chosen to be either of a CC message with a defined number, Pitch Wheel, or Channel Pressure. For each of the three, the incoming values are monitored and the current state is stored according to the input channel, i.e. for any keys simultaneously pressed on an MPE aware keyboard. In order to create a consistent value for the non-MPE-aware instrument to be played, appropriate modified messages for these control values are generated any time either such a control value change message is received or a Note-On or Note-Off message is received. This message will denote either the last received value ("latest", which in effect provides no change to that value), or a combination of the just received message and current values of all channels that currently hold a running sound, according to the previous Note-On and Note-Off messages. In "Polyphonic" mode, this combination will be computed as either of the minimum, the average, or the maximum. Hence the played polyphonic sound will be provided with more reasonable control values than just adhering to the latest value present on any of the MPE channels. In "Monophonic" mode, the three selected realtime control messages are never modified, but blocked while two or more channels provide a running sound. This allows for rather normal behavior of instruments that detect legato note transitions and trills. A graphic shows the currently "running" channels and the saved values of the three managed realtime controls. The last line shows the last value sent out. Moreover the first number in the last line denotes that one of the following states has been encountered since the last start: 1: a Note On has been detected on an already running channel. 2: a Note Off has been detected on a not running channel. 4: two running channel at the same time in "Monophonic" mode, resulting in blocking realtime messages. The second number shows the number of currently "running" channels. // ## usage // NO WARRANTY IS GRANTED. THIS PLUG-IN IS PROVIDED ON AN "AS IS" BASIS, WITHOUT // WARRANTY OF ANY KIND. NO LIABILITY IS GRANTED, INCLUDING, BUT NOT LIMITED TO, // ANY DIRECT OR INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGE ARISING // OUT OF THE USE OR INABILITY TO USE THIS PLUG-IN, COMPUTER FAILTURE OF // MALFUNCTION INCLUDED. THE USE OF THE SOURCE CODE, EITHER PARTIALLY OR IN // TOTAL, IS ONLY GRANTED, IF USED IN THE SENSE OF THE AUTHOR'S INTENTION, AND // USED WITH ACKNOWLEDGEMENT OF THE AUTHOR. FURTHERMORE IS THIS PLUG-IN A THIRD // PARTY CONTRIBUTION, EVEN IF INCLUDED IN REAPER(TM), COCKOS INCORPORATED OR // ITS AFFILIATES HAVE NOTHING TO DO WITH IT. LAST BUT NOT LEAST, BY USING THIS // PLUG-IN YOU RELINQUISH YOUR CLAIM TO SUE IT'S AUTHOR, AS WELL AS THE CLAIM TO // ENTRUST SOMEBODY ELSE WITH DOING SO. in_pin:none out_pin:none slider1:0<0,1,1{Polyphonic: compute controllers,Monophonic: support legato}>Mode slider2:1<0,15,1{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}>Output MIDI Channel slider3:128<0,129,1{0 Bank Sel M,1 Mod Wheel M,2 Breath M,3,4 Foot P M,5 Porta M,6 Data Entry M,7 Vol M,8 Balance M,9,10 Pan M,11 Expression M,12 Ctrl 1 M,13 Ctrl 2 M,14,15,16 GP Slider 1,17 GP Slider 2,18 GP Slider 3,19 GP Slider 4,20,21,22,23,24,25,26,27,28,29,30,31,32 Bank Sel L,33 Mod Wheel L,34 Breath L,35,36 Foot P L,37 Porta L,38 Data Entry L,39 Vol L,40 Balance L,41,42 Pan L,43 Expression L,44 Ctrl 1 L,45 Ctrl 2 L,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64 Hold P sw,65 Porta sw,66 Sustenuto sw,67 Soft P sw,68 Legato P sw,69 Hold 2 P sw,70 S.Variation,71 S.Timbre,72 S.Release,73 S.Attack,74 S.Brightness,75 S.Ctrl 6,76 S.Ctrl 7,77 S.Ctrl 8,78 S.Ctrl 9,79 S.Ctrl 10,80 GP B.1 sw,81 GP B.2 sw,82 GP B.3 sw,83 GP B.4 sw,84,85,86,87,88,89,90,91 Effects Lv,92 Trem Lv,93 Chorus Lv,94 Celeste Lv,95 Phaser Lv,96 Data B. Inc,97 Data B. Dec,98 NRP L,99 NRP M,100 RP L,101 RP M,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,Pitch Wheel, Channel Pressure}>Controller 1 slider4:2<0,2,1{latest,minimum,average,maximum}>Controller 1 Mode slider5:129<0,129,1{0 Bank Sel M,1 Mod Wheel M,2 Breath M,3,4 Foot P M,5 Porta M,6 Data Entry M,7 Vol M,8 Balance M,9,10 Pan M,11 Expression M,12 Ctrl 1 M,13 Ctrl 2 M,14,15,16 GP Slider 1,17 GP Slider 2,18 GP Slider 3,19 GP Slider 4,20,21,22,23,24,25,26,27,28,29,30,31,32 Bank Sel L,33 Mod Wheel L,34 Breath L,35,36 Foot P L,37 Porta L,38 Data Entry L,39 Vol L,40 Balance L,41,42 Pan L,43 Expression L,44 Ctrl 1 L,45 Ctrl 2 L,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64 Hold P sw,65 Porta sw,66 Sustenuto sw,67 Soft P sw,68 Legato P sw,69 Hold 2 P sw,70 S.Variation,71 S.Timbre,72 S.Release,73 S.Attack,74 S.Brightness,75 S.Ctrl 6,76 S.Ctrl 7,77 S.Ctrl 8,78 S.Ctrl 9,79 S.Ctrl 10,80 GP B.1 sw,81 GP B.2 sw,82 GP B.3 sw,83 GP B.4 sw,84,85,86,87,88,89,90,91 Effects Lv,92 Trem Lv,93 Chorus Lv,94 Celeste Lv,95 Phaser Lv,96 Data B. Inc,97 Data B. Dec,98 NRP L,99 NRP M,100 RP L,101 RP M,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,Pitch Wheel, Channel Pressure}>Controller 2 slider6:2<0,2,1{latest,minimum,average,maximum}>Controller 2 Mode slider7:074<0,129,1{0 Bank Sel M,1 Mod Wheel M,2 Breath M,3,4 Foot P M,5 Porta M,6 Data Entry M,7 Vol M,8 Balance M,9,10 Pan M,11 Expression M,12 Ctrl 1 M,13 Ctrl 2 M,14,15,16 GP Slider 1,17 GP Slider 2,18 GP Slider 3,19 GP Slider 4,20,21,22,23,24,25,26,27,28,29,30,31,32 Bank Sel L,33 Mod Wheel L,34 Breath L,35,36 Foot P L,37 Porta L,38 Data Entry L,39 Vol L,40 Balance L,41,42 Pan L,43 Expression L,44 Ctrl 1 L,45 Ctrl 2 L,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64 Hold P sw,65 Porta sw,66 Sustenuto sw,67 Soft P sw,68 Legato P sw,69 Hold 2 P sw,70 S.Variation,71 S.Timbre,72 S.Release,73 S.Attack,74 S.Brightness,75 S.Ctrl 6,76 S.Ctrl 7,77 S.Ctrl 8,78 S.Ctrl 9,79 S.Ctrl 10,80 GP B.1 sw,81 GP B.2 sw,82 GP B.3 sw,83 GP B.4 sw,84,85,86,87,88,89,90,91 Effects Lv,92 Trem Lv,93 Chorus Lv,94 Celeste Lv,95 Phaser Lv,96 Data B. Inc,97 Data B. Dec,98 NRP L,99 NRP M,100 RP L,101 RP M,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,Pitch Wheel, Channel Pressure}>Controller 3 slider8:2<0,2,1{latest,minimum,average,maximum}>Controller 3 Mode @init //ext_midi_bus = 1; NOTE_ON = 0x90; NOTE_OFF = 0x80; CHANNEL_PRESSURE= 0xD0; PITCH_BEND = 0xE0; CC = 0xB0; MISSING = 99999999999999; running = 0*16; // array for Note-On / Note-Off state contr1 = 1*16; // array for storing controller 1 contr2 = 2*16; // array for storing controller 2 contr3 = 3*16; // array for storing controller 3 //msg_nr = 0; // total message number. Do we need to consider overrun ? @slider mode = slider1; output_channel = slider2; cc1 = slider3; cc1mode = slider4; cc2 = slider5; cc2mode = slider6; cc3 = slider7; cc3mode = slider8; memset(running, 0, 16); memset(contr1, MISSING, 16); memset(contr2, MISSING, 16); memset(contr3, MISSING, 16); y = 0; ____ss = 0; ____cc1m = MISSING; ____cc2m = MISSING; ____cc3m = MISSING; @block function avg(c, cur, mode, channel) local(i, j, a) ( mode == 0 ? ( a = cur; ) : mode == 1 ? ( // Minimum i = 0; j = 0; a = 999999; loop(16, running[i] | (i == channel) ? ( c[i] != MISSING ? ( j += 1; a > c[i] ? a = c[i]; ); ); i += 1; ); !j ? ( a = cur; ); ) : mode == 2 ? ( // Avarage i = 0; j = 0; a = 0; loop(16, running[i] | (i == channel) ? ( c[i] != MISSING ? ( j += 1; a += c[i]; ); ); i += 1; ); j ? ( a /= j; ) : ( a = cur; ); ) : mode == 3 ? ( // Maximum i = 0; j = 0; a = -999999; loop(16, running[i] | (i == channel) ? ( c[i] != MISSING ? ( j += 1; a < c[i] ? a = c[i]; ); ); i += 1; ); !j ? ( a = cur; ); ) : ( a = MISSING; ); a; ); function sendstatus(m, cc_nr) local (message, msg2, msg3) ( cc == 128 ? ( message = PITCH_BEND; msg2 = m & 0x7F; msg3 = m >> 7; ) : cc == 129 ? ( message = CHANNEL_PRESSURE; msg2 = m; msg3 = 0; ) : ( message = CC; msg2 = cc_nr; msg3 = m; ); midisend(offset, message + output_channel, msg2, msg3); ); while (midirecv(offset, msg1, msg2, msg3)) ( // msg_nr += 1; f = 0; ____f = 0; input_channel = (msg1 & 0x0f); message = (msg1 & 0xf0); yon = (message == NOTE_ON) && (msg3 != 0) ; yoff = (message == NOTE_OFF) || ((message == NOTE_ON) && (msg3 == 0)); cc1m = MISSING; cc2m = MISSING; cc3m = MISSING; yon ? ( running[input_channel] ? ( ____ss |= 1; // Note On at an already running channel ); running[input_channel] = 1; ) : yoff ? ( !running[input_channel] ? ( ____ss |= 2; // Note OFF at a nnot running channel ); running[input_channel] = 0; // presumung MPE does not generate overlapping notes in a single channel ); yon || yoff ? ( y = 0; i = 0; loop (16, y += running[i]; i += 1; ); ____running = y; y ? ( m = avg(contr1, MISSING, cc1mode, MISSING); m != MISSING ? ( cc1m = m; !mode ? ____cc1m = m; ); m = avg(contr2, MISSING, cc2mode, MISSING); m != MISSING ? ( cc2m = m; !mode ? ____cc2m = m; ); m = avg(contr3, MISSING, cc3mode, MISSING); m != MISSING ? ( cc2m = m; !mode ? ____cc3m = m; ); ); ) : message == CC ? ( msg2 == cc1 ? ( contr1[input_channel] = msg3; m = avg(contr1, msg3, cc1mode, input_channel); !mode ? ____cc1m = m; f = 1; ____f = 1; ) : msg2 == cc2 ? ( contr2[input_channel] = msg3; m = avg(contr2, msg3, cc2mode, input_channel); !mode ? ____cc2m = m; f = 1; ____f = 2; ) : msg2 == cc3 ? ( contr3[input_channel] = msg3; m = avg(contr3, msg3, cc3mode, input_channel); !mode ? ____cc3m = m; f = 1; ____f = 3; ); ) : message == PITCH_BEND ? ( cur = 0x80*msg3 + msg2; cc1 == 128 ? ( contr1[input_channel] = cur; m = avg(contr1, cur, cc1mode, input_channel); !mode ? ____cc1m = m; f = 2; ____f = 1; ) : cc2 == 128 ? ( contr2[input_channel] = cur; m = avg(contr2, cur, cc2mode, input_channel); !mode ? ____cc2m = m; f = 2; ____f = 2; ) : cc3 == 128 ? ( contr3[input_channel] = cur; m = avg(contr2, cur, cc3mode, input_channel); !mode ? ____cc3m = m; f = 2; ____f = 3; ); ) : message == CHANNEL_PRESSURE ? ( cc1 == 129 ? ( contr1[input_channel] = msg2; m = avg(contr1, msg2, cc1mode, input_channel); !mode ? ____cc1m = m; f = 3; ____f = 1; ) : cc2 == 129 ? ( contr2[input_channel] = msg2; m = avg(contr2, msg2, cc2mode, input_channel); !mode ? ____cc2m = m; f = 3; ____f = 2; ) : cc3 == 129 ? ( contr3[input_channel] = msg2; m = avg(contr3, msg2, cc3mode, input_channel); !mode ? ____cc3m = m; f = 3; ____f = 3; ); ); ! mode ? ( // "Polyphonix" f ? ( // modified ? f == 1 ? ( // CC msg3 = m; ) : f == 2 ? ( // Pitch Bend msg2 = m & 0x7F; msg3 = m >> 7; ) : ( // 3 : Channel Pressure msg2 = m; ); ); cc1m != MISSING ? ( sendstatus(cc1m, cc1); ); cc2m != MISSING ? ( sendstatus(cc2m, cc2); ); cc3m != MISSING ? ( sendstatus(cc3m, cc3); ); midisend(offset, message + output_channel, msg2, msg3); ) : ( // "Monophonic" z = 1; y > 1 ? ( // two or more notes running f ? ( // one of the three selected controllers z = 0; ____ss |= 4; // two running channels in "Monophonic" mode -> block appropriate messages ); ); z ? ( midisend(offset, message + output_channel, msg2, msg3); ____f == 1 ? ( ____cc1m = m; ) : ____f == 2 ? ( ____cc2m = m; ) : ____f == 3 ? ( ____cc3m = m; ); ); ); ); @gfx 640 400 function convert(val, nr) ( val == MISSING ? ( " "; ) : ( nr == 128 ? ( sprintf(#, "%d", val-8192); ) : ( sprintf(#, "%d", val); ); ) ); gfx_r=gfx_g=gfx_b=1; gfx_a=1; gfx_setfont(2,#font,18); gfx_y = 5; gfx_x = 10; gfx_drawstr("Chan"); gfx_x = 10 + 40; gfx_drawstr("Run"); gfx_x = 10 + 80 * 1; gfx_drawstr("Contr. 1"); gfx_x = 10 + 80 * 2; gfx_drawstr("Contr. 2"); gfx_x = 10 + 80 * 3; gfx_drawstr("Contr. 3"); /* gfx_x = 10 + 80 * 4; gfx_drawstr("N_ON1"); gfx_x = 10 + 80 * 5; gfx_drawstr("N_ON_2"); gfx_x = 10 + 80 * 6; gfx_drawstr("Time"); */ _i = 0; loop (16, gfx_y = 30 + 20 * _i; gfx_x = 10; #_t = sprintf(#, "%d", _i+1); gfx_drawstr(#_t); gfx_x = 10 + 40; running[_i] ? gfx_drawstr ("*"); gfx_x = 10 + 80 * 1; #_t = convert(contr1[_i], cc1); gfx_drawstr(#_t); gfx_x = 10 + 80 * 2; #_t = convert(contr2[_i], cc2); gfx_drawstr(#_t); gfx_x = 10 + 80 * 3; #_t = convert(contr3[_i], cc3); gfx_drawstr(#_t); /* gfx_x = 10 + 80 * 4; #_t = sprintf(#, "%2.2x", _n1); gfx_drawstr(#_t); gfx_x = 10 + 80 * 4 + 30; #_t = sprintf(#, "%2.2x", _v1); gfx_drawstr(#_t); gfx_x = 10 + 80 * 5; #_t = sprintf(#, "%2.2x", _n2); gfx_drawstr(#_t); gfx_x = 10 + 80 * 5 + 30; #_t = sprintf(#, "%2.2x", _v2); gfx_drawstr(#_t); gfx_x = 10 + 80 * 6; #_t = sprintf(#, "%d", _t); gfx_drawstr(#_t); */ _i += 1; ); /* _n0 = (____0 >> 7) & 0x7F; _n1 = (____1 >> 7) & 0x7F; _n2 = (____2 >> 7) & 0x7F; _n11 = (____1 >> 7) & 0x7F; _n21 = (____2 >> 7) & 0x7F; */ gfx_y = 30 + 20 * (_i+1); /* gfx_x = 10 + 80 * 0; #_t = sprintf(#, "%2.2x", _n0); gfx_drawstr(#_t); */ gfx_x = 10 + 40 * 1; #_t = sprintf(#, "%d", ____running); gfx_drawstr(#_t); gfx_x = 10 + 80 * 1; #_t = convert(____cc1m, cc1); gfx_drawstr(#_t); gfx_x = 10 + 80 * 2; #_t = convert(____cc2m, cc2); gfx_drawstr(#_t); gfx_x = 10 + 80 * 3; #_t = convert(____cc3m, cc3); gfx_drawstr(#_t); gfx_x = 10 + 80 * 0; #_t = sprintf(#, "%d", ____ss); gfx_drawstr(#_t); /* gfx_x = 10 + 80 * 5; #_t = sprintf(#, "%2.2x", ____s); gfx_drawstr(#_t); */