X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=scherzo.c;h=e6a67ec1ce7bbc4e83046d9e94f2d10e7c5d1bcd;hb=03f4abc3182dfe2602d44d38ce7b3db2892e018e;hp=329884da93764973e0d595265d85dd5e2ac9b061;hpb=e808e5df23cb5d8deb23296c38d1eb6b4581563d;p=scherzo diff --git a/scherzo.c b/scherzo.c index 329884d..e6a67ec 100644 --- a/scherzo.c +++ b/scherzo.c @@ -19,14 +19,23 @@ #include #include +#include + #include "score.h" #define unused(foo) foo __attribute__((unused)) +#define MIDI_BUF_SIZE 4096 + typedef struct scherzo { + GtkWidget *window; score_t *score; int staff_height; + score_staff_t *treble; + score_staff_t *bass; + int midi_fd; + snd_midi_event_t *snd_midi_event; } scherzo_t; static int @@ -105,14 +114,148 @@ on_key_press_event (GtkWidget *widget, return FALSE; } +static void +_midi_to_score_pitch_and_octave (unsigned char midi_note, + score_pitch_t *pitch, + int *octave) +{ + *octave = midi_note / 12 - 1; + + switch (midi_note % 12) + { + case 0: + *pitch = SCORE_PITCH_C; + break; + case 1: + *pitch = SCORE_PITCH_Cs; + break; + case 2: + *pitch = SCORE_PITCH_D; + break; + case 3: + *pitch = SCORE_PITCH_Ds; + break; + case 4: + *pitch = SCORE_PITCH_E; + break; + case 5: + *pitch = SCORE_PITCH_F; + break; + case 6: + *pitch = SCORE_PITCH_Fs; + break; + case 7: + *pitch = SCORE_PITCH_G; + break; + case 8: + *pitch = SCORE_PITCH_Gs; + break; + case 9: + *pitch = SCORE_PITCH_A; + break; + case 10: + *pitch = SCORE_PITCH_As; + break; + case 11: + *pitch = SCORE_PITCH_B; + break; + } +} + +static void +scherzo_add_note_midi (scherzo_t *scherzo, unsigned char midi_note) +{ + score_staff_t *staff; + score_pitch_t pitch; + int octave; + + /* Anything at Middle C and above goes on the treble staff by default. */ + if (midi_note >= 60) + staff = scherzo->treble; + else + staff = scherzo->bass; + + _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave); + + score_staff_add_note (staff, pitch, octave, SCORE_DURATION_WHOLE); +} + +static void +scherzo_remove_note_midi (scherzo_t *scherzo, unsigned char midi_note) +{ + score_staff_t *staff; + score_pitch_t pitch; + int octave; + score_note_t *note; + + /* Anything at Middle C and above goes on the treble staff by default. */ + if (midi_note >= 60) + staff = scherzo->treble; + else + staff = scherzo->bass; + + _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave); + + note = score_staff_find_note (staff, pitch, octave, SCORE_DURATION_WHOLE); + score_staff_remove_note (staff, note); +} + +static int +on_midi_input (unused (GIOChannel *channel), + unused (GIOCondition condition), + void *user_data) +{ + unsigned char buf[MIDI_BUF_SIZE], *next; + scherzo_t *scherzo = user_data; + ssize_t remaining; + snd_seq_event_t event; + + remaining = read (scherzo->midi_fd, buf, MIDI_BUF_SIZE); + + next = buf; + while (remaining) { + long consumed; + + consumed = snd_midi_event_encode (scherzo->snd_midi_event, + next, remaining, &event); + + remaining -= consumed; + + switch (event.type) { + case SND_SEQ_EVENT_NONE: + /* Incomplete event. Nothing to do. */ + break; + case SND_SEQ_EVENT_NOTEON: + scherzo_add_note_midi (scherzo, event.data.note.note); + gtk_widget_queue_draw (scherzo->window); + break; + case SND_SEQ_EVENT_NOTEOFF: + scherzo_remove_note_midi (scherzo, event.data.note.note); + gtk_widget_queue_draw (scherzo->window); + break; + case SND_SEQ_EVENT_CLOCK: + /* Ignore for now as my piano sends a constant stream of these. */ + break; + case SND_SEQ_EVENT_SENSING: + /* Ignore for now as my piano sends a constant stream of these. */ + break; + default: + fprintf (stderr, "Fixme: Do not yet know how to handle MIDI event %d\n", + event.type); + break; + } + } + + /* Return TRUE to continue to get called in the future. */ + return TRUE; +} + int main (int argc, char *argv[]) { - GtkWidget *window; GtkWidget *drawing_area; scherzo_t scherzo; - score_staff_t *treble; - score_staff_t *bass; + int err; gtk_init (&argc, &argv); @@ -121,49 +264,52 @@ main (int argc, char *argv[]) score_set_staff_height (scherzo.score, scherzo.staff_height); score_add_brace (scherzo.score, 2); - treble = score_add_staff (scherzo.score, SCORE_CLEF_G); - bass = score_add_staff (scherzo.score, SCORE_CLEF_F); - - score_staff_add_note (treble, SCORE_BUILD_NOTE (D, 4, WHOLE)); - score_staff_add_note (treble, SCORE_BUILD_NOTE (E, 4, WHOLE)); - score_staff_add_note (treble, SCORE_BUILD_NOTE (F, 4, WHOLE)); - score_staff_add_note (treble, SCORE_BUILD_NOTE (G, 4, WHOLE)); - score_staff_add_note (treble, SCORE_BUILD_NOTE (A, 4, WHOLE)); - score_staff_add_note (treble, SCORE_BUILD_NOTE (B, 4, WHOLE)); - score_staff_add_note (treble, SCORE_BUILD_NOTE (C, 5, WHOLE)); - score_staff_add_note (treble, SCORE_BUILD_NOTE (D, 5, WHOLE)); - - score_staff_add_note (bass, SCORE_BUILD_NOTE (B, 2, WHOLE)); - score_staff_add_note (bass, SCORE_BUILD_NOTE (C, 3, WHOLE)); - score_staff_add_note (bass, SCORE_BUILD_NOTE (D, 3, WHOLE)); - score_staff_add_note (bass, SCORE_BUILD_NOTE (E, 3, WHOLE)); - score_staff_add_note (bass, SCORE_BUILD_NOTE (F, 3, WHOLE)); - score_staff_add_note (bass, SCORE_BUILD_NOTE (G, 3, WHOLE)); - score_staff_add_note (bass, SCORE_BUILD_NOTE (A, 3, WHOLE)); - score_staff_add_note (bass, SCORE_BUILD_NOTE (B, 3, WHOLE)); - - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - - gtk_window_set_default_size (GTK_WINDOW (window), 600, 400); - - g_signal_connect (window, "delete-event", + scherzo.treble = score_add_staff (scherzo.score, SCORE_CLEF_G); + scherzo.bass = score_add_staff (scherzo.score, SCORE_CLEF_F); + + err = snd_midi_event_new (MIDI_BUF_SIZE, &scherzo.snd_midi_event); + if (err) { + fprintf (stderr, "Out of memory.\n"); + return 1; + } + +#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); + g_io_channel_set_encoding (channel, NULL, NULL); + g_io_add_watch (channel, G_IO_IN, on_midi_input, &scherzo); + } + + scherzo.window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + + gtk_window_set_default_size (GTK_WINDOW (scherzo.window), 600, 400); + + g_signal_connect (scherzo.window, "delete-event", G_CALLBACK (on_delete_event_quit), NULL); drawing_area = gtk_drawing_area_new (); - gtk_container_add (GTK_CONTAINER (window), drawing_area); + gtk_container_add (GTK_CONTAINER (scherzo.window), drawing_area); g_signal_connect (drawing_area, "expose-event", G_CALLBACK (on_expose_event_draw), &scherzo); - g_signal_connect (window, "key-press-event", + g_signal_connect (scherzo.window, "key-press-event", G_CALLBACK (on_key_press_event), &scherzo); - gtk_widget_show_all (window); + gtk_widget_show_all (scherzo.window); gtk_main (); + snd_midi_event_free (scherzo.snd_midi_event); + talloc_free (scherzo.score); + return 0; }