MCC
Loading...
Searching...
No Matches
ScalePattern.h
1#ifndef MCC_SCALE_SCALE_PATTERN_H
2#define MCC_SCALE_SCALE_PATTERN_H
3
4#include <MCC_BuildSettings.h>
5
6#include <stdint.h>
7
8#include <MCC/Interval/Interval.h>
9
10namespace MCC {
11
13namespace Detail {
14struct ScalePatternStorage;
15}
17
32class ScalePattern {
33public:
35 static constexpr uint8_t MaximumDegrees = 12;
36
37private:
38 friend struct Detail::ScalePatternStorage;
39
40 uint16_t _semitones; // bit k set: a degree lies k semitones above the root
41 uint8_t _steps[6]; // diatonic steps of degree i in nibble i
42
43 constexpr ScalePattern(uint16_t semitones, uint8_t steps0,
44 uint8_t steps1, uint8_t steps2,
45 uint8_t steps3, uint8_t steps4,
46 uint8_t steps5) noexcept
47 : _semitones(semitones),
48 _steps{steps0, steps1, steps2, steps3, steps4, steps5} {}
49
50 // Semitones of the major-scale degree `steps` above the root: 0 2 4 5 7 9 11.
51 // Computed rather than tabled so AVR keeps no lookup table in RAM.
52 static constexpr int32_t NaturalSemitones(int32_t steps) noexcept {
53 return steps * 2 - (steps >= 3 ? 1 : 0);
54 }
55
56 constexpr uint8_t StepsAt(uint8_t index) const noexcept {
57 return static_cast<uint8_t>((_steps[index / 2] >> ((index % 2) * 4)) & 0x0F);
58 }
59
60 MCC_CONSTEXPR14 int32_t SemitonesAt(uint8_t index) const noexcept {
61 uint8_t seen = 0;
62 for (int32_t k = 0; k < 12; ++k) {
63 if ((static_cast<uint32_t>(_semitones) >> k) & 1u) {
64 if (seen == index) {
65 return k;
66 }
67 ++seen;
68 }
69 }
70 return -1;
71 }
72
73public:
75 constexpr ScalePattern() noexcept : _semitones(0), _steps{0, 0, 0, 0, 0, 0} {}
76
78 static constexpr ScalePattern Invalid() noexcept { return ScalePattern(); }
79
88 static MCC_CONSTEXPR14 ScalePattern FromFormula(const char* formula) noexcept {
89 ScalePattern pattern;
90 if (formula == nullptr) {
91 return Invalid();
92 }
93 uint8_t count = 0;
94 int32_t previous = -1;
95 // Index-based scanning without `continue`: GCC 7 (the Arduino AVR
96 // toolchain) miscounts constexpr loops that `continue` past an
97 // advancing pointer.
98 int32_t i = 0;
99 while (formula[i] != '\0') {
100 if (formula[i] == ' ') {
101 ++i;
102 } else {
103 int32_t accidental = 0;
104 while (formula[i] == 'b' || formula[i] == '#') {
105 accidental += (formula[i] == '#') ? 1 : -1;
106 ++i;
107 }
108 if (formula[i] < '1' || formula[i] > '7') {
109 return Invalid();
110 }
111 const int32_t steps = formula[i] - '1';
112 ++i;
113 if (formula[i] != ' ' && formula[i] != '\0') {
114 return Invalid();
115 }
116 const int32_t semitones = NaturalSemitones(steps) + accidental;
117 if (count == MaximumDegrees || semitones <= previous || semitones > 11 ||
118 (count == 0 && (steps != 0 || semitones != 0)) ||
119 !Interval::FromSteps(steps, semitones).IsValid()) {
120 return Invalid();
121 }
122 pattern._semitones = static_cast<uint16_t>(pattern._semitones | (1u << semitones));
123 pattern._steps[count / 2] = static_cast<uint8_t>(
124 pattern._steps[count / 2] | (steps << ((count % 2) * 4)));
125 previous = semitones;
126 ++count;
127 }
128 }
129 return count == 0 ? Invalid() : pattern;
130 }
131
133 constexpr bool IsValid() const noexcept { return _semitones != 0; }
134
136 MCC_CONSTEXPR14 uint8_t DegreeCount() const noexcept {
137 uint8_t count = 0;
138 for (int32_t k = 0; k < 12; ++k) {
139 count = static_cast<uint8_t>(count + ((static_cast<uint32_t>(_semitones) >> k) & 1u));
140 }
141 return count;
142 }
143
148 MCC_CONSTEXPR14 Interval DegreeInterval(int32_t degree) const noexcept {
149 if (degree < 1 || degree > DegreeCount()) {
150 return Interval::Invalid();
151 }
152 const uint8_t index = static_cast<uint8_t>(degree - 1);
153 return Interval::FromSteps(StepsAt(index), SemitonesAt(index));
154 }
155
160 MCC_CONSTEXPR14 bool ContainsSemitone(int32_t semitones) const noexcept {
161 const int32_t reduced = ((semitones % 12) + 12) % 12;
162 return IsValid() && ((static_cast<uint32_t>(_semitones) >> reduced) & 1u) != 0;
163 }
164
166 friend MCC_CONSTEXPR14 bool operator==(const ScalePattern& a, const ScalePattern& b) noexcept {
167 if (a._semitones != b._semitones) {
168 return false;
169 }
170 for (uint8_t i = 0; i < 6; ++i) {
171 if (a._steps[i] != b._steps[i]) {
172 return false;
173 }
174 }
175 return true;
176 }
177
179 friend MCC_CONSTEXPR14 bool operator!=(const ScalePattern& a, const ScalePattern& b) noexcept {
180 return !(a == b);
181 }
182};
183
185namespace Detail {
186
187struct ScalePatternStorage {
188 static constexpr ScalePattern Make(uint16_t semitones, uint8_t steps0,
189 uint8_t steps1, uint8_t steps2,
190 uint8_t steps3, uint8_t steps4,
191 uint8_t steps5) noexcept {
192 return ScalePattern(semitones, steps0, steps1, steps2,
193 steps3, steps4, steps5);
194 }
195};
196
197} // namespace Detail
199
200} // namespace MCC
201
202#endif // MCC_SCALE_SCALE_PATTERN_H
Directed interval: a diatonic step count and a chromatic semitone count (SPEC-INT-1).
Definition Interval.h:30
static MCC_CONSTEXPR14 Interval FromSteps(int32_t diatonicSteps, int32_t semitones) noexcept
Creates the interval spanning diatonicSteps letters and semitones semitones, both signed (SPEC-INT-1)...
Definition Interval.h:111
static constexpr Interval Invalid() noexcept
Returns the invalid interval.
Definition Interval.h:139
friend MCC_CONSTEXPR14 bool operator!=(const ScalePattern &a, const ScalePattern &b) noexcept
Negation of operator==.
Definition ScalePattern.h:179
MCC_CONSTEXPR14 bool ContainsSemitone(int32_t semitones) const noexcept
Returns true when a degree lies semitones (reduced modulo 12) above the root.
Definition ScalePattern.h:160
MCC_CONSTEXPR14 uint8_t DegreeCount() const noexcept
Returns the number of degrees, or 0 for the invalid pattern.
Definition ScalePattern.h:136
MCC_CONSTEXPR14 Interval DegreeInterval(int32_t degree) const noexcept
Returns the ascending interval from the root to degree (1-based), or the invalid interval outside [1,...
Definition ScalePattern.h:148
static constexpr ScalePattern Invalid() noexcept
Returns the invalid pattern.
Definition ScalePattern.h:78
static constexpr uint8_t MaximumDegrees
Largest number of degrees: every chromatic semitone.
Definition ScalePattern.h:35
static MCC_CONSTEXPR14 ScalePattern FromFormula(const char *formula) noexcept
Builds a pattern from a degree formula such as "1 2 b3 4 5 b6 b7".
Definition ScalePattern.h:88
constexpr ScalePattern() noexcept
Creates the invalid pattern (SPEC-ERR-2).
Definition ScalePattern.h:75
constexpr bool IsValid() const noexcept
Returns true unless this is the invalid pattern.
Definition ScalePattern.h:133
friend MCC_CONSTEXPR14 bool operator==(const ScalePattern &a, const ScalePattern &b) noexcept
Compares degrees, including their written steps.
Definition ScalePattern.h:166
Music Composition Core public namespace.
Definition Version.h:4