+/* Determine a chord name (if any) from the current notes pressed */
+
+typedef struct analyzed_note {
+ /* Original note being analzyed. */
+ score_note_t *note;
+
+ /* Absolute pitch (expressed as midi number). */
+ int midi_pitch;
+
+ /* Pitch relative to bass note. */
+ int relative_pitch;
+} analyzed_note_t;
+
+static int
+_compare_analyzed_note (const void *va, const void *vb)
+{
+ const analyzed_note_t *a = va, *b = vb;
+
+ return a->midi_pitch - b->midi_pitch;
+}
+
+static int
+_chord_signature_matches (analyzed_note_t *notes,
+ int num_notes,
+ int *signature_pitches,
+ int num_signature_pitches)
+{
+ int n, s;
+
+ for (n = 0, s = 0; s < num_signature_pitches; s++) {
+ if (n >= num_notes)
+ return 0;
+ if (notes[n].relative_pitch != signature_pitches[s])
+ return 0;
+ n++;
+ /* Skip repeated notes in chord being tested. */
+ while (n < num_notes &&
+ notes[n].relative_pitch == signature_pitches[s])
+ {
+ n++;
+ }
+ }
+
+ /* If there are fewer notes in the signature than in the chord,
+ * then there is no match. */
+ if (n < num_notes)
+ return 0;
+
+ return 1;
+}
+
+static void
+scherzo_analyze_chord (scherzo_t *scherzo)
+{
+ void *local = talloc_new (NULL);
+ analyzed_note_t *notes;
+ unsigned i, num_notes = scherzo->num_notes_pressed;
+ int bass_pitch;
+ const char *chord_name = NULL;
+
+ 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 { 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 { int pitches[4]; const char *name; } sevenths[] = {
+ { {0, 4, 8, 11}, "Augmented/major 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, 10}, "Half-diminished 7" },
+ { {0, 3, 6, 9}, "Diminished 7" },
+ { {0, 4, 8, 10}, "Augmented 7" },
+ { {0, 3, 6, 11}, "Diminished/major 7" },
+ };
+
+ if (scherzo->chord) {
+ score_remove_chord (scherzo->chord);
+ scherzo->chord = NULL;
+ }
+
+ if (num_notes == 0)
+ goto DONE;
+
+ notes = talloc_array (local, analyzed_note_t, num_notes);
+ if (notes == NULL)
+ goto DONE;
+
+ for (i = 0; i < num_notes; i++) {
+ score_note_t *note = scherzo->notes_pressed[i];
+ notes[i].note = note;
+ notes[i].midi_pitch = _score_pitch_and_octave_to_midi (note->pitch,
+ note->octave);
+ /* Relative pitch will be filled in after sorting. */
+ notes[i].relative_pitch = 0;
+ }
+
+ qsort (notes, num_notes, sizeof (analyzed_note_t), _compare_analyzed_note);
+
+ bass_pitch = notes[0].midi_pitch;
+
+ for (i = 0; i < num_notes; i++) {
+ notes[i].relative_pitch = notes[i].midi_pitch - bass_pitch;
+ }
+
+ for (i = 0; i < ARRAY_SIZE (intervals); i++) {
+ if (_chord_signature_matches (notes, num_notes, intervals[i].pitches, 2))
+ chord_name = intervals[i].name;
+ }
+
+ for (i = 0; i < ARRAY_SIZE (triads); i++) {
+ if (_chord_signature_matches (notes, num_notes, triads[i].pitches, 3))
+ chord_name = triads[i].name;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sevenths); i++) {
+ if (_chord_signature_matches (notes, num_notes, sevenths[i].pitches, 4))
+ chord_name = sevenths[i].name;
+ }
+
+ 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");
+
+DONE:
+ talloc_free (local);
+}
+