X-Git-Url: https://git.cworth.org/git?p=scherzo;a=blobdiff_plain;f=pitch.c;h=6bb64702936363813ac8ebdfae2d1c1f5ac5f5c0;hp=c628a76ac3076f52e540dce83789c11dc891eba4;hb=HEAD;hpb=54d2b1cb13bf52df22c93e58377a00b757644b94 diff --git a/pitch.c b/pitch.c index c628a76..6bb6470 100644 --- a/pitch.c +++ b/pitch.c @@ -18,6 +18,9 @@ * along with this program. If not, see http://www.gnu.org/licenses/ . */ +#include +#include + #include "pitch.h" const char * @@ -96,3 +99,199 @@ pitch_lower_by_octaves (pitch_t pitch, int octaves) return PITCH (PITCH_NAME (pitch), PITCH_ACCIDENTAL (pitch), new_octave); } + +/* Number of half steps from 'root' up to 'pitch' (that is, counting + * the steps up a chromatic scale), within the same octave. */ +int +pitch_from_root_in_half_steps (pitch_t pitch, pitch_t root) +{ + int root_midi = pitch_to_midi (root); + int pitch_midi = pitch_to_midi (pitch); + + while (pitch_midi < root_midi) + pitch_midi += 12; + + return (pitch_midi - root_midi) % 12; +} + +pitch_t +pitch_spell_as_degree (pitch_t pitch, pitch_t root, int degree) +{ + pitch_name_t name = PITCH_NAME (pitch); + pitch_accidental_t accidental = PITCH_ACCIDENTAL (pitch); + int octave = PITCH_OCTAVE (pitch); + int degree_zero_based = (degree - 1) % 7; + int degree_delta; + + int note_degree_zero_based = name - PITCH_NAME (root); + if (note_degree_zero_based < 0) + note_degree_zero_based += 7; + + if (note_degree_zero_based == degree_zero_based) + return pitch; + + degree_delta = note_degree_zero_based - degree_zero_based; + if (degree_delta > 3) + degree_delta = - (7 - degree_delta); + if (degree_delta < -3) + degree_delta = - (-7 - degree_delta); + + /* Cannot re-spell pitch more than one degree away. Return + * original pitch. */ + if (abs (degree_delta) != 1) + return pitch; + + if (degree_delta == -1) { + if (name == PITCH_NAME_B) { + name = PITCH_NAME_C; + octave++; + } else { + name++; + } + switch (name) { + case PITCH_NAME_D: + case PITCH_NAME_E: + case PITCH_NAME_G: + case PITCH_NAME_A: + case PITCH_NAME_B: + accidental -= 2; + break; + case PITCH_NAME_C: + case PITCH_NAME_F: + accidental -= 1; + break; + } + } + + if (degree_delta == +1) { + if (name == PITCH_NAME_C) { + name = PITCH_NAME_B; + octave--; + } else { + name--; + } + switch (name) { + case PITCH_NAME_C: + case PITCH_NAME_D: + case PITCH_NAME_F: + case PITCH_NAME_G: + case PITCH_NAME_A: + accidental += 2; + break; + case PITCH_NAME_E: + case PITCH_NAME_B: + accidental += 1; + } + } + + /* Also cannot use accidentals to respell more than two half steps + * either direction. Return original pitch. */ + if (accidental < 0 || accidental > PITCH_ACCIDENTAL_DOUBLE_SHARP) + return pitch; + + return PITCH (name, accidental, octave); +} + +/* Return a MIDI note value corresponding to 'pitch' */ +unsigned char +pitch_to_midi (pitch_t pitch) +{ + int octave = PITCH_OCTAVE (pitch); + unsigned char midi_note = 12 * (octave + 1); + + switch (PITCH_NAME (pitch)) { + case PITCH_NAME_C: + break; + case PITCH_NAME_D: + midi_note += 2; + break; + case PITCH_NAME_E: + midi_note += 4; + break; + case PITCH_NAME_F: + midi_note += 5; + break; + case PITCH_NAME_G: + midi_note += 7; + break; + case PITCH_NAME_A: + midi_note += 9; + break; + case PITCH_NAME_B: + midi_note += 11; + break; + } + + switch (PITCH_ACCIDENTAL (pitch)) { + case PITCH_ACCIDENTAL_DOUBLE_FLAT: + midi_note -= 2; + break; + case PITCH_ACCIDENTAL_FLAT: + midi_note -= 1; + break; + case PITCH_ACCIDENTAL_NATURAL: + break; + case PITCH_ACCIDENTAL_SHARP: + midi_note += 1; + break; + case PITCH_ACCIDENTAL_DOUBLE_SHARP: + midi_note += 2; + break; + } + + return midi_note; +} + +/* Return the pitch_t value corresponding to the given MIDI note value. */ +pitch_t +pitch_from_midi (unsigned char midi_note) +{ + int octave = octave = midi_note / 12 - 1; + + switch (midi_note % 12) + { + default: + case 0: + return PITCH_LITERAL (C, NATURAL, octave); + break; + case 1: + return PITCH_LITERAL (D, FLAT, octave); + break; + case 2: + return PITCH_LITERAL (D, NATURAL, octave); + break; + case 3: + return PITCH_LITERAL (E, FLAT, octave); + break; + case 4: + return PITCH_LITERAL (E, NATURAL, octave); + break; + case 5: + return PITCH_LITERAL (F, NATURAL, octave); + break; + case 6: + return PITCH_LITERAL (G, FLAT, octave); + break; + case 7: + return PITCH_LITERAL (G, NATURAL, octave); + break; + case 8: + return PITCH_LITERAL (A, FLAT, octave); + break; + case 9: + return PITCH_LITERAL (A, NATURAL, octave); + break; + case 10: + return PITCH_LITERAL (B, FLAT, octave); + break; + case 11: + return PITCH_LITERAL (B, NATURAL, octave); + break; + } +} + +int +pitch_enharmonic_to (pitch_t a, pitch_t b) +{ + return pitch_to_midi (a) == pitch_to_midi (b); +}