]> git.cworth.org Git - scherzo/commitdiff
Switch to a degree-specific scheme for specifying chord signatures
authorCarl Worth <cworth@cworth.org>
Sun, 22 Sep 2013 22:57:14 +0000 (15:57 -0700)
committerCarl Worth <cworth@cworth.org>
Sun, 22 Sep 2013 22:57:14 +0000 (15:57 -0700)
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

index b9a463368b86129dd1eee0cdd74cdabcecf7b561..c0582f132517c674e762d1952459c5860113a703 100644 (file)
--- 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 (&degrees[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;