]> git.cworth.org Git - scherzo/commitdiff
Integrate some simple mnemon quizzing into scherzo.
authorCarl Worth <cworth@cworth.org>
Mon, 26 Sep 2011 06:34:46 +0000 (23:34 -0700)
committerCarl Worth <cworth@cworth.org>
Mon, 26 Sep 2011 06:42:56 +0000 (23:42 -0700)
This will be a lot more interesting once we start drawing ledger lines
and accidentals. But it's already useful for white-key practice for
the notes directly on the staff. Could definitely use better feedback
such as different colors (or even just different horizontal spacing
for challenge vs. response notes).

Makefile
mnemon
scherzo.c
score.c
score.h

index db95a9d24fcc0e45b7d6cea7f91bbb32f5f601c0..08511cc8a05943bbf826e5ae08712f86d6fd3ba4 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,7 @@
 all: scherzo
 
 scherzo_srcs :=                \
 all: scherzo
 
 scherzo_srcs :=                \
+       mnemon/mnemon.c \
        scherzo.c       \
        score.c
 
        scherzo.c       \
        score.c
 
@@ -8,7 +9,7 @@ scherzo_modules = $(scherzo_srcs:.c=.o)
 
 WARN_CFLAGS = -Wall -Wextra
 
 
 WARN_CFLAGS = -Wall -Wextra
 
-SCHERZO_CFLAGS = $(CFLAGS) $(WARN_CFLAGS) $(CONFIGURE_CFLAGS) $(extra_cflags) `pkg-config --cflags gtk+-2.0 alsa`
+SCHERZO_CFLAGS = $(CFLAGS) $(WARN_CFLAGS) $(CONFIGURE_CFLAGS) $(extra_cflags) -I./mnemon `pkg-config --cflags gtk+-2.0 alsa`
 
 SCHERZO_LDFLAGS = $(LDFLAGS) `pkg-config --libs gtk+-2.0 alsa` -ltalloc
 
 
 SCHERZO_LDFLAGS = $(LDFLAGS) `pkg-config --libs gtk+-2.0 alsa` -ltalloc
 
diff --git a/mnemon b/mnemon
index 10d0f1e757ef8c22bf8c1d921b624296a25ab238..12dfc3592e237017c9a9afd8708728f58f8ab68a 160000 (submodule)
--- a/mnemon
+++ b/mnemon
@@ -1 +1 @@
-Subproject commit 10d0f1e757ef8c22bf8c1d921b624296a25ab238
+Subproject commit 12dfc3592e237017c9a9afd8708728f58f8ab68a
index e6a67ec1ce7bbc4e83046d9e94f2d10e7c5d1bcd..c285891f0a43d00167f4beb5d164189c227ccfa5 100644 (file)
--- a/scherzo.c
+++ b/scherzo.c
 #include <asoundlib.h>
 
 #include "score.h"
 #include <asoundlib.h>
 
 #include "score.h"
+#include "mnemon.h"
 
 #define unused(foo) foo __attribute__((unused))
 
 #define MIDI_BUF_SIZE 4096
 
 
 #define unused(foo) foo __attribute__((unused))
 
 #define MIDI_BUF_SIZE 4096
 
+typedef struct challenge
+{
+    bin_t *bin;
+    int item_index;
+    score_staff_t *staff;
+    score_note_t *note;
+
+    int satisfied;
+    int mistaken;
+} challenge_t;
+
 typedef struct scherzo
 {
 typedef struct scherzo
 {
+    void *ctx;
+
     GtkWidget *window;
     score_t *score;
     int staff_height;
     GtkWidget *window;
     score_t *score;
     int staff_height;
@@ -36,6 +50,12 @@ typedef struct scherzo
     score_staff_t *bass;
     int midi_fd;
     snd_midi_event_t *snd_midi_event;
     score_staff_t *bass;
     int midi_fd;
     snd_midi_event_t *snd_midi_event;
+
+    mnemon_t mnemon;
+    challenge_t challenge;
+
+    int num_notes_pressed;
+    score_note_t **notes_pressed;
 } scherzo_t;
 
 static int
 } scherzo_t;
 
 static int
@@ -72,7 +92,7 @@ on_expose_event_draw (GtkWidget *widget,
     cairo_paint (cr);
 
     /* Add some padding on the sides and top */
     cairo_paint (cr);
 
     /* Add some padding on the sides and top */
-    cairo_translate (cr, pad, (int) scherzo->staff_height / 2);
+    cairo_translate (cr, pad, 2 * scherzo->staff_height);
     score_set_staff_height (score, scherzo->staff_height);
     score_set_width (score, widget_width - 2 * pad);
 
     score_set_staff_height (score, scherzo->staff_height);
     score_set_width (score, widget_width - 2 * pad);
 
@@ -162,22 +182,33 @@ _midi_to_score_pitch_and_octave (unsigned char midi_note,
     }
 }
 
     }
 }
 
-static void
+static score_note_t *
 scherzo_add_note_midi (scherzo_t *scherzo, unsigned char midi_note)
 {
     score_staff_t *staff;
     score_pitch_t pitch;
     int octave;
 scherzo_add_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;
+    staff = scherzo->challenge.staff;
 
     _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave);
 
 
     _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave);
 
-    score_staff_add_note (staff, pitch, octave, SCORE_DURATION_WHOLE);
+    note = score_staff_add_note (staff, pitch, octave, SCORE_DURATION_WHOLE);
+
+    scherzo->num_notes_pressed++;
+    scherzo->notes_pressed = talloc_realloc (scherzo->ctx,
+                                            scherzo->notes_pressed,
+                                            score_note_t*,
+                                            scherzo->num_notes_pressed);
+    if (scherzo->notes_pressed == NULL) {
+       fprintf (stderr, "Out of memory.\n");
+       exit (1);
+    }
+
+    scherzo->notes_pressed[scherzo->num_notes_pressed - 1] = note;
+
+    return note;
 }
 
 static void
 }
 
 static void
@@ -187,17 +218,137 @@ scherzo_remove_note_midi (scherzo_t *scherzo, unsigned char midi_note)
     score_pitch_t pitch;
     int octave;
     score_note_t *note;
     score_pitch_t pitch;
     int octave;
     score_note_t *note;
+    int i;
 
 
-    /* Anything at Middle C and above goes on the treble staff by default. */
-    if (midi_note >= 60)
-       staff = scherzo->treble;
-    else
-       staff = scherzo->bass;
+    staff = scherzo->challenge.staff;
 
     _midi_to_score_pitch_and_octave (midi_note, &pitch, &octave);
 
 
     _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);
+    for (i = 0; i < scherzo->num_notes_pressed; i++) {
+       note = scherzo->notes_pressed[i];
+       if (note->pitch == pitch && note->octave == octave) {
+           score_staff_remove_note (staff, note);
+           if (i < scherzo->num_notes_pressed - 1) {
+               memmove (scherzo->notes_pressed + i,
+                        scherzo->notes_pressed + i + 1,
+                        (scherzo->num_notes_pressed - 1 - i) * sizeof (score_note_t*));
+           }
+           scherzo->num_notes_pressed--;
+           i--;
+       }
+    }
+}
+
+void
+_select_challenge (scherzo_t *scherzo)
+{
+    category_t *category_unused;
+    bool_t introduced_unused;
+    item_t *item;
+    challenge_t *challenge = &scherzo->challenge;
+    score_pitch_t pitch;
+    int octave;
+    char *s;
+
+    if (challenge->note) {
+       score_staff_remove_note (challenge->staff, challenge->note);
+       challenge->note = NULL;
+    }
+
+    mnemon_select_item (&scherzo->mnemon,
+                       &challenge->bin,
+                       &challenge->item_index,
+                       &category_unused,
+                       &introduced_unused);
+
+    item = challenge->bin->items[challenge->item_index];
+
+    s = item->challenge;
+    if (strncmp (s, "treble:", 7) == 0) {
+       s += 7;
+       challenge->staff = scherzo->treble;
+    } else if (strncmp (s, "bass:", 5) == 0) {
+       s += 5;
+       challenge->staff = scherzo->bass;
+    } else {
+       fprintf (stderr,
+                "Malformed staff name: %s (expected 'treble:' or 'bass:')\n",
+                s);
+       exit (1);
+    }
+
+    switch (*s) {
+    case 'C':
+       pitch = SCORE_PITCH_VALUE(C, NATURAL);
+       break;
+    case 'D':
+       pitch = SCORE_PITCH_VALUE(D, NATURAL);
+       break;
+    case 'E':
+       pitch = SCORE_PITCH_VALUE(E, NATURAL);
+       break;
+    case 'F':
+       pitch = SCORE_PITCH_VALUE(F, NATURAL);
+       break;
+    case 'G':
+       pitch = SCORE_PITCH_VALUE(G, NATURAL);
+       break;
+    case 'A':
+       pitch = SCORE_PITCH_VALUE(A, NATURAL);
+       break;
+    case 'B':
+       pitch = SCORE_PITCH_VALUE(B, NATURAL);
+       break;
+    default:
+       fprintf (stderr, "Malformed pitch name: %s (expected 'A' - 'G')\n", s);
+       exit (1);
+    }
+    s++;
+
+    if (*s < '0' || *s > '9') {
+       fprintf (stderr, "Malformed octave number: %s (expected '0' - '9')\n", s);
+       exit (1);
+    }
+
+    octave = *s - '0';
+
+    challenge->note = score_staff_add_note (challenge->staff, pitch, octave,
+                                           SCORE_DURATION_WHOLE);
+    challenge->satisfied = 0;
+    challenge->mistaken = 0;
+}
+
+/* Determine whether the user hit the correct note. */
+static void
+_judge_note (scherzo_t *scherzo, score_note_t *note)
+{
+    challenge_t *challenge = &scherzo->challenge;
+
+    if (note->pitch == challenge->note->pitch &&
+       note->octave == challenge->note->octave)
+    {
+       challenge->satisfied = 1;
+    }
+    else
+    {
+       challenge->mistaken = 1;
+    }
+}
+
+/* If the user got the right note (eventually), then score it in
+ * mnemon and show the next note. */
+static void
+_score_challenge (scherzo_t *scherzo)
+{
+    challenge_t *challenge = &scherzo->challenge;
+
+    if (! challenge->satisfied)
+       return;
+
+    mnemon_score_item (&scherzo->mnemon, challenge->bin, challenge->item_index,
+                      ! challenge->mistaken);
+
+    _select_challenge (scherzo);
 }
 
 static int
 }
 
 static int
@@ -209,6 +360,7 @@ on_midi_input (unused (GIOChannel *channel),
     scherzo_t *scherzo = user_data;
     ssize_t remaining;
     snd_seq_event_t event;
     scherzo_t *scherzo = user_data;
     ssize_t remaining;
     snd_seq_event_t event;
+    score_note_t *note;
 
     remaining = read (scherzo->midi_fd, buf, MIDI_BUF_SIZE);
 
 
     remaining = read (scherzo->midi_fd, buf, MIDI_BUF_SIZE);
 
@@ -226,11 +378,13 @@ on_midi_input (unused (GIOChannel *channel),
            /* Incomplete event. Nothing to do. */
            break;
        case SND_SEQ_EVENT_NOTEON:
            /* Incomplete event. Nothing to do. */
            break;
        case SND_SEQ_EVENT_NOTEON:
-           scherzo_add_note_midi (scherzo, event.data.note.note);
+           note = scherzo_add_note_midi (scherzo, event.data.note.note);
+           _judge_note (scherzo, 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_NOTEOFF:
            scherzo_remove_note_midi (scherzo, event.data.note.note);
+           _score_challenge (scherzo);
            gtk_widget_queue_draw (scherzo->window);
            break;
        case SND_SEQ_EVENT_CLOCK:
            gtk_widget_queue_draw (scherzo->window);
            break;
        case SND_SEQ_EVENT_CLOCK:
@@ -257,9 +411,13 @@ main (int argc, char *argv[])
     scherzo_t scherzo;
     int err;
 
     scherzo_t scherzo;
     int err;
 
+    srand (time (NULL));
+
     gtk_init (&argc, &argv);
 
     gtk_init (&argc, &argv);
 
-    scherzo.score = score_create (NULL);
+    scherzo.ctx = talloc_new (NULL);
+
+    scherzo.score = score_create (scherzo.ctx);
     scherzo.staff_height = 48;
     score_set_staff_height (scherzo.score, scherzo.staff_height);
 
     scherzo.staff_height = 48;
     score_set_staff_height (scherzo.score, scherzo.staff_height);
 
@@ -267,6 +425,16 @@ main (int argc, char *argv[])
     scherzo.treble = score_add_staff (scherzo.score, SCORE_CLEF_G);
     scherzo.bass = score_add_staff (scherzo.score, SCORE_CLEF_F);
 
     scherzo.treble = score_add_staff (scherzo.score, SCORE_CLEF_G);
     scherzo.bass = score_add_staff (scherzo.score, SCORE_CLEF_F);
 
+    scherzo.num_notes_pressed = 0;
+    scherzo.notes_pressed = NULL;
+
+    mnemon_init (&scherzo.mnemon);
+    /* XXX: Should create a default file if one cannot be loaded. */
+    mnemon_load_category (&scherzo.mnemon, "scherzo");
+
+    scherzo.challenge.note = NULL;
+    _select_challenge (&scherzo);
+
     err = snd_midi_event_new (MIDI_BUF_SIZE, &scherzo.snd_midi_event);
     if (err) {
        fprintf (stderr, "Out of memory.\n");
     err = snd_midi_event_new (MIDI_BUF_SIZE, &scherzo.snd_midi_event);
     if (err) {
        fprintf (stderr, "Out of memory.\n");
@@ -308,8 +476,11 @@ main (int argc, char *argv[])
     
     gtk_main ();
 
     
     gtk_main ();
 
+    mnemon_save (&scherzo.mnemon);
+
     snd_midi_event_free (scherzo.snd_midi_event);
     snd_midi_event_free (scherzo.snd_midi_event);
-    talloc_free (scherzo.score);
+
+    talloc_free (scherzo.ctx);
 
     return 0;
 }
 
     return 0;
 }
diff --git a/score.c b/score.c
index b2866096e3c0183c254ae6ae3838e9325c637dcb..965d127b2b645d89d08ca1d6b51831c68f9117d3 100644 (file)
--- a/score.c
+++ b/score.c
@@ -58,13 +58,6 @@ struct score
     int num_staves;
 };
 
     int num_staves;
 };
 
-typedef struct score_note
-{
-    score_pitch_t pitch;
-    int octave;
-    score_duration_t duration;
-} score_note_t;
-
 score_t *
 score_create (void *ctx)
 {
 score_t *
 score_create (void *ctx)
 {
diff --git a/score.h b/score.h
index a07b9266d4aa5725f0221200c791165e407d81dd..1e74b1abbe70ad702f4559a0244d797390b25519 100644 (file)
--- a/score.h
+++ b/score.h
@@ -26,7 +26,6 @@
 
 typedef struct score score_t;
 typedef struct score_staff score_staff_t;
 
 typedef struct score score_t;
 typedef struct score_staff score_staff_t;
-typedef struct score_note score_note_t;
 
 #define SCORE_PITCH_ACCIDENTAL_MASK 0x07
 #define SCORE_PITCH_ACCIDENTAL_SHIFT 0
 
 #define SCORE_PITCH_ACCIDENTAL_MASK 0x07
 #define SCORE_PITCH_ACCIDENTAL_SHIFT 0
@@ -62,18 +61,6 @@ typedef enum score_pitch_name
 
 typedef enum score_pitch
 {
 
 typedef enum score_pitch
 {
-    SCORE_PITCH_Aff = SCORE_PITCH_VALUE (A, DOUBLE_FLAT), 
-    SCORE_PITCH_Af  = SCORE_PITCH_VALUE (A, FLAT),       
-    SCORE_PITCH_A   = SCORE_PITCH_VALUE (A, NATURAL),    
-    SCORE_PITCH_As  = SCORE_PITCH_VALUE (A, SHARP),      
-    SCORE_PITCH_Ass = SCORE_PITCH_VALUE (A, DOUBLE_SHARP),
-
-    SCORE_PITCH_Bff = SCORE_PITCH_VALUE (B, DOUBLE_FLAT), 
-    SCORE_PITCH_Bf  = SCORE_PITCH_VALUE (B, FLAT),       
-    SCORE_PITCH_B   = SCORE_PITCH_VALUE (B, NATURAL),    
-    SCORE_PITCH_Bs  = SCORE_PITCH_VALUE (B, SHARP),      
-    SCORE_PITCH_Bss = SCORE_PITCH_VALUE (B, DOUBLE_SHARP),
-
     SCORE_PITCH_Cff = SCORE_PITCH_VALUE (C, DOUBLE_FLAT), 
     SCORE_PITCH_Cf  = SCORE_PITCH_VALUE (C, FLAT),       
     SCORE_PITCH_C   = SCORE_PITCH_VALUE (C, NATURAL),    
     SCORE_PITCH_Cff = SCORE_PITCH_VALUE (C, DOUBLE_FLAT), 
     SCORE_PITCH_Cf  = SCORE_PITCH_VALUE (C, FLAT),       
     SCORE_PITCH_C   = SCORE_PITCH_VALUE (C, NATURAL),    
@@ -103,6 +90,18 @@ typedef enum score_pitch
     SCORE_PITCH_G   = SCORE_PITCH_VALUE (G, NATURAL),    
     SCORE_PITCH_Gs  = SCORE_PITCH_VALUE (G, SHARP),      
     SCORE_PITCH_Gss = SCORE_PITCH_VALUE (G, DOUBLE_SHARP),
     SCORE_PITCH_G   = SCORE_PITCH_VALUE (G, NATURAL),    
     SCORE_PITCH_Gs  = SCORE_PITCH_VALUE (G, SHARP),      
     SCORE_PITCH_Gss = SCORE_PITCH_VALUE (G, DOUBLE_SHARP),
+
+    SCORE_PITCH_Aff = SCORE_PITCH_VALUE (A, DOUBLE_FLAT), 
+    SCORE_PITCH_Af  = SCORE_PITCH_VALUE (A, FLAT),       
+    SCORE_PITCH_A   = SCORE_PITCH_VALUE (A, NATURAL),    
+    SCORE_PITCH_As  = SCORE_PITCH_VALUE (A, SHARP),      
+    SCORE_PITCH_Ass = SCORE_PITCH_VALUE (A, DOUBLE_SHARP),
+
+    SCORE_PITCH_Bff = SCORE_PITCH_VALUE (B, DOUBLE_FLAT), 
+    SCORE_PITCH_Bf  = SCORE_PITCH_VALUE (B, FLAT),       
+    SCORE_PITCH_B   = SCORE_PITCH_VALUE (B, NATURAL),    
+    SCORE_PITCH_Bs  = SCORE_PITCH_VALUE (B, SHARP),      
+    SCORE_PITCH_Bss = SCORE_PITCH_VALUE (B, DOUBLE_SHARP)
 } score_pitch_t;
 
 typedef enum score_duration
 } score_pitch_t;
 
 typedef enum score_duration
@@ -127,6 +126,13 @@ typedef enum score_duration
 
 #define SCORE_BUILD_NOTE(pitch, octave, duration) SCORE_PITCH_##pitch, (octave), SCORE_DURATION_##duration
 
 
 #define SCORE_BUILD_NOTE(pitch, octave, duration) SCORE_PITCH_##pitch, (octave), SCORE_DURATION_##duration
 
+typedef struct score_note
+{
+    score_pitch_t pitch;
+    int octave;
+    score_duration_t duration;
+} score_note_t;
+
 typedef enum score_clef
 {
     SCORE_CLEF_G,
 typedef enum score_clef
 {
     SCORE_CLEF_G,