24 std::size_t tone_count = 3) noexcept {
27 const auto root = scale.degree_pitch_class(degree);
28 const auto root_offset = scale.degree_to_semitones(
static_cast<int>(degree));
29 if (!root || !root_offset)
32 std::array<int, ChordFormula::kMaxNotes> intervals{};
33 for (std::size_t tone = 0; tone < tone_count; ++tone) {
35 scale.degree_to_semitones(
static_cast<int>(degree) +
static_cast<int>(tone * 2));
38 intervals[tone] = *offset - *root_offset;
44 return DiatonicChord{*root, *formula,
static_cast<std::uint8_t
>(degree)};
53 constexpr bool exact() const noexcept {
74 constexpr std::size_t
size() const noexcept {
78 return best_equivalent_count_;
81 return best_equivalent_count_ > 1;
84 constexpr std::optional<ChordRecognition>
candidate(std::size_t index)
const noexcept {
87 return candidates_[index];
91 friend constexpr std::optional<ChordRecognitionList>
94 std::array<ChordRecognition, kMaxCandidates> candidates_{};
95 std::uint16_t count_ = 0;
96 std::uint16_t best_equivalent_count_ = 0;
103 const int lhs_distance = lhs.score.missing + lhs.score.extra;
104 const int rhs_distance = rhs.score.missing + rhs.score.extra;
105 if (lhs_distance != rhs_distance)
106 return lhs_distance < rhs_distance;
107 if (lhs.score.missing != rhs.score.missing)
108 return lhs.score.missing < rhs.score.missing;
109 if (lhs.score.extra != rhs.score.extra)
110 return lhs.score.extra < rhs.score.extra;
111 if (lhs.score.root_present != rhs.score.root_present)
112 return lhs.score.root_present;
113 const auto inversion_rank = [](
const ChordRecognition& candidate)
constexpr {
114 if (candidate.inversion_match_count == 1)
116 if (candidate.inversion_match_count > 1)
120 if (inversion_rank(lhs) != inversion_rank(rhs))
121 return inversion_rank(lhs) > inversion_rank(rhs);
122 if (lhs.inversion_match_count != rhs.inversion_match_count)
123 return lhs.inversion_match_count < rhs.inversion_match_count;
124 if (lhs.root != rhs.root)
125 return static_cast<unsigned>(lhs.root) <
static_cast<unsigned>(rhs.root);
126 return static_cast<unsigned>(lhs.quality) <
static_cast<unsigned>(rhs.quality);
131 return lhs.score.missing == rhs.score.missing && lhs.score.extra == rhs.score.extra &&
132 lhs.score.root_present == rhs.score.root_present &&
133 lhs.inversion_match_count == rhs.inversion_match_count;
138constexpr std::optional<ChordRecognitionList>
140 std::optional<PitchClass> bass = std::nullopt)
noexcept {
141 if (pitch_classes.empty() ||
142 (bass && (!
is_valid(*bass) || !pitch_classes.contains(*bass))))
147 for (std::size_t quality_index = 0; quality_index <
kChordQualityCount; ++quality_index) {
148 const auto quality =
static_cast<ChordQuality>(quality_index);
150 const auto candidate_set = formula->pitch_classes().transposed(root);
152 static_cast<std::uint16_t
>(candidate_set.mask() & pitch_classes.mask());
154 static_cast<std::uint16_t
>(candidate_set.mask() & ~pitch_classes.mask());
156 static_cast<std::uint16_t
>(pitch_classes.mask() & ~candidate_set.mask());
163 static_cast<std::uint8_t
>(std::popcount(missing)),
164 static_cast<std::uint8_t
>(std::popcount(extra)),
165 pitch_classes.contains(
static_cast<PitchClass>(root))};
167 for (std::size_t inversion = 0; inversion < formula->size(); ++inversion) {
170 candidate.inversion_match_mask =
static_cast<std::uint8_t
>(
171 candidate.inversion_match_mask | (1u << inversion));
175 if (
candidate.inversion_match_count == 1)
177 static_cast<std::uint8_t
>(std::countr_zero(
candidate.inversion_match_mask));
180 std::size_t insert = result.count_;
183 result.candidates_[insert] = result.candidates_[insert - 1];
190 result.best_equivalent_count_ = 1;
191 while (result.best_equivalent_count_ < result.count_ &&
193 result.candidates_[result.best_equivalent_count_]))
194 ++result.best_equivalent_count_;
198constexpr std::optional<ChordRecognitionList>
202 std::uint16_t mask = 0;
204 for (
const int pitch : midi_pitches) {
205 if (pitch < 0 || pitch > 127)
207 bass = pitch < bass ? pitch : bass;
208 mask =
static_cast<std::uint16_t
>(mask |
Definition harmony.hpp:70
constexpr bool ambiguous() const noexcept
Definition harmony.hpp:80
constexpr std::size_t size() const noexcept
Definition harmony.hpp:74
constexpr std::optional< ChordRecognition > candidate(std::size_t index) const noexcept
Definition harmony.hpp:84
friend constexpr std::optional< ChordRecognitionList > recognize_chord(PitchClassSet, std::optional< PitchClass >) noexcept
Definition harmony.hpp:139
constexpr std::size_t best_equivalent_count() const noexcept
Definition harmony.hpp:77
static constexpr std::size_t kMaxCandidates
Definition harmony.hpp:72
static constexpr std::optional< PitchClassSet > from_mask(std::uint16_t mask) noexcept
Definition pitch.hpp:60
Definition harmony.hpp:99
constexpr bool recognition_equivalent(const ChordRecognition &lhs, const ChordRecognition &rhs) noexcept
Definition harmony.hpp:129
constexpr bool recognition_better(const ChordRecognition &lhs, const ChordRecognition &rhs) noexcept
Definition harmony.hpp:101
constexpr bool is_valid(PitchClass pitch_class) noexcept
Definition pitch.hpp:34
constexpr std::size_t kChordQualityCount
Definition chord.hpp:31
constexpr PitchClass transpose(PitchClass pitch_class, int semitones) noexcept
Definition pitch.hpp:51
constexpr std::optional< ChordRecognitionList > recognize_chord(PitchClassSet pitch_classes, std::optional< PitchClass > bass=std::nullopt) noexcept
Definition harmony.hpp:139
constexpr std::optional< DiatonicChord > diatonic_chord(const Scale &scale, std::size_t degree, std::size_t tone_count=3) noexcept
Definition harmony.hpp:23
constexpr PitchClass wrap_pitch_class(int index) noexcept
Definition pitch.hpp:44
constexpr int kPitchClassesPerOctave
Definition pitch.hpp:16
PitchClass
Definition pitch.hpp:19
@ c
Definition pitch.hpp:20
ChordQuality
Definition chord.hpp:16
@ major
Definition chord.hpp:17
Definition harmony.hpp:59
PitchClass root
Definition harmony.hpp:60
std::uint8_t inversion_match_count
Definition harmony.hpp:64
constexpr auto operator<=>(const ChordRecognition &) const =default
std::uint8_t inversion_match_mask
Definition harmony.hpp:65
RecognitionScore score
Definition harmony.hpp:62
std::optional< std::uint8_t > inversion
Definition harmony.hpp:63
ChordQuality quality
Definition harmony.hpp:61
Definition harmony.hpp:15
constexpr auto operator<=>(const DiatonicChord &) const =default
std::uint8_t scale_degree
Definition harmony.hpp:18
ChordFormula formula
Definition harmony.hpp:17
PitchClass root
Definition harmony.hpp:16
Definition harmony.hpp:47
std::uint8_t extra
Definition harmony.hpp:50
std::uint8_t matched
Definition harmony.hpp:48
constexpr auto operator<=>(const RecognitionScore &) const =default
bool root_present
Definition harmony.hpp:51
constexpr bool exact() const noexcept
Definition harmony.hpp:53
std::uint8_t missing
Definition harmony.hpp:49