+
+/* 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 (C, SHARP, octave);
+ break;
+ case 2:
+ return PITCH_LITERAL (D, NATURAL, octave);
+ break;
+ case 3:
+ return PITCH_LITERAL (D, SHARP, 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 (F, SHARP, octave);
+ break;
+ case 7:
+ return PITCH_LITERAL (G, NATURAL, octave);
+ break;
+ case 8:
+ return PITCH_LITERAL (G, SHARP, octave);
+ break;
+ case 9:
+ return PITCH_LITERAL (A, NATURAL, octave);
+ break;
+ case 10:
+ return PITCH_LITERAL (A, SHARP, octave);
+ break;
+ case 11:
+ return PITCH_LITERAL (B, NATURAL, octave);
+ break;
+ }
+}