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 }