From: Carl Worth <cworth@cworth.org>
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?a=commitdiff_plain;h=73ba6a9e9a9308747fd0fa301e1a349746eaf47c;p=scherzo

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 (&degrees[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 (&degrees[i]) -
+		 _modified_degree_to_half_steps (&degrees[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,