X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=scherzo.c;h=d1a9e9f5d98bf5fd0cdaf993fccb6a8f2dea5d2f;hb=b588ae80ff8009695f7ed299fd27e3f16de01e9e;hp=6a5f2b3176c7f441fdf097714746705a74a91a2c;hpb=aae6b7f55870ab7f9cd41f95e22231e66a673aea;p=scherzo diff --git a/scherzo.c b/scherzo.c index 6a5f2b3..d1a9e9f 100644 --- a/scherzo.c +++ b/scherzo.c @@ -66,7 +66,6 @@ typedef struct scherzo * keyboard". Any "piano keyboard" key knows its own octave and * accidental already. */ int keyboard_octave; - pitch_accidental_t keyboard_accidental; int midi_fd; snd_midi_event_t *snd_midi_event; @@ -78,6 +77,8 @@ typedef struct scherzo pitch_group_t notes_pedaled; int pedal_pressed; + + pitch_t key; } scherzo_t; /* Forward declarations. */ @@ -99,6 +100,12 @@ _judge_pitch (scherzo_t *scherzo, pitch_t pitch); static void _score_challenge (scherzo_t *scherzo); +static void +pitch_group_remove_pitches (pitch_group_t *group); + +static void +scherzo_update_notes_and_chord (scherzo_t *scherzo); + static int on_delete_event_quit (unused (GtkWidget *widget), unused (GdkEvent *event), @@ -149,9 +156,7 @@ on_key_press_event (GtkWidget *widget, { scherzo_t *scherzo = user_data; int octave; - /* Initialize to keep the compiler quiet. */ - pitch_name_t pitch_name = PITCH_NATURAL (C, 0); - pitch_t pitch; + pitch_t pitch = PITCH_NOT_A_PITCH; if (scherzo->challenge.active) octave = PITCH_OCTAVE (scherzo->challenge.pitch); @@ -173,38 +178,89 @@ on_key_press_event (GtkWidget *widget, gtk_widget_queue_draw (widget); return TRUE; break; - case GDK_KEY_q: - case GDK_KEY_Q: case GDK_KEY_Escape: gtk_main_quit (); return FALSE; - case GDK_KEY_c: - case GDK_KEY_C: - pitch_name = PITCH_NAME_C; + /* Map a piano keyboard onto the computer keyboard with white keys + * along the home row, (piano C on computer A, up to next octave + * higher C on computer K). Black keys are in the expected + * positions, (C# is W, D# is E, etc.). + */ + case GDK_KEY_a: + case GDK_KEY_A: + pitch = PITCH_LITERAL (C, NATURAL, octave); + break; + case GDK_KEY_s: + case GDK_KEY_S: + pitch = PITCH_LITERAL (D, NATURAL, octave); break; case GDK_KEY_d: case GDK_KEY_D: - pitch_name = PITCH_NAME_D; - break; - case GDK_KEY_e: - case GDK_KEY_E: - pitch_name = PITCH_NAME_E; + pitch = PITCH_LITERAL (E, NATURAL, octave); break; case GDK_KEY_f: case GDK_KEY_F: - pitch_name = PITCH_NAME_F; + pitch = PITCH_LITERAL (F, NATURAL, octave); break; case GDK_KEY_g: case GDK_KEY_G: - pitch_name = PITCH_NAME_G; + pitch = PITCH_LITERAL (G, NATURAL, octave); break; - case GDK_KEY_a: - case GDK_KEY_A: - pitch_name = PITCH_NAME_A; + case GDK_KEY_h: + case GDK_KEY_H: + pitch = PITCH_LITERAL (A, NATURAL, octave); break; - case GDK_KEY_b: - case GDK_KEY_B: - pitch_name = PITCH_NAME_B; + case GDK_KEY_j: + case GDK_KEY_J: + pitch = PITCH_LITERAL (B, NATURAL, octave); + break; + case GDK_KEY_k: + case GDK_KEY_K: + pitch = PITCH_LITERAL (C, NATURAL, octave + 1); + break; + case GDK_KEY_l: + case GDK_KEY_L: + pitch = PITCH_LITERAL (D, NATURAL, octave + 1); + break; + case GDK_KEY_semicolon: + case GDK_KEY_colon: + pitch = PITCH_LITERAL (E, NATURAL, octave + 1); + break; + case GDK_KEY_apostrophe: + case GDK_KEY_quotedbl: + pitch = PITCH_LITERAL (F, NATURAL, octave + 1); + break; + case GDK_KEY_w: + case GDK_KEY_W: + pitch = PITCH_LITERAL (C, SHARP, octave); + break; + case GDK_KEY_e: + case GDK_KEY_E: + pitch = PITCH_LITERAL (D, SHARP, octave); + break; + case GDK_KEY_t: + case GDK_KEY_T: + pitch = PITCH_LITERAL (F, SHARP, octave); + break; + case GDK_KEY_y: + case GDK_KEY_Y: + pitch = PITCH_LITERAL (G, SHARP, octave); + break; + case GDK_KEY_u: + case GDK_KEY_U: + pitch = PITCH_LITERAL (A, SHARP, octave); + break; + case GDK_KEY_o: + case GDK_KEY_O: + pitch = PITCH_LITERAL (C, SHARP, octave + 1); + break; + case GDK_KEY_p: + case GDK_KEY_P: + pitch = PITCH_LITERAL (D, SHARP, octave + 1); + break; + case GDK_KEY_bracketright: + case GDK_KEY_braceright: + pitch = PITCH_LITERAL (F, SHARP, octave + 1); break; case GDK_KEY_0: case GDK_KEY_1: @@ -220,20 +276,15 @@ on_key_press_event (GtkWidget *widget, case GDK_KEY_space: scherzo_press_pedal (scherzo); break; - case GDK_KEY_Up: - if (scherzo->keyboard_accidental < PITCH_ACCIDENTAL_DOUBLE_SHARP) - scherzo->keyboard_accidental++; - break; - case GDK_KEY_Down: - if (scherzo->keyboard_accidental > PITCH_ACCIDENTAL_DOUBLE_FLAT) - scherzo->keyboard_accidental--; + case GDK_KEY_Return: + /* Clear all notes when Return is pressed. */ + pitch_group_remove_pitches (&scherzo->notes_pressed); + pitch_group_remove_pitches (&scherzo->notes_pedaled); + scherzo_update_notes_and_chord (scherzo); break; } - pitch = PITCH (pitch_name, scherzo->keyboard_accidental, octave); - - if ((key->keyval >= GDK_KEY_A && key->keyval <= GDK_KEY_G) || - (key->keyval >= GDK_KEY_a && key->keyval <= GDK_KEY_g)) + if (pitch != PITCH_NOT_A_PITCH) { scherzo_press_note (scherzo, pitch); _judge_pitch (scherzo, pitch); @@ -242,7 +293,6 @@ on_key_press_event (GtkWidget *widget, return TRUE; } - /* Allow an unhandled event to propagate to other handlers. */ return FALSE; } @@ -254,9 +304,7 @@ on_key_release_event (unused (GtkWidget *widget), { scherzo_t *scherzo = user_data; int octave; - /* Initialize to keep the compiler quiet. */ - pitch_name_t pitch_name = PITCH_NAME_C; - pitch_t pitch; + pitch_t pitch = PITCH_NOT_A_PITCH; if (scherzo->challenge.active) octave = PITCH_OCTAVE (scherzo->challenge.pitch); @@ -264,43 +312,88 @@ on_key_release_event (unused (GtkWidget *widget), octave = scherzo->keyboard_octave; switch (key->keyval) { - case GDK_KEY_c: - case GDK_KEY_C: - pitch_name = PITCH_NAME_C; + case GDK_KEY_a: + case GDK_KEY_A: + pitch = PITCH_LITERAL (C, NATURAL, octave); + break; + case GDK_KEY_s: + case GDK_KEY_S: + pitch = PITCH_LITERAL (D, NATURAL, octave); break; case GDK_KEY_d: case GDK_KEY_D: - pitch_name = PITCH_NAME_D; - break; - case GDK_KEY_e: - case GDK_KEY_E: - pitch_name = PITCH_NAME_E; + pitch = PITCH_LITERAL (E, NATURAL, octave); break; case GDK_KEY_f: case GDK_KEY_F: - pitch_name = PITCH_NAME_F; + pitch = PITCH_LITERAL (F, NATURAL, octave); break; case GDK_KEY_g: case GDK_KEY_G: - pitch_name = PITCH_NAME_G; + pitch = PITCH_LITERAL (G, NATURAL, octave); break; - case GDK_KEY_a: - case GDK_KEY_A: - pitch_name = PITCH_NAME_A; + case GDK_KEY_h: + case GDK_KEY_H: + pitch = PITCH_LITERAL (A, NATURAL, octave); break; - case GDK_KEY_b: - case GDK_KEY_B: - pitch_name = PITCH_NAME_B; + case GDK_KEY_j: + case GDK_KEY_J: + pitch = PITCH_LITERAL (B, NATURAL, octave); + break; + case GDK_KEY_k: + case GDK_KEY_K: + pitch = PITCH_LITERAL (C, NATURAL, octave + 1); + break; + case GDK_KEY_l: + case GDK_KEY_L: + pitch = PITCH_LITERAL (D, NATURAL, octave + 1); + break; + case GDK_KEY_semicolon: + case GDK_KEY_colon: + pitch = PITCH_LITERAL (E, NATURAL, octave + 1); + break; + case GDK_KEY_apostrophe: + case GDK_KEY_quotedbl: + pitch = PITCH_LITERAL (F, NATURAL, octave + 1); + break; + case GDK_KEY_w: + case GDK_KEY_W: + pitch = PITCH_LITERAL (C, SHARP, octave); + break; + case GDK_KEY_e: + case GDK_KEY_E: + pitch = PITCH_LITERAL (D, SHARP, octave); + break; + case GDK_KEY_t: + case GDK_KEY_T: + pitch = PITCH_LITERAL (F, SHARP, octave); + break; + case GDK_KEY_y: + case GDK_KEY_Y: + pitch = PITCH_LITERAL (G, SHARP, octave); + break; + case GDK_KEY_u: + case GDK_KEY_U: + pitch = PITCH_LITERAL (A, SHARP, octave); + break; + case GDK_KEY_o: + case GDK_KEY_O: + pitch = PITCH_LITERAL (C, SHARP, octave + 1); + break; + case GDK_KEY_p: + case GDK_KEY_P: + pitch = PITCH_LITERAL (D, SHARP, octave + 1); + break; + case GDK_KEY_bracketright: + case GDK_KEY_braceright: + pitch = PITCH_LITERAL (F, SHARP, octave + 1); break; case GDK_KEY_space: scherzo_release_pedal (scherzo); break; } - pitch = PITCH (pitch_name, scherzo->keyboard_accidental, octave); - - if ((key->keyval >= GDK_KEY_A && key->keyval <= GDK_KEY_G) || - (key->keyval >= GDK_KEY_a && key->keyval <= GDK_KEY_g)) + if (pitch != PITCH_NOT_A_PITCH) { scherzo_release_note (scherzo, pitch); _score_challenge (scherzo); @@ -561,61 +654,6 @@ _chord_signature_matches (analyzed_pitch_t *pitches, return 1; } -static const char * -_pitch_str (pitch_t pitch) -{ - static char double_flat[] = "X𝄫"; - static char flat[] = "X♭"; - static char natural[] = "X"; - static char sharp[] = "X♯"; - static char double_sharp[] = "X𝄪"; - char *ret; - - switch (PITCH_ACCIDENTAL (pitch)) { - case PITCH_ACCIDENTAL_DOUBLE_FLAT: - ret = double_flat; - break; - case PITCH_ACCIDENTAL_FLAT: - ret = flat; - break; - case PITCH_ACCIDENTAL_NATURAL: - ret = natural; - break; - case PITCH_ACCIDENTAL_SHARP: - ret = sharp; - break; - case PITCH_ACCIDENTAL_DOUBLE_SHARP: - ret = double_sharp; - break; - } - - switch (PITCH_NAME(pitch)) { - case PITCH_NAME_C: - ret[0] = 'C'; - break; - case PITCH_NAME_D: - ret[0] = 'D'; - break; - case PITCH_NAME_E: - ret[0] = 'E'; - break; - case PITCH_NAME_F: - ret[0] = 'F'; - break; - case PITCH_NAME_G: - ret[0] = 'G'; - break; - case PITCH_NAME_A: - ret[0] = 'A'; - break; - case PITCH_NAME_B: - ret[0] = 'B'; - break; - } - - return ret; -} - typedef struct chord_signature { int num_degrees; @@ -1058,6 +1096,14 @@ pitch_group_remove_pitch (pitch_group_t *group, pitch_t pitch) } */ +/* Remove all pitches from a pitch_group_t */ +static void +pitch_group_remove_pitches (pitch_group_t *group) +{ + talloc_free (group->pitches); + pitch_group_init (group->ctx, group); +} + static void scherzo_update_notes_and_chord (scherzo_t *scherzo) { @@ -1115,7 +1161,7 @@ scherzo_press_note (scherzo_t *scherzo, pitch_t pitch) signature, *root); chord_name = talloc_asprintf (local, "%s%s", - _pitch_str (*root), + pitch_string (*root), signature->name); } } else if (chord->num_pitches > 2) { @@ -1400,15 +1446,17 @@ main (int argc, char *argv[]) scherzo.chord = NULL; - /* Default to octave 4 and natural for computer keyboard keypresses. */ + /* Default to octave 4 for computer keyboard keypresses. */ scherzo.keyboard_octave = 4; - scherzo.keyboard_accidental = PITCH_ACCIDENTAL_NATURAL; pitch_group_init (scherzo.ctx, &scherzo.notes_pressed); pitch_group_init (scherzo.ctx, &scherzo.notes_pedaled); scherzo.pedal_pressed = 0; + scherzo.key = PITCH_CLASS_LITERAL (C, NATURAL); + score_set_key (scherzo.score, scherzo.key); + mnemon_init (&scherzo.mnemon); /* XXX: Should create a default file if one cannot be loaded. */ mnemon_load_category (&scherzo.mnemon, "scherzo-notes");