From: Carl Worth Date: Mon, 23 Sep 2013 04:06:29 +0000 (-0700) Subject: Add support for recognizing inverted chords. X-Git-Url: https://git.cworth.org/git?p=scherzo;a=commitdiff_plain;h=73ba6a9e9a9308747fd0fa301e1a349746eaf47c Add support for recognizing inverted chords. This isn't using any fancy figured-bass-derived names for these yet. For now, it's just spelling things out as "1st inversion", "2nd inversion", etc. --- diff --git a/scherzo.c b/scherzo.c index c0582f1..d6b3af5 100644 --- a/scherzo.c +++ b/scherzo.c @@ -486,10 +486,12 @@ static int _chord_signature_matches (analyzed_note_t *notes, int num_notes, modified_degree_t *degrees, - int num_degrees) + int num_degrees, + int *inversion_ret) { #define MAX_DEGREES 4 int relative_pitches[MAX_DEGREES]; + int inversion, max_inversions; int i; assert (num_degrees <= MAX_DEGREES); @@ -497,14 +499,39 @@ _chord_signature_matches (analyzed_note_t *notes, if (num_notes != num_degrees) return 0; - for (i = 0; i < num_degrees; i++) - relative_pitches[i] = _modified_degree_to_half_steps (°rees[i]); + max_inversions = num_degrees; - for (i = 0; i < num_notes; i++) - if (notes[i].relative_pitch != relative_pitches[i]) - return 0; + /* We never spell simple intervals as inversions. */ + if (num_degrees == 2) + max_inversions = 1; + + for (inversion = 0; inversion < max_inversions; inversion++) { + for (i = 0; i < num_degrees; i++) { + /* The num_degrees is in the addition just to ensure all + * inputs to the modulus operator remain positive. */ + int index = (i + num_degrees - inversion) % num_degrees; + + /* Again, adding a 12 to keep things positive. */ + relative_pitches[index] = + (12 + + _modified_degree_to_half_steps (°rees[i]) - + _modified_degree_to_half_steps (°rees[inversion])) % 12; + + } + + for (i = 0; i < num_notes; i++) + if (notes[i].relative_pitch != relative_pitches[i]) + goto NEXT_INVERSION; + + *inversion_ret = inversion; - return 1; + return 1; + + NEXT_INVERSION: + ; + } + + return 0; } static void @@ -514,7 +541,7 @@ scherzo_analyze_chord (scherzo_t *scherzo) analyzed_note_t *notes; note_group_t *note_group; unsigned i, j, num_notes; - int bass_pitch; + int bass_pitch, inversion; const char *chord_name = NULL; if (scherzo->pedal_pressed) @@ -525,7 +552,7 @@ scherzo_analyze_chord (scherzo_t *scherzo) num_notes = note_group->num_notes; struct { modified_degree_t degrees[1]; const char *name; } octaves[] = { - { {{0, 0}}, "Octave"} + { {{1, 0}}, "Octave"} }; struct { modified_degree_t degrees[2]; const char *name; } intervals[] = { @@ -627,37 +654,73 @@ scherzo_analyze_chord (scherzo_t *scherzo) case 1: for (i = 0; i < ARRAY_SIZE (octaves); i++) { if (_chord_signature_matches (notes, num_notes, - octaves[i].degrees, 1)) + octaves[i].degrees, 1, &inversion)) + { chord_name = octaves[i].name; + break; + } } break; case 2: for (i = 0; i < ARRAY_SIZE (intervals); i++) { if (_chord_signature_matches (notes, num_notes, - intervals[i].degrees, 2)) + intervals[i].degrees, 2, &inversion)) + { chord_name = intervals[i].name; + break; + } } break; case 3: for (i = 0; i < ARRAY_SIZE (triads); i++) { if (_chord_signature_matches (notes, num_notes, - triads[i].degrees, 3)) + triads[i].degrees, 3, &inversion)) + { chord_name = triads[i].name; + break; + } } break; case 4: for (i = 0; i < ARRAY_SIZE(sevenths); i++) { if (_chord_signature_matches (notes, num_notes, - sevenths[i].degrees, 4)) + sevenths[i].degrees, 4, &inversion)) + { chord_name = sevenths[i].name; + break; + } } break; } - if (chord_name) - scherzo->chord = score_add_chord (scherzo->treble, chord_name); - else - scherzo->chord = score_add_chord (scherzo->treble, "Unknown or not a chord"); + if (chord_name) { + if (inversion) { + const char *inversion_str; + switch (inversion) { + case 1: + inversion_str = "1st inversion"; + break; + case 2: + inversion_str = "2nd inversion"; + break; + case 3: + inversion_str = "3rd inversion"; + break; + default: + fprintf (stderr, "Internal error: Unexpected inversion: %d\n", + inversion); + exit(1); + } + chord_name = talloc_asprintf (local, "%s %s", + chord_name, inversion_str); + } else { + chord_name = talloc_strdup (local, chord_name); + } + } else { + chord_name = talloc_strdup (local, "Unknown chord"); + } + + scherzo->chord = score_add_chord (scherzo->treble, chord_name); DONE: talloc_free (local); diff --git a/score.c b/score.c index 25187ba..fe7622d 100644 --- a/score.c +++ b/score.c @@ -528,6 +528,8 @@ score_add_chord (score_staff_t *staff, if (chord == NULL) return NULL; + talloc_steal (chord, name); + chord->staff = staff; chord->name = talloc_strdup (chord, name); diff --git a/score.h b/score.h index 6d2fef5..03add03 100644 --- a/score.h +++ b/score.h @@ -204,6 +204,9 @@ score_add_note (score_staff_t *staff, /* Add a chord symbol of 'name' to a staff. * * For now, the chord symbols are free-form names. + * + * The chord name must be a talloc'ed string, which the returned + * score_chord_t will talloc_steal. */ score_chord_t * score_add_chord (score_staff_t *staff,