]> git.cworth.org Git - scherzo/blob - pitch.c
Automatically change key whenever user plays a major scale.
[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 <stdlib.h>
22
23 #include "pitch.h"
24
25 const char *
26 pitch_string (pitch_t pitch)
27 {
28     static char double_flat[] = "X𝄫";
29     static char flat[] = "X♭";
30     static char natural[] = "X";
31     static char sharp[] = "X♯";
32     static char double_sharp[] = "X𝄪";
33     char *ret;
34
35     switch (PITCH_ACCIDENTAL (pitch)) {
36     case PITCH_ACCIDENTAL_DOUBLE_FLAT:
37         ret = double_flat;
38         break;
39     case PITCH_ACCIDENTAL_FLAT:
40         ret = flat;
41         break;
42     case PITCH_ACCIDENTAL_NATURAL:
43         ret = natural;
44         break;
45     case PITCH_ACCIDENTAL_SHARP:
46         ret = sharp;
47         break;
48     case PITCH_ACCIDENTAL_DOUBLE_SHARP:
49         ret = double_sharp;
50         break;
51     }
52
53     switch (PITCH_NAME (pitch)) {
54     case PITCH_NAME_C:
55         ret[0] = 'C';
56         break;
57     case PITCH_NAME_D:
58         ret[0] = 'D';
59         break;
60     case PITCH_NAME_E:
61         ret[0] = 'E';
62         break;
63     case PITCH_NAME_F:
64         ret[0] = 'F';
65         break;
66     case PITCH_NAME_G:
67         ret[0] = 'G';
68         break;
69     case PITCH_NAME_A:
70         ret[0] = 'A';
71         break;
72     case PITCH_NAME_B:
73         ret[0] = 'B';
74         break;
75     }
76
77     return ret;
78 }
79
80 pitch_t
81 pitch_raise_by_octaves (pitch_t pitch, int octaves)
82 {
83     int new_octave = PITCH_OCTAVE (pitch) + octaves;
84
85     if (new_octave > 8)
86         new_octave = 8;
87
88     return PITCH (PITCH_NAME (pitch), PITCH_ACCIDENTAL (pitch), new_octave);
89 }
90
91 pitch_t
92 pitch_lower_by_octaves (pitch_t pitch, int octaves)
93 {
94     int new_octave = PITCH_OCTAVE (pitch) - octaves;
95
96     if (new_octave < 0)
97         new_octave = 0;
98
99     return PITCH (PITCH_NAME (pitch), PITCH_ACCIDENTAL (pitch), new_octave);
100 }
101
102 /* Number of half steps from 'root' up to 'pitch' (that is, counting
103  * the steps up a chromatic scale), within the same octave. */
104 int
105 pitch_from_root_in_half_steps (pitch_t pitch, pitch_t root)
106 {
107     int root_midi = pitch_to_midi (root);
108     int pitch_midi = pitch_to_midi (pitch);
109
110     while (pitch_midi < root_midi)
111         pitch_midi += 12;
112
113     return (pitch_midi - root_midi) % 12;
114 }
115
116 pitch_t
117 pitch_spell_as_degree (pitch_t pitch, pitch_t root, int degree)
118 {
119     pitch_name_t name = PITCH_NAME (pitch);
120     pitch_accidental_t accidental = PITCH_ACCIDENTAL (pitch);
121     int octave = PITCH_OCTAVE (pitch);
122     int degree_zero_based = (degree - 1) % 7;
123     int degree_delta;
124
125     int note_degree_zero_based = name - PITCH_NAME (root);
126     if (note_degree_zero_based < 0)
127         note_degree_zero_based += 7;
128
129     if (note_degree_zero_based == degree_zero_based)
130         return pitch;
131
132     degree_delta = note_degree_zero_based - degree_zero_based;
133     if (degree_delta > 3)
134         degree_delta = - (7 - degree_delta);
135     if (degree_delta < -3)
136         degree_delta = - (-7 - degree_delta);
137
138     /* Cannot re-spell pitch more than one degree away. Return
139      * original pitch. */
140     if (abs (degree_delta) != 1)
141         return pitch;
142
143     if (degree_delta == -1) {
144         if (name == PITCH_NAME_B) {
145             name = PITCH_NAME_C;
146             octave++;
147         } else {
148             name++;
149         }
150         switch (name) {
151         case PITCH_NAME_D:
152         case PITCH_NAME_E:
153         case PITCH_NAME_G:
154         case PITCH_NAME_A:
155         case PITCH_NAME_B:
156             accidental -= 2;
157             break;
158         case PITCH_NAME_C:
159         case PITCH_NAME_F:
160             accidental -= 1;
161             break;
162         }
163     }
164
165     if (degree_delta == +1) {
166         if (name == PITCH_NAME_C) {
167             name = PITCH_NAME_B;
168             octave--;
169         } else {
170             name--;
171         }
172         switch (name) {
173         case PITCH_NAME_C:
174         case PITCH_NAME_D:
175         case PITCH_NAME_F:
176         case PITCH_NAME_G:
177         case PITCH_NAME_A:
178             accidental += 2;
179             break;
180         case PITCH_NAME_E:
181         case PITCH_NAME_B:
182             accidental += 1;
183         }
184     }
185
186     /* Also cannot use accidentals to respell more than two half steps
187      * either direction. Return original pitch. */
188     if (accidental < 0 || accidental > PITCH_ACCIDENTAL_DOUBLE_SHARP)
189         return pitch;
190
191     return PITCH (name, accidental, octave);
192 }
193
194 /* Return a MIDI note value corresponding to 'pitch' */
195 unsigned char
196 pitch_to_midi (pitch_t pitch)
197 {
198     int octave = PITCH_OCTAVE (pitch);
199     unsigned char midi_note = 12 * (octave + 1);
200
201     switch (PITCH_NAME (pitch)) {
202     case PITCH_NAME_C:
203         break;
204     case PITCH_NAME_D:
205         midi_note += 2;
206         break;
207     case PITCH_NAME_E:
208         midi_note += 4;
209         break;
210     case PITCH_NAME_F:
211         midi_note += 5;
212         break;
213     case PITCH_NAME_G:
214         midi_note += 7;
215         break;
216     case PITCH_NAME_A:
217         midi_note += 9;
218         break;
219     case PITCH_NAME_B:
220         midi_note += 11;
221         break;
222     }
223
224     switch (PITCH_ACCIDENTAL (pitch)) {
225     case PITCH_ACCIDENTAL_DOUBLE_FLAT:
226         midi_note -= 2;
227         break;
228     case PITCH_ACCIDENTAL_FLAT:
229         midi_note -= 1;
230         break;
231     case PITCH_ACCIDENTAL_NATURAL:
232         break;
233     case PITCH_ACCIDENTAL_SHARP:
234         midi_note += 1;
235         break;
236     case PITCH_ACCIDENTAL_DOUBLE_SHARP:
237         midi_note += 2;
238         break;
239     }
240
241     return midi_note;
242 }
243
244 /* Return the pitch_t value corresponding to the given MIDI note value. */
245 pitch_t
246 pitch_from_midi (unsigned char midi_note)
247 {
248     int octave = octave = midi_note / 12 - 1;
249
250     switch (midi_note % 12)
251     {
252     default:
253     case 0:
254         return PITCH_LITERAL (C, NATURAL, octave);
255         break;
256     case 1:
257         return PITCH_LITERAL (C, SHARP, octave);
258         break;
259     case 2:
260         return PITCH_LITERAL (D, NATURAL, octave);
261         break;
262     case 3:
263         return PITCH_LITERAL (D, SHARP, octave);
264         break;
265     case 4:
266         return PITCH_LITERAL (E, NATURAL, octave);
267         break;
268     case 5:
269         return PITCH_LITERAL (F, NATURAL, octave);
270         break;
271     case 6:
272         return PITCH_LITERAL (F, SHARP, octave);
273         break;
274     case 7:
275         return PITCH_LITERAL (G, NATURAL, octave);
276         break;
277     case 8:
278         return PITCH_LITERAL (G, SHARP, octave);
279         break;
280     case 9:
281         return PITCH_LITERAL (A, NATURAL, octave);
282         break;
283     case 10:
284         return PITCH_LITERAL (A, SHARP, octave);
285         break;
286     case 11:
287         return PITCH_LITERAL (B, NATURAL, octave);
288         break;
289     }
290 }