Created
August 22, 2020 14:59
-
-
Save liquidev/76d5953503f7c5367d8ceff9e8e36e8a to your computer and use it in GitHub Desktop.
Novation Launchpad I/O with ALSA
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <alsa/asoundlib.h> | |
#define LOG(...) do { \ | |
fprintf(stderr, __VA_ARGS__); \ | |
fflush(stderr); \ | |
} while (0) | |
int get_launchpad_client_id(snd_seq_t *seq_handle) | |
{ | |
snd_seq_client_info_t *client_info; | |
int error; | |
int own_id = snd_seq_client_id(seq_handle); | |
int launchpad_id = -1; | |
snd_seq_client_info_malloc(&client_info); | |
snd_seq_client_info_set_client(client_info, -1); | |
while ((error = snd_seq_query_next_client(seq_handle, client_info)) >= 0) { | |
int id = snd_seq_client_info_get_client(client_info); | |
const char *name = snd_seq_client_info_get_name(client_info); | |
LOG("client %i: %s", id, name); | |
if (id == own_id) { | |
LOG(" -- that's me!"); | |
} | |
if (strcmp(name, "Launchpad S") == 0) { | |
LOG(" -- that's the launchpad\n"); | |
return id; | |
} | |
LOG("\n"); | |
} | |
snd_seq_client_info_free(client_info); | |
return -1; | |
} | |
int get_launchpad_port_id(snd_seq_t *seq_handle, int launchpad_id) | |
{ | |
snd_seq_port_info_t *port_info; | |
int error; | |
snd_seq_port_info_malloc(&port_info); | |
snd_seq_port_info_set_client(port_info, launchpad_id); | |
snd_seq_port_info_set_port(port_info, -1); | |
while ((error = snd_seq_query_next_port(seq_handle, port_info)) >= 0) { | |
int id = snd_seq_port_info_get_port(port_info); | |
const char *name = snd_seq_port_info_get_name(port_info); | |
LOG("port %i: %s", id, name); | |
if (strcmp(name, "Launchpad S MIDI 1") == 0) { | |
LOG(" -- that's the port\n"); | |
return id; | |
} | |
LOG("\n"); | |
} | |
snd_seq_port_info_free(port_info); | |
return -1; | |
} | |
void setup_launchpad_io( | |
snd_seq_t *seq_handle, | |
snd_seq_addr_t *lp_address, | |
int input_port, int output_port, | |
int input_event_queue, int output_event_queue | |
) | |
{ | |
snd_seq_addr_t seq_input = { | |
.client = snd_seq_client_id(seq_handle), | |
.port = input_port, | |
}; | |
snd_seq_addr_t seq_output = { | |
.client = snd_seq_client_id(seq_handle), | |
.port = output_port, | |
}; | |
snd_seq_port_subscribe_t *sub_i, *sub_o; | |
// input | |
LOG( | |
"routing input from launchpad - %i:%i -> %i:%i\n", | |
lp_address->client, lp_address->port, | |
seq_input.client, seq_input.port | |
); | |
snd_seq_port_subscribe_malloc(&sub_i); | |
snd_seq_port_subscribe_set_sender(sub_i, lp_address); | |
snd_seq_port_subscribe_set_dest(sub_i, &seq_input); | |
snd_seq_port_subscribe_set_queue(sub_i, input_event_queue); | |
snd_seq_port_subscribe_set_time_update(sub_i, 1); | |
snd_seq_port_subscribe_set_time_real(sub_i, 1); | |
snd_seq_subscribe_port(seq_handle, sub_i); | |
snd_seq_port_subscribe_free(sub_i); | |
// output | |
LOG( | |
"routing output to launchpad - %i:%i -> %i:%i\n", | |
seq_output.client, seq_output.port, | |
lp_address->client, lp_address->port | |
); | |
snd_seq_port_subscribe_malloc(&sub_o); | |
snd_seq_port_subscribe_set_sender(sub_o, &seq_output); | |
snd_seq_port_subscribe_set_dest(sub_o, lp_address); | |
snd_seq_port_subscribe_set_queue(sub_o, output_event_queue); | |
snd_seq_subscribe_port(seq_handle, sub_o); | |
snd_seq_port_subscribe_free(sub_o); | |
} | |
int main(void) | |
{ | |
snd_seq_t *seq_handle; | |
int error; | |
int input_port, output_port; | |
int input_event_queue, output_event_queue; | |
snd_seq_addr_t lp_address; | |
LOG("opening\n"); | |
error = snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_DUPLEX, 0); | |
if (error < 0) { | |
return 1; | |
} | |
LOG("setting client name\n"); | |
if (snd_seq_set_client_name(seq_handle, "ALSA MIDI testing")) { | |
return 2; | |
} | |
lp_address.client = get_launchpad_client_id(seq_handle); | |
lp_address.port = get_launchpad_port_id(seq_handle, lp_address.client); | |
LOG("launchpad address - %i:%i\n", lp_address.client, lp_address.port); | |
LOG("creating input port\n"); | |
input_port = snd_seq_create_simple_port( | |
seq_handle, "launchpad_input", | |
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, | |
SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION | |
); | |
if (input_port < 0) { | |
return 3; | |
} | |
output_port = snd_seq_create_simple_port( | |
seq_handle, "launchpad_output", | |
SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, | |
SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION | |
); | |
if (output_port < 0) { | |
return 3; | |
} | |
LOG("creating event queues\n"); | |
input_event_queue = snd_seq_alloc_named_queue(seq_handle, "input_events"); | |
if (input_event_queue < 0) { | |
return 4; | |
} | |
output_event_queue = snd_seq_alloc_named_queue(seq_handle, "output_events"); | |
if (output_event_queue < 0) { | |
return 4; | |
} | |
setup_launchpad_io( | |
seq_handle, &lp_address, | |
input_port, output_port, | |
input_event_queue, output_event_queue | |
); | |
LOG("listening for events\n"); | |
for (;;) { | |
snd_seq_event_t *event = 0; | |
snd_seq_event_input(seq_handle, &event); | |
if (event->type & SND_SEQ_EVENT_NOTEON) { | |
snd_seq_ev_note_t note = event->data.note; | |
LOG( | |
"note on: CH%i N%i V%i OV%i D%i\n", | |
note.channel, note.note, | |
note.velocity, note.off_velocity, | |
note.duration | |
); | |
snd_seq_ev_set_source(event, output_port); | |
snd_seq_ev_set_subs(event); | |
snd_seq_ev_set_direct(event); | |
snd_seq_event_output(seq_handle, event); | |
snd_seq_drain_output(seq_handle); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment