119 lines
3.7 KiB
Text
119 lines
3.7 KiB
Text
|
desc:Midi Monoizer
|
||
|
/* Midi Monoizer (c)2019 by Andrew Shakinovsky andrew@afrittemple.com
|
||
|
I welcome comments or suggestions.
|
||
|
Description:
|
||
|
Insert this before a VSTi in the chain. It will prevent multiple midi notes from
|
||
|
sounding at the same time. When a note is playing and another note is received,
|
||
|
a note-off is sent for the first note before(*) the new note is sent. This is akin
|
||
|
to typical monosynths when you are holding a key and then press and release another
|
||
|
key: the first key plays, then the second, and when you release the second, the first
|
||
|
key will play again. A lot of VSTi's behave in this way, but there are some that do
|
||
|
not, and that is where this becomes helpful.
|
||
|
|
||
|
(*) will be sent after the note on depending on the legato slider. If the legato slider
|
||
|
is no zero, the note off will be delayed by that many samples to allow the synth to
|
||
|
treat it as legato playing
|
||
|
*/
|
||
|
slider1:0<0,256>Legato Delay Samples
|
||
|
@init
|
||
|
stackptr=-1;
|
||
|
// array offsetting (there is only one local mem space)
|
||
|
stacknote=0;
|
||
|
stackvel=1024;
|
||
|
stackchan=2048;
|
||
|
nno_Off=0;
|
||
|
nno_Chan=0;
|
||
|
nno_Note=0;
|
||
|
@block
|
||
|
|
||
|
// play note off, applying legator delay
|
||
|
function note_off(pos, chan, note) (
|
||
|
// make sure it fits in this block given the delay
|
||
|
pos+slider1 < samplesblock ? (
|
||
|
midisend(pos+slider1,0x80 | chan, note);
|
||
|
) : (
|
||
|
// doesn't belong in this block, send it in the next block
|
||
|
nno_Off = 1 + ((pos+slider1) - samplesblock);
|
||
|
nno_Chan=chan;
|
||
|
nno_Note=note;
|
||
|
);
|
||
|
);
|
||
|
|
||
|
// do we have a note off to send?
|
||
|
nno_Off ? (
|
||
|
midisend(nno_Off,0x80 | nno_Chan, nno_Note);
|
||
|
nno_Off=0;
|
||
|
);
|
||
|
|
||
|
while (
|
||
|
// if we receive a message
|
||
|
midirecv(ofs, msg1, msg23) ? (
|
||
|
|
||
|
status = msg1 & 0xF0; // hi 4 bits
|
||
|
chan = msg1 & 0x0F; // low 4 bits
|
||
|
note = msg23 & 0xFF; //low order byte is note
|
||
|
velocity = msg23 >> 8; // high order byte is velocity
|
||
|
|
||
|
// if its a Note On
|
||
|
status == 0x90 && velocity ? (
|
||
|
// if we have items on stack
|
||
|
stackptr > -1 ? (
|
||
|
note_off(ofs, stackchan[stackptr], stacknote[stackptr]);
|
||
|
);
|
||
|
|
||
|
// push curent new note to stack
|
||
|
stackptr+=1;
|
||
|
stacknote[stackptr]=note;
|
||
|
stackvel[stackptr]=velocity;
|
||
|
stackchan[stackptr]=chan;
|
||
|
);
|
||
|
|
||
|
// if its a Note Off
|
||
|
status == 0x80 || (status == 0x90 && !velocity) ? (
|
||
|
|
||
|
// if the note doesn't match top of stack, remove it below in the stack
|
||
|
// shifting everything down (we don't care about it just get rid of it)
|
||
|
note != stacknote[stackptr] ? (
|
||
|
i=0;
|
||
|
rp=0;
|
||
|
while(i <= stackptr) (
|
||
|
stacknote[i]==note ? ( // if it matches the note, move our read position
|
||
|
rp += 1;
|
||
|
);
|
||
|
// copy source to target
|
||
|
stacknote[i] = stacknote[rp];
|
||
|
stackvel[i] = stackvel[rp];
|
||
|
stackchan[i] = stackchan[rp];
|
||
|
i += 1;
|
||
|
rp += 1;
|
||
|
);
|
||
|
// set new top of stack
|
||
|
stackptr-= (rp-i);
|
||
|
|
||
|
) : ( // otherwise, note matches top of stack ...
|
||
|
stackptr > -1 ? (
|
||
|
// pop stack
|
||
|
stackptr-=1;
|
||
|
// send note on for tos (if any)
|
||
|
midisend(ofs,0x90 | stackchan[stackptr],
|
||
|
(stackvel[stackptr] << 8) | stacknote[stackptr]);
|
||
|
);
|
||
|
);
|
||
|
):
|
||
|
|
||
|
// All Notes Off
|
||
|
status == 0xB0 && cc == 123 & n ? (
|
||
|
stackptr=-1; // discard all
|
||
|
);
|
||
|
|
||
|
// send original incoming message, but if it's a note off, apply delay
|
||
|
msg1 ? (
|
||
|
status == 0x80 || (status == 0x90 && !velocity) ? (
|
||
|
note_off(ofs, chan, note);
|
||
|
):(
|
||
|
midisend(ofs, msg1, msg23);
|
||
|
);
|
||
|
);
|
||
|
); // if we recv a msg (otherwise the while will exit)
|
||
|
); // while
|