]> git.cworth.org Git - scherzo/blob - scherzo-key.c
Automatically change key whenever user plays a major scale.
[scherzo] / scherzo-key.c
1 /* scherzo - Music notation training
2  *
3  *      key.c - Common structures and functions for keys
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 <stdbool.h>
22
23 #include "scherzo-key.h"
24
25 #define ARRAY_SIZE(arr) ((int) (sizeof(arr) / sizeof(arr[0])))
26
27 void
28 scherzo_key_init (scherzo_key_t *key, pitch_t pitch)
29 {
30     int i;
31
32     pitch_t sharp_keys[] = {
33         PITCH_CLASS_LITERAL (C, NATURAL),
34         PITCH_CLASS_LITERAL (G, NATURAL),
35         PITCH_CLASS_LITERAL (D, NATURAL),
36         PITCH_CLASS_LITERAL (A, NATURAL),
37         PITCH_CLASS_LITERAL (E, NATURAL),
38         PITCH_CLASS_LITERAL (B, NATURAL),
39         PITCH_CLASS_LITERAL (F, SHARP),
40         PITCH_CLASS_LITERAL (C, SHARP),
41     };
42
43     pitch_t flat_keys[] = {
44         PITCH_CLASS_LITERAL (C, NATURAL),
45         PITCH_CLASS_LITERAL (F, NATURAL),
46         PITCH_CLASS_LITERAL (B, FLAT),
47         PITCH_CLASS_LITERAL (E, FLAT),
48         PITCH_CLASS_LITERAL (A, FLAT),
49         PITCH_CLASS_LITERAL (D, FLAT),
50         PITCH_CLASS_LITERAL (G, FLAT),
51         PITCH_CLASS_LITERAL (C, FLAT)
52     };
53
54     key->pitch = PITCH_CLASS (PITCH_NAME (pitch), PITCH_ACCIDENTAL (pitch));
55
56     key->num_sharps = 0;
57     for (i = 0; i < ARRAY_SIZE (sharp_keys); i++)
58         if (sharp_keys[i] == key->pitch)
59             key->num_sharps = i;
60
61     key->num_flats = 0;
62     for (i = 0; i < ARRAY_SIZE (flat_keys); i++)
63         if (flat_keys[i] == key->pitch)
64             key->num_flats = i;
65 }
66
67 bool
68 scherzo_key_contains_pitch (scherzo_key_t *key, pitch_t pitch)
69 {
70     pitch_accidental_t accidental = PITCH_ACCIDENTAL (pitch);
71     int i;
72
73     pitch_name_t sharps_order[] = {
74         PITCH_NAME_F,
75         PITCH_NAME_C,
76         PITCH_NAME_G,
77         PITCH_NAME_D,
78         PITCH_NAME_A,
79         PITCH_NAME_E,
80         PITCH_NAME_B
81     };
82
83     pitch_name_t flats_order[] = {
84         PITCH_NAME_B,
85         PITCH_NAME_E,
86         PITCH_NAME_A,
87         PITCH_NAME_D,
88         PITCH_NAME_G,
89         PITCH_NAME_C,
90         PITCH_NAME_F
91     };
92
93     if (accidental == PITCH_ACCIDENTAL_DOUBLE_SHARP ||
94         accidental == PITCH_ACCIDENTAL_DOUBLE_FLAT)
95     {
96         return false;
97     }
98
99     if (key->num_sharps) {
100         if (accidental == PITCH_ACCIDENTAL_FLAT)
101             return false;
102         for (i = 0; i < key->num_sharps; i++) {
103             if (sharps_order[i] == PITCH_NAME (pitch)) {
104                 if (accidental == PITCH_ACCIDENTAL_SHARP)
105                     return true;
106                 else
107                     return false;
108             }
109         }
110         if (accidental == PITCH_ACCIDENTAL_SHARP)
111             return false;
112         else
113             return true;
114     }
115
116     if (key->num_flats) {
117         if (accidental == PITCH_ACCIDENTAL_SHARP)
118             return false;
119         for (i = 0; i < key->num_flats; i++) {
120             if (flats_order[i] == PITCH_NAME (pitch)) {
121                 if (accidental == PITCH_ACCIDENTAL_FLAT)
122                     return true;
123                 else
124                     return false;
125             }
126         }
127         if (accidental == PITCH_ACCIDENTAL_FLAT)
128             return false;
129         else
130             return true;
131     }
132
133     if (accidental == PITCH_ACCIDENTAL_NATURAL)
134         return true;
135     else
136         return false;
137 }
138
139 pitch_t
140 scherzo_key_spell_pitch (scherzo_key_t *key, pitch_t pitch)
141 {
142     pitch_t root = key->pitch;
143     int half_steps = pitch_from_root_in_half_steps (pitch, root);
144     pitch_name_t new_pitch_name;
145     int i;
146     int half_steps_per_degree[] = {
147         0, 2, 4, 5, 7, 9, 11
148     };
149
150     for (i = 0; i < ARRAY_SIZE (half_steps_per_degree); i++)
151         if (half_steps_per_degree[i] == half_steps)
152             goto WITHIN_SCALE;
153
154     return pitch;
155
156 WITHIN_SCALE:
157     new_pitch_name = (PITCH_NAME (root) + i) % 7;
158
159     /* Original pitch is spelled correctly. Return it as is. */
160     if (new_pitch_name == PITCH_NAME (pitch))
161         return pitch;
162
163     return pitch_spell_as_degree (pitch, root, i + 1);
164 }