We need two NPM packages:
npm install midi i3
The "midi" pacakge is a native package, and has its own dependencies and you'll need have build tools installed. (e.g. build-essential package, python, ALSA (which is typically already installed in any linux environment, and libasound2-dev (in ubuntu) or alsa-lib-devel (in fedora/redhat) package).
We typically need to start with finding the port number of the midi device we want to use:
const midi = require('midi');
const input = new midi.Input();
const portsCount = input.getPortCount(); // this return the number of midi devices
// Based on the number of midi devices we'll step through them to find the one we need by its name:
for (let i=0; i<portsCount; i++) {
console.log(`At port number ${i} we have ${ input.getPortName(i) }`);
}
This, e.g. will spit out something like:
At port number 0 we have Midi Through:Midi Through Port-0 14:0
At port number 1 we have nanoKONTROL2:nanoKONTROL2 MIDI 1 24:0
We if want to use a nanoKONTROL2 midi ctrl, now we know it's at port 1. So:
input.openPort(1);
And to listen to events, we simply attach an event listener:
input.on('message', (_d, message) => {
console.log(`m: ${message} `);
});
Now if we move or tap on etc. any controller on our device the script will show arrays of three numbers: e.g.:
m: 176,2,97
m: 176,2,98
m: 176,2,99
(which stands for: 176 when a knob is moved, 2 is the ID of which knob I moved, and the third number is the actual value that we're sending when moving the knob. When we tap on a pad, it's usually two events: a note on (144) and a note off (e.g. 128) event, we obviously need only the note-on events when want to use pads to switch workspaces.)
This is the input part. For the i3 part, also add this at the top of your script:
const i3 = require('i3').createClient();
Now we can use the
i3.command('XXXXXXXXXX');
format to send commands to i3. In place of the Xs can be any string that is accepted by i3: you can look them up in the config file of i3 when you bind keys to commands. (e.g. "focus left", or "workplace 1" or "resize shrink width 10 px").
All that is left, is take the triplets of numbers that moving a knob (or pressing a key) shows, and turn them into i3 commands. E.g.:
input.on('message', (_d, message) => {
const [first, second, third] = message;
if (first === 144) { //a key was pressed
// calculate which workplace you want to drop to
// e.g. number of first controller minus one, extracted from the incoming number
i3.command(`workplace ${whichWorkPlace}`);
}
});