MCC
Loading...
Searching...
No Matches
Meter.h
1#ifndef MCC_RHYTHM_METER_H
2#define MCC_RHYTHM_METER_H
3
4#include <stdint.h>
5
6#include <MCC_BuildSettings.h>
7#include <MCC/Rhythm/NoteValue.h>
8
9namespace MCC {
10
12enum class MeterKind : uint8_t {
13 Simple = 0,
14 Compound,
15 Irregular,
16 Invalid
17};
18
31class Meter {
32 uint8_t _numerator;
33 MCC::NoteValue _pulseValue;
34
35 static constexpr bool IsUndotted(MCC::NoteValue value) noexcept {
36 return value.IsValid() && value.DotCount() == 0u;
37 }
38
39 static constexpr bool LooksCompound(int32_t numerator) noexcept {
40 return numerator >= 6 && numerator % 3 == 0;
41 }
42
43 static constexpr bool IsUsable(
44 int32_t numerator, MCC::NoteValue pulseValue) noexcept {
45 return numerator >= 1 && numerator <= 255 && IsUndotted(pulseValue) &&
46 (!LooksCompound(numerator) || pulseValue.BaseDenominator() >= 2u);
47 }
48
49 static MCC_CONSTEXPR14 uint16_t GreatestCommonDivisor(
50 uint16_t a, uint16_t b) noexcept {
51 while (b != 0u) {
52 const uint16_t remainder = static_cast<uint16_t>(a % b);
53 a = b;
54 b = remainder;
55 }
56 return a;
57 }
58
59public:
61 constexpr Meter() noexcept : _numerator(0u), _pulseValue() {}
62
70 MCC_CONSTEXPR14 Meter(int32_t numerator, int32_t denominator) noexcept
71 : Meter(numerator, MCC::NoteValue::FromDenominator(denominator)) {}
72
76 constexpr Meter(int32_t numerator, MCC::NoteValue pulseValue) noexcept
77 : _numerator(IsUsable(numerator, pulseValue)
78 ? static_cast<uint8_t>(numerator)
79 : uint8_t(0u)),
80 _pulseValue(IsUsable(numerator, pulseValue)
81 ? pulseValue
83
85 static constexpr Meter Invalid() noexcept { return Meter(); }
86
88 static MCC_CONSTEXPR14 Meter CommonTime() noexcept { return Meter(4, 4); }
89
91 static MCC_CONSTEXPR14 Meter CutTime() noexcept { return Meter(2, 2); }
92
98 static constexpr Meter Simple(
99 int32_t beatCount,
100 MCC::NoteValue beatValue = MCC::NoteValue::Quarter()) noexcept {
101 return beatCount >= 1 && beatCount <= 4 && IsUndotted(beatValue)
102 ? Meter(beatCount, beatValue)
103 : Invalid();
104 }
105
113 static MCC_CONSTEXPR14 Meter Compound(
114 int32_t beatCount,
115 MCC::NoteValue beatValue = MCC::NoteValue::Quarter(1)) noexcept {
116 if (beatCount < 2 || beatCount > 85 || !beatValue.IsValid() ||
117 beatValue.DotCount() != 1u || beatValue.BaseDenominator() > 128u) {
118 return Invalid();
119 }
120 return Meter(
121 beatCount * 3,
122 static_cast<int32_t>(beatValue.BaseDenominator()) * 2);
123 }
124
126 constexpr bool IsValid() const noexcept { return _numerator != 0u; }
127
129 constexpr uint8_t Numerator() const noexcept { return _numerator; }
130
132 constexpr uint16_t Denominator() const noexcept {
133 return _pulseValue.BaseDenominator();
134 }
135
137 constexpr MCC::NoteValue PulseValue() const noexcept { return _pulseValue; }
138
140 constexpr uint8_t PulseCount() const noexcept { return _numerator; }
141
143 constexpr MeterKind Kind() const noexcept {
144 return !IsValid() ? MeterKind::Invalid
145 : (_numerator <= 4u ? MeterKind::Simple
146 : (LooksCompound(_numerator)
147 ? MeterKind::Compound
148 : MeterKind::Irregular));
149 }
150
151 constexpr bool IsSimple() const noexcept { return Kind() == MeterKind::Simple; }
152 constexpr bool IsCompound() const noexcept { return Kind() == MeterKind::Compound; }
153 constexpr bool IsIrregular() const noexcept { return Kind() == MeterKind::Irregular; }
154
158 constexpr uint8_t BeatCount() const noexcept {
159 return IsSimple() ? _numerator
160 : (IsCompound() ? static_cast<uint8_t>(_numerator / 3u) : 0u);
161 }
162
169 MCC_CONSTEXPR14 MCC::NoteValue BeatValue() const noexcept {
170 if (IsSimple()) {
171 return _pulseValue;
172 }
173 if (IsCompound()) {
175 static_cast<int32_t>(Denominator() / 2u), 1);
176 }
178 }
179
181 MCC_CONSTEXPR14 uint16_t MeasureNumerator() const noexcept {
182 if (!IsValid()) {
183 return 0u;
184 }
185 const uint16_t divisor = GreatestCommonDivisor(_numerator, Denominator());
186 return static_cast<uint16_t>(_numerator / divisor);
187 }
188
190 MCC_CONSTEXPR14 uint16_t MeasureDenominator() const noexcept {
191 if (!IsValid()) {
192 return 0u;
193 }
194 const uint16_t divisor = GreatestCommonDivisor(_numerator, Denominator());
195 return static_cast<uint16_t>(Denominator() / divisor);
196 }
197
199 friend constexpr bool operator==(Meter a, Meter b) noexcept {
200 return a._numerator == b._numerator && a._pulseValue == b._pulseValue;
201 }
202
203 friend constexpr bool operator!=(Meter a, Meter b) noexcept {
204 return !(a == b);
205 }
206
208 friend constexpr bool operator<(Meter a, Meter b) noexcept {
209 return a.IsValid() != b.IsValid() ? a.IsValid()
210 : (!a.IsValid() ? false
211 : (a._numerator != b._numerator
212 ? a._numerator < b._numerator
213 : a.Denominator() < b.Denominator()));
214 }
215
216 friend constexpr bool operator>(Meter a, Meter b) noexcept { return b < a; }
217 friend constexpr bool operator<=(Meter a, Meter b) noexcept { return !(b < a); }
218 friend constexpr bool operator>=(Meter a, Meter b) noexcept { return !(a < b); }
219};
220
228constexpr bool HasSameMeasureDuration(Meter a, Meter b) noexcept {
229 return a.IsValid() && b.IsValid() &&
230 static_cast<uint32_t>(a.Numerator()) * b.Denominator() ==
231 static_cast<uint32_t>(b.Numerator()) * a.Denominator();
232}
233
234} // namespace MCC
235
236#endif // MCC_RHYTHM_METER_H
Exact written time signature and its simple/compound interpretation.
Definition Meter.h:31
MCC_CONSTEXPR14 uint16_t MeasureDenominator() const noexcept
Reduced denominator of the measure length in whole notes.
Definition Meter.h:190
constexpr uint8_t PulseCount() const noexcept
Returns the number of denominator units in the measure.
Definition Meter.h:140
constexpr MeterKind Kind() const noexcept
Classifies this meter as simple, compound, irregular or invalid.
Definition Meter.h:143
static MCC_CONSTEXPR14 Meter CommonTime() noexcept
Returns common time (4/4).
Definition Meter.h:88
static MCC_CONSTEXPR14 Meter CutTime() noexcept
Returns cut time (2/2).
Definition Meter.h:91
constexpr uint8_t BeatCount() const noexcept
Returns the interpreted beat count, or 0 for irregular/invalid.
Definition Meter.h:158
MCC_CONSTEXPR14 MCC::NoteValue BeatValue() const noexcept
Returns the beat value, or invalid for irregular/invalid meters.
Definition Meter.h:169
static constexpr Meter Simple(int32_t beatCount, MCC::NoteValue beatValue=MCC::NoteValue::Quarter()) noexcept
Creates a simple meter from one to four undotted beats.
Definition Meter.h:98
friend constexpr bool operator<(Meter a, Meter b) noexcept
Written order by numerator then denominator; invalid sorts last.
Definition Meter.h:208
constexpr MCC::NoteValue PulseValue() const noexcept
Returns the denominator unit, or the invalid NoteValue.
Definition Meter.h:137
constexpr uint16_t Denominator() const noexcept
Returns the written denominator, or 0 for the invalid meter.
Definition Meter.h:132
constexpr Meter(int32_t numerator, MCC::NoteValue pulseValue) noexcept
Creates a meter from its numerator and undotted denominator unit.
Definition Meter.h:76
static constexpr Meter Invalid() noexcept
Returns the invalid meter.
Definition Meter.h:85
constexpr Meter() noexcept
Creates the invalid meter (SPEC-ERR-2).
Definition Meter.h:61
MCC_CONSTEXPR14 Meter(int32_t numerator, int32_t denominator) noexcept
Creates the written signature numerator / denominator.
Definition Meter.h:70
constexpr bool IsValid() const noexcept
Returns true unless this is the invalid meter.
Definition Meter.h:126
friend constexpr bool operator==(Meter a, Meter b) noexcept
Written equality; equivalent durations need not be equal.
Definition Meter.h:199
constexpr uint8_t Numerator() const noexcept
Returns the written numerator, or 0 for the invalid meter.
Definition Meter.h:129
MCC_CONSTEXPR14 uint16_t MeasureNumerator() const noexcept
Reduced numerator of the measure length in whole notes.
Definition Meter.h:181
static MCC_CONSTEXPR14 Meter Compound(int32_t beatCount, MCC::NoteValue beatValue=MCC::NoteValue::Quarter(1)) noexcept
Creates a compound meter from dotted beats.
Definition Meter.h:113
Exact written note length: a power-of-two base and zero to four dots.
Definition NoteValue.h:21
static constexpr NoteValue Invalid() noexcept
Returns the invalid note value.
Definition NoteValue.h:51
constexpr uint8_t DotCount() const noexcept
Returns the number of dots, or InvalidDotCount when invalid.
Definition NoteValue.h:136
constexpr bool IsValid() const noexcept
Returns true unless this is the invalid note value.
Definition NoteValue.h:121
static MCC_CONSTEXPR14 NoteValue FromDenominator(int32_t baseDenominator, int32_t dots=0) noexcept
Creates a value from the undotted base denominator.
Definition NoteValue.h:104
constexpr uint16_t BaseDenominator() const noexcept
Returns the undotted denominator (1 through 256), or 0.
Definition NoteValue.h:129
static constexpr NoteValue Quarter(int32_t dots=0) noexcept
Creates a quarter note, optionally augmented by dots.
Definition NoteValue.h:64
MeterKind
Rhythmic interpretation of a written meter.
Definition Meter.h:12
constexpr bool HasSameMeasureDuration(Meter a, Meter b) noexcept
Returns true when two valid meters span the same whole-note length.
Definition Meter.h:228
Music Composition Core public namespace.
Definition Version.h:4