]> git.cworth.org Git - scherzo/blobdiff - pitch.c
Fix high octave numbers (8+) to not be interpreted as 0.
[scherzo] / pitch.c
diff --git a/pitch.c b/pitch.c
index c628a76ac3076f52e540dce83789c11dc891eba4..6bb64702936363813ac8ebdfae2d1c1f5ac5f5c0 100644 (file)
--- a/pitch.c
+++ b/pitch.c
@@ -18,6 +18,9 @@
  * along with this program.  If not, see http://www.gnu.org/licenses/ .
  */
 
+#include <stdio.h>
+#include <stdlib.h>
+
 #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);
+}