]> git.cworth.org Git - scherzo/blobdiff - pitch.h
Fix high octave numbers (8+) to not be interpreted as 0.
[scherzo] / pitch.h
diff --git a/pitch.h b/pitch.h
index 13c29057bf5d25272d438e427d5c0f2c3ca4223d..683d72772650138fa224e7b209af35e19c1f71e0 100644 (file)
--- a/pitch.h
+++ b/pitch.h
  * along with this program.  If not, see http://www.gnu.org/licenses/ .
  */
 
-#define PITCH_ACCIDENTAL_MASK 0x07
-#define PITCH_ACCIDENTAL_SHIFT 0
+#include <stdint.h>
+
+#ifndef PITCH_H
+#define PITCH_H
+
+typedef uint32_t pitch_t;
+
+#define PITCH_ACCIDENTAL_SHIFT (0)
+#define PITCH_ACCIDENTAL_MASK  (0x07 << PITCH_ACCIDENTAL_SHIFT)
+
+#define PITCH_NAME_SHIFT       (3)
+#define PITCH_NAME_MASK                (0x07 << PITCH_NAME_SHIFT)
+
+#define PITCH_OCTAVE_SHIFT     (6)
+#define PITCH_OCTAVE_MASK      (0x0F << PITCH_OCTAVE_SHIFT)
+
+#define PITCH_ACCIDENTAL(pitch)        \
+    (((pitch) & PITCH_ACCIDENTAL_MASK) >> PITCH_ACCIDENTAL_SHIFT)
+#define PITCH_NAME(pitch)      \
+    (((pitch) & PITCH_NAME_MASK) >> PITCH_NAME_SHIFT)
+#define PITCH_OCTAVE(pitch)    \
+    (((pitch) & PITCH_OCTAVE_MASK) >> PITCH_OCTAVE_SHIFT)
+
+#define PITCH(name, accidental, octave)                  \
+    (((octave) << PITCH_OCTAVE_SHIFT)          | \
+     ((name) << PITCH_NAME_SHIFT)              | \
+     ((accidental) << PITCH_ACCIDENTAL_SHIFT))
+
+#define PITCH_LITERAL(literal_name, literal_accidental, octave)        \
+    PITCH(PITCH_NAME_##literal_name,                           \
+         PITCH_ACCIDENTAL_##literal_accidental,                \
+         octave)
+
+#define PITCH_NATURAL(literal_name, octave) \
+    PITCH_LITERAL(literal_name, NATURAL, octave)
+
+/* PITCH_CLASS is useful for comparing pitches while ignoring any octave. */
+#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)
+
+#define PITCH_NOT_A_PITCH ((pitch_t) -1)
 
 typedef enum pitch_accidental
 {
@@ -30,11 +71,6 @@ typedef enum pitch_accidental
     PITCH_ACCIDENTAL_DOUBLE_SHARP
 } pitch_accidental_t;
 
-#define PITCH_ACCIDENTAL(pitch) (((pitch) & PITCH_ACCIDENTAL_MASK) >> PITCH_ACCIDENTAL_SHIFT)
-
-#define PITCH_NAME_MASK 0x38
-#define PITCH_NAME_SHIFT 3
-
 typedef enum pitch_name
 {
     PITCH_NAME_C,
@@ -46,53 +82,62 @@ typedef enum pitch_name
     PITCH_NAME_B,
 } pitch_name_t;
 
-#define PITCH_NAME(pitch) (((pitch) & PITCH_NAME_MASK) >> PITCH_NAME_SHIFT)
+/* Octave numbers are ISO octave numbers [0:8], (so Octave 4 is from
+ * middle C to the B above middle C).
+ */
+
+/* Get a string representation of a pitch_t value.
+ *
+ * Note: The returned value is static to the pithc_string function and
+ * may be modified by future calls to pitch_string. So the caller
+ * should copy the value if needed. */
+const char *
+pitch_string (pitch_t pitch);
+
+/* Return a new pitch, a number of octaves above 'pitch'
+ *
+ * Note: Maximum octave value is 8. A pitch with octave 8 will not be
+ * raised.
+ */
+pitch_t
+pitch_raise_by_octaves (pitch_t pitch, int octaves);
 
-#define PITCH(name, accidental) (((name) << PITCH_NAME_SHIFT) | (accidental))
+/* Return a new pitch, a number of octaves below 'pitch'
+ *
+ * Note: Minimum octave value is 9. A pitch with octave 8 will not be
+ * lowered.
+ */
+pitch_t
+pitch_lower_by_octaves (pitch_t pitch, int octaves);
 
-#define PITCH_LITERAL(literal_name, literal_accidental) PITCH(PITCH_NAME_##literal_name, PITCH_ACCIDENTAL_##literal_accidental)
+/* 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);
 
-typedef enum pitch
-{
-    PITCH_Cff = PITCH_LITERAL (C, DOUBLE_FLAT), 
-    PITCH_Cf  = PITCH_LITERAL (C, FLAT),         
-    PITCH_C   = PITCH_LITERAL (C, NATURAL),      
-    PITCH_Cs  = PITCH_LITERAL (C, SHARP),        
-    PITCH_Css = PITCH_LITERAL (C, DOUBLE_SHARP),
-
-    PITCH_Dff = PITCH_LITERAL (D, DOUBLE_FLAT), 
-    PITCH_Df  = PITCH_LITERAL (D, FLAT),         
-    PITCH_D   = PITCH_LITERAL (D, NATURAL),      
-    PITCH_Ds  = PITCH_LITERAL (D, SHARP),        
-    PITCH_Dss = PITCH_LITERAL (D, DOUBLE_SHARP),
-
-    PITCH_Eff = PITCH_LITERAL (E, DOUBLE_FLAT), 
-    PITCH_Ef  = PITCH_LITERAL (E, FLAT),         
-    PITCH_E   = PITCH_LITERAL (E, NATURAL),      
-    PITCH_Es  = PITCH_LITERAL (E, SHARP),        
-    PITCH_Ess = PITCH_LITERAL (E, DOUBLE_SHARP),
-
-    PITCH_Fff = PITCH_LITERAL (F, DOUBLE_FLAT), 
-    PITCH_Ff  = PITCH_LITERAL (F, FLAT),         
-    PITCH_F   = PITCH_LITERAL (F, NATURAL),      
-    PITCH_Fs  = PITCH_LITERAL (F, SHARP),        
-    PITCH_Fss = PITCH_LITERAL (F, DOUBLE_SHARP),
-
-    PITCH_Gff = PITCH_LITERAL (G, DOUBLE_FLAT), 
-    PITCH_Gf  = PITCH_LITERAL (G, FLAT),         
-    PITCH_G   = PITCH_LITERAL (G, NATURAL),      
-    PITCH_Gs  = PITCH_LITERAL (G, SHARP),        
-    PITCH_Gss = PITCH_LITERAL (G, DOUBLE_SHARP),
-
-    PITCH_Aff = PITCH_LITERAL (A, DOUBLE_FLAT), 
-    PITCH_Af  = PITCH_LITERAL (A, FLAT),         
-    PITCH_A   = PITCH_LITERAL (A, NATURAL),      
-    PITCH_As  = PITCH_LITERAL (A, SHARP),        
-    PITCH_Ass = PITCH_LITERAL (A, DOUBLE_SHARP),
-
-    PITCH_Bff = PITCH_LITERAL (B, DOUBLE_FLAT), 
-    PITCH_Bf  = PITCH_LITERAL (B, FLAT),         
-    PITCH_B   = PITCH_LITERAL (B, NATURAL),      
-    PITCH_Bs  = PITCH_LITERAL (B, SHARP),        
-    PITCH_Bss = PITCH_LITERAL (B, DOUBLE_SHARP)
-} pitch_t;
+/* Respell 'pitch' to have a name that matches the (diatonic) scale
+ * 'degree' from 'root'.
+ *
+ * If the pitch is too distant from the specified degree to be
+ * respelled, the original pitch will be returned unmodified.
+ */
+pitch_t
+pitch_spell_as_degree (pitch_t pitch, pitch_t root, int degree);
+
+/* Return a MIDI note value corresponding to 'pitch' */
+unsigned char
+pitch_to_midi (pitch_t pitch);
+
+/* Return the pitch_t value corresponding to the given MIDI note value. */
+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 */