MCC
Loading...
Searching...
No Matches
ChordPattern.h
1#ifndef MCC_CHORD_CHORD_PATTERN_H
2#define MCC_CHORD_CHORD_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 ChordPatternStorage;
15}
17
33class ChordPattern {
34public:
36 static constexpr uint8_t MaximumTones = 7;
37
38private:
39 friend struct Detail::ChordPatternStorage;
40
41 uint32_t _semitones; // bit k set: a tone lies k semitones above the root
42 uint8_t _steps[4]; // diatonic steps of tone i in nibble i
43
44 constexpr ChordPattern(uint32_t semitones, uint8_t steps0,
45 uint8_t steps1, uint8_t steps2,
46 uint8_t steps3) noexcept
47 : _semitones(semitones), _steps{steps0, steps1, steps2, steps3} {}
48
49 // Semitones of the major-scale degree `steps` above the root, extended
50 // past the octave. Computed rather than tabled so AVR keeps no RAM table.
51 static constexpr int32_t NaturalSemitones(int32_t steps) noexcept {
52 return (steps / 7) * 12 + (steps % 7) * 2 - ((steps % 7) >= 3 ? 1 : 0);
53 }
54
55 constexpr uint8_t StepsAt(uint8_t index) const noexcept {
56 return static_cast<uint8_t>((_steps[index / 2] >> ((index % 2) * 4)) & 0x0F);
57 }
58
59 MCC_CONSTEXPR14 int32_t SemitonesAt(uint8_t index) const noexcept {
60 uint8_t seen = 0;
61 for (int32_t k = 0; k < 24; ++k) {
62 if ((_semitones >> k) & 1u) {
63 if (seen == index) {
64 return k;
65 }
66 ++seen;
67 }
68 }
69 return -1;
70 }
71
72public:
74 constexpr ChordPattern() noexcept : _semitones(0), _steps{0, 0, 0, 0} {}
75
77 static constexpr ChordPattern Invalid() noexcept { return ChordPattern(); }
78
88 static MCC_CONSTEXPR14 ChordPattern FromFormula(const char* formula) noexcept {
89 ChordPattern 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` keeps GCC 7 (the Arduino
96 // AVR toolchain) able to evaluate the loop at compile time.
97 int32_t i = 0;
98 while (formula[i] != '\0') {
99 if (formula[i] == ' ') {
100 ++i;
101 } else {
102 int32_t accidental = 0;
103 while (formula[i] == 'b' || formula[i] == '#') {
104 accidental += (formula[i] == '#') ? 1 : -1;
105 ++i;
106 }
107 int32_t number = 0;
108 while (formula[i] >= '0' && formula[i] <= '9' && number < 100) {
109 number = number * 10 + (formula[i] - '0');
110 ++i;
111 }
112 if (number < 1 || number > 13 ||
113 (formula[i] != ' ' && formula[i] != '\0')) {
114 return Invalid();
115 }
116 const int32_t steps = number - 1;
117 const int32_t semitones = NaturalSemitones(steps) + accidental;
118 if (count == MaximumTones || semitones <= previous || semitones > 23 ||
119 (count == 0 && (steps != 0 || semitones != 0)) ||
120 !Interval::FromSteps(steps, semitones).IsValid()) {
121 return Invalid();
122 }
123 pattern._semitones |= (static_cast<uint32_t>(1) << semitones);
124 pattern._steps[count / 2] = static_cast<uint8_t>(
125 pattern._steps[count / 2] | (steps << ((count % 2) * 4)));
126 previous = semitones;
127 ++count;
128 }
129 }
130 return count == 0 ? Invalid() : pattern;
131 }
132
134 constexpr bool IsValid() const noexcept { return _semitones != 0; }
135
137 MCC_CONSTEXPR14 uint8_t ToneCount() const noexcept {
138 uint8_t count = 0;
139 for (int32_t k = 0; k < 24; ++k) {
140 count = static_cast<uint8_t>(count + ((_semitones >> k) & 1u));
141 }
142 return count;
143 }
144
150 MCC_CONSTEXPR14 Interval ToneInterval(int32_t tone) const noexcept {
151 if (tone < 1 || tone > ToneCount()) {
152 return Interval::Invalid();
153 }
154 const uint8_t index = static_cast<uint8_t>(tone - 1);
155 return Interval::FromSteps(StepsAt(index), SemitonesAt(index));
156 }
157
162 constexpr uint16_t PitchClassMask() const noexcept {
163 return static_cast<uint16_t>((_semitones | (_semitones >> 12)) & 0x0FFFu);
164 }
165
167 friend MCC_CONSTEXPR14 bool operator==(const ChordPattern& a, const ChordPattern& b) noexcept {
168 if (a._semitones != b._semitones) {
169 return false;
170 }
171 for (uint8_t i = 0; i < 4; ++i) {
172 if (a._steps[i] != b._steps[i]) {
173 return false;
174 }
175 }
176 return true;
177 }
178
180 friend MCC_CONSTEXPR14 bool operator!=(const ChordPattern& a, const ChordPattern& b) noexcept {
181 return !(a == b);
182 }
183};
184
186namespace Detail {
187
188struct ChordPatternStorage {
189 static constexpr ChordPattern Make(uint32_t semitones, uint8_t steps0,
190 uint8_t steps1, uint8_t steps2,
191 uint8_t steps3) noexcept {
192 return ChordPattern(semitones, steps0, steps1, steps2, steps3);
193 }
194};
195
196} // namespace Detail
198
199} // namespace MCC
200
201#endif // MCC_CHORD_CHORD_PATTERN_H
constexpr ChordPattern() noexcept
Creates the invalid pattern (SPEC-ERR-2).
Definition ChordPattern.h:74
static constexpr ChordPattern Invalid() noexcept
Returns the invalid pattern.
Definition ChordPattern.h:77
friend MCC_CONSTEXPR14 bool operator!=(const ChordPattern &a, const ChordPattern &b) noexcept
Negation of operator==.
Definition ChordPattern.h:180
constexpr bool IsValid() const noexcept
Returns true unless this is the invalid pattern.
Definition ChordPattern.h:134
static MCC_CONSTEXPR14 ChordPattern FromFormula(const char *formula) noexcept
Builds a pattern from a chord formula such as "1 3 5 b7 9".
Definition ChordPattern.h:88
friend MCC_CONSTEXPR14 bool operator==(const ChordPattern &a, const ChordPattern &b) noexcept
Compares tones, including their written steps.
Definition ChordPattern.h:167
static constexpr uint8_t MaximumTones
Largest number of tones: a full thirteenth chord.
Definition ChordPattern.h:36
constexpr uint16_t PitchClassMask() const noexcept
Returns the twelve-bit set of semitones above the root reduced modulo 12; bit k is set when a tone so...
Definition ChordPattern.h:162
MCC_CONSTEXPR14 uint8_t ToneCount() const noexcept
Returns the number of tones, or 0 for the invalid pattern.
Definition ChordPattern.h:137
MCC_CONSTEXPR14 Interval ToneInterval(int32_t tone) const noexcept
Returns the ascending interval from the root to tone (1-based, in root-position order),...
Definition ChordPattern.h:150
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
Music Composition Core public namespace.
Definition Version.h:4