1use crate::{
2 Chord, ChordShape, Degree, Interval, IntervalCollection, IntervalStepSequence, Mode, Note,
3 NoteIter, NoteIterator, NoteName, PitchClass, PitchClassCollection, Scale,
4};
5use std::hash::{Hash, Hasher};
6use std::ops::RangeBounds;
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11#[cfg(feature = "redact-composer")]
12use redact_composer_core::derive::Element;
13
14#[derive(Debug, Clone, Copy, Eq)]
22#[cfg_attr(feature = "redact-composer", derive(Element))]
23#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
24pub struct Key {
25 pub(crate) root: PitchClass,
27 pub(crate) scale: Scale,
29 pub(crate) mode: Mode,
31 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
33 pub(crate) name_pref: Option<NoteName>,
34}
35
36impl PartialEq for Key {
37 fn eq(&self, other: &Self) -> bool {
38 self.root == other.root && self.scale == other.scale && self.mode == other.mode
39 }
40}
41
42impl Hash for Key {
43 fn hash<H: Hasher>(&self, state: &mut H) {
44 self.root.hash(state);
45 self.scale.hash(state);
46 self.mode.hash(state);
47 }
48}
49
50impl From<(PitchClass, Scale, Mode)> for Key {
51 fn from(value: (PitchClass, Scale, Mode)) -> Self {
52 let (root, scale, mode, name_pref) = (value.0, value.1, value.2, None);
53
54 Key {
55 root,
56 scale,
57 mode,
58 name_pref,
59 }
60 }
61}
62
63impl From<(PitchClass, Scale)> for Key {
64 fn from(value: (PitchClass, Scale)) -> Self {
65 Self::from((value.0, value.1, Mode::default()))
66 }
67}
68
69impl From<(NoteName, Scale, Mode)> for Key {
70 fn from(value: (NoteName, Scale, Mode)) -> Self {
71 let (tonic, scale, mode, name_pref) = (
72 PitchClass::from(value.0),
73 value.1,
74 value.2,
75 Some(Self::simplify_root(value.0)),
76 );
77
78 Key {
79 root: tonic,
80 scale,
81 mode,
82 name_pref,
83 }
84 }
85}
86
87impl From<(NoteName, Scale)> for Key {
88 fn from(value: (NoteName, Scale)) -> Self {
89 Self::from((value.0, value.1, Mode::default()))
90 }
91}
92
93impl IntervalStepSequence for Key {
94 fn interval_steps(&self) -> Vec<Interval> {
95 let steps = self.scale.interval_steps();
96 let num_steps = steps.len();
97 steps
98 .into_iter()
99 .cycle()
100 .skip(self.mode as usize)
101 .take(num_steps)
102 .collect()
103 }
104}
105
106impl PitchClassCollection for Key {
107 fn pitch_classes(&self) -> Vec<PitchClass> {
108 self.intervals()
109 .into_iter()
110 .map(|i| self.root + i)
111 .collect()
112 }
113}
114
115impl NoteIterator for Key {
116 fn iter_notes_in_range<R: RangeBounds<Note>>(&self, note_range: R) -> NoteIter<R> {
117 NoteIter::from((self.root, self.intervals(), note_range))
118 }
119}
120
121impl Key {
122 pub fn new(root: PitchClass, scale: Scale, mode: Mode) -> Key {
137 Key {
138 root,
139 scale,
140 mode,
141 name_pref: None,
142 }
143 }
144
145 pub fn root(&self) -> PitchClass {
147 self.root
148 }
149
150 pub fn scale(&self) -> Scale {
152 self.scale
153 }
154
155 pub fn mode(&self) -> Mode {
157 self.mode
158 }
159
160 pub fn chords(&self) -> Vec<Chord> {
162 self.chords_with_shape(ChordShape::all())
163 }
164
165 pub fn chords_with_shape(&self, shape: Vec<ChordShape>) -> Vec<Chord> {
185 Degree::values()
186 .into_iter()
187 .map(|d| self.relative_pitch(d))
188 .flat_map(|root| shape.iter().map(move |chord_shape| (root, *chord_shape)))
189 .map(Chord::from)
190 .filter(|chord| self.contains(chord))
191 .collect()
192 }
193
194 pub fn contains<P: PitchClassCollection>(&self, pitches: &P) -> bool {
202 let scale_pitches = self.pitch_classes();
203 pitches
204 .pitch_classes()
205 .iter()
206 .all(|pitch| scale_pitches.contains(pitch))
207 }
208
209 pub fn relative_pitch<D: Into<Degree>>(&self, degree: D) -> PitchClass {
217 self.root + self.intervals()[degree.into() as usize]
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use crate::NoteName::C;
224 use crate::{Key, Note, NoteIterator, NoteName::*, Scale::*};
225
226 #[test]
227 fn middle_c_major_scale() {
228 assert_eq!(
229 Key::from((C, Major)).notes_in_range(Note(60)..=Note(72)),
230 [
231 (C, 4),
232 (D, 4),
233 (E, 4),
234 (F, 4),
235 (G, 4),
236 (A, 4),
237 (B, 4),
238 (C, 5)
239 ]
240 )
241 }
242
243 #[test]
244 fn middle_c_natural_minor_scale() {
245 assert_eq!(
246 Key::from((C, NaturalMinor)).notes_in_range(Note(60)..=Note(72)),
247 [
248 (C, 4),
249 (D, 4),
250 (Eb, 4),
251 (F, 4),
252 (G, 4),
253 (Ab, 4),
254 (Bb, 4),
255 (C, 5)
256 ]
257 )
258 }
259
260 #[test]
261 fn middle_c_melodic_minor_scale() {
262 assert_eq!(
263 Key::from((C, MelodicMinor)).notes_in_range(C.in_octave(4)..=C.in_octave(5)),
264 [
265 (C, 4),
266 (D, 4),
267 (Eb, 4),
268 (F, 4),
269 (G, 4),
270 (A, 4),
271 (B, 4),
272 (C, 5)
273 ]
274 )
275 }
276
277 #[test]
278 fn middle_c_harmonic_minor_scale() {
279 assert_eq!(
280 Key::from((C, HarmonicMinor)).notes_in_range(Note(60)..=Note(72)),
281 [
282 (C, 4),
283 (D, 4),
284 (Eb, 4),
285 (F, 4),
286 (G, 4),
287 (Ab, 4),
288 (B, 4),
289 (C, 5)
290 ]
291 )
292 }
293}