From 5f7bba84e7e609d6dba0a960d0e4afb00bce3c85 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Sun, 22 Sep 2013 15:57:14 -0700 Subject: [PATCH] Switch to a degree-specific scheme for specifying chord signatures The old scheme specified each chord signature by the number of half steps between the root note and each note of the chord. This made it impossible to distinguish between alternate names of enharmonic chords (such as an augmented 4th and a diminished 5th which both have 6 half steps). The new specification indicates which degree should appear in the chord, and which modification each degree has, (from -2 to +2 half steps). So, under the old scheme an augmented 5th/diminished 4th would appear as: {0, 6} /* Half steps from root to each note */ And in the new scheme these can be identified separately as: {{1, 0}, {4, +1}} /* Augmented 4th */ {{1, 0}, {5, -1}} /* Diminished 5th */ We don't yet have much of a user-visibile change with this commit, (other than that the above interval will now only be identified as a "Diminished 5th" rathern than the "Augmented 4th/Diminished 5th" we had before). But this gives us the necessary information we will need in order to correctly spell chords on a staff after the chord has been identified. --- scherzo.c | 129 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 92 insertions(+), 37 deletions(-) diff --git a/scherzo.c b/scherzo.c index b9a4633..c0582f1 100644 --- a/scherzo.c +++ b/scherzo.c @@ -438,19 +438,70 @@ _compare_analyzed_note_by_relative_pitch (const void *va, const void *vb) return a->relative_pitch - b->relative_pitch; } +typedef struct modified_degree +{ + int degree; + int modification; +} modified_degree_t; + +static int +_modified_degree_to_half_steps (const modified_degree_t *degree) +{ + int half_steps; + + /* Number of half steps from root to specified degree within a + * diatonic scaled. */ + switch (degree->degree) { + case 1: + half_steps = 0; + break; + case 2: + half_steps = 2; + break; + case 3: + half_steps = 4; + break; + case 4: + half_steps = 5; + break; + case 5: + half_steps = 7; + break; + case 6: + half_steps = 9; + break; + case 7: + half_steps = 11; + break; + default: + fprintf (stderr, "Internal: Invalid degree %d\n", degree->degree); + exit (1); + break; + } + + return half_steps + degree->modification; +} + static int _chord_signature_matches (analyzed_note_t *notes, int num_notes, - int *signature_pitches, - int num_signature_pitches) + modified_degree_t *degrees, + int num_degrees) { +#define MAX_DEGREES 4 + int relative_pitches[MAX_DEGREES]; int i; - if (num_notes != num_signature_pitches) + assert (num_degrees <= MAX_DEGREES); + + if (num_notes != num_degrees) return 0; + for (i = 0; i < num_degrees; i++) + relative_pitches[i] = _modified_degree_to_half_steps (°rees[i]); + for (i = 0; i < num_notes; i++) - if (notes[i].relative_pitch != signature_pitches[i]) + if (notes[i].relative_pitch != relative_pitches[i]) return 0; return 1; @@ -473,41 +524,41 @@ scherzo_analyze_chord (scherzo_t *scherzo) num_notes = note_group->num_notes; - struct { int pitches[1]; const char *name; } octaves[] = { - { {0}, "Octave"} + struct { modified_degree_t degrees[1]; const char *name; } octaves[] = { + { {{0, 0}}, "Octave"} }; - struct { int pitches[2]; const char *name; } intervals[] = { - { {0, 1}, "Minor 2nd"}, - { {0, 2}, "Major 2nd"}, - { {0, 3}, "Minor 3rd"}, - { {0, 4}, "Major 3rd"}, - { {0, 5}, "Perfect 4th"}, - { {0, 6}, "Diminished 5th/Augmented 4th"}, - { {0, 7}, "Perfect 5th"}, - { {0, 8}, "Minor 6th"}, - { {0, 9}, "Major 6th"}, - { {0, 10}, "Minor 7th"}, - { {0, 11}, "Major 7th"} + struct { modified_degree_t degrees[2]; const char *name; } intervals[] = { + { {{1, 0}, {2, -1}}, "Minor 2nd"}, + { {{1, 0}, {2, 0}}, "Major 2nd"}, + { {{1, 0}, {3, -1}}, "Minor 3rd"}, + { {{1, 0}, {3, 0}}, "Major 3rd"}, + { {{1, 0}, {4, 0}}, "Perfect 4th"}, + { {{1, 0}, {5, -1}}, "Diminished 5th"}, + { {{1, 0}, {5, 0}}, "Perfect 5th"}, + { {{1, 0}, {6, -1}}, "Minor 6th"}, + { {{1, 0}, {6, 0}}, "Major 6th"}, + { {{1, 0}, {7, -1}}, "Minor 7th"}, + { {{1, 0}, {7, 0}}, "Major 7th"} }; - struct { int pitches[3]; const char *name; } triads[] = { - { {0, 4, 8}, "Augmented triad" }, - { {0, 4, 7}, "Major triad" }, - { {0, 3, 7}, "Minor triad" }, - { {0, 3, 6}, "Diminished triad" } + struct { modified_degree_t degrees[3]; const char *name; } triads[] = { + { {{1, 0}, {3, 0}, {5, +1}}, "Augmented triad" }, + { {{1, 0}, {3, 0}, {5, 0}}, "Major triad" }, + { {{1, 0}, {3, -1}, {5, 0}}, "Minor triad" }, + { {{1, 0}, {3, -1}, {5, -1}}, "Diminished triad" } }; - struct { int pitches[4]; const char *name; } sevenths[] = { - { {0, 4, 8, 11}, "Augmented/major 7" }, - { {0, 4, 8, 10}, "Augmented 7" }, - { {0, 4, 7, 11}, "Major 7" }, - { {0, 4, 7, 10}, "Dominant 7" }, - { {0, 3, 7, 11}, "Minor/major 7" }, - { {0, 3, 7, 10}, "Minor 7" }, - { {0, 3, 6, 11}, "Diminished/major 7" }, - { {0, 3, 6, 10}, "Half-diminished 7" }, - { {0, 3, 6, 9}, "Diminished 7" } + struct { modified_degree_t degrees[4]; const char *name; } sevenths[] = { + { {{1, 0}, {3, 0}, {5, +1}, {7, 0}}, "Augmented/major 7" }, + { {{1, 0}, {3, 0}, {5, +1}, {7, -1}}, "Augmented 7" }, + { {{1, 0}, {3, 0}, {5, 0}, {7, 0}}, "Major 7" }, + { {{1, 0}, {3, 0}, {5, 0}, {7, -1}}, "Dominant 7" }, + { {{1, 0}, {3, -1}, {5, 0}, {7, 0}}, "Minor/major 7" }, + { {{1, 0}, {3, -1}, {5, 0}, {7, -1}}, "Minor 7" }, + { {{1, 0}, {3, -1}, {5, -1}, {7, 0}}, "Diminished/major 7" }, + { {{1, 0}, {3, -1}, {5, -1}, {7, -1}}, "Half-diminished 7" }, + { {{1, 0}, {3, -1}, {5, -1}, {7, -2}}, "Diminished 7" } }; if (scherzo->chord) { @@ -575,25 +626,29 @@ scherzo_analyze_chord (scherzo_t *scherzo) switch (num_notes) { case 1: for (i = 0; i < ARRAY_SIZE (octaves); i++) { - if (_chord_signature_matches (notes, num_notes, octaves[i].pitches, 1)) + if (_chord_signature_matches (notes, num_notes, + octaves[i].degrees, 1)) chord_name = octaves[i].name; } break; case 2: for (i = 0; i < ARRAY_SIZE (intervals); i++) { - if (_chord_signature_matches (notes, num_notes, intervals[i].pitches, 2)) + if (_chord_signature_matches (notes, num_notes, + intervals[i].degrees, 2)) chord_name = intervals[i].name; } break; case 3: for (i = 0; i < ARRAY_SIZE (triads); i++) { - if (_chord_signature_matches (notes, num_notes, triads[i].pitches, 3)) + if (_chord_signature_matches (notes, num_notes, + triads[i].degrees, 3)) chord_name = triads[i].name; } break; case 4: for (i = 0; i < ARRAY_SIZE(sevenths); i++) { - if (_chord_signature_matches (notes, num_notes, sevenths[i].pitches, 4)) + if (_chord_signature_matches (notes, num_notes, + sevenths[i].degrees, 4)) chord_name = sevenths[i].name; } break; -- 2.43.0