1use crate::note::Note;
2use crate::{Interval, Tonality};
3use std::ops::Add;
4
5#[derive(Clone, Debug)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct Chord {
9 pub notes: Vec<Note>,
10}
11
12#[derive(Copy, Clone, Debug)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16#[repr(transparent)]
17pub struct Inversion(u8);
18
19impl Default for Inversion {
20 fn default() -> Self {
21 Self::ROOT
22 }
23}
24
25impl Inversion {
26 pub fn new(inversion: u8) -> Self {
30 Self(inversion)
31 }
32
33 pub fn value(&self) -> u8 {
34 self.0
35 }
36
37 pub fn value_for(&self, size: u8) -> u8 {
38 (self.0 + size) % size
39 }
40
41 pub const ROOT: Self = Self(0);
42 pub const FIRST: Self = Self(1);
43 pub const SECOND: Self = Self(2);
44 pub const THIRD: Self = Self(3);
45}
46
47impl Chord {
48 pub const fn new(notes: Vec<Note>) -> Self {
50 Self { notes }
51 }
52
53 pub fn single(note: Note) -> Self {
55 Self { notes: vec![note] }
56 }
57
58 pub fn rotate(&self) -> Self {
59 let mut notes = self.notes.clone();
60 let first = notes.remove(0);
61 notes.push(first);
62 Self { notes }
63 }
64
65 pub fn rotate_by(&self, n: usize) -> Self {
66 let mut notes = self.notes.clone();
67 for _ in 0..n {
68 let first = notes.remove(0);
69 notes.push(first + Interval::OCTAVE);
70 }
71 Self { notes }
72 }
73
74 pub fn triad_from_root(tonality: Tonality, root: Note, inversion: Inversion) -> Self {
85 let inversion = inversion.value_for(3);
86 let mut notes = vec![root];
87 match tonality {
88 Tonality::Major => {
89 notes.push(root + Interval::MAJOR_THIRD);
90 notes.push(root + Interval::PERFECT_FIFTH);
91 }
92 Tonality::Minor => {
93 notes.push(root + Interval::MINOR_THIRD);
94 notes.push(root + Interval::PERFECT_FIFTH);
95 }
96 Tonality::Diminished => {
97 notes.push(root + Interval::MINOR_THIRD);
98 notes.push(root + Interval::TRITONE);
99 }
100 Tonality::Augmented => {
101 notes.push(root + Interval::MAJOR_THIRD);
102 notes.push(root + Interval::AUGMENTED_FIFTH);
103 }
104 }
105 Self { notes }.rotate_by(inversion as usize)
106 }
107
108 pub fn triad_from_base(tonality: Tonality, base: Note, inversion: Inversion) -> Self {
119 let inversion = inversion.value_for(3);
120 match inversion {
121 0 => Self::triad_from_root(tonality, base, Inversion::ROOT),
122 1 => {
123 let int = match tonality {
124 Tonality::Major => Interval::MAJOR_THIRD,
125 Tonality::Minor => Interval::MINOR_THIRD,
126 Tonality::Diminished => Interval::MINOR_THIRD,
127 Tonality::Augmented => Interval::MAJOR_THIRD,
128 };
129 let root = base - int;
130 Self::triad_from_root(tonality, root, Inversion::FIRST)
131 }
132 2 => {
133 let int = match tonality {
134 Tonality::Major => Interval::PERFECT_FIFTH,
135 Tonality::Minor => Interval::PERFECT_FIFTH,
136 Tonality::Diminished => Interval::TRITONE,
137 Tonality::Augmented => Interval::AUGMENTED_FIFTH,
138 };
139 let root = base - int;
140 Self::triad_from_root(tonality, root, Inversion::SECOND)
141 }
142 _ => unreachable!(),
143 }
144 }
145}
146
147impl Add<Note> for Chord {
148 type Output = Chord;
149 fn add(self, note: Note) -> Chord {
150 let mut notes = self.notes;
151 notes.push(note);
152 Chord { notes }
153 }
154}
155
156impl PartialEq for Chord {
157 fn eq(&self, other: &Self) -> bool {
158 self.notes == other.notes
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 #[test]
165 fn test_chord_gen() {
166 use crate::prelude::*;
167 let chord = Chord::single(Note::new(Alphabet::C, Accidental::Natural, 4));
168 assert_eq!(
169 chord.notes,
170 vec![Note::new(Alphabet::C, Accidental::Natural, 4)]
171 );
172 }
173
174 #[test]
175 fn test_root_chord_gen_major() {
176 use crate::prelude::*;
177 let chord = Chord::triad_from_root(
178 Tonality::Major,
179 Note::new(Alphabet::C, Accidental::Natural, 4),
180 Inversion::default(),
181 );
182 assert_eq!(
183 chord,
184 Note::new(Alphabet::C, Accidental::Natural, 4)
185 + Note::new(Alphabet::E, Accidental::Natural, 4)
186 + Note::new(Alphabet::G, Accidental::Natural, 4)
187 );
188 }
189
190 #[test]
191 fn test_root_chord_gen_minor() {
192 use crate::prelude::*;
193 let chord = Chord::triad_from_root(
194 Tonality::Minor,
195 Note::new(Alphabet::C, Accidental::Natural, 4),
196 Inversion::ROOT,
197 );
198 assert_eq!(
199 chord,
200 Note::new(Alphabet::C, Accidental::Natural, 4)
201 + Note::new(Alphabet::E, Accidental::Flat, 4)
202 + Note::new(Alphabet::G, Accidental::Natural, 4)
203 );
204 }
205
206 #[test]
207 fn test_root_chord_gen_inversion() {
208 use crate::prelude::*;
209 let chord = Chord::triad_from_root(
210 Tonality::Major,
211 Note::new(Alphabet::C, Accidental::Natural, 4),
212 Inversion::FIRST,
213 );
214 assert_eq!(
215 chord,
216 Note::new(Alphabet::E, Accidental::Natural, 4)
217 + Note::new(Alphabet::G, Accidental::Natural, 4)
218 + Note::new(Alphabet::C, Accidental::Natural, 5)
219 );
220 let chord = Chord::triad_from_root(
221 Tonality::Major,
222 Note::new(Alphabet::C, Accidental::Natural, 4),
223 Inversion::new(2),
224 );
225 assert_eq!(
226 chord,
227 Note::new(Alphabet::G, Accidental::Natural, 4)
228 + Note::new(Alphabet::C, Accidental::Natural, 5)
229 + Note::new(Alphabet::E, Accidental::Natural, 5)
230 );
231 }
232}