X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=scherzo.c;h=e6a67ec1ce7bbc4e83046d9e94f2d10e7c5d1bcd;hb=ce623b95b1f8275c489a768c0c5b8eab886893c6;hp=c49584c2465e2c153f3f6690fa2df3045e829751;hpb=0261a9e510c766d7c8c69a8d66783bf050683c9f;p=scherzo diff --git a/scherzo.c b/scherzo.c index c49584c..e6a67ec 100644 --- a/scherzo.c +++ b/scherzo.c @@ -17,15 +17,25 @@ */ #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 @@ -41,8 +51,8 @@ on_delete_event_quit (unused (GtkWidget *widget), } static int -on_expose_event_draw (GtkWidget *widget, - unused (GdkEventExpose *event), +on_expose_event_draw (GtkWidget *widget, + unused (GdkEventExpose *expose), void * user_data) { scherzo_t *scherzo = user_data; @@ -61,8 +71,9 @@ on_expose_event_draw (GtkWidget *widget, cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); cairo_paint (cr); - /* Add some padding on the left/right */ - cairo_translate (cr, pad, pad); + /* Add some padding on the sides and top */ + cairo_translate (cr, pad, (int) scherzo->staff_height / 2); + score_set_staff_height (score, scherzo->staff_height); score_set_width (score, widget_width - 2 * pad); score_draw (score, cr); @@ -70,37 +81,235 @@ on_expose_event_draw (GtkWidget *widget, return TRUE; } +static int +on_key_press_event (GtkWidget *widget, + GdkEventKey *key, + void *user_data) +{ + scherzo_t *scherzo = user_data; + + switch (key->keyval) { + case GDK_KEY_plus: + case GDK_KEY_KP_Add: + case GDK_KEY_equal: + case GDK_KEY_KP_Equal: + scherzo->staff_height += 4; + gtk_widget_queue_draw (widget); + return TRUE; + break; + case GDK_KEY_minus: + case GDK_KEY_KP_Subtract: + scherzo->staff_height -= 4; + gtk_widget_queue_draw (widget); + return TRUE; + break; + case GDK_KEY_q: + case GDK_KEY_Q: + case GDK_KEY_Escape: + gtk_main_quit (); + return FALSE; + } + + /* Allow the event to propagate to other handlers. */ + 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; + int err; gtk_init (&argc, &argv); scherzo.score = score_create (NULL); - scherzo.staff_height = 20; + scherzo.staff_height = 48; score_set_staff_height (scherzo.score, scherzo.staff_height); - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + score_add_brace (scherzo.score, 2); + 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; - gtk_window_set_default_size (GTK_WINDOW (window), 600, 400); + 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); + } - g_signal_connect (window, "delete-event", + 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 (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; }