]> git.cworth.org Git - scherzo/blob - scherzo.c
Allow keys 0-8 to specify desired octave.
[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 scherzo
45 {
46     void *ctx;
47
48     GtkWidget *window;
49     score_t *score;
50     int staff_height;
51     score_staff_t *treble;
52     score_staff_t *bass;
53     score_chord_t *chord;
54
55     /* This is for a "computer keyboard". Any "piano keyboard" key
56      * knows its own octave. */
57     int keyboard_octave;
58
59     int midi_fd;
60     snd_midi_event_t *snd_midi_event;
61
62     mnemon_t mnemon;
63     challenge_t challenge;
64
65     int num_notes_pressed;
66     score_note_t **notes_pressed;
67 } scherzo_t;
68
69 /* Forward declarations. */
70 static score_note_t *
71 scherzo_add_note (scherzo_t *scherzo, score_pitch_t pitch, int octave);
72
73 static void
74 scherzo_remove_note (scherzo_t *scherzo, score_pitch_t pitch, int octave);
75
76 static void
77 _judge_note (scherzo_t *scherzo, score_note_t *note);
78
79 static void
80 _score_challenge (scherzo_t *scherzo);
81
82 static int
83 on_delete_event_quit (unused (GtkWidget *widget),
84                       unused (GdkEvent *event),
85                       unused (gpointer user_data))
86 {
87     gtk_main_quit ();
88
89     /* Returning FALSE allows the default handler for delete-event
90      * to proceed to cleanup the widget. */
91     return FALSE;
92 }
93
94 static int
95 on_expose_event_draw (GtkWidget *widget,
96                       unused (GdkEventExpose *expose),
97                       void * user_data)
98 {
99     scherzo_t *scherzo = user_data;
100     score_t *score = scherzo->score;
101     cairo_t *cr;
102     GtkAllocation allocation;
103     static const int pad = 10;
104     int widget_width;
105
106     gtk_widget_get_allocation (widget, &allocation);
107     widget_width = allocation.width;
108
109     cr = gdk_cairo_create (widget->window);
110
111     /* White background */
112     cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
113     cairo_paint (cr);
114
115     /* Add some padding on the sides and top */
116     cairo_translate (cr, pad, scherzo->staff_height);
117     score_set_staff_height (score, scherzo->staff_height);
118     score_set_width (score, widget_width - 2 * pad);
119
120     score_draw (score, cr);
121  
122     return TRUE;
123 }
124
125 static int
126 on_key_press_event (GtkWidget *widget,
127                     GdkEventKey *key,
128                     void *user_data)
129 {
130     scherzo_t *scherzo = user_data;
131     int octave;
132     score_pitch_t pitch;
133
134     if (scherzo->challenge.note)
135         octave = scherzo->challenge.note->octave;
136     else
137         octave = scherzo->keyboard_octave;
138
139     switch (key->keyval) {
140     case GDK_KEY_plus:
141     case GDK_KEY_KP_Add:
142     case GDK_KEY_equal:
143     case GDK_KEY_KP_Equal:
144         scherzo->staff_height += 4;
145         gtk_widget_queue_draw (widget);
146         return TRUE;
147         break;
148     case GDK_KEY_minus:
149     case GDK_KEY_KP_Subtract:
150         scherzo->staff_height -= 4;
151         gtk_widget_queue_draw (widget);
152         return TRUE;
153         break;
154     case GDK_KEY_q:
155     case GDK_KEY_Q:
156     case GDK_KEY_Escape:
157         gtk_main_quit ();
158         return FALSE;
159     case GDK_KEY_c:
160     case GDK_KEY_C:
161         pitch = SCORE_PITCH_C;
162         break;
163     case GDK_KEY_d:
164     case GDK_KEY_D:
165         pitch = SCORE_PITCH_D;
166         break;
167     case GDK_KEY_e:
168     case GDK_KEY_E:
169         pitch = SCORE_PITCH_E;
170         break;
171     case GDK_KEY_f:
172     case GDK_KEY_F:
173         pitch = SCORE_PITCH_F;
174         break;
175     case GDK_KEY_g:
176     case GDK_KEY_G:
177         pitch = SCORE_PITCH_G;
178         break;
179     case GDK_KEY_a:
180     case GDK_KEY_A:
181         pitch = SCORE_PITCH_A;
182         break;
183     case GDK_KEY_b:
184     case GDK_KEY_B:
185         pitch = SCORE_PITCH_B;
186         break;
187     case GDK_KEY_0:
188     case GDK_KEY_1:
189     case GDK_KEY_2:
190     case GDK_KEY_3:
191     case GDK_KEY_4:
192     case GDK_KEY_5:
193     case GDK_KEY_6:
194     case GDK_KEY_7:
195     case GDK_KEY_8:
196         scherzo->keyboard_octave = key->keyval - GDK_KEY_0;
197         break;
198     }
199
200     if ((key->keyval >= GDK_KEY_A && key->keyval <= GDK_KEY_G) ||
201         (key->keyval >= GDK_KEY_a && key->keyval <= GDK_KEY_g))
202     {
203         score_note_t *note;
204
205         note = scherzo_add_note (scherzo, pitch, octave);
206         _judge_note (scherzo, note);
207         gtk_widget_queue_draw (scherzo->window);
208
209         return TRUE;
210     }
211
212
213     /* Allow an unhandled event to propagate to other handlers. */
214     return FALSE;
215 }
216
217 static int
218 on_key_release_event (unused (GtkWidget *widget),
219                       GdkEventKey *key,
220                       void *user_data)
221 {
222     scherzo_t *scherzo = user_data;
223     int octave;
224     score_pitch_t pitch;
225
226     if (scherzo->challenge.note)
227         octave = scherzo->challenge.note->octave;
228     else
229         octave = scherzo->keyboard_octave;
230
231     switch (key->keyval) {
232     case GDK_KEY_c:
233     case GDK_KEY_C:
234         pitch = SCORE_PITCH_C;
235         break;
236     case GDK_KEY_d:
237     case GDK_KEY_D:
238         pitch = SCORE_PITCH_D;
239         break;
240     case GDK_KEY_e:
241     case GDK_KEY_E:
242         pitch = SCORE_PITCH_E;
243         break;
244     case GDK_KEY_f:
245     case GDK_KEY_F:
246         pitch = SCORE_PITCH_F;
247         break;
248     case GDK_KEY_g:
249     case GDK_KEY_G:
250         pitch = SCORE_PITCH_G;
251         break;
252     case GDK_KEY_a:
253     case GDK_KEY_A:
254         pitch = SCORE_PITCH_A;
255         break;
256     case GDK_KEY_b:
257     case GDK_KEY_B:
258         pitch = SCORE_PITCH_B;
259         break;
260     }
261
262     if ((key->keyval >= GDK_KEY_A && key->keyval <= GDK_KEY_G) ||
263         (key->keyval >= GDK_KEY_a && key->keyval <= GDK_KEY_g))
264     {
265         scherzo_remove_note (scherzo, pitch, octave);
266         _score_challenge (scherzo);
267         gtk_widget_queue_draw (scherzo->window);
268
269         return TRUE;
270     }
271
272
273     /* Allow an unhandled event to propagate to other handlers. */
274     return FALSE;
275 }
276
277 static unsigned char
278 _score_pitch_and_octave_to_midi (score_pitch_t pitch,
279                                  int octave)
280 {
281     unsigned char midi_note = 12 * (octave + 1);
282
283     switch (SCORE_PITCH_NAME (pitch)) {
284     case SCORE_PITCH_NAME_C:
285         break;
286     case SCORE_PITCH_NAME_D:
287         midi_note += 2;
288         break;
289     case SCORE_PITCH_NAME_E:
290         midi_note += 4;
291         break;
292     case SCORE_PITCH_NAME_F:
293         midi_note += 5;
294         break;
295     case SCORE_PITCH_NAME_G:
296         midi_note += 7;
297         break;
298     case SCORE_PITCH_NAME_A:
299         midi_note += 9;
300         break;
301     case SCORE_PITCH_NAME_B:
302         midi_note += 11;
303         break;
304     }
305
306     switch (SCORE_PITCH_ACCIDENTAL (pitch)) {
307     case SCORE_PITCH_ACCIDENTAL_DOUBLE_FLAT:
308         midi_note -= 2;
309         break;
310     case SCORE_PITCH_ACCIDENTAL_FLAT:
311         midi_note -= 1;
312         break;
313     case SCORE_PITCH_ACCIDENTAL_NATURAL:
314         break;
315     case SCORE_PITCH_ACCIDENTAL_SHARP:
316         midi_note += 1;
317         break;
318     case SCORE_PITCH_ACCIDENTAL_DOUBLE_SHARP:
319         midi_note += 2;
320         break;
321     }
322
323     return midi_note;
324 }
325
326 static void
327 _midi_to_score_pitch_and_octave (unsigned char midi_note,
328                                  score_pitch_t *pitch,
329                                  int *octave)
330 {
331     *octave = midi_note / 12 - 1;
332
333     switch (midi_note % 12)
334     {
335     case 0:
336         *pitch = SCORE_PITCH_C;
337         break;
338     case 1:
339         *pitch = SCORE_PITCH_Cs;
340         break;
341     case 2:
342         *pitch = SCORE_PITCH_D;
343         break;
344     case 3:
345         *pitch = SCORE_PITCH_Ds;
346         break;
347     case 4:
348         *pitch = SCORE_PITCH_E;
349         break;
350     case 5:
351         *pitch = SCORE_PITCH_F;
352         break;
353     case 6:
354         *pitch = SCORE_PITCH_Fs;
355         break;
356     case 7:
357         *pitch = SCORE_PITCH_G;
358         break;
359     case 8:
360         *pitch = SCORE_PITCH_Gs;
361         break;
362     case 9:
363         *pitch = SCORE_PITCH_A;
364         break;
365     case 10:
366         *pitch = SCORE_PITCH_As;
367         break;
368     case 11:
369         *pitch = SCORE_PITCH_B;
370         break;
371     }
372 }
373
374 /* Determine a chord name (if any) from the current notes pressed */
375
376 typedef struct analyzed_note {
377     /* Original note being analzyed. */
378     score_note_t *note;
379
380     /* Absolute pitch (expressed as midi number). */
381     int midi_pitch;
382
383     /* Pitch relative to bass note. */
384     int relative_pitch;
385 } analyzed_note_t;
386
387 static int
388 _compare_analyzed_note (const void *va, const void *vb)
389 {
390     const analyzed_note_t *a = va, *b = vb;
391
392     return a->midi_pitch - b->midi_pitch;
393 }
394
395 static int
396 _chord_signature_matches (analyzed_note_t *notes,
397                           int num_notes,
398                           int *signature_pitches,
399                           int num_signature_pitches)
400 {
401     int n, s;
402
403     for (n = 0, s = 0; s < num_signature_pitches; s++) {
404         if (n >= num_notes)
405             return 0;
406         if (notes[n].relative_pitch != signature_pitches[s])
407             return 0;
408         n++;
409         /* Skip repeated notes in chord being tested. */
410         while (n < num_notes &&
411                notes[n].relative_pitch == signature_pitches[s])
412         {
413             n++;
414         }
415     }
416
417     /* If there are fewer notes in the signature than in the chord,
418      * then there is no match. */
419     if (n < num_notes)
420             return 0;
421
422     return 1;
423 }
424
425 static void
426 scherzo_analyze_chord (scherzo_t *scherzo)
427 {
428     void *local = talloc_new (NULL);
429     analyzed_note_t *notes;
430     unsigned i, num_notes = scherzo->num_notes_pressed;
431     int bass_pitch;
432     const char *chord_name = NULL;
433
434     struct { int pitches[2]; const char *name; } intervals[] = {
435         { {0, 1}, "Minor 2nd"},
436         { {0, 2}, "Major 2nd"},
437         { {0, 3}, "Minor 3rd"},
438         { {0, 4}, "Major 3rd"},
439         { {0, 5}, "Perfect 4th"},
440         { {0, 6}, "Diminished 5th/Augmented 4th"},
441         { {0, 7}, "Perfect 5th"},
442         { {0, 8}, "Minor 6th"},
443         { {0, 9}, "Major 6th"},
444         { {0, 10}, "Minor 7th"},
445         { {0, 11}, "Major 7th"}
446     };
447
448     struct { int pitches[3]; const char *name; } triads[] = {
449         { {0, 4, 8}, "Augmented triad" },
450         { {0, 4, 7}, "Major triad" },
451         { {0, 3, 7}, "Minor triad" },
452         { {0, 3, 6}, "Diminished triad" }
453     };
454
455     struct { int pitches[4]; const char *name; } sevenths[] = {
456         { {0, 4, 8, 11}, "Augmented/major 7" },
457         { {0, 4, 7, 11}, "Major 7" },
458         { {0, 4, 7, 10}, "Dominant 7" },
459         { {0, 3, 7, 11}, "Minor/major 7" },
460         { {0, 3, 7, 10}, "Minor 7" },
461         { {0, 3, 6, 10}, "Half-diminished 7" },
462         { {0, 3, 6, 9},  "Diminished 7" },
463         { {0, 4, 8, 10}, "Augmented 7" },
464         { {0, 3, 6, 11}, "Diminished/major 7" },
465     };
466
467     if (scherzo->chord) {
468         score_remove_chord (scherzo->chord);
469         scherzo->chord = NULL;
470     }
471
472     if (num_notes <= 1)
473         goto DONE;
474
475     notes = talloc_array (local, analyzed_note_t, num_notes);
476     if (notes == NULL)
477         goto DONE;
478
479     for (i = 0; i < num_notes; i++) {
480         score_note_t *note = scherzo->notes_pressed[i];
481         notes[i].note = note;
482         notes[i].midi_pitch = _score_pitch_and_octave_to_midi (note->pitch,
483                                                                note->octave);
484         /* Relative pitch will be filled in after sorting. */
485         notes[i].relative_pitch = 0;
486     }
487
488     qsort (notes, num_notes, sizeof (analyzed_note_t), _compare_analyzed_note);
489     
490     bass_pitch = notes[0].midi_pitch;
491
492     for (i = 0; i < num_notes; i++) {
493         notes[i].relative_pitch = notes[i].midi_pitch - bass_pitch;
494     }
495
496     for (i = 0; i < ARRAY_SIZE (intervals); i++) {
497         if (_chord_signature_matches (notes, num_notes, intervals[i].pitches, 2))
498             chord_name = intervals[i].name;
499     }
500
501     for (i = 0; i < ARRAY_SIZE (triads); i++) {
502         if (_chord_signature_matches (notes, num_notes, triads[i].pitches, 3))
503             chord_name = triads[i].name;
504     }
505
506     for (i = 0; i < ARRAY_SIZE(sevenths); i++) {
507         if (_chord_signature_matches (notes, num_notes, sevenths[i].pitches, 4))
508             chord_name = sevenths[i].name;
509     }
510
511     if (chord_name)
512         scherzo->chord = score_add_chord (scherzo->treble, chord_name);
513     else
514         scherzo->chord = score_add_chord (scherzo->treble, "Unknown or not a chord");
515
516 DONE:
517     talloc_free (local);
518 }
519
520 static score_note_t *
521 scherzo_add_note (scherzo_t *scherzo, score_pitch_t pitch, int octave)
522 {
523     score_staff_t *staff;
524     score_note_t *note;
525     int i;
526
527     if (scherzo->challenge.note) {
528         staff = scherzo->challenge.staff;
529     } else if (octave >= 4) {
530         staff = scherzo->treble;
531     } else {
532         staff = scherzo->bass;
533     }
534
535     /* Do nothing if this note is already pressed. */
536     for (i = 0; i < scherzo->num_notes_pressed; i++) {
537         if (scherzo->notes_pressed[i]->pitch == pitch &&
538             scherzo->notes_pressed[i]->octave == octave)
539         {
540             return scherzo->notes_pressed[i];
541         }
542     }
543
544     note = score_add_note (staff, pitch, octave, SCORE_DURATION_WHOLE);
545
546     scherzo->num_notes_pressed++;
547     scherzo->notes_pressed = talloc_realloc (scherzo->ctx,
548                                              scherzo->notes_pressed,
549                                              score_note_t*,
550                                              scherzo->num_notes_pressed);
551     if (scherzo->notes_pressed == NULL) {
552         fprintf (stderr, "Out of memory.\n");
553         exit (1);
554     }
555
556     scherzo->notes_pressed[scherzo->num_notes_pressed - 1] = note;
557
558     scherzo_analyze_chord (scherzo);
559
560     return note;
561 }
562
563
564 static void
565 scherzo_remove_note (scherzo_t *scherzo, score_pitch_t pitch, int octave)
566 {
567     score_note_t *note;
568     int i;
569
570     for (i = 0; i < scherzo->num_notes_pressed; i++) {
571         note = scherzo->notes_pressed[i];
572         if (note->pitch == pitch && note->octave == octave) {
573             score_remove_note (note);
574             if (i < scherzo->num_notes_pressed - 1) {
575                 memmove (scherzo->notes_pressed + i,
576                          scherzo->notes_pressed + i + 1,
577                          (scherzo->num_notes_pressed - 1 - i) * sizeof (score_note_t*));
578             }
579             scherzo->num_notes_pressed--;
580             i--;
581         }
582     }
583
584     scherzo_analyze_chord (scherzo);
585 }
586
587 static score_note_t *
588 scherzo_add_note_midi (scherzo_t *scherzo, unsigned char midi_note)
589 {
590     score_pitch_t pitch;
591     int octave;
592
593     _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave);
594
595     return scherzo_add_note (scherzo, pitch, octave);
596 }
597
598
599 static void
600 scherzo_remove_note_midi (scherzo_t *scherzo, unsigned char midi_note)
601 {
602     score_pitch_t pitch;
603     int octave;
604  
605     _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave);
606
607     scherzo_remove_note (scherzo, pitch, octave);
608 }
609
610 void
611 _select_challenge (scherzo_t *scherzo)
612 {
613     category_t *category_unused;
614     bool_t introduced_unused;
615     item_t *item;
616     challenge_t *challenge = &scherzo->challenge;
617     score_pitch_t pitch;
618     int octave;
619     char *s;
620
621     if (challenge->note) {
622         score_remove_note (challenge->note);
623         challenge->note = NULL;
624     }
625
626     mnemon_select_item (&scherzo->mnemon,
627                         &challenge->bin,
628                         &challenge->item_index,
629                         &category_unused,
630                         &introduced_unused);
631
632     item = challenge->bin->items[challenge->item_index];
633
634     s = item->challenge;
635     if (strncmp (s, "treble:", 7) == 0) {
636         s += 7;
637         challenge->staff = scherzo->treble;
638     } else if (strncmp (s, "bass:", 5) == 0) {
639         s += 5;
640         challenge->staff = scherzo->bass;
641     } else {
642         fprintf (stderr,
643                  "Malformed staff name: %s (expected 'treble:' or 'bass:')\n",
644                  s);
645         exit (1);
646     }
647
648     switch (*s) {
649     case 'C':
650         pitch = SCORE_PITCH_VALUE(C, NATURAL);
651         break;
652     case 'D':
653         pitch = SCORE_PITCH_VALUE(D, NATURAL);
654         break;
655     case 'E':
656         pitch = SCORE_PITCH_VALUE(E, NATURAL);
657         break;
658     case 'F':
659         pitch = SCORE_PITCH_VALUE(F, NATURAL);
660         break;
661     case 'G':
662         pitch = SCORE_PITCH_VALUE(G, NATURAL);
663         break;
664     case 'A':
665         pitch = SCORE_PITCH_VALUE(A, NATURAL);
666         break;
667     case 'B':
668         pitch = SCORE_PITCH_VALUE(B, NATURAL);
669         break;
670     default:
671         fprintf (stderr, "Malformed pitch name: %s (expected 'A' - 'G')\n", s);
672         exit (1);
673     }
674     s++;
675
676     if (*s < '0' || *s > '9') {
677         fprintf (stderr, "Malformed octave number: %s (expected '0' - '9')\n", s);
678         exit (1);
679     }
680
681     octave = *s - '0';
682
683     challenge->note = score_add_note (challenge->staff, pitch, octave,
684                                       SCORE_DURATION_WHOLE);
685     challenge->satisfied = 0;
686     challenge->mistaken = 0;
687 }
688
689 /* Determine whether the user hit the correct note. */
690 static void
691 _judge_note (scherzo_t *scherzo, score_note_t *note)
692 {
693     challenge_t *challenge = &scherzo->challenge;
694
695     if (! scherzo->challenge.note) {
696         score_set_note_color_rgb (note, 0.0, 0.0, 0.0); /* black */
697         return;
698     }
699
700     if (note->pitch == challenge->note->pitch &&
701         note->octave == challenge->note->octave)
702     {
703         challenge->satisfied = 1;
704         score_set_note_color_rgb (note, 18/256., 130/256., 28/256.); /* green */
705     }
706     else
707     {
708         challenge->mistaken = 1;
709         score_set_note_color_rgb (note, 184/256., 4/256., 22/256.); /* red */
710     }
711 }
712
713 /* If the user got the right note (eventually), then score it in
714  * mnemon and show the next note. */
715 static void
716 _score_challenge (scherzo_t *scherzo)
717 {
718     challenge_t *challenge = &scherzo->challenge;
719
720     if (! challenge->note)
721         return;
722
723     if (! challenge->satisfied)
724         return;
725
726     mnemon_score_item (&scherzo->mnemon, challenge->bin, challenge->item_index,
727                        ! challenge->mistaken);
728
729     _select_challenge (scherzo);
730 }
731
732 static int
733 on_midi_input (unused (GIOChannel *channel),
734                unused (GIOCondition condition),
735                void *user_data)
736 {
737     unsigned char buf[MIDI_BUF_SIZE], *next;
738     scherzo_t *scherzo = user_data;
739     ssize_t remaining;
740     snd_seq_event_t event;
741     score_note_t *note;
742     int need_redraw = FALSE;
743
744     remaining = read (scherzo->midi_fd, buf, MIDI_BUF_SIZE);
745
746     next = buf;
747     while (remaining) {
748         long consumed;
749
750         consumed = snd_midi_event_encode (scherzo->snd_midi_event,
751                                           next, remaining, &event);
752
753         remaining -= consumed;
754         next += consumed;
755
756         switch (event.type) {
757         case SND_SEQ_EVENT_NONE:
758             /* Incomplete event. Nothing to do. */
759             break;
760         case SND_SEQ_EVENT_NOTEON:
761             note = scherzo_add_note_midi (scherzo, event.data.note.note);
762             _judge_note (scherzo, note);
763             need_redraw = TRUE;
764             break;
765         case SND_SEQ_EVENT_NOTEOFF:
766             scherzo_remove_note_midi (scherzo, event.data.note.note);
767             _score_challenge (scherzo);
768             need_redraw = TRUE;
769             break;
770         case SND_SEQ_EVENT_CLOCK:
771             /* Ignore for now as my piano sends a constant stream of these. */
772             break;
773         case SND_SEQ_EVENT_SENSING:
774             /* Ignore for now as my piano sends a constant stream of these. */
775             break;
776         default:
777             fprintf (stderr, "Fixme: Do not yet know how to handle MIDI event %d\n",
778                      event.type);
779             break;
780         }
781     }
782
783     if (need_redraw)
784         gtk_widget_queue_draw (scherzo->window);
785
786     /* Return TRUE to continue to get called in the future. */
787     return TRUE;
788 }
789
790 int
791 main (int argc, char *argv[])
792 {
793     GtkWidget *drawing_area;
794     scherzo_t scherzo;
795     int err;
796
797     srand (time (NULL));
798
799     gtk_init (&argc, &argv);
800
801     scherzo.ctx = talloc_new (NULL);
802
803     scherzo.score = score_create (scherzo.ctx);
804     scherzo.staff_height = 100;
805     score_set_staff_height (scherzo.score, scherzo.staff_height);
806
807     score_add_brace (scherzo.score, 2);
808     scherzo.treble = score_add_staff (scherzo.score, SCORE_CLEF_G);
809     scherzo.bass = score_add_staff (scherzo.score, SCORE_CLEF_F);
810
811     scherzo.chord = NULL;
812
813     /* Default to octave 4 for computer keyboard keypresses. */
814     scherzo.keyboard_octave = 4;
815
816     scherzo.num_notes_pressed = 0;
817     scherzo.notes_pressed = NULL;
818
819     mnemon_init (&scherzo.mnemon);
820     /* XXX: Should create a default file if one cannot be loaded. */
821     mnemon_load_category (&scherzo.mnemon, "scherzo-notes");
822
823     scherzo.challenge.note = NULL;
824 /*
825     _select_challenge (&scherzo);
826 */
827
828     err = snd_midi_event_new (MIDI_BUF_SIZE, &scherzo.snd_midi_event);
829     if (err) {
830         fprintf (stderr, "Out of memory.\n");
831         return 1;
832     }
833
834 #define MIDI_DEVICE "/dev/midi1"
835     scherzo.midi_fd = open (MIDI_DEVICE, O_RDONLY);
836     if (scherzo.midi_fd < 0) {
837         printf ("failed to open " MIDI_DEVICE ". Midi input will not be available.\n");
838     } else {
839         GIOChannel *channel;
840
841         channel = g_io_channel_unix_new (scherzo.midi_fd);
842         g_io_channel_set_encoding (channel, NULL, NULL);
843         g_io_add_watch (channel, G_IO_IN, on_midi_input, &scherzo);
844     }
845
846     scherzo.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
847
848     gtk_window_set_default_size (GTK_WINDOW (scherzo.window), 1000, 600);
849
850     g_signal_connect (scherzo.window, "delete-event",
851                       G_CALLBACK (on_delete_event_quit), NULL);
852
853     drawing_area = gtk_drawing_area_new ();
854
855     gtk_container_add (GTK_CONTAINER (scherzo.window), drawing_area);
856
857     g_signal_connect (drawing_area, "expose-event",  
858                       G_CALLBACK (on_expose_event_draw),
859                       &scherzo);
860
861     g_signal_connect (scherzo.window, "key-press-event",
862                       G_CALLBACK (on_key_press_event),
863                       &scherzo);
864
865     g_signal_connect (scherzo.window, "key-release-event",
866                       G_CALLBACK (on_key_release_event),
867                       &scherzo);
868     
869     gtk_widget_show_all (scherzo.window);
870     
871     gtk_main ();
872
873     mnemon_save (&scherzo.mnemon);
874
875     snd_midi_event_free (scherzo.snd_midi_event);
876
877     talloc_free (scherzo.ctx);
878
879     return 0;
880 }