1 /**
2 * Copyright © Underground Rekordz 2019
3 * License: MIT (https://github.com/UndergroundRekordz/Musicpulator/blob/master/LICENSE)
4 * Author: Jacob Jensen (bausshf)
5 */
6 module musicpulator.musicalscale;
7 
8 import std.string : format;
9 
10 import musicpulator.musicalnote;
11 import musicpulator.tools;
12 
13 /// Enumeration of scale names.
14 enum MusicalScaleName : string
15 {
16   /// The C major scale.
17   cMajor = "C Major",
18   /// The C minor scale.
19   cMinor = "C Minor",
20   /// The C# major scale.
21   cSharpMajor = "C# Major",
22   /// The C# minor scale.
23   cSharpMinor = "C# Minor",
24   /// The D major scale.
25   dMajor = "D Major",
26   /// The D minor scale.
27   dMinor = "D Minor",
28   /// The D# major scale.
29   dSharpMajor = "D# Major",
30   /// The D# minor scale.
31   dSharpMinor = "D# Minor",
32   /// The Db major scale.
33   dFlatMajor = "Db Major",
34   /// The Db minor scale.
35   dFlatMinor = "Db Minor",
36   /// The E major scale.
37   eMajor = "E Major",
38   /// The E minor scale.
39   eMinor = "E Minor",
40   /// The Eb major scale.
41   eFlatMajor = "Eb Major",
42   /// The Eb minor scale.
43   eFlatMinor = "Eb Minor",
44   /// The F major scale.
45   fMajor = "F Major",
46   /// The F minor scale.
47   fMinor = "F Minor",
48   /// The F# major scale.
49   fSharpMajor = "F# Major",
50   /// The F# minor scale.
51   fSharpMinor = "F# Minor",
52   /// The G major scale.
53   gMajor = "G Major",
54   /// The G minor scale.
55   gMinor = "G Minor",
56   /// The G# major scale.
57   gSharpMajor = "G# Major",
58   /// The G# minor scale.
59   gSharpMinor = "G# Minor",
60   /// The Gb major scale.
61   gFlatMajor = "Gb Major",
62   /// The Gb minor scale.
63   gFlatMinor = "Gb Minor",
64   /// The A major scale.
65   aMajor = "A Major",
66   /// The A minor scale.
67   aMinor = "A Minor",
68   /// The A# major scale.
69   aSharpMajor = "A# Major",
70   /// The A# minor scale.
71   aSharpMinor = "A# Minor",
72   /// The Ab major scale.
73   aFlatMajor = "Ab Major",
74   /// The Ab minor scale.
75   aFlatMinor = "Ab Minor",
76   /// The b major scale.
77   bMajor = "B Major",
78   /// The B minor scale.
79   bMinor = "B Minor",
80   /// The Bb major scale.
81   bFlatMajor = "Bb Major",
82   /// The Bb minor scale.
83   bFlatMinor = "Bb Minor"
84 }
85 
86 /// Enumeration of scale types.
87 enum MusicalScaleType : string
88 {
89   /// A major scale.
90   major = "Major",
91   /// A minor scale.
92   minor = "Minor"
93 }
94 
95 /// A wrapper around a scale note.
96 final class MusicalScaleNote
97 {
98   private:
99   /// The note.
100   MusicalNote _note;
101   /// The octave incrementer.
102   size_t _octaveIncrementer;
103 
104   public:
105   final:
106   /**
107   * Creates a new scale note.
108   * Params:
109   *   note = The note.
110   *   octaveIncrementer = The octave incrementer. (This is by default 0.)
111   */
112   this(MusicalNote note, size_t octaveIncrementer = 0)
113   {
114     _note = note;
115     _octaveIncrementer = octaveIncrementer;
116   }
117 
118   @property
119   {
120     /// Gets the note.
121     MusicalNote note() { return _note; }
122 
123     /// Gets the octave incrementer.
124     size_t octaveIncrementer() { return _octaveIncrementer; }
125   }
126 
127   /// Converts the scale note to a string. This will call toJson().
128   override string toString()
129   {
130     return toJson();
131   }
132 
133   /// Converts the scale note to json.
134   string toJson()
135   {
136     return `{"note":%s,"octaveIncrementer":%d}`
137       .format(_note.toJson(), _octaveIncrementer);
138   }
139 
140   /// Converts the scale note to xml.
141   string toXml()
142   {
143     return `<ScaleNote note="%s" octaveIncrementer="%d" />`
144       .format(_note.toXml(), _octaveIncrementer);
145   }
146 }
147 
148 /// Wrapper around a musical scale.
149 final class MusicalScale
150 {
151   private:
152   /// The name of the scale.
153   MusicalScaleName _scale;
154   /// The note of the scale.
155   MusicalNote _note;
156   /// The type of the scale.
157   MusicalScaleType _type;
158 
159   public:
160   final:
161   /**
162   * Creates a new musical scale.
163   * Params:
164   *   scale = The name of the scale.
165   *   note = The base note of the scale.
166   *   type = The type of the scale.
167   */
168   this(MusicalScaleName scale, MusicalNote note, MusicalScaleType type)
169   {
170     _scale = scale;
171     _note = note;
172     _type = type;
173   }
174 
175   @property
176   {
177     /// Gets the scale name.
178     MusicalScaleName scale() { return _scale; }
179 
180     /// Gets the name.
181     MusicalNote note() { return _note; }
182 
183     /// Gets the type.
184     MusicalScaleType type() { return _type; }
185   }
186 
187   /// Gets a set of natural notes within the scale.
188   MusicalNote[] getNaturalNotes()
189   {
190     return getNotes().translateToNaturalNotes();
191   }
192 
193   /// Gets a set of notes within the scale.
194   MusicalScaleNote[] getNotes()
195   {
196     final switch (_scale)
197     {
198       // Major Scales:
199 
200       case MusicalScaleName.cMajor:
201         return
202         [
203           new MusicalScaleNote(MusicalNote.c), new MusicalScaleNote(MusicalNote.d),
204           new MusicalScaleNote(MusicalNote.e), new MusicalScaleNote(MusicalNote.f),
205           new MusicalScaleNote(MusicalNote.g), new MusicalScaleNote(MusicalNote.a),
206           new MusicalScaleNote(MusicalNote.b)
207         ];
208 
209       case MusicalScaleName.dMajor:
210         return
211         [
212           new MusicalScaleNote(MusicalNote.d), new MusicalScaleNote(MusicalNote.e),
213           new MusicalScaleNote(MusicalNote.fSharp), new MusicalScaleNote(MusicalNote.g),
214           new MusicalScaleNote(MusicalNote.a), new MusicalScaleNote(MusicalNote.b),
215           new MusicalScaleNote(MusicalNote.cSharp, 1)
216         ];
217 
218       case MusicalScaleName.eMajor:
219         return
220         [
221           new MusicalScaleNote(MusicalNote.e), new MusicalScaleNote(MusicalNote.fSharp),
222           new MusicalScaleNote(MusicalNote.gSharp), new MusicalScaleNote(MusicalNote.a),
223           new MusicalScaleNote(MusicalNote.b), new MusicalScaleNote(MusicalNote.cSharp, 1),
224           new MusicalScaleNote(MusicalNote.dSharp, 1)
225         ];
226 
227       case MusicalScaleName.fMajor:
228         return
229         [
230           new MusicalScaleNote(MusicalNote.f), new MusicalScaleNote(MusicalNote.g),
231           new MusicalScaleNote(MusicalNote.a), new MusicalScaleNote(MusicalNote.bFlat),
232           new MusicalScaleNote(MusicalNote.c, 1), new MusicalScaleNote(MusicalNote.d, 1),
233           new MusicalScaleNote(MusicalNote.e, 1)
234         ];
235 
236       case MusicalScaleName.gMajor:
237         return
238         [
239           new MusicalScaleNote(MusicalNote.g), new MusicalScaleNote(MusicalNote.a),
240           new MusicalScaleNote(MusicalNote.b), new MusicalScaleNote(MusicalNote.c, 1),
241           new MusicalScaleNote(MusicalNote.d, 1), new MusicalScaleNote(MusicalNote.e, 1),
242           new MusicalScaleNote(MusicalNote.fSharp, 1)
243         ];
244 
245       case MusicalScaleName.aMajor:
246         return
247         [
248           new MusicalScaleNote(MusicalNote.a), new MusicalScaleNote(MusicalNote.b),
249           new MusicalScaleNote(MusicalNote.cSharp, 1), new MusicalScaleNote(MusicalNote.d, 1),
250           new MusicalScaleNote(MusicalNote.e, 1), new MusicalScaleNote(MusicalNote.fSharp, 1),
251           new MusicalScaleNote(MusicalNote.gSharp, 1)
252         ];
253 
254       case MusicalScaleName.bMajor:
255         return
256         [
257           new MusicalScaleNote(MusicalNote.b), new MusicalScaleNote(MusicalNote.cSharp, 1),
258           new MusicalScaleNote(MusicalNote.dSharp, 1), new MusicalScaleNote(MusicalNote.e, 1),
259           new MusicalScaleNote(MusicalNote.fSharp, 1), new MusicalScaleNote(MusicalNote.gSharp, 1),
260           new MusicalScaleNote(MusicalNote.aSharp, 1)
261         ];
262 
263       case MusicalScaleName.cSharpMajor:
264       case MusicalScaleName.dFlatMajor:
265         return
266         [
267           new MusicalScaleNote(MusicalNote.dFlat), new MusicalScaleNote(MusicalNote.eFlat),
268           new MusicalScaleNote(MusicalNote.f), new MusicalScaleNote(MusicalNote.gFlat),
269           new MusicalScaleNote(MusicalNote.aFlat), new MusicalScaleNote(MusicalNote.bFlat),
270           new MusicalScaleNote(MusicalNote.c, 1)
271         ];
272 
273       case MusicalScaleName.dSharpMajor:
274       case MusicalScaleName.eFlatMajor:
275         return
276         [
277           new MusicalScaleNote(MusicalNote.eFlat), new MusicalScaleNote(MusicalNote.f),
278           new MusicalScaleNote(MusicalNote.g), new MusicalScaleNote(MusicalNote.aFlat),
279           new MusicalScaleNote(MusicalNote.bFlat), new MusicalScaleNote(MusicalNote.c, 1),
280           new MusicalScaleNote(MusicalNote.d, 1)
281         ];
282 
283       case MusicalScaleName.fSharpMajor:
284       case MusicalScaleName.gFlatMajor:
285         return
286         [
287           new MusicalScaleNote(MusicalNote.gFlat), new MusicalScaleNote(MusicalNote.aFlat),
288           new MusicalScaleNote(MusicalNote.bFlat), new MusicalScaleNote(MusicalNote.cFlat),
289           new MusicalScaleNote(MusicalNote.dFlat, 1), new MusicalScaleNote(MusicalNote.eFlat, 1),
290           new MusicalScaleNote(MusicalNote.f)
291         ];
292 
293       case MusicalScaleName.gSharpMajor:
294       case MusicalScaleName.aFlatMajor:
295         return
296         [
297           new MusicalScaleNote(MusicalNote.aFlat), new MusicalScaleNote(MusicalNote.bFlat),
298           new MusicalScaleNote(MusicalNote.c, 1), new MusicalScaleNote(MusicalNote.dFlat, 1),
299           new MusicalScaleNote(MusicalNote.eFlat, 1), new MusicalScaleNote(MusicalNote.f, 1),
300           new MusicalScaleNote(MusicalNote.g, 1)
301         ];
302 
303       case MusicalScaleName.aSharpMajor:
304       case MusicalScaleName.bFlatMajor:
305         return
306         [
307           new MusicalScaleNote(MusicalNote.bFlat), new MusicalScaleNote(MusicalNote.c, 1),
308           new MusicalScaleNote(MusicalNote.d, 1), new MusicalScaleNote(MusicalNote.eFlat, 1),
309           new MusicalScaleNote(MusicalNote.f, 1), new MusicalScaleNote(MusicalNote.g, 1),
310           new MusicalScaleNote(MusicalNote.a, 1)
311         ];
312 
313       // Minor Scales:
314 
315       case MusicalScaleName.cMinor:
316         return
317         [
318           new MusicalScaleNote(MusicalNote.c), new MusicalScaleNote(MusicalNote.d),
319           new MusicalScaleNote(MusicalNote.eFlat), new MusicalScaleNote(MusicalNote.f),
320           new MusicalScaleNote(MusicalNote.g), new MusicalScaleNote(MusicalNote.aFlat),
321           new MusicalScaleNote(MusicalNote.bFlat)
322         ];
323 
324       case MusicalScaleName.dMinor:
325         return
326         [
327           new MusicalScaleNote(MusicalNote.d), new MusicalScaleNote(MusicalNote.e),
328           new MusicalScaleNote(MusicalNote.f), new MusicalScaleNote(MusicalNote.g),
329           new MusicalScaleNote(MusicalNote.a), new MusicalScaleNote(MusicalNote.bFlat),
330           new MusicalScaleNote(MusicalNote.c, 1)
331         ];
332 
333       case MusicalScaleName.eMinor:
334         return
335         [
336           new MusicalScaleNote(MusicalNote.e), new MusicalScaleNote(MusicalNote.fSharp),
337           new MusicalScaleNote(MusicalNote.g), new MusicalScaleNote(MusicalNote.a),
338           new MusicalScaleNote(MusicalNote.b), new MusicalScaleNote(MusicalNote.c, 1),
339           new MusicalScaleNote(MusicalNote.d, 1)
340         ];
341 
342       case MusicalScaleName.fMinor:
343         return
344         [
345           new MusicalScaleNote(MusicalNote.f), new MusicalScaleNote(MusicalNote.g),
346           new MusicalScaleNote(MusicalNote.aFlat), new MusicalScaleNote(MusicalNote.bFlat),
347           new MusicalScaleNote(MusicalNote.c, 1), new MusicalScaleNote(MusicalNote.dFlat, 1),
348           new MusicalScaleNote(MusicalNote.eFlat, 1)
349         ];
350 
351       case MusicalScaleName.gMinor:
352         return
353         [
354           new MusicalScaleNote(MusicalNote.g), new MusicalScaleNote(MusicalNote.a),
355           new MusicalScaleNote(MusicalNote.bFlat), new MusicalScaleNote(MusicalNote.c, 1),
356           new MusicalScaleNote(MusicalNote.d, 1), new MusicalScaleNote(MusicalNote.eFlat, 1),
357           new MusicalScaleNote(MusicalNote.f, 1)
358         ];
359 
360       case MusicalScaleName.aMinor:
361         return
362         [
363           new MusicalScaleNote(MusicalNote.a), new MusicalScaleNote(MusicalNote.b),
364           new MusicalScaleNote(MusicalNote.c), new MusicalScaleNote(MusicalNote.d),
365           new MusicalScaleNote(MusicalNote.e), new MusicalScaleNote(MusicalNote.f),
366           new MusicalScaleNote(MusicalNote.g)
367         ];
368 
369       case MusicalScaleName.bMinor:
370         return
371         [
372           new MusicalScaleNote(MusicalNote.b), new MusicalScaleNote(MusicalNote.cSharp, 1),
373           new MusicalScaleNote(MusicalNote.d, 1), new MusicalScaleNote(MusicalNote.e, 1),
374           new MusicalScaleNote(MusicalNote.fSharp, 1), new MusicalScaleNote(MusicalNote.g, 1),
375           new MusicalScaleNote(MusicalNote.a, 1)
376         ];
377 
378       case MusicalScaleName.dFlatMinor:
379       case MusicalScaleName.cSharpMinor:
380         return
381         [
382           new MusicalScaleNote(MusicalNote.cSharp), new MusicalScaleNote(MusicalNote.dSharp, 1),
383           new MusicalScaleNote(MusicalNote.e), new MusicalScaleNote(MusicalNote.fSharp),
384           new MusicalScaleNote(MusicalNote.gSharp), new MusicalScaleNote(MusicalNote.a),
385           new MusicalScaleNote(MusicalNote.b)
386         ];
387 
388       case MusicalScaleName.dSharpMinor:
389       case MusicalScaleName.eFlatMinor:
390         return
391         [
392           new MusicalScaleNote(MusicalNote.eFlat), new MusicalScaleNote(MusicalNote.g),
393           new MusicalScaleNote(MusicalNote.gFlat), new MusicalScaleNote(MusicalNote.aFlat),
394           new MusicalScaleNote(MusicalNote.bFlat), new MusicalScaleNote(MusicalNote.cFlat),
395           new MusicalScaleNote(MusicalNote.dFlat, 1)
396         ];
397 
398       case MusicalScaleName.gFlatMinor:
399       case MusicalScaleName.fSharpMinor:
400         return
401         [
402           new MusicalScaleNote(MusicalNote.fSharp), new MusicalScaleNote(MusicalNote.gSharp),
403           new MusicalScaleNote(MusicalNote.a), new MusicalScaleNote(MusicalNote.b),
404           new MusicalScaleNote(MusicalNote.cSharp, 1), new MusicalScaleNote(MusicalNote.d, 1),
405           new MusicalScaleNote(MusicalNote.e, 1)
406         ];
407 
408       case MusicalScaleName.aFlatMinor:
409       case MusicalScaleName.gSharpMinor:
410         return
411         [
412           new MusicalScaleNote(MusicalNote.gSharp), new MusicalScaleNote(MusicalNote.aSharp),
413           new MusicalScaleNote(MusicalNote.b), new MusicalScaleNote(MusicalNote.cSharp, 1),
414           new MusicalScaleNote(MusicalNote.dSharp, 1), new MusicalScaleNote(MusicalNote.e, 1),
415           new MusicalScaleNote(MusicalNote.fSharp, 1)
416         ];
417 
418       case MusicalScaleName.aSharpMinor:
419       case MusicalScaleName.bFlatMinor:
420         return
421         [
422           new MusicalScaleNote(MusicalNote.bFlat), new MusicalScaleNote(MusicalNote.c, 1),
423           new MusicalScaleNote(MusicalNote.dFlat, 1), new MusicalScaleNote(MusicalNote.eFlat, 1),
424           new MusicalScaleNote(MusicalNote.f, 1), new MusicalScaleNote(MusicalNote.gFlat, 1),
425           new MusicalScaleNote(MusicalNote.aFlat, 1)
426         ];
427     }
428   }
429 
430   /// Converts the scale to a string. This will call toJson().
431   override string toString()
432   {
433     return toJson();
434   }
435 
436   /// Converts the scale to json.
437   string toJson()
438   {
439     string notes = "[";
440 
441     auto scaleNotes = getNotes();
442 
443     foreach (scaleNote; scaleNotes)
444     {
445       notes ~= scaleNote.toJson() ~ ",";
446     }
447 
448     notes.length -= 1;
449     notes ~= "]";
450 
451     return `{"scale":"%s","note":"%s","type":"%s","notes":%s}`
452       .format(_scale, _note, _type, notes);
453   }
454 
455   /// Converts the scale to xml.
456   string toXml()
457   {
458     string notes = "";
459 
460     auto scaleNotes = getNotes();
461 
462     foreach (scaleNote; scaleNotes)
463     {
464       notes ~= scaleNote.toXml();
465     }
466 
467     return `<MusicalScale scale="%s" note="%s" type="%s"><ScaleNotes>%s</ScaleNotes></MusicalScale>`
468       .format(_scale, _note, _type, notes);
469   }
470 }