note_pen/
chord.rs

1use crate::note::Note;
2use crate::{Interval, Tonality};
3use std::ops::Add;
4
5/// A chord is a collection of notes that are played simultaneously for the same duration.
6#[derive(Clone, Debug)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct Chord {
9    pub notes: Vec<Note>,
10}
11
12/// An inversion is a way to rearrange the notes of a chord so that a different note is the lowest note.
13/// Root inversion is stored as 0.
14#[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    /// Creates a new inversion.
27    /// # Arguments
28    /// * `inversion` - The inversion value, 0 means root/no inversion.
29    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    /// Creates a new chord with the given notes.
49    pub const fn new(notes: Vec<Note>) -> Self {
50        Self { notes }
51    }
52
53    /// Creates a new chord with the single note provided.
54    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    /// Creates a trait with a given tonality, root, and inversion.
75    ///
76    /// # Examples
77    /// ```rust
78    /// use note_pen::prelude::*;
79    /// let chord = Chord::triad_from_root(Tonality::Major, Note::new(Alphabet::C, Accidental::Natural, 4), Inversion::ROOT);
80    /// assert_eq!(chord, Note::new(Alphabet::C, Accidental::Natural, 4) +
81    ///   Note::new(Alphabet::E, Accidental::Natural, 4) +
82    ///  Note::new(Alphabet::G, Accidental::Natural, 4)
83    /// );
84    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    /// Creates a triad with a given tonality, base, and inversion.
109    /// # Examples
110    /// ```rust
111    /// use note_pen::prelude::*;
112    /// let chord = Chord::triad_from_base(Tonality::Major, Note::new(Alphabet::E, Accidental::Natural, 4), Inversion::ROOT);
113    /// assert_eq!(chord, Note::new(Alphabet::E, Accidental::Natural, 4) +
114    ///  Note::new(Alphabet::G, Accidental::Sharp, 4) +
115    /// Note::new(Alphabet::B, Accidental::Natural, 5)
116    ///);
117    /// ```
118    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}