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