]> git.cworth.org Git - scherzo/blob - pitch.c
Prefer flats rather than sharps when converting from MIDI
[scherzo] / pitch.c
1 /* scherzo - Music notation training
2  *
3  *      pitch.c - Common structures and functions for pitches, etc.
4  *
5  * Copyright © 2013 Carl Worth
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see http://www.gnu.org/licenses/ .
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 #include "pitch.h"
25
26 const char *
27 pitch_string (pitch_t pitch)
28 {
29     static char double_flat[] = "X𝄫";
30     static char flat[] = "X♭";
31     static char natural[] = "X";
32     static char sharp[] = "X♯";
33     static char double_sharp[] = "X𝄪";
34     char *ret;
35
36     switch (PITCH_ACCIDENTAL (pitch)) {
37     case PITCH_ACCIDENTAL_DOUBLE_FLAT:
38         ret = double_flat;
39         break;
40     case PITCH_ACCIDENTAL_FLAT:
41         ret = flat;
42         break;
43     case PITCH_ACCIDENTAL_NATURAL:
44         ret = natural;
45         break;
46     case PITCH_ACCIDENTAL_SHARP:
47         ret = sharp;
48         break;
49     case PITCH_ACCIDENTAL_DOUBLE_SHARP:
50         ret = double_sharp;
51         break;
52     }
53
54     switch (PITCH_NAME (pitch)) {
55     case PITCH_NAME_C:
56         ret[0] = 'C';
57         break;
58     case PITCH_NAME_D:
59         ret[0] = 'D';
60         break;
61     case PITCH_NAME_E:
62         ret[0] = 'E';
63         break;
64     case PITCH_NAME_F:
65         ret[0] = 'F';
66         break;
67     case PITCH_NAME_G:
68         ret[0] = 'G';
69         break;
70     case PITCH_NAME_A:
71         ret[0] = 'A';
72         break;
73     case PITCH_NAME_B:
74         ret[0] = 'B';
75         break;
76     }
77
78     return ret;
79 }
80
81 pitch_t
82 pitch_raise_by_octaves (pitch_t pitch, int octaves)
83 {
84     int new_octave = PITCH_OCTAVE (pitch) + octaves;
85
86     if (new_octave > 8)
87         new_octave = 8;
88
89     return PITCH (PITCH_NAME (pitch), PITCH_ACCIDENTAL (pitch), new_octave);
90 }
91
92 pitch_t
93 pitch_lower_by_octaves (pitch_t pitch, int octaves)
94 {
95     int new_octave = PITCH_OCTAVE (pitch) - octaves;
96
97     if (new_octave < 0)
98         new_octave = 0;
99
100     return PITCH (PITCH_NAME (pitch), PITCH_ACCIDENTAL (pitch), new_octave);
101 }
102
103 /* Number of half steps from 'root' up to 'pitch' (that is, counting
104  * the steps up a chromatic scale), within the same octave. */
105 int
106 pitch_from_root_in_half_steps (pitch_t pitch, pitch_t root)
107 {
108     int root_midi = pitch_to_midi (root);
109     int pitch_midi = pitch_to_midi (pitch);
110
111     while (pitch_midi < root_midi)
112         pitch_midi += 12;
113
114     return (pitch_midi - root_midi) % 12;
115 }
116
117 pitch_t
118 pitch_spell_as_degree (pitch_t pitch, pitch_t root, int degree)
119 {
120     pitch_name_t name = PITCH_NAME (pitch);
121     pitch_accidental_t accidental = PITCH_ACCIDENTAL (pitch);
122     int octave = PITCH_OCTAVE (pitch);
123     int degree_zero_based = (degree - 1) % 7;
124     int degree_delta;
125
126     int note_degree_zero_based = name - PITCH_NAME (root);
127     if (note_degree_zero_based < 0)
128         note_degree_zero_based += 7;
129
130     if (note_degree_zero_based == degree_zero_based)
131         return pitch;
132
133     degree_delta = note_degree_zero_based - degree_zero_based;
134     if (degree_delta > 3)
135         degree_delta = - (7 - degree_delta);
136     if (degree_delta < -3)
137         degree_delta = - (-7 - degree_delta);
138
139     /* Cannot re-spell pitch more than one degree away. Return
140      * original pitch. */
141     if (abs (degree_delta) != 1)
142         return pitch;
143
144     if (degree_delta == -1) {
145         if (name == PITCH_NAME_B) {
146             name = PITCH_NAME_C;
147             octave++;
148         } else {
149             name++;
150         }
151         switch (name) {
152         case PITCH_NAME_D:
153         case PITCH_NAME_E:
154         case PITCH_NAME_G:
155         case PITCH_NAME_A:
156         case PITCH_NAME_B:
157             accidental -= 2;
158             break;
159         case PITCH_NAME_C:
160         case PITCH_NAME_F:
161             accidental -= 1;
162             break;
163         }
164     }
165
166     if (degree_delta == +1) {
167         if (name == PITCH_NAME_C) {
168             name = PITCH_NAME_B;
169             octave--;
170         } else {
171             name--;
172         }
173         switch (name) {
174         case PITCH_NAME_C:
175         case PITCH_NAME_D:
176         case PITCH_NAME_F:
177         case PITCH_NAME_G:
178         case PITCH_NAME_A:
179             accidental += 2;
180             break;
181         case PITCH_NAME_E:
182         case PITCH_NAME_B:
183             accidental += 1;
184         }
185     }
186
187     /* Also cannot use accidentals to respell more than two half steps
188      * either direction. Return original pitch. */
189     if (accidental < 0 || accidental > PITCH_ACCIDENTAL_DOUBLE_SHARP)
190         return pitch;
191
192     return PITCH (name, accidental, octave);
193 }
194
195 /* Return a MIDI note value corresponding to 'pitch' */
196 unsigned char
197 pitch_to_midi (pitch_t pitch)
198 {
199     int octave = PITCH_OCTAVE (pitch);
200     unsigned char midi_note = 12 * (octave + 1);
201
202     switch (PITCH_NAME (pitch)) {
203     case PITCH_NAME_C:
204         break;
205     case PITCH_NAME_D:
206         midi_note += 2;
207         break;
208     case PITCH_NAME_E:
209         midi_note += 4;
210         break;
211     case PITCH_NAME_F:
212         midi_note += 5;
213         break;
214     case PITCH_NAME_G:
215         midi_note += 7;
216         break;
217     case PITCH_NAME_A:
218         midi_note += 9;
219         break;
220     case PITCH_NAME_B:
221         midi_note += 11;
222         break;
223     }
224
225     switch (PITCH_ACCIDENTAL (pitch)) {
226     case PITCH_ACCIDENTAL_DOUBLE_FLAT:
227         midi_note -= 2;
228         break;
229     case PITCH_ACCIDENTAL_FLAT:
230         midi_note -= 1;
231         break;
232     case PITCH_ACCIDENTAL_NATURAL:
233         break;
234     case PITCH_ACCIDENTAL_SHARP:
235         midi_note += 1;
236         break;
237     case PITCH_ACCIDENTAL_DOUBLE_SHARP:
238         midi_note += 2;
239         break;
240     }
241
242     return midi_note;
243 }
244
245 /* Return the pitch_t value corresponding to the given MIDI note value. */
246 pitch_t
247 pitch_from_midi (unsigned char midi_note)
248 {
249     int octave = octave = midi_note / 12 - 1;
250
251     switch (midi_note % 12)
252     {
253     default:
254     case 0:
255         return PITCH_LITERAL (C, NATURAL, octave);
256         break;
257     case 1:
258         return PITCH_LITERAL (D, FLAT, octave);
259         break;
260     case 2:
261         return PITCH_LITERAL (D, NATURAL, octave);
262         break;
263     case 3:
264         return PITCH_LITERAL (E, FLAT, octave);
265         break;
266     case 4:
267         return PITCH_LITERAL (E, NATURAL, octave);
268         break;
269     case 5:
270         return PITCH_LITERAL (F, NATURAL, octave);
271         break;
272     case 6:
273         return PITCH_LITERAL (G, FLAT, octave);
274         break;
275     case 7:
276         return PITCH_LITERAL (G, NATURAL, octave);
277         break;
278     case 8:
279         return PITCH_LITERAL (A, FLAT, octave);
280         break;
281     case 9:
282         return PITCH_LITERAL (A, NATURAL, octave);
283         break;
284     case 10:
285         return PITCH_LITERAL (B, FLAT, octave);
286         break;
287     case 11:
288         return PITCH_LITERAL (B, NATURAL, octave);
289         break;
290     }
291 }
292
293 int
294 pitch_enharmonic_to (pitch_t a, pitch_t b)
295 {
296     return pitch_to_midi (a) == pitch_to_midi (b);
297 }