]> git.cworth.org Git - scherzo/blob - scherzo.c
Don't bother looking at signatures that we know can't match
[scherzo] / scherzo.c
1 /* scherzo - Music notation training
2  *
3  * Copyright © 2010 Carl Worth
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see http://www.gnu.org/licenses/ .
17  */
18
19 #include <gtk/gtk.h>
20 #include <gdk/gdkkeysyms.h>
21
22 #include <asoundlib.h>
23
24 #include "score.h"
25 #include "mnemon.h"
26
27 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
28
29 #define unused(foo) foo __attribute__((unused))
30
31 #define MIDI_BUF_SIZE 4096
32
33 typedef struct challenge
34 {
35     bin_t *bin;
36     int item_index;
37     score_staff_t *staff;
38     score_note_t *note;
39
40     int satisfied;
41     int mistaken;
42 } challenge_t;
43
44 typedef struct note_group
45 {
46     void *ctx;
47     score_note_t **notes;
48     int size;
49     int num_notes;
50 } note_group_t;
51
52 typedef struct scherzo
53 {
54     void *ctx;
55
56     GtkWidget *window;
57     score_t *score;
58     int staff_height;
59     score_staff_t *treble;
60     score_staff_t *bass;
61     score_chord_t *chord;
62
63     /* This is for a "computer keyboard". Any "piano keyboard" key
64      * knows its own octave. */
65     int keyboard_octave;
66
67     int midi_fd;
68     snd_midi_event_t *snd_midi_event;
69
70     mnemon_t mnemon;
71     challenge_t challenge;
72
73     note_group_t notes_pressed;
74     note_group_t notes_pedaled;
75
76     int pedal_pressed;
77 } scherzo_t;
78
79 /* Forward declarations. */
80 static score_note_t *
81 scherzo_press_note (scherzo_t *scherzo, score_pitch_t pitch, int octave);
82
83 static void
84 scherzo_release_note (scherzo_t *scherzo, score_pitch_t pitch, int octave);
85
86 static void
87 scherzo_press_pedal (scherzo_t *scherzo);
88
89 static void
90 scherzo_release_pedal (scherzo_t *scherzo);
91
92 static void
93 _judge_note (scherzo_t *scherzo, score_note_t *note);
94
95 static void
96 _score_challenge (scherzo_t *scherzo);
97
98 static int
99 on_delete_event_quit (unused (GtkWidget *widget),
100                       unused (GdkEvent *event),
101                       unused (gpointer user_data))
102 {
103     gtk_main_quit ();
104
105     /* Returning FALSE allows the default handler for delete-event
106      * to proceed to cleanup the widget. */
107     return FALSE;
108 }
109
110 static int
111 on_expose_event_draw (GtkWidget *widget,
112                       unused (GdkEventExpose *expose),
113                       void * user_data)
114 {
115     scherzo_t *scherzo = user_data;
116     score_t *score = scherzo->score;
117     cairo_t *cr;
118     GtkAllocation allocation;
119     static const int pad = 10;
120     int widget_width;
121
122     gtk_widget_get_allocation (widget, &allocation);
123     widget_width = allocation.width;
124
125     cr = gdk_cairo_create (widget->window);
126
127     /* White background */
128     cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
129     cairo_paint (cr);
130
131     /* Add some padding on the sides and top */
132     cairo_translate (cr, pad, scherzo->staff_height);
133     score_set_staff_height (score, scherzo->staff_height);
134     score_set_width (score, widget_width - 2 * pad);
135
136     score_draw (score, cr);
137  
138     return TRUE;
139 }
140
141 static int
142 on_key_press_event (GtkWidget *widget,
143                     GdkEventKey *key,
144                     void *user_data)
145 {
146     scherzo_t *scherzo = user_data;
147     int octave;
148     score_pitch_t pitch;
149
150     if (scherzo->challenge.note)
151         octave = scherzo->challenge.note->octave;
152     else
153         octave = scherzo->keyboard_octave;
154
155     switch (key->keyval) {
156     case GDK_KEY_plus:
157     case GDK_KEY_KP_Add:
158     case GDK_KEY_equal:
159     case GDK_KEY_KP_Equal:
160         scherzo->staff_height += 4;
161         gtk_widget_queue_draw (widget);
162         return TRUE;
163         break;
164     case GDK_KEY_minus:
165     case GDK_KEY_KP_Subtract:
166         scherzo->staff_height -= 4;
167         gtk_widget_queue_draw (widget);
168         return TRUE;
169         break;
170     case GDK_KEY_q:
171     case GDK_KEY_Q:
172     case GDK_KEY_Escape:
173         gtk_main_quit ();
174         return FALSE;
175     case GDK_KEY_c:
176     case GDK_KEY_C:
177         pitch = SCORE_PITCH_C;
178         break;
179     case GDK_KEY_d:
180     case GDK_KEY_D:
181         pitch = SCORE_PITCH_D;
182         break;
183     case GDK_KEY_e:
184     case GDK_KEY_E:
185         pitch = SCORE_PITCH_E;
186         break;
187     case GDK_KEY_f:
188     case GDK_KEY_F:
189         pitch = SCORE_PITCH_F;
190         break;
191     case GDK_KEY_g:
192     case GDK_KEY_G:
193         pitch = SCORE_PITCH_G;
194         break;
195     case GDK_KEY_a:
196     case GDK_KEY_A:
197         pitch = SCORE_PITCH_A;
198         break;
199     case GDK_KEY_b:
200     case GDK_KEY_B:
201         pitch = SCORE_PITCH_B;
202         break;
203     case GDK_KEY_0:
204     case GDK_KEY_1:
205     case GDK_KEY_2:
206     case GDK_KEY_3:
207     case GDK_KEY_4:
208     case GDK_KEY_5:
209     case GDK_KEY_6:
210     case GDK_KEY_7:
211     case GDK_KEY_8:
212         scherzo->keyboard_octave = key->keyval - GDK_KEY_0;
213         break;
214     case GDK_KEY_space:
215         scherzo_press_pedal (scherzo);
216         break;
217     }
218
219     if ((key->keyval >= GDK_KEY_A && key->keyval <= GDK_KEY_G) ||
220         (key->keyval >= GDK_KEY_a && key->keyval <= GDK_KEY_g))
221     {
222         score_note_t *note;
223
224         note = scherzo_press_note (scherzo, pitch, octave);
225         _judge_note (scherzo, note);
226         gtk_widget_queue_draw (scherzo->window);
227
228         return TRUE;
229     }
230
231
232     /* Allow an unhandled event to propagate to other handlers. */
233     return FALSE;
234 }
235
236 static int
237 on_key_release_event (unused (GtkWidget *widget),
238                       GdkEventKey *key,
239                       void *user_data)
240 {
241     scherzo_t *scherzo = user_data;
242     int octave;
243     score_pitch_t pitch;
244
245     if (scherzo->challenge.note)
246         octave = scherzo->challenge.note->octave;
247     else
248         octave = scherzo->keyboard_octave;
249
250     switch (key->keyval) {
251     case GDK_KEY_c:
252     case GDK_KEY_C:
253         pitch = SCORE_PITCH_C;
254         break;
255     case GDK_KEY_d:
256     case GDK_KEY_D:
257         pitch = SCORE_PITCH_D;
258         break;
259     case GDK_KEY_e:
260     case GDK_KEY_E:
261         pitch = SCORE_PITCH_E;
262         break;
263     case GDK_KEY_f:
264     case GDK_KEY_F:
265         pitch = SCORE_PITCH_F;
266         break;
267     case GDK_KEY_g:
268     case GDK_KEY_G:
269         pitch = SCORE_PITCH_G;
270         break;
271     case GDK_KEY_a:
272     case GDK_KEY_A:
273         pitch = SCORE_PITCH_A;
274         break;
275     case GDK_KEY_b:
276     case GDK_KEY_B:
277         pitch = SCORE_PITCH_B;
278         break;
279     case GDK_KEY_space:
280         scherzo_release_pedal (scherzo);
281         break;
282     }
283
284     if ((key->keyval >= GDK_KEY_A && key->keyval <= GDK_KEY_G) ||
285         (key->keyval >= GDK_KEY_a && key->keyval <= GDK_KEY_g))
286     {
287         scherzo_release_note (scherzo, pitch, octave);
288         _score_challenge (scherzo);
289         gtk_widget_queue_draw (scherzo->window);
290
291         return TRUE;
292     }
293
294
295     /* Allow an unhandled event to propagate to other handlers. */
296     return FALSE;
297 }
298
299 static unsigned char
300 _score_pitch_and_octave_to_midi (score_pitch_t pitch,
301                                  int octave)
302 {
303     unsigned char midi_note = 12 * (octave + 1);
304
305     switch (SCORE_PITCH_NAME (pitch)) {
306     case SCORE_PITCH_NAME_C:
307         break;
308     case SCORE_PITCH_NAME_D:
309         midi_note += 2;
310         break;
311     case SCORE_PITCH_NAME_E:
312         midi_note += 4;
313         break;
314     case SCORE_PITCH_NAME_F:
315         midi_note += 5;
316         break;
317     case SCORE_PITCH_NAME_G:
318         midi_note += 7;
319         break;
320     case SCORE_PITCH_NAME_A:
321         midi_note += 9;
322         break;
323     case SCORE_PITCH_NAME_B:
324         midi_note += 11;
325         break;
326     }
327
328     switch (SCORE_PITCH_ACCIDENTAL (pitch)) {
329     case SCORE_PITCH_ACCIDENTAL_DOUBLE_FLAT:
330         midi_note -= 2;
331         break;
332     case SCORE_PITCH_ACCIDENTAL_FLAT:
333         midi_note -= 1;
334         break;
335     case SCORE_PITCH_ACCIDENTAL_NATURAL:
336         break;
337     case SCORE_PITCH_ACCIDENTAL_SHARP:
338         midi_note += 1;
339         break;
340     case SCORE_PITCH_ACCIDENTAL_DOUBLE_SHARP:
341         midi_note += 2;
342         break;
343     }
344
345     return midi_note;
346 }
347
348 static void
349 _midi_to_score_pitch_and_octave (unsigned char midi_note,
350                                  score_pitch_t *pitch,
351                                  int *octave)
352 {
353     *octave = midi_note / 12 - 1;
354
355     switch (midi_note % 12)
356     {
357     case 0:
358         *pitch = SCORE_PITCH_C;
359         break;
360     case 1:
361         *pitch = SCORE_PITCH_Cs;
362         break;
363     case 2:
364         *pitch = SCORE_PITCH_D;
365         break;
366     case 3:
367         *pitch = SCORE_PITCH_Ds;
368         break;
369     case 4:
370         *pitch = SCORE_PITCH_E;
371         break;
372     case 5:
373         *pitch = SCORE_PITCH_F;
374         break;
375     case 6:
376         *pitch = SCORE_PITCH_Fs;
377         break;
378     case 7:
379         *pitch = SCORE_PITCH_G;
380         break;
381     case 8:
382         *pitch = SCORE_PITCH_Gs;
383         break;
384     case 9:
385         *pitch = SCORE_PITCH_A;
386         break;
387     case 10:
388         *pitch = SCORE_PITCH_As;
389         break;
390     case 11:
391         *pitch = SCORE_PITCH_B;
392         break;
393     }
394 }
395
396 /* Determine a chord name (if any) from the current notes pressed */
397
398 typedef struct analyzed_note {
399     /* Original note being analzyed. */
400     score_note_t *note;
401
402     /* Absolute pitch (expressed as midi number). */
403     int midi_pitch;
404
405     /* Pitch relative to bass note. */
406     int relative_pitch;
407 } analyzed_note_t;
408
409 static int
410 _compare_analyzed_note_by_midi_pitch (const void *va, const void *vb)
411 {
412     const analyzed_note_t *a = va, *b = vb;
413
414     return a->midi_pitch - b->midi_pitch;
415 }
416
417 static int
418 _compare_analyzed_note_by_relative_pitch (const void *va, const void *vb)
419 {
420     const analyzed_note_t *a = va, *b = vb;
421
422     return a->relative_pitch - b->relative_pitch;
423 }
424
425 static int
426 _chord_signature_matches (analyzed_note_t *notes,
427                           int num_notes,
428                           int *signature_pitches,
429                           int num_signature_pitches)
430 {
431     int i;
432
433     if (num_notes != num_signature_pitches)
434             return 0;
435
436     for (i = 0; i < num_notes; i++)
437         if (notes[i].relative_pitch != signature_pitches[i])
438             return 0;
439
440     return 1;
441 }
442
443 static void
444 scherzo_analyze_chord (scherzo_t *scherzo)
445 {
446     void *local = talloc_new (NULL);
447     analyzed_note_t *notes;
448     note_group_t *note_group;
449     unsigned i, j, num_notes;
450     int bass_pitch;
451     const char *chord_name = NULL;
452
453     if (scherzo->pedal_pressed)
454         note_group = &scherzo->notes_pedaled;
455     else
456         note_group = &scherzo->notes_pressed;
457
458     num_notes = note_group->num_notes;
459
460     struct { int pitches[1]; const char *name; } octaves[] = {
461         { {0}, "Octave"}
462     };
463
464     struct { int pitches[2]; const char *name; } intervals[] = {
465         { {0, 1}, "Minor 2nd"},
466         { {0, 2}, "Major 2nd"},
467         { {0, 3}, "Minor 3rd"},
468         { {0, 4}, "Major 3rd"},
469         { {0, 5}, "Perfect 4th"},
470         { {0, 6}, "Diminished 5th/Augmented 4th"},
471         { {0, 7}, "Perfect 5th"},
472         { {0, 8}, "Minor 6th"},
473         { {0, 9}, "Major 6th"},
474         { {0, 10}, "Minor 7th"},
475         { {0, 11}, "Major 7th"}
476     };
477
478     struct { int pitches[3]; const char *name; } triads[] = {
479         { {0, 4, 8}, "Augmented triad" },
480         { {0, 4, 7}, "Major triad" },
481         { {0, 3, 7}, "Minor triad" },
482         { {0, 3, 6}, "Diminished triad" }
483     };
484
485     struct { int pitches[4]; const char *name; } sevenths[] = {
486         { {0, 4, 8, 11}, "Augmented/major 7" },
487         { {0, 4, 8, 10}, "Augmented 7" },
488         { {0, 4, 7, 11}, "Major 7" },
489         { {0, 4, 7, 10}, "Dominant 7" },
490         { {0, 3, 7, 11}, "Minor/major 7" },
491         { {0, 3, 7, 10}, "Minor 7" },
492         { {0, 3, 6, 11}, "Diminished/major 7" },
493         { {0, 3, 6, 10}, "Half-diminished 7" },
494         { {0, 3, 6, 9},  "Diminished 7" }
495     };
496
497     if (scherzo->chord) {
498         score_remove_chord (scherzo->chord);
499         scherzo->chord = NULL;
500     }
501
502     if (num_notes <= 1)
503         goto DONE;
504
505     notes = talloc_array (local, analyzed_note_t, num_notes);
506     if (notes == NULL)
507         goto DONE;
508
509     for (i = 0; i < num_notes; i++) {
510         score_note_t *note = note_group->notes[i];
511         notes[i].note = note;
512         notes[i].midi_pitch = _score_pitch_and_octave_to_midi (note->pitch,
513                                                                note->octave);
514         /* Relative pitch will be filled in after sorting. */
515         notes[i].relative_pitch = 0;
516     }
517
518     /* First, sort by midi pitch to find the bass note. */
519     qsort (notes, num_notes, sizeof (analyzed_note_t),
520            _compare_analyzed_note_by_midi_pitch);
521     
522     bass_pitch = notes[0].midi_pitch;
523
524     /* With the bass note identified, we can find all relative pitches. */
525     for (i = 0; i < num_notes; i++) {
526         notes[i].relative_pitch = notes[i].midi_pitch - bass_pitch;
527         while (notes[i].relative_pitch >= 12)
528             notes[i].relative_pitch -= 12;
529     }
530
531     /* Now, sort again by relative pitch. */
532     qsort (notes, num_notes, sizeof (analyzed_note_t),
533            _compare_analyzed_note_by_relative_pitch);
534
535     /* Finally, eliminate all duplicate notes. */
536     for (i = 0; i < num_notes - 1; i++) {
537             if (notes[i+1].relative_pitch == notes[i].relative_pitch) {
538                     j = i+1;
539                     while (j < num_notes &&
540                            notes[j].relative_pitch == notes[i].relative_pitch)
541                     {
542                             j++;
543                     }
544                     /* The loop incremented j one past the last
545                      * duplicate. Decrement so that it points to the
546                      * last duplicate (and is guaranteed to not exceed
547                      * the array bounds).*/
548                     j--;
549
550                     if (j < num_notes - 1) {
551                             memmove (&notes[i+1], &notes[j+1],
552                                      (num_notes - j) * sizeof (analyzed_note_t));
553                     }
554
555                     num_notes -= (j - i);
556             }
557     }
558
559     switch (num_notes) {
560     case 1:
561         for (i = 0; i < ARRAY_SIZE (octaves); i++) {
562             if (_chord_signature_matches (notes, num_notes, octaves[i].pitches, 1))
563                 chord_name = octaves[i].name;
564         }
565         break;
566     case 2:
567         for (i = 0; i < ARRAY_SIZE (intervals); i++) {
568             if (_chord_signature_matches (notes, num_notes, intervals[i].pitches, 2))
569                 chord_name = intervals[i].name;
570         }
571         break;
572     case 3:
573         for (i = 0; i < ARRAY_SIZE (triads); i++) {
574             if (_chord_signature_matches (notes, num_notes, triads[i].pitches, 3))
575                 chord_name = triads[i].name;
576         }
577         break;
578     case 4:
579         for (i = 0; i < ARRAY_SIZE(sevenths); i++) {
580             if (_chord_signature_matches (notes, num_notes, sevenths[i].pitches, 4))
581                 chord_name = sevenths[i].name;
582         }
583         break;
584     }
585
586     if (chord_name)
587         scherzo->chord = score_add_chord (scherzo->treble, chord_name);
588     else
589         scherzo->chord = score_add_chord (scherzo->treble, "Unknown or not a chord");
590
591 DONE:
592     talloc_free (local);
593 }
594
595 static void
596 note_group_init (void *ctx, note_group_t *group)
597 {
598     group->ctx = ctx;
599     group->notes = NULL;
600     group->size = 0;
601     group->num_notes = 0;
602 }
603
604 static void
605 note_group_add_note (note_group_t *group, score_note_t *note)
606 {
607     int i;
608
609     /* Do nothing if note is already in group. */
610     for (i = 0; i < group->num_notes; i++) {
611         if (group->notes[i]->pitch == note->pitch &&
612             group->notes[i]->octave == note->octave)
613         {
614             return;
615         }
616     }
617
618     group->num_notes++;
619
620     if (group->num_notes > group->size) {
621         group->size++;
622         group->notes = talloc_realloc (group->ctx, group->notes,
623                                        score_note_t*, group->size);
624
625         if (group->notes == NULL) {
626             fprintf (stderr, "Out of memory.\n");
627             exit (1);
628         }
629     }
630
631     group->notes[group->num_notes - 1] = note;
632 }
633
634 static void
635 note_group_remove_note_at (note_group_t *group, int i)
636 {
637     if (i >= group->num_notes) {
638         fprintf (stderr, "Internal error: No note to remove at index %d\n", i);
639         exit (1);
640     }
641
642     if (i < group->num_notes - 1) {
643         memmove (group->notes + i, group->notes + i + 1,
644                  (group->num_notes - 1 - i) * sizeof (score_note_t*));
645     }
646     group->num_notes--;
647 }
648
649 static score_note_t *
650 scherzo_press_note (scherzo_t *scherzo, score_pitch_t pitch, int octave)
651 {
652     score_staff_t *staff;
653     score_note_t *note;
654     int i;
655
656     if (scherzo->challenge.note) {
657         staff = scherzo->challenge.staff;
658     } else if (octave >= 4) {
659         staff = scherzo->treble;
660     } else {
661         staff = scherzo->bass;
662     }
663
664     /* Do nothing if this note is already pressed. */
665     for (i = 0; i < scherzo->notes_pressed.num_notes; i++) {
666         if (scherzo->notes_pressed.notes[i]->pitch == pitch &&
667             scherzo->notes_pressed.notes[i]->octave == octave)
668         {
669             return scherzo->notes_pressed.notes[i];
670         }
671     }
672
673     note = score_add_note (staff, pitch, octave, SCORE_DURATION_WHOLE);
674
675     note_group_add_note (&scherzo->notes_pressed, note);
676
677     if (scherzo->pedal_pressed)
678         note_group_add_note (&scherzo->notes_pedaled, note);
679
680     scherzo_analyze_chord (scherzo);
681
682     return note;
683 }
684
685 static void
686 scherzo_release_note (scherzo_t *scherzo, score_pitch_t pitch, int octave)
687 {
688     score_note_t *note;
689     int i;
690     int found = 0;
691
692     for (i = scherzo->notes_pressed.num_notes - 1; i >=0; i--) {
693         note = scherzo->notes_pressed.notes[i];
694         if (note->pitch == pitch && note->octave == octave) {
695             found = 1;
696             if (! scherzo->pedal_pressed)
697                 score_remove_note (note);
698             note_group_remove_note_at (&scherzo->notes_pressed, i);
699         }
700     }
701
702     if (found == 0) {
703         fprintf (stderr, "Internal error: Failed to find note to release.\n");
704     }
705
706     scherzo_analyze_chord (scherzo);
707 }
708
709 static score_note_t *
710 scherzo_press_note_midi (scherzo_t *scherzo, unsigned char midi_note)
711 {
712     score_pitch_t pitch;
713     int octave;
714
715     _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave);
716
717     return scherzo_press_note (scherzo, pitch, octave);
718 }
719
720 static void
721 scherzo_release_note_midi (scherzo_t *scherzo, unsigned char midi_note)
722 {
723     score_pitch_t pitch;
724     int octave;
725  
726     _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave);
727
728     scherzo_release_note (scherzo, pitch, octave);
729 }
730
731 static void
732 scherzo_press_pedal (scherzo_t *scherzo)
733 {
734     int i;
735
736     scherzo->pedal_pressed = 1;
737
738     /* Copy all pressed notes to pedaled notes */
739     for (i = 0; i < scherzo->notes_pressed.num_notes; i++)
740         note_group_add_note (&scherzo->notes_pedaled, scherzo->notes_pressed.notes[i]);
741 }
742
743 static void
744 scherzo_release_pedal (scherzo_t *scherzo)
745 {
746     score_note_t *note, *new_note;
747     int i;
748
749     /* Make new notes in score for all pressed notes. */
750     for (i = 0; i < scherzo->notes_pressed.num_notes; i++) {
751         note = scherzo->notes_pressed.notes[i];
752         new_note = score_add_note (note->staff, note->pitch, note->octave, note->duration);
753         scherzo->notes_pressed.notes[i] = new_note;
754     }
755
756     /* Then remove all previously pedaled notes from the score. */
757     for (i = scherzo->notes_pedaled.num_notes - 1; i >=0; i--) {
758         note = scherzo->notes_pedaled.notes[i];
759         score_remove_note (note);
760         note_group_remove_note_at (&scherzo->notes_pedaled, i);
761     }
762
763     scherzo->pedal_pressed = 0;
764
765     scherzo_analyze_chord (scherzo);
766
767     gtk_widget_queue_draw (scherzo->window);
768 }
769
770 void
771 _select_challenge (scherzo_t *scherzo)
772 {
773     category_t *category_unused;
774     bool_t introduced_unused;
775     item_t *item;
776     challenge_t *challenge = &scherzo->challenge;
777     score_pitch_t pitch;
778     int octave;
779     char *s;
780
781     if (challenge->note) {
782         score_remove_note (challenge->note);
783         challenge->note = NULL;
784     }
785
786     mnemon_select_item (&scherzo->mnemon,
787                         &challenge->bin,
788                         &challenge->item_index,
789                         &category_unused,
790                         &introduced_unused);
791
792     item = challenge->bin->items[challenge->item_index];
793
794     s = item->challenge;
795     if (strncmp (s, "treble:", 7) == 0) {
796         s += 7;
797         challenge->staff = scherzo->treble;
798     } else if (strncmp (s, "bass:", 5) == 0) {
799         s += 5;
800         challenge->staff = scherzo->bass;
801     } else {
802         fprintf (stderr,
803                  "Malformed staff name: %s (expected 'treble:' or 'bass:')\n",
804                  s);
805         exit (1);
806     }
807
808     switch (*s) {
809     case 'C':
810         pitch = SCORE_PITCH_VALUE(C, NATURAL);
811         break;
812     case 'D':
813         pitch = SCORE_PITCH_VALUE(D, NATURAL);
814         break;
815     case 'E':
816         pitch = SCORE_PITCH_VALUE(E, NATURAL);
817         break;
818     case 'F':
819         pitch = SCORE_PITCH_VALUE(F, NATURAL);
820         break;
821     case 'G':
822         pitch = SCORE_PITCH_VALUE(G, NATURAL);
823         break;
824     case 'A':
825         pitch = SCORE_PITCH_VALUE(A, NATURAL);
826         break;
827     case 'B':
828         pitch = SCORE_PITCH_VALUE(B, NATURAL);
829         break;
830     default:
831         fprintf (stderr, "Malformed pitch name: %s (expected 'A' - 'G')\n", s);
832         exit (1);
833     }
834     s++;
835
836     if (*s < '0' || *s > '9') {
837         fprintf (stderr, "Malformed octave number: %s (expected '0' - '9')\n", s);
838         exit (1);
839     }
840
841     octave = *s - '0';
842
843     challenge->note = score_add_note (challenge->staff, pitch, octave,
844                                       SCORE_DURATION_WHOLE);
845     challenge->satisfied = 0;
846     challenge->mistaken = 0;
847 }
848
849 /* Determine whether the user hit the correct note. */
850 static void
851 _judge_note (scherzo_t *scherzo, score_note_t *note)
852 {
853     challenge_t *challenge = &scherzo->challenge;
854
855     if (! scherzo->challenge.note) {
856         score_set_note_color_rgb (note, 0.0, 0.0, 0.0); /* black */
857         return;
858     }
859
860     if (note->pitch == challenge->note->pitch &&
861         note->octave == challenge->note->octave)
862     {
863         challenge->satisfied = 1;
864         score_set_note_color_rgb (note, 18/256., 130/256., 28/256.); /* green */
865     }
866     else
867     {
868         challenge->mistaken = 1;
869         score_set_note_color_rgb (note, 184/256., 4/256., 22/256.); /* red */
870     }
871 }
872
873 /* If the user got the right note (eventually), then score it in
874  * mnemon and show the next note. */
875 static void
876 _score_challenge (scherzo_t *scherzo)
877 {
878     challenge_t *challenge = &scherzo->challenge;
879
880     if (! challenge->note)
881         return;
882
883     if (! challenge->satisfied)
884         return;
885
886     mnemon_score_item (&scherzo->mnemon, challenge->bin, challenge->item_index,
887                        ! challenge->mistaken);
888
889     _select_challenge (scherzo);
890 }
891
892 static int
893 on_midi_input (unused (GIOChannel *channel),
894                unused (GIOCondition condition),
895                void *user_data)
896 {
897     unsigned char buf[MIDI_BUF_SIZE], *next;
898     scherzo_t *scherzo = user_data;
899     ssize_t remaining;
900     snd_seq_event_t event;
901     score_note_t *note;
902     int need_redraw = FALSE;
903
904     remaining = read (scherzo->midi_fd, buf, MIDI_BUF_SIZE);
905
906     next = buf;
907     while (remaining) {
908         long consumed;
909
910         consumed = snd_midi_event_encode (scherzo->snd_midi_event,
911                                           next, remaining, &event);
912
913         remaining -= consumed;
914         next += consumed;
915
916         switch (event.type) {
917         case SND_SEQ_EVENT_NONE:
918             /* Incomplete event. Nothing to do. */
919             break;
920         case SND_SEQ_EVENT_NOTEON:
921             note = scherzo_press_note_midi (scherzo, event.data.note.note);
922             _judge_note (scherzo, note);
923             need_redraw = TRUE;
924             break;
925         case SND_SEQ_EVENT_NOTEOFF:
926             scherzo_release_note_midi (scherzo, event.data.note.note);
927             _score_challenge (scherzo);
928             need_redraw = TRUE;
929             break;
930         case SND_SEQ_EVENT_CLOCK:
931             /* Ignore for now as my piano sends a constant stream of these. */
932             break;
933         case SND_SEQ_EVENT_SENSING:
934             /* Ignore for now as my piano sends a constant stream of these. */
935             break;
936         case SND_SEQ_EVENT_CONTROLLER:
937             /* XXX: My piano gives me 64 for the sustain pedal, is
938              * that universal? */
939             if (event.data.control.param == 64) {
940                 if (event.data.control.value == 0)
941                     scherzo_release_pedal (scherzo);
942                 else
943                     scherzo_press_pedal (scherzo);
944             } else {
945                 fprintf (stderr, "Fixme: Unhandled MIDI Control event (param=%d, value=%d)\n",
946                          event.data.control.param, event.data.control.value);
947             }
948             break;
949         default:
950             fprintf (stderr, "Fixme: Do not yet know how to handle MIDI event %d\n",
951                      event.type);
952             break;
953         }
954     }
955
956     if (need_redraw)
957         gtk_widget_queue_draw (scherzo->window);
958
959     /* Return TRUE to continue to get called in the future. */
960     return TRUE;
961 }
962
963 int
964 main (int argc, char *argv[])
965 {
966     GtkWidget *drawing_area;
967     scherzo_t scherzo;
968     int err;
969
970     srand (time (NULL));
971
972     gtk_init (&argc, &argv);
973
974     scherzo.ctx = talloc_new (NULL);
975
976     scherzo.score = score_create (scherzo.ctx);
977     scherzo.staff_height = 100;
978     score_set_staff_height (scherzo.score, scherzo.staff_height);
979
980     score_add_brace (scherzo.score, 2);
981     scherzo.treble = score_add_staff (scherzo.score, SCORE_CLEF_G);
982     scherzo.bass = score_add_staff (scherzo.score, SCORE_CLEF_F);
983
984     scherzo.chord = NULL;
985
986     /* Default to octave 4 for computer keyboard keypresses. */
987     scherzo.keyboard_octave = 4;
988
989     note_group_init (scherzo.ctx, &scherzo.notes_pressed);
990     note_group_init (scherzo.ctx, &scherzo.notes_pedaled);
991
992     scherzo.pedal_pressed = 0;
993
994     mnemon_init (&scherzo.mnemon);
995     /* XXX: Should create a default file if one cannot be loaded. */
996     mnemon_load_category (&scherzo.mnemon, "scherzo-notes");
997
998     scherzo.challenge.note = NULL;
999 /*
1000     _select_challenge (&scherzo);
1001 */
1002
1003     err = snd_midi_event_new (MIDI_BUF_SIZE, &scherzo.snd_midi_event);
1004     if (err) {
1005         fprintf (stderr, "Out of memory.\n");
1006         return 1;
1007     }
1008
1009 #define MIDI_DEVICE "/dev/midi1"
1010     scherzo.midi_fd = open (MIDI_DEVICE, O_RDONLY);
1011     if (scherzo.midi_fd < 0) {
1012         printf ("failed to open " MIDI_DEVICE ". Midi input will not be available.\n");
1013     } else {
1014         GIOChannel *channel;
1015
1016         channel = g_io_channel_unix_new (scherzo.midi_fd);
1017         g_io_channel_set_encoding (channel, NULL, NULL);
1018         g_io_add_watch (channel, G_IO_IN, on_midi_input, &scherzo);
1019     }
1020
1021     scherzo.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1022
1023     gtk_window_set_default_size (GTK_WINDOW (scherzo.window), 1000, 600);
1024
1025     g_signal_connect (scherzo.window, "delete-event",
1026                       G_CALLBACK (on_delete_event_quit), NULL);
1027
1028     drawing_area = gtk_drawing_area_new ();
1029
1030     gtk_container_add (GTK_CONTAINER (scherzo.window), drawing_area);
1031
1032     g_signal_connect (drawing_area, "expose-event",  
1033                       G_CALLBACK (on_expose_event_draw),
1034                       &scherzo);
1035
1036     g_signal_connect (scherzo.window, "key-press-event",
1037                       G_CALLBACK (on_key_press_event),
1038                       &scherzo);
1039
1040     g_signal_connect (scherzo.window, "key-release-event",
1041                       G_CALLBACK (on_key_release_event),
1042                       &scherzo);
1043     
1044     gtk_widget_show_all (scherzo.window);
1045     
1046     gtk_main ();
1047
1048     mnemon_save (&scherzo.mnemon);
1049
1050     snd_midi_event_free (scherzo.snd_midi_event);
1051
1052     talloc_free (scherzo.ctx);
1053
1054     return 0;
1055 }