From: Carl Worth Date: Fri, 27 Sep 2013 15:24:53 +0000 (-0700) Subject: Use matched chord signature to guide spelling of note on staff. X-Git-Url: https://git.cworth.org/git?p=scherzo;a=commitdiff_plain;h=f0c5237be2f92b640c7f7ba619cb7c1d8e4a1538 Use matched chord signature to guide spelling of note on staff. We now use the chord's signature to choose the correct spelling of each enharmonic note. For example, an A mjor triad has a C-sharp in it, not a D-flat. We can determine this since a major triad should have a note of the 3 scale degree (as recorded in the chord signature). Still to be done is to draw note heads without collision, draw accidentals without collision, and to improve the heuristic for deciding between ambiguous chord signatures. --- diff --git a/scherzo.c b/scherzo.c index 92dc595..ce57e9d 100644 --- a/scherzo.c +++ b/scherzo.c @@ -363,12 +363,14 @@ _score_pitch_and_octave_to_midi (score_pitch_t pitch, return midi_note; } +/* octave can optionally by NULL */ static void _midi_to_score_pitch_and_octave (unsigned char midi_note, score_pitch_t *pitch, int *octave) { - *octave = midi_note / 12 - 1; + if (octave) + *octave = midi_note / 12 - 1; switch (midi_note % 12) { @@ -457,7 +459,7 @@ _modified_degree_to_half_steps (const modified_degree_t *degree) scale_degree = (scale_degree % 8) + 1; /* Number of half steps from root to specified degree within a - * diatonic scaled. */ + * diatonic scale. */ switch (scale_degree) { case 1: half_steps = 0; @@ -489,6 +491,25 @@ _modified_degree_to_half_steps (const modified_degree_t *degree) return half_steps + degree->modification; } +/* Number of half steps from 'root' up to 'pitch' (that is, counting + * the steps up a chromatic scale). */ +static int +_pitch_from_root_in_half_steps (score_pitch_t pitch, score_pitch_t root) +{ + int root_midi = _score_pitch_and_octave_to_midi (root, 0); + int pitch_midi = _score_pitch_and_octave_to_midi (pitch, 0); + + if (pitch_midi < root_midi) + pitch_midi += 12; + + if (pitch_midi < root_midi) { + fprintf (stderr, "Internal error: midi values differ by more than expected.\n"); + exit (1); + } + + return (pitch_midi - root_midi) % 12; +} + static int _chord_signature_matches (analyzed_note_t *notes, int num_notes, @@ -764,6 +785,118 @@ chord_signature_t signatures[] = { { 6, {{1, 0}, {9, -1}, {3, -1}, {11, -1}, {5, -1}, {7, -2}}, "°" SUP "11" PUS } }; +static void +scherzo_adjust_note_to_match_modified_degree (score_note_t *note, + score_pitch_t root, + modified_degree_t *degree) +{ + score_pitch_name_t name = SCORE_PITCH_NAME (note->pitch); + score_pitch_accidental_t accidental = SCORE_PITCH_ACCIDENTAL (note->pitch); + int degree_zero_based = (degree->degree - 1) % 7; + int degree_delta; + + int note_degree_zero_based = name - SCORE_PITCH_NAME (root); + if (note_degree_zero_based < 0) + note_degree_zero_based += 7; + + if (note_degree_zero_based == degree_zero_based) + return; + + degree_delta = note_degree_zero_based - degree_zero_based; + if (degree_delta > 3) + degree_delta = - (7 - degree_delta); + if (degree_delta < -3) + degree_delta = - (-7 - degree_delta); + + if (abs (degree_delta) != 1) { + fprintf (stderr, "Internal error: Cannot adjust a note more than one degree (%d vs. %d).\n", note_degree_zero_based + 1, degree->degree); + exit (1); + } + + if (degree_delta == -1) { + if (name == SCORE_PITCH_NAME_B) { + name = SCORE_PITCH_NAME_C; + note->octave++; + } else { + name++; + } + switch (name) { + case SCORE_PITCH_NAME_D: + case SCORE_PITCH_NAME_E: + case SCORE_PITCH_NAME_G: + case SCORE_PITCH_NAME_A: + case SCORE_PITCH_NAME_B: + accidental -= 2; + break; + case SCORE_PITCH_NAME_C: + case SCORE_PITCH_NAME_F: + accidental -= 1; + break; + } + } + + if (degree_delta == +1) { + if (name == SCORE_PITCH_NAME_C) { + name = SCORE_PITCH_NAME_B; + note->octave--; + } else { + name--; + } + switch (name) { + case SCORE_PITCH_NAME_C: + case SCORE_PITCH_NAME_D: + case SCORE_PITCH_NAME_F: + case SCORE_PITCH_NAME_G: + case SCORE_PITCH_NAME_A: + accidental += 2; + break; + case SCORE_PITCH_NAME_E: + case SCORE_PITCH_NAME_B: + accidental += 1; + } + } + + if (accidental < 0 || accidental > SCORE_PITCH_ACCIDENTAL_DOUBLE_SHARP) { + fprintf (stderr, "Internal error: Trying to adjust an accidental out of range (degree_delta == %d (%d - %d), new accidental == %d).\n", degree_delta, note_degree_zero_based, degree_zero_based, accidental); + exit (1); + } + + note->pitch = SCORE_PITCH (name, accidental); +} + +static void +_spell_chord_by_signature (note_group_t *chord, + int num_notes, + chord_signature_t *signature, + score_pitch_t root) +{ + int i, degree, note_half_steps, degree_half_steps; + int root_midi; + + /* Sanitize root to eliminate things like double-flats, + * double-sharps, Cb, E#, etc. */ + root_midi = _score_pitch_and_octave_to_midi (root, 0); + _midi_to_score_pitch_and_octave (root_midi, &root, NULL); + + for (i = 0; i < num_notes; i++) { + note_half_steps = _pitch_from_root_in_half_steps (chord->notes[i]->pitch, + root); + for (degree = 0; degree < signature->num_degrees; degree++) { + degree_half_steps = _modified_degree_to_half_steps (&signature->degrees[degree]); + if (note_half_steps == degree_half_steps) { + scherzo_adjust_note_to_match_modified_degree (chord->notes[i], + root, + &signature->degrees[degree]); + break; + } + } + if (note_half_steps != degree_half_steps) { + fprintf (stderr, "Internal error: Chord and degree mis-match\n"); + exit (1); + } + } +} + static void scherzo_analyze_chord (scherzo_t *scherzo) { @@ -773,7 +906,8 @@ scherzo_analyze_chord (scherzo_t *scherzo) unsigned i, j, num_notes; int bass_pitch, inversion; score_pitch_t root; - const char *chord_name = NULL; + chord_signature_t *match = NULL; + char *chord_name; if (scherzo->pedal_pressed) note_group = &scherzo->notes_pedaled; @@ -851,14 +985,16 @@ scherzo_analyze_chord (scherzo_t *scherzo) signatures[i].num_degrees, inversion, &root)) { - chord_name = signatures[i].name; - goto CHORD_NAME_KNOWN; + match = &signatures[i]; + goto HAVE_MATCH; } } } - CHORD_NAME_KNOWN: +HAVE_MATCH: + + chord_name = (char *) signatures[i].name; - if (chord_name) { + if (match) { /* Don't print root pitch for octaves and inversions, * (since a pitch name alone looks like a major triad) */ if (num_notes < 3) { @@ -868,6 +1004,9 @@ scherzo_analyze_chord (scherzo_t *scherzo) _pitch_str (root), chord_name); } + + _spell_chord_by_signature (note_group, num_notes, + match, root); } else { chord_name = talloc_strdup (local, "Unknown chord"); } @@ -974,10 +1113,13 @@ scherzo_release_note (scherzo_t *scherzo, score_pitch_t pitch, int octave) score_note_t *note; int i; int found = 0; + int target_midi = _score_pitch_and_octave_to_midi (pitch, octave); + int note_midi; for (i = scherzo->notes_pressed.num_notes - 1; i >=0; i--) { note = scherzo->notes_pressed.notes[i]; - if (note->pitch == pitch && note->octave == octave) { + note_midi = _score_pitch_and_octave_to_midi (note->pitch, note->octave); + if (note_midi == target_midi) { found = 1; if (! scherzo->pedal_pressed) score_remove_note (note); diff --git a/score.c b/score.c index e047f97..b9df23b 100644 --- a/score.c +++ b/score.c @@ -712,4 +712,3 @@ score_staff_find_note (score_staff_t *staff, return NULL; } -