I gathered quite some knowledge about MIDI since I started developing modules for Rack a few years ago - and so I thought it is time for some generic MIDI processor within a module. As far as I know there is no similar module which can do this (except for VCV Host and using some script VST). The module is completely working though I haven’t pushed the code yet to my GitHub repository for my own reasons.
Since version 2.2.0 Rack has its own Loopback MIDI driver and with this driver MIDI-KIT can be used as an insert: incoming MIDI messages can be processed before reaching the actual MIDI module (like MIDI-CC or MIDI-CV or MIDI-MAP or one of my own modules like MIDI-CAT). Outgoing MIDI messages can be processed the same way.
The module has four CV input and four parameters. These can be used within the scripts for adding some dynamic facets if needed. So, CV-modulated MIDI message are doable - even “crazy things” like selecting the MIDI channel by CV
There is also a Trigger-input which typically will receive a clock signal for synchonizing (means, delaying) MIDI messages until an upcoming trigger.
The module is purely event-based: It is only active if a MIDI message arrives on the selected MIDI device. But the API provides ability for creating new MIDI messages, one arriving message can result in up to 16 MIDI messages.
The module uses internally a very basic implementation of a JavaScript engine (Elk) for interpreting your custom scripts. It is certainly not the fastest way for running JavaScript from C/C++ but MIDI messages are relatively rare events (in contrast to audio/dsp-processing) and I don’t think perfomence will be an issue. I suspect scripts will be quite simple most of the time.
This is a sample script I’m using for testing:
let processMidi = function(msg) {
if (midi.isCc(msg)) {
if (midi.getChannel(msg) === 1) {
midi.setChannel(msg, 2);
let msg2 = midi.create();
midi.setCc(msg2, 4, 11, midi.getValue(msg));
midi.send(msg2);
}
if (midi.getChannel(msg) === 4) {
let ch = number.ceil(param.getValue(1) * 16);
midi.setChannel(msg, ch);
log("ch " + number.toString(ch));
overlay(number.toString(midi.getValue(msg)));
}
midi.send(msg);
}
if (midi.isNoteOn(msg)) {
if (midi.getChannel(msg) === 5) {
midi.send(msg);
let msg2 = midi.create();
midi.setNoteOn(msg2, 6, midi.getNote(msg), midi.getValue(msg));
midi.sendAfterTrigger(msg2, 2);
}
}
};
As the underlaying script engine provides no standard libraries or built-in functions I needed to add everything on my own - which I consider a good thing for security and simplicity reasons. This is the list of API I implemented so far: See updated post.
As mentioned the module is fully working but I’m still working on the internal structure for some possible expanders in the future.
I would appreciate any feedback and feature ideas are welcome as always.