Skip to content

Instantly share code, notes, and snippets.

@gyula-ny
Last active September 6, 2022 23:50
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save gyula-ny/0e1b323fac114d416a39b8412bf67c25 to your computer and use it in GitHub Desktop.
Save gyula-ny/0e1b323fac114d416a39b8412bf67c25 to your computer and use it in GitHub Desktop.
Control i3 via IPC with a MIDI device.

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}`);
  }
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment