From ff7437e171dab5bc3868ce65ecacf8e77c6e8df3 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Tue, 1 Oct 2013 12:06:31 -0700 Subject: [PATCH] Fix key signature setting to accept an enharmonic pitch If the user asks for a key with a specific, real, name such a Cb or B, then the user will get that key. But if the user asks for a key that is not a real name, (such as B#), then the user will get a real key that is enharmonic, (such as C in this case). --- pitch.c | 7 +++++ pitch.h | 10 ++++++- scherzo-key.c | 74 +++++++++++++++++++++++++++++++++++++++------------ scherzo.c | 13 +-------- 4 files changed, 74 insertions(+), 30 deletions(-) diff --git a/pitch.c b/pitch.c index 211e68c..03eb481 100644 --- a/pitch.c +++ b/pitch.c @@ -18,6 +18,7 @@ * along with this program. If not, see http://www.gnu.org/licenses/ . */ +#include #include #include "pitch.h" @@ -288,3 +289,9 @@ pitch_from_midi (unsigned char midi_note) break; } } + +int +pitch_enharmonic_to (pitch_t a, pitch_t b) +{ + return pitch_to_midi (a) == pitch_to_midi (b); +} diff --git a/pitch.h b/pitch.h index ffe9782..d376a22 100644 --- a/pitch.h +++ b/pitch.h @@ -55,7 +55,7 @@ typedef uint32_t pitch_t; PITCH_LITERAL(literal_name, NATURAL, octave) /* PITCH_CLASS is useful for comparing pitches while ignoring any octave. */ -#define PITCH_CLASS(name, accidental) PITCH(name, accidental, 0) +#define PITCH_CLASS(pitch) PITCH(PITCH_NAME(pitch), PITCH_ACCIDENTAL(pitch), 0) #define PITCH_CLASS_LITERAL(literal_name, literal_accidental) \ PITCH_LITERAL(literal_name, literal_accidental, 0) @@ -132,4 +132,12 @@ pitch_to_midi (pitch_t pitch); pitch_t pitch_from_midi (unsigned char midi_note); +/* Return true if 'a' and 'b' sound identical, (even if spelled differently) + * + * This comparison considers octaves as significant. So Bb and A# in + * the same octave are considered enharmonic, but Bb and A# in + * different octaves are not. */ +int +pitch_enharmonic_to (pitch_t a, pitch_t b); + #endif /* PITCH_H */ diff --git a/scherzo-key.c b/scherzo-key.c index 18cd261..8fea502 100644 --- a/scherzo-key.c +++ b/scherzo-key.c @@ -19,6 +19,8 @@ */ #include +#include +#include #include "scherzo-key.h" @@ -29,17 +31,6 @@ scherzo_key_init (scherzo_key_t *key, pitch_t pitch) { int i; - pitch_t sharp_keys[] = { - PITCH_CLASS_LITERAL (C, NATURAL), - PITCH_CLASS_LITERAL (G, NATURAL), - PITCH_CLASS_LITERAL (D, NATURAL), - PITCH_CLASS_LITERAL (A, NATURAL), - PITCH_CLASS_LITERAL (E, NATURAL), - PITCH_CLASS_LITERAL (B, NATURAL), - PITCH_CLASS_LITERAL (F, SHARP), - PITCH_CLASS_LITERAL (C, SHARP), - }; - pitch_t flat_keys[] = { PITCH_CLASS_LITERAL (C, NATURAL), PITCH_CLASS_LITERAL (F, NATURAL), @@ -51,17 +42,66 @@ scherzo_key_init (scherzo_key_t *key, pitch_t pitch) PITCH_CLASS_LITERAL (C, FLAT) }; - key->pitch = PITCH_CLASS (PITCH_NAME (pitch), PITCH_ACCIDENTAL (pitch)); + pitch_t sharp_keys[] = { + PITCH_CLASS_LITERAL (C, NATURAL), + PITCH_CLASS_LITERAL (G, NATURAL), + PITCH_CLASS_LITERAL (D, NATURAL), + PITCH_CLASS_LITERAL (A, NATURAL), + PITCH_CLASS_LITERAL (E, NATURAL), + PITCH_CLASS_LITERAL (B, NATURAL), + PITCH_CLASS_LITERAL (F, SHARP), + PITCH_CLASS_LITERAL (C, SHARP), + }; + + /* Remove octave from 'pitch' */ + pitch = PITCH_CLASS (pitch); key->num_sharps = 0; - for (i = 0; i < ARRAY_SIZE (sharp_keys); i++) - if (sharp_keys[i] == key->pitch) + key->num_flats = 0; + + /* First, look for a key that exactly matches the specified pitch. */ + for (i = 0; i < ARRAY_SIZE (flat_keys); i++) { + if (flat_keys[i] == pitch) { + key->pitch = flat_keys[i]; + key->num_flats = i; + return; + } + } + + for (i = 0; i < ARRAY_SIZE (sharp_keys); i++) { + if (sharp_keys[i] == pitch) { + key->pitch = sharp_keys[i]; key->num_sharps = i; + return; + } + } - key->num_flats = 0; - for (i = 0; i < ARRAY_SIZE (flat_keys); i++) - if (flat_keys[i] == key->pitch) + /* Second, if we haven't found a key, look for something enharmonic. */ + for (i = 0; i < ARRAY_SIZE (flat_keys); i++) { + if (pitch_enharmonic_to (flat_keys[i], pitch)) { + key->pitch = flat_keys[i]; key->num_flats = i; + return; + } + } + + for (i = 0; i < ARRAY_SIZE (sharp_keys); i++) { + if (pitch_enharmonic_to (sharp_keys[i], pitch)) { + key->pitch = sharp_keys[i]; + key->num_sharps = i; + return; + } + } + + /* The pitch_enharmonic_to function won't catch this wraparound. */ + if (pitch == PITCH_CLASS_LITERAL (B, SHARP)) { + key->pitch = PITCH_CLASS_LITERAL (C, NATURAL); + return; + } + + fprintf (stderr, "Interal error: Failed to find a key for %s.\n", + pitch_string (pitch)); + exit (1); } bool diff --git a/scherzo.c b/scherzo.c index dacefa8..bdd1a0f 100644 --- a/scherzo.c +++ b/scherzo.c @@ -397,17 +397,6 @@ on_key_release_event (unused (GtkWidget *widget), return FALSE; } -/* Return true if 'a' and 'b' sound identical, (even if spelled differently) - * - * This comparison considers octaves as significant. So Bb and A# in - * the same octave are considered enharmonic, but Bb and A# in - * different octaves are not. */ -static int -_pitches_are_enharmonic (pitch_t a, pitch_t b) -{ - return pitch_to_midi (a) == pitch_to_midi (b); -} - /* Determine a chord name (if any) from the current notes pressed */ typedef struct analyzed_pitch { @@ -871,7 +860,7 @@ pitch_group_remove_pitch_enharmonic (pitch_group_t *group, pitch_t pitch) int i; for (i = 0; i < group->num_pitches; i++) - if (_pitches_are_enharmonic (group->pitches[i], pitch)) + if (pitch_enharmonic_to (group->pitches[i], pitch)) pitch_group_remove_pitch_at (group, i); } -- 2.43.0