1use std::sync::LazyLock;
4
5#[cfg(feature = "wasm")]
6use wasm_bindgen::prelude::*;
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::core::base::HasStaticName;
12
13pub trait HasIsDominant {
17 fn is_dominant(&self) -> bool;
19}
20
21#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Ord, PartialOrd)]
25#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
26#[repr(u8)]
27pub enum Degree {
28 Seven,
30 Nine,
32 Eleven,
34 Thirteen,
36}
37
38#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Ord, PartialOrd)]
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46#[repr(u8)]
47pub enum Modifier {
48 Minor,
50
51 Flat5,
53 Augmented5,
55
56 Major7,
58 Dominant(Degree),
60
61 Flat9,
63 Sharp9,
65
66 Sharp11,
68
69 Flat13,
71
72 Diminished,
74}
75
76#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug, Ord, PartialOrd)]
82#[repr(u8)]
83#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
84#[cfg_attr(feature = "wasm", wasm_bindgen(js_name = KordExtension))]
85pub enum Extension {
86 Sus2,
88 Sus4,
90
91 Flat11,
93
94 Sharp13,
96
97 Add2,
99 Add4,
101 Add6,
103
104 Add9,
106 Add11,
108 Add13,
110}
111
112impl HasIsDominant for Modifier {
115 fn is_dominant(&self) -> bool {
116 matches!(self, Modifier::Dominant(_))
117 }
118}
119
120impl HasStaticName for Degree {
121 #[coverage(off)]
122 fn static_name(&self) -> &'static str {
123 match self {
124 Degree::Seven => "7",
125 Degree::Nine => "9",
126 Degree::Eleven => "11",
127 Degree::Thirteen => "13",
128 }
129 }
130}
131
132impl HasStaticName for Modifier {
133 #[coverage(off)]
134 fn static_name(&self) -> &'static str {
135 match self {
136 Modifier::Minor => "m",
137
138 Modifier::Flat5 => "♭5",
139 Modifier::Augmented5 => "+",
140
141 Modifier::Major7 => "maj7",
142 Modifier::Dominant(dominant) => dominant.static_name(),
143
144 Modifier::Flat9 => "♭9",
145 Modifier::Sharp9 => "♯9",
146
147 Modifier::Sharp11 => "♯11",
148
149 Modifier::Flat13 => "♭13",
150
151 Modifier::Diminished => "°",
152 }
153 }
154}
155
156impl HasStaticName for Extension {
157 #[coverage(off)]
158 fn static_name(&self) -> &'static str {
159 match self {
160 Extension::Sus2 => "sus2",
161 Extension::Sus4 => "sus4",
162
163 Extension::Flat11 => "♭11",
164
165 Extension::Sharp13 => "♯13",
166
167 Extension::Add2 => "add2",
168 Extension::Add4 => "add4",
169 Extension::Add6 => "add6",
170
171 Extension::Add9 => "add9",
172 Extension::Add11 => "add11",
173 Extension::Add13 => "add13",
174 }
175 }
176}
177
178pub fn known_modifier_sets() -> &'static [Vec<Modifier>] {
182 KNOWN_MODIFIER_SETS.as_ref()
183}
184
185pub fn one_off_modifier_sets() -> &'static [Vec<Modifier>] {
187 ONE_OFF_MODIFIER_SETS.as_ref()
188}
189
190pub fn likely_extension_sets() -> &'static [Vec<Extension>] {
192 LIKELY_EXTENSION_SETS.as_ref()
193}
194
195static KNOWN_MODIFIER_SETS: LazyLock<[Vec<Modifier>; 43]> = LazyLock::new(|| {
198 [
199 vec![],
200 vec![Modifier::Minor],
201 vec![Modifier::Major7],
202 vec![Modifier::Dominant(Degree::Seven)],
203 vec![Modifier::Dominant(Degree::Nine)],
204 vec![Modifier::Dominant(Degree::Eleven)],
205 vec![Modifier::Dominant(Degree::Thirteen)],
206 vec![Modifier::Minor, Modifier::Major7],
207 vec![Modifier::Minor, Modifier::Dominant(Degree::Seven)],
208 vec![Modifier::Minor, Modifier::Dominant(Degree::Nine)],
209 vec![Modifier::Minor, Modifier::Dominant(Degree::Eleven)],
210 vec![Modifier::Minor, Modifier::Dominant(Degree::Thirteen)],
211 vec![Modifier::Sharp11, Modifier::Dominant(Degree::Seven)],
212 vec![Modifier::Sharp11, Modifier::Dominant(Degree::Nine)],
213 vec![Modifier::Sharp11, Modifier::Dominant(Degree::Eleven)],
214 vec![Modifier::Sharp11, Modifier::Dominant(Degree::Thirteen)],
215 vec![Modifier::Augmented5],
216 vec![Modifier::Augmented5, Modifier::Major7],
217 vec![Modifier::Augmented5, Modifier::Dominant(Degree::Seven)],
218 vec![Modifier::Augmented5, Modifier::Dominant(Degree::Nine)],
219 vec![Modifier::Augmented5, Modifier::Dominant(Degree::Eleven)],
220 vec![Modifier::Augmented5, Modifier::Dominant(Degree::Thirteen)],
221 vec![Modifier::Minor, Modifier::Flat5, Modifier::Dominant(Degree::Seven)],
222 vec![Modifier::Minor, Modifier::Flat5, Modifier::Dominant(Degree::Nine)],
223 vec![Modifier::Minor, Modifier::Flat5, Modifier::Dominant(Degree::Eleven)],
224 vec![Modifier::Minor, Modifier::Flat5, Modifier::Dominant(Degree::Thirteen)],
225 vec![Modifier::Diminished],
226 vec![Modifier::Flat9, Modifier::Dominant(Degree::Seven)],
227 vec![Modifier::Flat9, Modifier::Dominant(Degree::Nine)],
228 vec![Modifier::Flat9, Modifier::Dominant(Degree::Eleven)],
229 vec![Modifier::Flat9, Modifier::Dominant(Degree::Thirteen)],
230 vec![Modifier::Sharp9, Modifier::Dominant(Degree::Seven)],
231 vec![Modifier::Sharp9, Modifier::Dominant(Degree::Nine)],
232 vec![Modifier::Sharp9, Modifier::Dominant(Degree::Eleven)],
233 vec![Modifier::Sharp9, Modifier::Dominant(Degree::Thirteen)],
234 vec![Modifier::Minor, Modifier::Flat13, Modifier::Dominant(Degree::Seven)],
235 vec![Modifier::Minor, Modifier::Flat13, Modifier::Dominant(Degree::Nine)],
236 vec![Modifier::Minor, Modifier::Flat13, Modifier::Dominant(Degree::Eleven)],
237 vec![Modifier::Minor, Modifier::Flat13, Modifier::Dominant(Degree::Thirteen)],
238 vec![Modifier::Minor, Modifier::Flat9, Modifier::Flat13, Modifier::Dominant(Degree::Seven)],
239 vec![Modifier::Minor, Modifier::Flat9, Modifier::Flat13, Modifier::Dominant(Degree::Nine)],
240 vec![Modifier::Minor, Modifier::Flat9, Modifier::Flat13, Modifier::Dominant(Degree::Eleven)],
241 vec![Modifier::Minor, Modifier::Flat9, Modifier::Flat13, Modifier::Dominant(Degree::Thirteen)],
242 ]
243});
244
245static ONE_OFF_MODIFIER_SETS: LazyLock<[Vec<Modifier>; 7]> = LazyLock::new(|| {
246 [
247 vec![],
248 vec![Modifier::Augmented5],
249 vec![Modifier::Flat5],
250 vec![Modifier::Flat9],
251 vec![Modifier::Sharp9],
252 vec![Modifier::Sharp11],
253 vec![Modifier::Flat13],
254 ]
255});
256
257static LIKELY_EXTENSION_SETS: LazyLock<[Vec<Extension>; 11]> = LazyLock::new(|| {
258 [
259 vec![],
260 vec![Extension::Sus2],
261 vec![Extension::Sus4],
262 vec![Extension::Add2],
263 vec![Extension::Add4],
264 vec![Extension::Add6],
265 vec![Extension::Add9],
266 vec![Extension::Add11],
267 vec![Extension::Add13],
268 vec![Extension::Flat11],
269 vec![Extension::Sharp13],
270 ]
271});