1 /* B.Scale
2 * Basic music note operation tools
3 *
4 * Copyright (C) 2018, 2019 by Sven Jähnichen
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #ifndef BSCALE_HPP_
22 #define BSCALE_HPP_
23
24 #include <cstdint>
25 #include <array>
26 #include <string>
27 #include <cmath>
28 #include <cstring>
29
30 #define ENOTE -128
31
32 #define CROMATICSCALE 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
33 #define MAJORSCALE 0, 2, 4, 5, 7, 9, 11, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
34 #define MINORSCALE 0, 2, 3, 5, 7, 8, 10, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
35 #define HARMONICMAJORSCALE 0, 2, 4, 5, 7, 8, 11, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
36 #define HARMONICMINORSCALE 0, 2, 3, 5, 7, 8, 11, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
37 #define MELODICMINORSCALE 0, 2, 3, 5, 7, 9, 10, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
38 #define DORIANSCALE 0, 2, 3, 5, 7, 9, 10, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
39 #define PHRYGIANSCALE 0, 1, 3, 5, 7, 8, 10, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
40 #define LYDIANSCALE 0, 2, 4, 6, 7, 9, 11, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
41 #define MIXOLYDIANSCALE 0, 2, 4, 5, 7, 9, 10, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
42 #define LOKRIANSCALE 0, 1, 3, 5, 6, 8, 10, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
43 #define HUNGARIANMINORSCALE 0, 2, 3, 6, 7, 8, 11, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
44 #define MAJORPENTATONICSCALE 0, 2, 4, 7, 9, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
45 #define MINORPENTATONICSCALE 0, 3, 5, 7, 10, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE, ENOTE
46
47 typedef std::array<int, 12> BScaleNotes;
48
49 typedef enum {
50 FLAT = -1,
51 NATURAL = 0,
52 SHARP = 1
53 } SignatureIndex;
54
55 const BScaleNotes defaultScale = {CROMATICSCALE};
56
57 const char noteSymbols[12] = {'C', 0, 'D', 0, 'E', 'F', 0, 'G', 0, 'A', 0, 'B'};
58
59 class BScale {
60 public:
61 BScale (const int root, const BScaleNotes& elementarray);
62 BScale (const int root, const SignatureIndex signature, const BScaleNotes& elementarray);
63 void setRoot (int root);
64 int getRoot ();
65 void setScale (BScaleNotes& elementarray);
66 BScaleNotes getScale ();
67 int getMIDInote (int element);
68 int getElement (int midiNote);
69 int getSize ();
70 std::string getSymbol (int element);
71
72 protected:
73 void createSymbols ();
74 int rootNote;
75 SignatureIndex signature;
76 BScaleNotes scale;
77 char symbols[12][6];
78 };
79
BScale(const int root,const BScaleNotes & elementarray)80 BScale::BScale (const int root, const BScaleNotes& elementarray) : BScale (root, NATURAL, elementarray) {}
BScale(const int root,const SignatureIndex signature,const BScaleNotes & elementarray)81 BScale::BScale (const int root, const SignatureIndex signature, const BScaleNotes& elementarray) :
82 rootNote (root), signature (signature), scale (elementarray)
83 {
84 memset (symbols, 0, sizeof symbols);
85 createSymbols ();
86 }
87
createSymbols()88 void BScale::createSymbols ()
89 {
90 // Build a flat scale and a sharp scale
91 char flatSymbols[12][6];
92 char sharpSymbols[12][6];
93 memset (flatSymbols, 0, sizeof flatSymbols);
94 memset (sharpSymbols, 0, sizeof sharpSymbols);
95 for (int i = 0; (i < 12) && (scale[i] != ENOTE); ++i)
96 {
97 int midiNote = getMIDInote (i);
98 if ((midiNote >= 0) && (midiNote <= 127))
99 {
100 // Note without signature => take this symbol
101 if (noteSymbols[midiNote % 12])
102 {
103 flatSymbols[i][0] = noteSymbols[midiNote % 12];
104 sharpSymbols[i][0] = noteSymbols[midiNote % 12];
105 }
106
107 // Or with signature => build from neighbor
108 else
109 {
110 flatSymbols[i][0] = noteSymbols[(midiNote + 1) % 12];
111 strcat (flatSymbols[i], "b");
112 sharpSymbols[i][0] = noteSymbols[(midiNote + 11) % 12];
113 strcat (sharpSymbols[i], "#");
114 }
115 }
116
117 // Note out of range => break
118 else break;
119 }
120
121 switch (signature)
122 {
123 case FLAT: memcpy (symbols, flatSymbols, sizeof symbols);
124 break;
125
126 case SHARP: memcpy (symbols, sharpSymbols, sizeof symbols);
127 break;
128
129 default: {
130 // Count redundant symbols
131 int flatRedunds = 0;
132 int sharpRedunds = 0;
133 for (int i = 1; (i < 12) && (flatSymbols[i][0]); ++i)
134 {
135 if (flatSymbols[i][0] == flatSymbols[i - 1][0]) ++flatRedunds;
136 if (sharpSymbols[i][0] == sharpSymbols[i - 1][0]) ++sharpRedunds;
137 }
138
139 // Store the more relevant scale
140 if (flatRedunds < sharpRedunds) memcpy (symbols, flatSymbols, sizeof symbols);
141 else memcpy (symbols, sharpSymbols, sizeof symbols);
142 }
143 }
144 }
145
setRoot(int root)146 void BScale::setRoot (int root)
147 {
148 rootNote = root;
149 createSymbols ();
150 }
151
getRoot()152 int BScale::getRoot () {return rootNote;}
153
setScale(BScaleNotes & elementarray)154 void BScale::setScale (BScaleNotes& elementarray)
155 {
156 int i = 0;
157 for (; (i < 12) && (elementarray[i] != ENOTE); ++i) scale[i] = elementarray[i] % 12;
158 for (; i < 12; ++i) scale[i] = ENOTE;
159 createSymbols ();
160 }
161
getScale()162 BScaleNotes BScale::getScale () {return scale;}
163
164 /* Calculates a MIDI note for an element within a BScale
165 * @param element: note position relative to root note in number of (scale-specific) notes,
166 * e.g. note F in C major will be element 3
167 * Returns MIDI note (00 .. 7F) or ENOTE if out of range
168 */
getMIDInote(int element)169 int BScale::getMIDInote (int element)
170 {
171 int size = getSize ();
172 int octave = (int) floor ((float) element / size);
173 int midiNote = octave * 12 + rootNote + scale [element - octave * size];
174 if ((midiNote >=0) && (midiNote <= 127)) return midiNote;
175 else return ENOTE;
176 }
177
178 /* Calculates the number of an element (note) within a BScale
179 * @param midiNote: MIDI note (00 .. 7F)
180 * Returns the element (note) relative to the root of the scale, e. g.
181 * returns 3 for the note F4 in a C(4) major scale
182 */
getElement(int midiNote)183 int BScale::getElement (int midiNote)
184 {
185 int ssize = getSize ();
186 int octDiff = (int) floor (((float)midiNote - rootNote) / 12);
187 int noteDiff = (midiNote - rootNote) - octDiff * 12;
188
189 for (int i = 0; i < ssize; ++i)
190 {
191 if (scale[i] == noteDiff) return i + octDiff * ssize;
192 }
193
194 return ENOTE;
195 }
196
197 /* Returns number of elements of the BScale object, max. 12
198 *
199 */
getSize()200 int BScale::getSize()
201 {
202 for (int8_t i = 0; i < 12; ++i)
203 {
204 if (scale[i] == ENOTE) return i;
205 }
206 return 12;
207
208 }
209
210 /* Returns the note symbol for an element (note) within a BScale.
211 * !!! Don't use this method in a realtime process !!!
212 * @param element: note position relative to root note in number of (scale-specific) notes,
213 * e.g. note F in C major will be element 3
214 * @return Note symbol string
215 *
216 */
getSymbol(int element)217 std::string BScale::getSymbol (int element)
218 {
219 if (element < 0) return "";
220
221 int ssize = getSize ();
222 return std::string (symbols[element % ssize]);
223 }
224
225
226 #endif /* BSCALE_HPP_ */
227