From 8ad0ae52c3d5bdbbe8757ed530e9e51ba2e3afe0 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Wed, 2 Oct 2013 11:10:49 -0700 Subject: [PATCH] Listen for MIDI event through ALSA "virtual" rawmidi interface This allows for connecting up any MIDI device (hardware or software) through programs such as aconnect or qjackctl that allow one to connect MIDI outputs to inputs. This is quite convenient for connecting a virtual keyboard (such as vkeybd or vmpk) to scherzo. It can also be used to connect a real, hardware MIDI keyboard to scherzo, but we keep around the code that does a direct connection to /dev/midi1 since that simplifies things by reducing one step in the usage, (the user needn't run aconnect nor qjackctl to make the connection). --- scherzo.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/scherzo.c b/scherzo.c index bdd1a0f..0ef44ea 100644 --- a/scherzo.c +++ b/scherzo.c @@ -66,8 +66,6 @@ typedef struct scherzo * keyboard". Any "piano keyboard" key knows its own octave and * accidental already. */ int keyboard_octave; - - int midi_fd; snd_midi_event_t *snd_midi_event; mnemon_t mnemon; @@ -1182,17 +1180,25 @@ _score_challenge (scherzo_t *scherzo) } #endif +typedef struct +{ + scherzo_t *scherzo; + int fd; +} scherzo_midi_closure_t; + static int on_midi_input (unused (GIOChannel *channel), unused (GIOCondition condition), void *user_data) { + scherzo_midi_closure_t *closure = user_data; unsigned char buf[MIDI_BUF_SIZE], *next; - scherzo_t *scherzo = user_data; + scherzo_t *scherzo = closure->scherzo; + int fd = closure->fd; ssize_t remaining; snd_seq_event_t event; - remaining = read (scherzo->midi_fd, buf, MIDI_BUF_SIZE); + remaining = read (fd, buf, MIDI_BUF_SIZE); next = buf; while (remaining) { @@ -1244,11 +1250,38 @@ on_midi_input (unused (GIOChannel *channel), return TRUE; } +static void * +listen_to_alsa_midi (void *data) +{ + scherzo_midi_closure_t *closure = data; + int out_fd = closure->fd; + snd_rawmidi_t *midi_in; + unsigned char buf[MIDI_BUF_SIZE]; + ssize_t bytes; + int err; + + err = snd_rawmidi_open (&midi_in, NULL, "virtual", 0); + if (err) { + fprintf (stderr, "Failed to open virtual MIDI handle."); + return NULL; + } + + while (1) { + bytes = snd_rawmidi_read (midi_in, buf, MIDI_BUF_SIZE); + write (out_fd, buf, bytes); + } + + return NULL; +} + int main (int argc, char *argv[]) { GtkWidget *drawing_area; + GIOChannel *channel; scherzo_t scherzo; + int alsa_midi_fd[2]; + scherzo_midi_closure_t midi_out, alsa_midi_in, alsa_midi_out; int err; srand (time (NULL)); @@ -1294,17 +1327,29 @@ main (int argc, char *argv[]) } #define MIDI_DEVICE "/dev/midi1" - scherzo.midi_fd = open (MIDI_DEVICE, O_RDONLY); - if (scherzo.midi_fd < 0) { - printf ("failed to open " MIDI_DEVICE ". Midi input will not be available.\n"); - } else { - GIOChannel *channel; - channel = g_io_channel_unix_new (scherzo.midi_fd); + midi_out.scherzo = &scherzo; + midi_out.fd = open (MIDI_DEVICE, O_RDONLY); + if (midi_out.fd < 0) { + printf ("Failed to open " MIDI_DEVICE ". You will need to connect a MIDI device.\n"); + } else { + channel = g_io_channel_unix_new (midi_out.fd); g_io_channel_set_encoding (channel, NULL, NULL); - g_io_add_watch (channel, G_IO_IN, on_midi_input, &scherzo); + g_io_add_watch (channel, G_IO_IN, on_midi_input, &midi_out); } + pipe (alsa_midi_fd); + + alsa_midi_in.scherzo = &scherzo; + alsa_midi_in.fd = alsa_midi_fd[1]; + g_thread_new ("scherzo-vmidi", listen_to_alsa_midi, &alsa_midi_in); + + alsa_midi_out.scherzo = &scherzo; + alsa_midi_out.fd = alsa_midi_fd[0]; + channel = g_io_channel_unix_new (alsa_midi_out.fd); + g_io_channel_set_encoding (channel, NULL, NULL); + g_io_add_watch (channel, G_IO_IN, on_midi_input, &alsa_midi_out); + scherzo.window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (scherzo.window), 1000, 600); -- 2.43.0