LEARNING MIDI WITH THE NOVATION LAUNCHPAD
       
 (IMG) Launchpad lit up showing a happy face
       
       Novation Launchpad (NOVLPD01) is a MIDI device. It features a grid of
       70 soft LED buttons capable of being independently addressed and
       illuminated in various brightness of green, red, yellow, and
       amber. When pressed, a button will transmit a message for the initial
       strike (press) and its termination (release).
       
       I bought this device when it was first released around 2010. At the
       time, I was making music with Ableton Live and wanted a keyboard-less
       way of triggering clips. Later, I would use the Launchpad as an
       interface and/or display for projects (e.g.: Conway's Game of Life
       implementation) built using Processing, a programming language and
       environment built on top of Java. Eventually, it was put in a box as
       my other interests emerged.
       
       I recently resurfaced the device. It is very pretty, fun to touch, and
       certainly can have some use in my life today. So I'm endeavoring to
       discover some yet-to-be-defined new purpose for the Launchpad. I have
       a few ideas so far: weather display, tamagatchi-like pet, temperature
       display, controller and partial display for a 8-bit-ish paint
       program. The Launchpad is capable of much and I have hardly scratched
       the surface for what it can do or how it can be used.
       
       This phlog covers my attempt and success to get setup with
       documentation, programming and debugging tools for working with the
       Launchpad. To my surprise and delight, the success of this attempt
       came quick and easy. I expected headaches, confusion, and the usual
       tremors of incapability. But it took little more than a lazy Sunday
       afternoon to feel like this project will work out ok!
       
       
       MIDI communication
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       In this section I captured in brief what I used to discover and begin
       using the MIDI device. The short of it is: I needed ALSA and its
       development libraries. That's it! :) I didn't have to install any USB
       drivers or weird bits of firmware. The Launchpad was immediately
       recognized by `dbus' when plugged in, and my probes with ALSA's
       `amidi' tool worked once I found the right messages to send. Anyways,
       read on to learn the intricacies.
       
       
       Over command line
       ----------------------------------------------------------------------
       `lsusb' shows the file descriptor corresponding to the device. This
       wasn't important for sending messages or getting them, but I found it
       helpful to verify and understand the device's location. The output:
       
       ,----
       | Bus 001 Device 015: ID 1235:000e Focusrite-Novation Launchpad
       `----
       
       This means that the device's file node (is that the right language?)
       is `/dev/bus/usb/001/015'.
       
       `amidi -l' shows the MIDI device name for the device. This was crucial
       for revealing the name needed to send and receive communications. Its
       output:
       
       ,----
       | Dir Device    Name
       | IO  hw:2,0,0  Launchpad MIDI 1
       `----
       
       `amidi -p hw:2,0,0 -d' dumps all MIDI messages from the device in
       hex. This was helpful for verifying that the buttons worked.
       
       `amidi -p hw:2,0,0 -S "B0 00 7D"' sends a MIDI message to the device
       (hw:2,0,0) in hex. Sending this message and seeing the buttons all
       light up was a happy moment.
       
       More MIDI messages can be inferred or copied from the Launchpad
       programmer's reference (linked at end). Here are some examples:
       
       - "B0 00 7D": Reset device and turn all LEDs on amber high.
       - "90 01 3C": Turn second LED to green high.
       - "80 00 00": Turn first LED to off.
       
       These messages can be tested using `amidi', or used in a program,
       which I illustrate in the next section.
       
       
       Using <alsa/asoundlib.h>
       ----------------------------------------------------------------------
       `alsa-lib-devel' library needs to be installed. C programs using
       library are compiled with `-lasound' flag. A very basic program to
       send a single message is illustrated below:
       
       ,----
       | #include <alsa/asoundlib.h>
       | int main() {
       |     int err;
       |     snd_rawmidi_t *handle_out = 0;
       |     err = snd_rawmidi_open(NULL, &handle_out, "hw:2,0,0", SND_RAWMIDI_SYNC);    
       |     if (err) {
       |         printf("Error opening ALSA sequencer: %s\n", snd_strerror(err));
       |         return 1;
       |     }
       |     uint8_t buffer[] = {0xB0, 0x00, 0x7D};
       |     snd_rawmidi_write(handle_out, buffer, sizeof(buffer));            
       |     return 1;
       | }
       `----
       
       After the program successfully compiled and ran, and the button lit
       up, I knew I had cracked this project open.
       
       
       Misdirections and lessons learned
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       ChatGPT seemed to hallucinate a CommonLisp library called `cl-midi'
       that could interface with a MIDI device. After looking around, I saw
       no such library capable of bi-directional communication to a
       device. There is a library called `midi', but that is for reading and
       writing MIDI files.
       
       Clojure has a MIDI library for talking to devices. I briefly
       considered using that library, since a friendly tutorial was available
       for MIDI (). But I haven't learned much about Clojure yet, so thought
       best to avoid.
       
       Novation has legacy documentation available for their Launchpad
       devices. At first, I read and referenced the programmer's guide for
       the Launchpad S. This was a mistake I only caught and corrected
       (crucially) later.
       
       The Launchpad needs three byte messages, and always (mostly) listens
       on MIDI channel 1. At first, I was including the channel number in my
       messages, resulting in four byte messages. Reading the manual
       straightened this out for me. Like I paraphrased, it states:
       
       ,----
       | Launchpad transmits and receives on MIDI channel 1. There is one exception to
       | this, which will be covered later, but it is not essential to learn it.
       | Hence a Launchpad MIDI message is always three bytes long. 
       `----
       
       
       Example: paint program
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       Below is the code to a very primitive paint program. Requires
       `alsa-lib-devel', compiles with `-l lasound' flag.
       
       ,----
       | #include <alsa/asoundlib.h>
       | int main() {
       |     int err_in, err_out;
       |     snd_rawmidi_t *handle_out, *handle_in = 0;
       |     err_out = snd_rawmidi_open(NULL, &handle_out, "hw:2,0,0", SND_RAWMIDI_SYNC);
       |     err_in = snd_rawmidi_open(&handle_in, NULL, "hw:2,0,0", SND_RAWMIDI_SYNC); // check: not sure last arg is needed?
       |     if (err_out || err_in) {
       |         printf("Error opening ALSA sequencer: %s\n", snd_strerror(err_out || err_in)); // fix: won't show both errors
       |       return 1;
       |     }
       |     int i;
       |     ssize_t ret;
       |     unsigned char buf[1024];
       |     int selected_color = 0;
       |     int colors[] = {0x00, 0x7D, 0x1D, 0x3F, 0x0D, 0x0F, 0x3C, 0x1C, 0x3C};    
       | 
       |     while (1) {
       |       ret = snd_rawmidi_read(handle_in, buf, sizeof(buf));
       |       if (ret < 0) {
       |       fprintf(stderr, "read error: %d - %s\n", (int)ret, snd_strerror(ret));
       |       }
       |       fprintf(stderr, "%02x %02x %02x\n", buf[0], buf[1], buf[2]);
       |       // button: erase all
       |       if (buf[0] == 0x68 && buf[1] == 0x7f) {
       |       uint8_t out_buffer[] = {0xB0, 0x00, 0x00}; // harsh! will reset device duty cycle, buffer mode, mappings.
       |       snd_rawmidi_write(handle_out, out_buffer, sizeof(out_buffer));
       |       continue;
       Happy helping ☃ here: You tried to output a spurious TAB character. This will break gopher. Please review your scripts. Have a nice day!
       |       // button: change color
       |       if (buf[0] == 0x78 && buf[1] == 0x7f) {
       |       selected_color++;
       |       }
       |       if (selected_color >= sizeof(colors)/sizeof(int)) {
       |       selected_color = 0;
       |       }
       |       uint8_t out_buffer[] = {0x90, buf[0], colors[selected_color]};
       |       snd_rawmidi_write(handle_out, out_buffer, sizeof(out_buffer));
       |     }
       |     return 1;
       | }
       `----
       
       
       References
       ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++