diff --git a/README.md b/README.md index cf4b208..560d9ef 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,19 @@ A Rust crate for musical basic elements. ```rust -use musika_rs::{ - chords::{self}, - C, C_SHARP, -}; +use musika_rs::C; -let chord = chords::Major::from(C); -println!("Major C: {chord:X}"); +let chord = C.maj(); +println!("{chord:X}"); -let chord = chords::Major7::from(C); -println!("Major7 C: {chord:X}"); +let chord = C.maj7(); +println!("{chord:X}"); -let chord = chords::Major7::from(C_SHARP); -println!("Major7 C#: {chord:X}"); +let chord = C.min(); +println!("{chord:X}"); -let chord = chords::Dominant7::from(C); -println!("Dom7 C: {chord:X}"); +let chord = C.dom7(); +println!("{chord:X}"); ``` You can find more examples in the [examples][examples_folder] folder. @@ -44,20 +41,6 @@ The create allows you to build the following chords: You can find all the chords in the [chords][chords_folder] folder. -```rust -use musika_rs::* - -let cmaj = C.major(); -println!("{:X}", cmaj); - -let amin = A.minor(); -println!("{:X}", amin); - -let cmaj7 = C.major7(); -println!("{:X}", cmaj7); - -``` - ## Piano Exercises You can find all piano exercises implemented in the exercises examples. You can see the practices by running: ```bsh @@ -65,12 +48,14 @@ cargo run --example exercise1 cargo run --example exercise2 cargo run --example exercise3 cargo run --example exercise4 +cargo run --example exercise5 ``` - [Exercise 1][exercise1_file] | C - Am | F - G | - [Exercise 2][exercise2_file] | C - G | Am - F | C - G | F - Em - Dm - C | - [Exercise 3][exercise3_file] | Cx4 | Gx4 | Gx4 | Cx4 | Fx4 | Cx4 | Gx4 | Cx4 | - [Exercise 4][exercise4_file] | C | F | Bdim | Em | Am | Dm | G | C | +- [Exercise 4][exercise4_file] | C | F | Bdim | Em | Am | Dm | G | C | ## Resources - [Piano Chords][piano_chords_url] @@ -102,4 +87,5 @@ cargo run --example exercise4 [exercise1_file]: ./examples/exercise1.rs [exercise2_file]: ./examples/exercise2.rs [exercise3_file]: ./examples/exercise3.rs -[exercise4_file]: ./examples/exercise4.rs \ No newline at end of file +[exercise4_file]: ./examples/exercise4.rs +[exercise5_file]: ./examples/exercise5.rs \ No newline at end of file diff --git a/examples/chords.rs b/examples/chords.rs index 3b1f70a..6aea19a 100644 --- a/examples/chords.rs +++ b/examples/chords.rs @@ -1,18 +1,15 @@ -use musika_rs::{ - chords::{self}, - C, C_SHARP, -}; +use musika_rs::C; fn main() { - let chord = chords::Major::from(C); - println!("Major C: {chord:X}"); + let chord = C.maj(); + println!("{chord:X}"); - let chord = chords::Major7::from(C); - println!("Major7 C: {chord:X}"); + let chord = C.maj7(); + println!("{chord:X}"); - let chord = chords::Major7::from(C_SHARP); - println!("Major7 C#: {chord:X}"); + let chord = C.min(); + println!("{chord:X}"); - let chord = chords::Dominant7::from(C); - println!("Dom7 C: {chord:X}"); + let chord = C.dom7(); + println!("{chord:X}"); } diff --git a/examples/exercise1.rs b/examples/exercise1.rs index 2a4ae45..52feefd 100644 --- a/examples/exercise1.rs +++ b/examples/exercise1.rs @@ -3,8 +3,8 @@ use musika_rs::*; fn main() { println!(); println!("Exercise 1: | C - Am | F - G |"); - println!("{:X}", C.major()); // C [C, E, G] - println!("{:X}", A.minor()); // Am [A, C, E] - println!("{:X}", F.major()); // F [F, A, C] - println!("{:X}", G.major()); // G [G, B, D] + println!("{:X}", C.maj()); // C [C, E, G] + println!("{:X}", A.min()); // Am [A, C, E] + println!("{:X}", F.maj()); // F [F, A, C] + println!("{:X}", G.maj()); // G [G, B, D] } diff --git a/examples/exercise2.rs b/examples/exercise2.rs index f044125..336402d 100644 --- a/examples/exercise2.rs +++ b/examples/exercise2.rs @@ -3,14 +3,14 @@ use musika_rs::*; fn main() { println!(); println!("Exercise 2: | C - G | Am - F | C - G | F - Em - Dm - C |"); - println!("{:X}", C.major()); // C [C, E, G] - println!("{:X}", G.major()); // G [G, B, D] - println!("{:X}", A.minor()); // Am [A, C, E] - println!("{:X}", F.major()); // F [F, A, C] - println!("{:X}", C.major()); // C [C, E, G] - println!("{:X}", G.major()); // G [G, B, D] - println!("{:X}", F.major()); // F [F, A, C] - println!("{:X}", E.minor()); // Em [E, G, B] - println!("{:X}", D.minor()); // Dm [D, F, A] - println!("{:X}", C.major()); // C [C, E, G] + println!("{:X}", C.maj()); // C [C, E, G] + println!("{:X}", G.maj()); // G [G, B, D] + println!("{:X}", A.min()); // Am [A, C, E] + println!("{:X}", F.maj()); // F [F, A, C] + println!("{:X}", C.maj()); // C [C, E, G] + println!("{:X}", G.maj()); // G [G, B, D] + println!("{:X}", F.maj()); // F [F, A, C] + println!("{:X}", E.min()); // Em [E, G, B] + println!("{:X}", D.min()); // Dm [D, F, A] + println!("{:X}", C.maj()); // C [C, E, G] } diff --git a/examples/exercise3.rs b/examples/exercise3.rs index d3c322f..2b28d46 100644 --- a/examples/exercise3.rs +++ b/examples/exercise3.rs @@ -4,9 +4,9 @@ fn main() { println!(); println!("Exercise 3: | Cx4 | Gx4 | Gx4 | Cx4 | Fx4 | Cx4 | Gx4 | Cx4 |"); - let cmaj = C.major(); - let gmaj = G.major(); - let fmaj = F.major(); + let cmaj = C.maj(); + let gmaj = G.maj(); + let fmaj = F.maj(); // bar 1 for _ in 0..4 { diff --git a/examples/exercise4.rs b/examples/exercise4.rs index dcadb9c..381c466 100644 --- a/examples/exercise4.rs +++ b/examples/exercise4.rs @@ -4,12 +4,12 @@ fn main() { println!(); println!("Exercise 4: | C | F | Bdim | Em | Am | Dm | G | C |"); - println!("{:X}", C.major()); - println!("{:X}", F.major()); - println!("{:X}", B.diminished()); - println!("{:X}", E.minor()); - println!("{:X}", A.minor()); - println!("{:X}", D.minor()); - println!("{:X}", G.major()); - println!("{:X}", C.major()); + println!("{:X}", C.maj()); + println!("{:X}", F.maj()); + println!("{:X}", B.dim()); + println!("{:X}", E.min()); + println!("{:X}", A.min()); + println!("{:X}", D.min()); + println!("{:X}", G.maj()); + println!("{:X}", C.maj()); } diff --git a/examples/exercise5.rs b/examples/exercise5.rs index 4bfbeb6..8e2555f 100644 --- a/examples/exercise5.rs +++ b/examples/exercise5.rs @@ -5,12 +5,12 @@ fn main() { println!(); println!("Exercise 5: | Dm9x2 | G13x2 | Cmaj9x2 | Fmaj13X2 | A7 | Dm | G | C |"); - println!("{:X}", D.minor9()); // D - F - A - C - E - println!("{:X}", D.minor9()); // D - F - A - C - E - println!("{:X}", G.dominant9()); // G - F - A - B - E - println!("{:X}", C.major9()); // C - E - G - B - D - // TODO: Find the Fmaj13 chord (4:48) - println!("{:X}", F.major9()); // F - E - G - A - D - // TODO: Find the A7 b5 b13 - println!("{:X}", A.dominant9()); // G - F - A - B - E + println!("{:X}", D.min9()); // D - F - A - C - E + println!("{:X}", D.min9()); // D - F - A - C - E + println!("{:X}", G.dom9()); // G - F - A - B - E + println!("{:X}", C.maj9()); // C - E - G - B - D + // TODO: Find the Fmaj13 chord (4:48) + println!("{:X}", F.maj9()); // F - E - G - A - D + // TODO: Find the A7 b5 b13 + println!("{:X}", A.dom9()); // G - F - A - B - E } diff --git a/src/bar.rs b/src/bar.rs index b87decc..a9f50cd 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -1,19 +1,12 @@ +use crate::chords::Chords; use std::fmt::Display; -use crate::{chords::Chord, Note}; - -pub enum BarElement -where - C: Chord as IntoIterator>::IntoIter>, -{ +pub enum BarElement { Silence(u8), - Chord(C, u8), + Chord(Chords, u8), } -impl Display for BarElement -where - C: Display + Chord as IntoIterator>::IntoIter>, -{ +impl Display for BarElement { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { BarElement::Silence(_) => write!(f, "_"), @@ -22,19 +15,14 @@ where } } -pub struct Bar(Vec>) -where - C: Chord as IntoIterator>::IntoIter>; +pub struct Bar(Vec); -impl Bar -where - C: Chord as IntoIterator>::IntoIter>, -{ +impl Bar { pub fn new() -> Self { Self(vec![]) } - pub fn with_chord(self, chord: C, fraction: u8) -> Self { + pub fn with_chord(self, chord: Chords, fraction: u8) -> Self { let mut items = self.0; items.push(BarElement::Chord(chord, fraction)); Self(items) @@ -47,19 +35,13 @@ where } } -impl Default for Bar -where - C: Chord as IntoIterator>::IntoIter>, -{ +impl Default for Bar { fn default() -> Self { Self::new() } } -impl Display for Bar -where - C: Display + Chord as IntoIterator>::IntoIter>, -{ +impl Display for Bar { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let s = self .0 @@ -71,10 +53,7 @@ where } } -pub fn show(bars: impl Iterator>) -> String -where - C: Display + Chord as IntoIterator>::IntoIter>, -{ +pub fn show(bars: impl Iterator) -> String { let s = bars .map(|b| b.to_string()) .collect::>() @@ -90,14 +69,14 @@ mod tests { #[test] fn display() { - let bar = Bar::new().with_chord(C.major(), 2).with_chord(G.major(), 2); + let bar = Bar::new().with_chord(C.maj(), 2).with_chord(G.maj(), 2); assert_eq!(bar.to_string(), "C - G"); } #[test] fn displa_bars() { - let bar1 = Bar::new().with_chord(C.major(), 2).with_chord(G.major(), 2); - let bar2 = Bar::new().with_chord(C.major(), 2).with_chord(G.major(), 2); + let bar1 = Bar::new().with_chord(C.maj(), 2).with_chord(G.maj(), 2); + let bar2 = Bar::new().with_chord(C.maj(), 2).with_chord(G.maj(), 2); let bars = [bar1, bar2]; let s = show(bars.into_iter()); diff --git a/src/chords/diminished.rs b/src/chords/diminished.rs index 84acec6..4ab5f3c 100644 --- a/src/chords/diminished.rs +++ b/src/chords/diminished.rs @@ -1,106 +1,36 @@ -use super::{Chord, InnerChord}; +use super::Chords; use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; -/// Implements the **diminished** chord. -/// [Cdim chord](https://www.pianochord.org/c-dim.html) (C - Eb - Gb) -pub struct Diminished(InnerChord); - -impl Diminished { - pub fn new(root: Note) -> Self { - let steps = [3, 3]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } -} - -impl From for Diminished { - fn from(root: Note) -> Self { - Diminished::new(root) - } -} - -impl Display for Diminished { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}dim7", self.root()) - } +pub fn dim(root: Note) -> Chords { + let steps = [3, 3]; + Chords::diminished_with_steps("dim", root, steps.into_iter()) } -impl Debug for Diminished { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: dim7, {:?}, {:?}", self.root(), self.0) - } -} - -impl UpperHex for Diminished { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}dim7 {:X}", self.root(), self.0) - } +pub fn dim7(root: Note) -> Chords { + let steps = [3, 3, 3]; + Chords::diminished_with_steps("dim7", root, steps.into_iter()) } -impl LowerHex for Diminished { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}dim7 {:x}", self.root(), self.0) - } -} - -impl IntoIterator for Diminished { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Diminished { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) - } - - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) - } +pub fn diminished_chords(root: Note) -> impl Iterator { + [dim(root), dim7(root)].into_iter() } #[cfg(test)] mod tests { use super::*; - use crate::{C, D_SHARP, F_SHARP}; + use crate::C; #[test] - fn new() { - let chord = Diminished::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 3); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes, vec![C, D_SHARP, F_SHARP]); + fn test_dim() { + let chord = dim(C); + assert_eq!(format!("{chord:X}"), "Cdim [C, D#, F#]"); + assert_eq!(format!("{chord:x}"), "Cdim [C, Eb, Gb]"); } #[test] - fn show() { - let chord = Diminished::new(C); - assert_eq!(format!("{chord}"), "Cdim7"); - assert_eq!( - format!("{chord:?}"), - "Chord: dim7, C4:C:0, [C4:C:0, C4:D#:3, C4:F#:6]" - ); - assert_eq!(format!("{chord:X}"), "Cdim7 [C, D#, F#]"); - assert_eq!(format!("{chord:x}"), "Cdim7 [C, Eb, Gb]"); + fn test_dim7() { + let chord = dim7(C); + assert_eq!(format!("{chord:X}"), "Cdim7 [C, D#, F#, A]"); + assert_eq!(format!("{chord:x}"), "Cdim7 [C, Eb, Gb, A]"); } } diff --git a/src/chords/diminished7.rs b/src/chords/diminished7.rs deleted file mode 100644 index 1eec9be..0000000 --- a/src/chords/diminished7.rs +++ /dev/null @@ -1,106 +0,0 @@ -use super::{Chord, InnerChord}; -use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; - -/// Implements the **diminished 7** chord. -/// [Cdim7 chord](https://www.pianochord.org/c-dim7.html) (C - Eb - Gb - A) -pub struct Diminished7(InnerChord); - -impl Diminished7 { - pub fn new(root: Note) -> Self { - let steps = [3, 3, 3]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } -} - -impl From for Diminished7 { - fn from(root: Note) -> Self { - Diminished7::new(root) - } -} - -impl Display for Diminished7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}dim7", self.root()) - } -} - -impl Debug for Diminished7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: dim7, {:?}, {:?}", self.root(), self.0) - } -} - -impl UpperHex for Diminished7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}dim7 {:X}", self.root(), self.0) - } -} - -impl LowerHex for Diminished7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}dim7 {:x}", self.root(), self.0) - } -} - -impl IntoIterator for Diminished7 { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Diminished7 { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) - } - - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{A, C, D_SHARP, F_SHARP}; - - #[test] - fn new() { - let chord = Diminished7::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 4); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes, vec![C, D_SHARP, F_SHARP, A]); - } - - #[test] - fn show() { - let chord = Diminished7::new(C); - assert_eq!(format!("{chord}"), "Cdim7"); - assert_eq!( - format!("{chord:?}"), - "Chord: dim7, C4:C:0, [C4:C:0, C4:D#:3, C4:F#:6, C5:A:9]" - ); - assert_eq!(format!("{chord:X}"), "Cdim7 [C, D#, F#, A]"); - assert_eq!(format!("{chord:x}"), "Cdim7 [C, Eb, Gb, A]"); - } -} diff --git a/src/xchords/dominant.rs b/src/chords/dominant.rs similarity index 63% rename from src/xchords/dominant.rs rename to src/chords/dominant.rs index 667e5fc..f20fe86 100644 --- a/src/xchords/dominant.rs +++ b/src/chords/dominant.rs @@ -1,34 +1,46 @@ -use super::XChords; +use super::Chords; use crate::Note; -pub fn dom7(root: Note) -> XChords { +pub fn dom7(root: Note) -> Chords { let steps = [4, 3, 3]; - XChords::dominant_with_steps("7", root, steps.into_iter()) + Chords::dominant_with_steps("7", root, steps.into_iter()) } -pub fn dom7b5(root: Note) -> XChords { +pub fn dom7b5(root: Note) -> Chords { let steps = [4, 2, 4]; - XChords::dominant_with_steps("7b5", root, steps.into_iter()) + Chords::dominant_with_steps("7b5", root, steps.into_iter()) } -pub fn dom7s5(root: Note) -> XChords { +pub fn dom7s5(root: Note) -> Chords { let steps = [4, 4, 2]; - XChords::dominant_with_steps("7#5", root, steps.into_iter()) + Chords::dominant_with_steps("7#5", root, steps.into_iter()) } -pub fn dom9(root: Note) -> XChords { +pub fn dom9(root: Note) -> Chords { let steps = [4, 3, 3, 4]; - XChords::dominant_with_steps("9", root, steps.into_iter()) + Chords::dominant_with_steps("9", root, steps.into_iter()) } -pub fn dom11(root: Note) -> XChords { +pub fn dom11(root: Note) -> Chords { let steps = [4, 3, 3, 4, 3]; - XChords::dominant_with_steps("11", root, steps.into_iter()) + Chords::dominant_with_steps("11", root, steps.into_iter()) } -pub fn dom13(root: Note) -> XChords { +pub fn dom13(root: Note) -> Chords { let steps = [4, 3, 3, 4, 3, 4]; - XChords::dominant_with_steps("13", root, steps.into_iter()) + Chords::dominant_with_steps("13", root, steps.into_iter()) +} + +pub fn dominant_chords(root: Note) -> impl Iterator { + [ + dom7(root), + dom7b5(root), + dom7s5(root), + dom9(root), + dom11(root), + dom13(root), + ] + .into_iter() } #[cfg(test)] diff --git a/src/chords/dominant7.rs b/src/chords/dominant7.rs deleted file mode 100644 index d341a36..0000000 --- a/src/chords/dominant7.rs +++ /dev/null @@ -1,106 +0,0 @@ -use super::{Chord, InnerChord}; -use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; - -/// Implements the **dominant 7** chord. -/// [C7 chord](https://www.pianochord.org/c7.html) (C - E - G - Bb) -pub struct Dominant7(InnerChord); - -impl Dominant7 { - pub fn new(root: Note) -> Self { - let steps = [4, 3, 3]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } -} - -impl From for Dominant7 { - fn from(root: Note) -> Self { - Dominant7::new(root) - } -} - -impl Display for Dominant7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}7", self.root()) - } -} - -impl Debug for Dominant7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: 7, {:?}, {:?}", self.root(), self.0) - } -} - -impl UpperHex for Dominant7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}7 {:X}", self.root(), self.0) - } -} - -impl LowerHex for Dominant7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}7 {:x}", self.root(), self.0) - } -} - -impl IntoIterator for Dominant7 { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Dominant7 { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) - } - - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{A_SHARP, C, E, G}; - - #[test] - fn new() { - let chord = Dominant7::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 4); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes, vec![C, E, G, A_SHARP]); - } - - #[test] - fn show() { - let chord = Dominant7::new(C); - assert_eq!(format!("{chord}"), "C7"); - assert_eq!( - format!("{chord:?}"), - "Chord: 7, C4:C:0, [C4:C:0, C4:E:4, C4:G:7, C5:A#:10]" - ); - assert_eq!(format!("{chord:X}"), "C7 [C, E, G, A#]"); - assert_eq!(format!("{chord:x}"), "C7 [C, E, G, Bb]"); - } -} diff --git a/src/chords/dominant9.rs b/src/chords/dominant9.rs deleted file mode 100644 index b15f0d0..0000000 --- a/src/chords/dominant9.rs +++ /dev/null @@ -1,106 +0,0 @@ -use super::{Chord, InnerChord}; -use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; - -/// Implements the **dominant 9** chord. -/// [C9 chord](https://www.pianochord.org/c-extended.html) (C - E - G - Bb - D) -pub struct Dominant9(InnerChord); - -impl Dominant9 { - pub fn new(root: Note) -> Self { - let steps = [4, 3, 3, 4]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } -} - -impl From for Dominant9 { - fn from(root: Note) -> Self { - Dominant9::new(root) - } -} - -impl Display for Dominant9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}7", self.root()) - } -} - -impl Debug for Dominant9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: 7, {:?}, {:?}", self.root(), self.0) - } -} - -impl UpperHex for Dominant9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}7 {:X}", self.root(), self.0) - } -} - -impl LowerHex for Dominant9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}7 {:x}", self.root(), self.0) - } -} - -impl IntoIterator for Dominant9 { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Dominant9 { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) - } - - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{A_SHARP, C, D, E, G}; - - #[test] - fn new() { - let chord = Dominant9::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 5); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes, vec![C, E, G, A_SHARP, D]); - } - - #[test] - fn show() { - let chord = Dominant9::new(C); - assert_eq!(format!("{chord}"), "C7"); - assert_eq!( - format!("{chord:?}"), - "Chord: 7, C4:C:0, [C4:C:0, C4:E:4, C4:G:7, C5:A#:10, C5:D:14]" - ); - assert_eq!(format!("{chord:X}"), "C7 [C, E, G, A#, D]"); - assert_eq!(format!("{chord:x}"), "C7 [C, E, G, Bb, D]"); - } -} diff --git a/src/chords/major.rs b/src/chords/major.rs index 7784804..a2eedc7 100644 --- a/src/chords/major.rs +++ b/src/chords/major.rs @@ -1,107 +1,67 @@ -use super::{Chord, InnerChord}; +use super::Chords; use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; -/// Implements the **major** chord. -/// [C chord](https://www.pianochord.org/c-major.html) (C - E - G) -pub struct Major(InnerChord); - -impl Major { - pub fn new(root: Note) -> Self { - let steps = [4, 3]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } +pub fn maj(root: Note) -> Chords { + let steps = [4, 3]; + Chords::major_with_steps("", root, steps.into_iter()) } -impl From for Major { - fn from(root: Note) -> Self { - Major::new(root) - } +pub fn maj7(root: Note) -> Chords { + let steps = [4, 3, 4]; + Chords::major_with_steps("maj7", root, steps.into_iter()) } -impl Display for Major { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.root()) - } +pub fn maj9(root: Note) -> Chords { + let steps = [4, 3, 4, 3]; + Chords::major_with_steps("maj9", root, steps.into_iter()) } -impl Debug for Major { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: Maj, {:?}, {:?}", self.root(), self.0) - } +pub fn maj11(root: Note) -> Chords { + let steps = [4, 3, 4, 3, 3]; + Chords::major_with_steps("maj11", root, steps.into_iter()) } -impl UpperHex for Major { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X} {:X}", self.root(), self.0) - } +pub fn maj13(root: Note) -> Chords { + let steps = [4, 3, 4, 3, 3, 4]; + Chords::major_with_steps("maj13", root, steps.into_iter()) } -impl LowerHex for Major { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x} {:x}", self.root(), self.0) - } +pub fn major_chords(root: Note) -> impl Iterator { + [maj(root), maj7(root), maj9(root), maj11(root), maj13(root)].into_iter() } -impl IntoIterator for Major { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Major { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } +#[cfg(test)] +mod tests { + use super::*; + use crate::C; - fn is_empty(&self) -> bool { - self.0.is_empty() + #[test] + fn test_maj() { + let chord = maj(C); + assert_eq!(format!("{chord:X}"), "C [C, E, G]") } - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) + #[test] + fn test_maj7() { + let chord = maj7(C); + assert_eq!(format!("{chord:X}"), "Cmaj7 [C, E, G, B]") } - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) + #[test] + fn test_maj9() { + let chord = maj9(C); + assert_eq!(format!("{chord:X}"), "Cmaj9 [C, E, G, B, D]") } -} - -#[cfg(test)] -mod tests { - use crate::{C, E, G}; - - use super::*; #[test] - fn new() { - let chord = Major::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 3); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().collect::>(); - assert_eq!(notes, vec![C, E, G]); + fn test_maj11() { + let chord = maj11(C); + assert_eq!(format!("{chord:X}"), "Cmaj11 [C, E, G, B, D, F]") } #[test] - fn show() { - let chord = Major::new(C); - assert_eq!(format!("{chord}"), "C"); - assert_eq!( - format!("{chord:?}"), - "Chord: Maj, C4:C:0, [C4:C:0, C4:E:4, C4:G:7]" - ); - assert_eq!(format!("{chord:X}"), "C [C, E, G]"); - assert_eq!(format!("{chord:x}"), "C [C, E, G]"); + fn test_maj13() { + let chord = maj13(C); + assert_eq!(format!("{chord:X}"), "Cmaj13 [C, E, G, B, D, F, A]") } } diff --git a/src/chords/major7.rs b/src/chords/major7.rs deleted file mode 100644 index 071d24d..0000000 --- a/src/chords/major7.rs +++ /dev/null @@ -1,107 +0,0 @@ -use super::{Chord, InnerChord}; -use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; - -/// Implements the **major 7** chord. -/// [C7 chord](https://www.pianochord.org/cmaj7.html) (C - E - G - B) -pub struct Major7(InnerChord); - -impl Major7 { - pub fn new(root: Note) -> Self { - let steps = [4, 3, 4]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } -} - -impl From for Major7 { - fn from(root: Note) -> Self { - Major7::new(root) - } -} - -impl Display for Major7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}maj7", self.root()) - } -} - -impl Debug for Major7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: maj7, {:?}, {:?}", self.root(), self.0) - } -} - -impl UpperHex for Major7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}maj7 {:X}", self.root(), self.0) - } -} - -impl LowerHex for Major7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}maj7 {:x}", self.root(), self.0) - } -} - -impl IntoIterator for Major7 { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Major7 { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) - } - - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) - } -} - -#[cfg(test)] -mod tests { - use crate::{B, C, E, G}; - - use super::*; - - #[test] - fn new() { - let chord = Major7::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 4); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes, vec![C, E, G, B]); - } - - #[test] - fn show() { - let chord = Major7::new(C); - assert_eq!(format!("{chord}"), "Cmaj7"); - assert_eq!( - format!("{chord:?}"), - "Chord: maj7, C4:C:0, [C4:C:0, C4:E:4, C4:G:7, C5:B:11]" - ); - assert_eq!(format!("{chord:X}"), "Cmaj7 [C, E, G, B]"); - assert_eq!(format!("{chord:x}"), "Cmaj7 [C, E, G, B]"); - } -} diff --git a/src/chords/major9.rs b/src/chords/major9.rs deleted file mode 100644 index 70e74bd..0000000 --- a/src/chords/major9.rs +++ /dev/null @@ -1,107 +0,0 @@ -use super::{Chord, InnerChord}; -use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; - -/// Implements the **major 7** chord. -/// [Cmaj9 chord](https://www.pianochord.org/cmaj9.html) (C - E - G - B) -pub struct Major9(InnerChord); - -impl Major9 { - pub fn new(root: Note) -> Self { - let steps = [4, 3, 4, 3]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } -} - -impl From for Major9 { - fn from(root: Note) -> Self { - Major9::new(root) - } -} - -impl Display for Major9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}maj9", self.root()) - } -} - -impl Debug for Major9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: maj9, {:?}, {:?}", self.root(), self.0) - } -} - -impl UpperHex for Major9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}maj9 {:X}", self.root(), self.0) - } -} - -impl LowerHex for Major9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}maj9 {:x}", self.root(), self.0) - } -} - -impl IntoIterator for Major9 { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Major9 { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) - } - - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) - } -} - -#[cfg(test)] -mod tests { - use crate::{B, C, D, E, G}; - - use super::*; - - #[test] - fn new() { - let chord = Major9::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 5); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes, vec![C, E, G, B, D]); - } - - #[test] - fn show() { - let chord = Major9::new(C); - assert_eq!(format!("{chord}"), "Cmaj9"); - assert_eq!( - format!("{chord:?}"), - "Chord: maj9, C4:C:0, [C4:C:0, C4:E:4, C4:G:7, C5:B:11, C5:D:14]" - ); - assert_eq!(format!("{chord:X}"), "Cmaj9 [C, E, G, B, D]"); - assert_eq!(format!("{chord:x}"), "Cmaj9 [C, E, G, B, D]"); - } -} diff --git a/src/chords/minor.rs b/src/chords/minor.rs index 3ce750e..2c6e484 100644 --- a/src/chords/minor.rs +++ b/src/chords/minor.rs @@ -1,106 +1,92 @@ -use super::{Chord, InnerChord}; +use super::Chords; use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; -/// Implements the **minor** chord. -/// [Cm chord](https://www.pianochord.org/cm.html) (C - Eb - G) -pub struct Minor(InnerChord); - -impl Minor { - pub fn new(root: Note) -> Self { - let steps = [3, 4]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } +pub fn min(root: Note) -> Chords { + let steps = [3, 4]; + Chords::minor_with_steps("m", root, steps.into_iter()) } -impl From for Minor { - fn from(root: Note) -> Self { - Minor::new(root) - } +pub fn min7(root: Note) -> Chords { + let steps = [3, 4, 3]; + Chords::minor_with_steps("m7", root, steps.into_iter()) } -impl Display for Minor { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}m", self.root()) - } +pub fn min7b5(root: Note) -> Chords { + let steps = [3, 3, 4]; + Chords::minor_with_steps("m7(b5)", root, steps.into_iter()) } -impl Debug for Minor { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: m, {:?}, {:?}", self.root(), self.0) - } +pub fn min9(root: Note) -> Chords { + let steps = [3, 4, 3, 4]; + Chords::minor_with_steps("m9", root, steps.into_iter()) } -impl UpperHex for Minor { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}m {:X}", self.root(), self.0) - } +pub fn min11(root: Note) -> Chords { + let steps = [3, 4, 3, 4, 3]; + Chords::minor_with_steps("m11", root, steps.into_iter()) } -impl LowerHex for Minor { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}m {:x}", self.root(), self.0) - } +pub fn min13(root: Note) -> Chords { + let steps = [3, 4, 3, 4, 3, 4]; + Chords::minor_with_steps("m13", root, steps.into_iter()) } -impl IntoIterator for Minor { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } +pub fn minor_chords(root: Note) -> impl Iterator { + [ + min(root), + min7(root), + min7b5(root), + min9(root), + min11(root), + min13(root), + ] + .into_iter() } -impl Chord for Minor { - fn root(&self) -> &Note { - self.0.root() - } +#[cfg(test)] +mod tests { + use super::*; + use crate::C; - fn len(&self) -> usize { - self.0.len() + #[test] + fn test_min() { + let chord = min(C); + assert_eq!(format!("{chord:X}"), "Cm [C, D#, G]"); + assert_eq!(format!("{chord:x}"), "Cm [C, Eb, G]"); } - fn is_empty(&self) -> bool { - self.0.is_empty() + #[test] + fn test_min7() { + let chord = min7(C); + assert_eq!(format!("{chord:X}"), "Cm7 [C, D#, G, A#]"); + assert_eq!(format!("{chord:x}"), "Cm7 [C, Eb, G, Bb]") } - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) + #[test] + fn test_min7b5() { + let chord = min7b5(C); + assert_eq!(format!("{chord:X}"), "Cm7(b5) [C, D#, F#, A#]"); + assert_eq!(format!("{chord:x}"), "Cm7(b5) [C, Eb, Gb, Bb]") } - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) + #[test] + fn test_min9() { + let chord = min9(C); + assert_eq!(format!("{chord:X}"), "Cm9 [C, D#, G, A#, D]"); + assert_eq!(format!("{chord:x}"), "Cm9 [C, Eb, G, Bb, D]") } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{C, D_SHARP, G}; #[test] - fn new() { - let chord = Minor::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 3); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().collect::>(); - assert_eq!(notes, vec![C, D_SHARP, G]); + fn test_min11() { + let chord = min11(C); + assert_eq!(format!("{chord:X}"), "Cm11 [C, D#, G, A#, D, F]"); + assert_eq!(format!("{chord:x}"), "Cm11 [C, Eb, G, Bb, D, F]") } #[test] - fn show() { - let chord = Minor::new(C); - assert_eq!(format!("{chord}"), "Cm"); - assert_eq!( - format!("{chord:?}"), - "Chord: m, C4:C:0, [C4:C:0, C4:D#:3, C4:G:7]" - ); - assert_eq!(format!("{chord:X}"), "Cm [C, D#, G]"); - assert_eq!(format!("{chord:x}"), "Cm [C, Eb, G]"); + fn test_min13() { + let chord = min13(C); + assert_eq!(format!("{chord:X}"), "Cm13 [C, D#, G, A#, D, F, A]"); + assert_eq!(format!("{chord:x}"), "Cm13 [C, Eb, G, Bb, D, F, A]") } } diff --git a/src/chords/minor7.rs b/src/chords/minor7.rs deleted file mode 100644 index a7c2e56..0000000 --- a/src/chords/minor7.rs +++ /dev/null @@ -1,106 +0,0 @@ -use super::{Chord, InnerChord}; -use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; - -/// Implements the **minor 7** chord. -/// [Cm7 chord](https://www.pianochord.org/cm7.html) (C - Eb - G - Bb) -pub struct Minor7(InnerChord); - -impl Minor7 { - pub fn new(root: Note) -> Self { - let steps = [3, 4, 3]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } -} - -impl From for Minor7 { - fn from(root: Note) -> Self { - Minor7::new(root) - } -} - -impl Display for Minor7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}m7", self.root()) - } -} - -impl Debug for Minor7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: m7, {:?}, {:?}", self.root(), self.0) - } -} - -impl UpperHex for Minor7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}m7 {:X}", self.root(), self.0) - } -} - -impl LowerHex for Minor7 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}m7 {:x}", self.root(), self.0) - } -} - -impl IntoIterator for Minor7 { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Minor7 { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) - } - - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{A_SHARP, C, D_SHARP, G}; - - #[test] - fn new() { - let chord = Minor7::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 4); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes, vec![C, D_SHARP, G, A_SHARP]); - } - - #[test] - fn show() { - let chord = Minor7::new(C); - assert_eq!(format!("{chord}"), "Cm7"); - assert_eq!( - format!("{chord:?}"), - "Chord: m7, C4:C:0, [C4:C:0, C4:D#:3, C4:G:7, C5:A#:10]" - ); - assert_eq!(format!("{chord:X}"), "Cm7 [C, D#, G, A#]"); - assert_eq!(format!("{chord:x}"), "Cm7 [C, Eb, G, Bb]"); - } -} diff --git a/src/chords/minor7b5.rs b/src/chords/minor7b5.rs deleted file mode 100644 index a63aa69..0000000 --- a/src/chords/minor7b5.rs +++ /dev/null @@ -1,106 +0,0 @@ -use super::{Chord, InnerChord}; -use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; - -/// Implements the **minor 7b5** chord. -/// [Cm7b5 chord](https://www.pianochord.org/cm7b5.html) (C - Eb - Gb - Bb) -pub struct Minor7b5(InnerChord); - -impl Minor7b5 { - pub fn new(root: Note) -> Self { - let steps = [3, 3, 4]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } -} - -impl From for Minor7b5 { - fn from(root: Note) -> Self { - Minor7b5::new(root) - } -} - -impl Display for Minor7b5 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}m7b5", self.root()) - } -} - -impl Debug for Minor7b5 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: m7b5, {:?}, {:?}", self.root(), self.0) - } -} - -impl UpperHex for Minor7b5 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}m7b5 {:X}", self.root(), self.0) - } -} - -impl LowerHex for Minor7b5 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}m7b5 {:x}", self.root(), self.0) - } -} - -impl IntoIterator for Minor7b5 { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Minor7b5 { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) - } - - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{A_SHARP, C, D_SHARP, F_SHARP}; - - #[test] - fn new() { - let chord = Minor7b5::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 4); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes, vec![C, D_SHARP, F_SHARP, A_SHARP]); - } - - // #[test] - // fn show() { - // let chord = Minor7b5::new(C); - // assert_eq!(format!("{chord}"), "Cm7b5"); - // assert_eq!( - // format!("{chord:?}"), - // "Chord: m7b5, C4:C:0, [C4:C:0, C4:D#:3, C4:F#:6, C5:A#:10]" - // ); - // assert_eq!(format!("{chord:X}"), "Cm7b5 [C, D#, F#, A#]"); - // assert_eq!(format!("{chord:x}"), "Cm7b5 [C, Eb, Gb, Bb]"); - // } -} diff --git a/src/chords/minor9.rs b/src/chords/minor9.rs deleted file mode 100644 index e0dbaea..0000000 --- a/src/chords/minor9.rs +++ /dev/null @@ -1,106 +0,0 @@ -use super::{Chord, InnerChord}; -use crate::Note; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; - -/// Implements the **minor 7** chord. -/// [Cm9 chord](https://www.pianochord.org/cm9.html) (C - Eb - G - Bb - D) -pub struct Minor9(InnerChord); - -impl Minor9 { - pub fn new(root: Note) -> Self { - let steps = [3, 4, 3, 4]; - Self(InnerChord::with_steps(root, steps.into_iter())) - } -} - -impl From for Minor9 { - fn from(root: Note) -> Self { - Minor9::new(root) - } -} - -impl Display for Minor9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}m9", self.root()) - } -} - -impl Debug for Minor9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Chord: m9, {:?}, {:?}", self.root(), self.0) - } -} - -impl UpperHex for Minor9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:X}m9 {:X}", self.root(), self.0) - } -} - -impl LowerHex for Minor9 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:x}m9 {:x}", self.root(), self.0) - } -} - -impl IntoIterator for Minor9 { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl Chord for Minor9 { - fn root(&self) -> &Note { - self.0.root() - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - fn up_one_octave(self) -> Self { - Self(self.0.up_one_octave()) - } - - fn down_one_octave(self) -> Self { - Self(self.0.down_one_octave()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{A_SHARP, C, D, D_SHARP, G}; - - #[test] - fn new() { - let chord = Minor9::new(C); - - assert_eq!(chord.root(), &C); - assert_eq!(chord.len(), 5); - assert!(!chord.is_empty()); - - let notes = chord.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes, vec![C, D_SHARP, G, A_SHARP, D]); - } - - #[test] - fn show() { - let chord = Minor9::new(C); - assert_eq!(format!("{chord}"), "Cm9"); - assert_eq!( - format!("{chord:?}"), - "Chord: m9, C4:C:0, [C4:C:0, C4:D#:3, C4:G:7, C5:A#:10, C5:D:14]" - ); - assert_eq!(format!("{chord:X}"), "Cm9 [C, D#, G, A#, D]"); - assert_eq!(format!("{chord:x}"), "Cm9 [C, Eb, G, Bb, D]"); - } -} diff --git a/src/chords/mod.rs b/src/chords/mod.rs index d1a92a5..54458d2 100644 --- a/src/chords/mod.rs +++ b/src/chords/mod.rs @@ -1,174 +1,208 @@ +use crate::{Note, NoteStepperIterator, Tone}; +use std::fmt::{Display, LowerHex, UpperHex}; + mod diminished; -mod diminished7; -mod dominant7; -mod dominant9; +mod dominant; mod major; -mod major7; -mod major9; mod minor; -mod minor7; -mod minor7b5; -mod minor9; pub use diminished::*; -pub use diminished7::*; -pub use dominant7::*; -pub use dominant9::*; +pub use dominant::*; pub use major::*; -pub use major7::*; -pub use major9::*; pub use minor::*; -pub use minor7::*; -pub use minor7b5::*; -pub use minor9::*; -use crate::{Note, NoteStepperIterator, Tone, OCTAVE}; -use std::fmt::{Debug, Display, LowerHex, UpperHex}; +pub trait Chord { + fn root(&self) -> Note; + fn notes(&self) -> impl Iterator; + fn as_steps(&self) -> impl Iterator; +} -/// The chord behavior. The chord always has to be -/// convertable to a sequence of [`Note`] notes. -pub trait Chord: IntoIterator { - /// Returns the root of the chord - fn root(&self) -> &Note; +pub enum Chords { + Major(&'static str, Vec), + Minor(&'static str, Vec), + Dominant(&'static str, Vec), + Diminished(&'static str, Vec), +} - /// Returns the number of notes in the chord - fn len(&self) -> usize; +impl Chords { + fn major(name: &'static str, notes: N) -> Self + where + N: Iterator, + { + Self::Major(name, notes.map(|n| n.base()).collect()) + } - /// Determines if the chord is empty. - fn is_empty(&self) -> bool; + fn major_with_steps(name: &'static str, root: Note, steps: S) -> Self + where + S: Iterator, + Tone: From, + { + let notes = NoteStepperIterator::new(root, steps.into_iter()); + Self::major(name, notes) + } - /// Moves the chord one octave up. - fn up_one_octave(self) -> Self; + fn minor(name: &'static str, notes: N) -> Self + where + N: Iterator, + { + Self::Minor(name, notes.map(|n| n.base()).collect()) + } - /// Move the chord one octave down. - fn down_one_octave(self) -> Self; -} + fn minor_with_steps(name: &'static str, root: Note, steps: S) -> Self + where + S: Iterator, + Tone: From, + { + let notes = NoteStepperIterator::new(root, steps.into_iter()); + Self::minor(name, notes) + } -pub(crate) struct InnerChord(Vec); + fn dominant(name: &'static str, notes: N) -> Self + where + N: Iterator, + { + Self::Dominant(name, notes.map(|n| n.base()).collect()) + } -impl InnerChord { - fn new(n: impl Iterator) -> Self { - Self(n.collect()) + fn dominant_with_steps(name: &'static str, root: Note, steps: S) -> Self + where + S: Iterator, + Tone: From, + { + let notes = NoteStepperIterator::new(root, steps.into_iter()); + Self::dominant(name, notes) } - pub(crate) fn with_steps(root: Note, steps: impl Iterator) -> Self + fn diminished(name: &'static str, notes: N) -> Self where + N: Iterator, + { + Self::Diminished(name, notes.map(|n| n.base()).collect()) + } + + pub fn diminished_with_steps(name: &'static str, root: Note, steps: S) -> Self + where + S: Iterator, Tone: From, { let notes = NoteStepperIterator::new(root, steps.into_iter()); - Self::new(notes) + Self::diminished(name, notes) } - fn notes_debug(&self) -> String { - self.0 - .iter() - .map(|n| format!("{n:?}")) - .collect::>() - .join(", ") + pub fn contains_notes(&self, others: &mut N) -> bool + where + N: Iterator, + { + let ns = self.inner_notes(); + others.into_iter().all(|note| ns.contains(¬e)) } + pub fn find

(root: Note, predicate: P) -> impl Iterator + where + P: FnMut(&Self) -> bool, + { + let mut chords = major_chords(root).collect::>(); + chords.extend(minor_chords(root).into_iter()); + chords.extend(dominant_chords(root).into_iter()); + chords.extend(diminished_chords(root)); + + chords.into_iter().filter(predicate) + } + + pub fn find_contain_notes(root: Note, notes: N) -> impl Iterator + where + N: Iterator, + { + let notes = notes.collect::>(); + + Self::find(root, move |chord| { + chord.contains_notes(&mut notes.clone().into_iter()) + }) + } + + fn inner_notes(&self) -> &Vec { + match self { + Chords::Major(_, notes) => notes, + Chords::Minor(_, notes) => notes, + Chords::Dominant(_, notes) => notes, + Chords::Diminished(_, notes) => notes, + } + } + + fn inner_name(&self) -> &'static str { + match self { + Chords::Major(name, _) => name, + Chords::Minor(name, _) => name, + Chords::Dominant(name, _) => name, + Chords::Diminished(name, _) => name, + } + } + + const SEPARATOR: &'static str = ", "; + fn notes_upper_hex(&self) -> String { - self.0 + self.inner_notes() .iter() .map(|n| format!("{n:X}")) .collect::>() - .join(", ") + .join(Self::SEPARATOR) } fn notes_lower_hex(&self) -> String { - self.0 + self.inner_notes() .iter() .map(|n| format!("{n:x}")) .collect::>() - .join(", ") + .join(Self::SEPARATOR) } } -impl Display for InnerChord { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.root()) +impl Chord for Chords { + fn root(&self) -> Note { + self.inner_notes()[0] } -} -impl Debug for InnerChord { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let notes = self.notes_debug(); - write!(f, "[{notes}]") + fn notes(&self) -> impl Iterator { + self.inner_notes().iter() } -} -impl UpperHex for InnerChord { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let notes = self.notes_upper_hex(); - write!(f, "[{notes}]") + fn as_steps(&self) -> impl Iterator { + self.inner_notes() + .as_slice() + .windows(2) + .map(|notes| notes[0] - notes[1]) } } -impl LowerHex for InnerChord { +impl Display for Chords { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let notes = self.notes_lower_hex(); - write!(f, "[{notes}]") + write!(f, "{}{}", self.root(), self.inner_name()) } } -impl Chord for InnerChord { - fn root(&self) -> &Note { - &self.0[0] - } - - fn len(&self) -> usize { - self.0.len() - } - - fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Moves the chord one octave up. - fn up_one_octave(self) -> Self { - let notes = self.into_iter().map(|n| n + OCTAVE); - Self::new(notes) - } - - fn down_one_octave(self) -> Self { - let notes = self.into_iter().map(|n| n - OCTAVE); - Self::new(notes) +impl UpperHex for Chords { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let notes = self.notes_upper_hex(); + write!(f, "{self} [{notes}]") } } -impl IntoIterator for InnerChord { - type Item = Note; - - type IntoIter = as IntoIterator>::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() +impl LowerHex for Chords { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let notes = self.notes_lower_hex(); + write!(f, "{self} [{notes}]") } } #[cfg(test)] mod tests { - use crate::{A, C, D, E, F, G}; - use super::*; + use crate::{C, E, G}; #[test] - fn move_up() { - let chord1 = InnerChord::new([C, D, E, F, G, A].into_iter()); - let chord2 = InnerChord::new([C, D, E, F, G, A].into_iter()).up_one_octave(); - - let notes1 = chord1.into_iter().map(|n| n.base()).collect::>(); - let notes2 = chord2.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes1, notes2) - } - - #[test] - fn move_down() { - let chord1 = InnerChord::new([C, D, E, F, G, A].into_iter()); - let chord2 = InnerChord::new([C, D, E, F, G, A].into_iter()).down_one_octave(); - - let notes1 = chord1.into_iter().map(|n| n.base()).collect::>(); - let notes2 = chord2.into_iter().map(|n| n.base()).collect::>(); - assert_eq!(notes1, notes2) + fn find_notes() { + let notes = [C, E, G]; + let res = Chords::find_contain_notes(C, notes.into_iter()).collect::>(); + assert!(!res.is_empty()); } } diff --git a/src/lib.rs b/src/lib.rs index 7d02363..60d0f0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,24 +8,23 @@ //! C, C_SHARP, //! }; //! -//! let chord = chords::Major::from(C); -//! println!("Major C: {chord:X}"); +//! let chord = C.maj(); +//! println!("C: {chord:X}"); //! -//! let chord = chords::Major7::from(C); -//! println!("Major7 C: {chord:X}"); +//! let chord = C.maj7(); +//! println!("Cmaj7: {chord:X}"); //! -//! let chord = chords::Major7::from(C_SHARP); -//! println!("Major7 C#: {chord:X}"); +//! let chord = C.dom7(); +//! println!("C7: {chord:X}"); //! -//! let chord = chords::Dominant7::from(C); -//! println!("Dom7 C: {chord:X}"); +//! let chord = C.maj13(); +//! println!("Cmaj13: {chord:X}"); //! ``` mod bar; pub mod chords; mod note; mod tone; -pub mod xchords; pub use bar::*; pub use note::*; diff --git a/src/note.rs b/src/note.rs index c00a7b3..d1ee128 100644 --- a/src/note.rs +++ b/src/note.rs @@ -3,7 +3,7 @@ use std::{ ops::{Add, Sub}, }; -use crate::chords; +use crate::chords::{self, Chords}; use super::{Tone, OCTAVE}; @@ -51,38 +51,72 @@ impl Note { // Functions which build chords // - pub fn diminished(self) -> chords::Diminished { - chords::Diminished::from(self) + // + // Diminished chords + // + + /// Builds a **dim** chord with the root in the current note. + pub fn dim(self) -> chords::Chords { + chords::dim(self) } - /// Builds a diminished7 chord with the root in the current note. + /// Builds a **dim7** chord with the root in the current note. /// /// # Example /// ``` /// use musika_rs::*; /// - /// let chord = C.diminished7(); + /// let chord = C.dim7(); /// println!("{:X}", chord); /// ``` - pub fn diminished7(self) -> chords::Diminished7 { - chords::Diminished7::from(self) + pub fn dim7(self) -> chords::Chords { + chords::dim7(self) + } + + /// Return all the diminished chords for the current note. + pub fn dimished_chords(self) -> impl Iterator { + chords::diminished_chords(self) } + // + // Dominant chords + // + /// Builds a dominant7 choard with the root in the current note. /// /// # Example /// ``` /// use musika_rs::*; /// - /// let chord = C.dominant7(); + /// let chord = C.dom7(); /// println!("{:X}", chord); /// ``` - pub fn dominant7(self) -> chords::Dominant7 { - chords::Dominant7::from(self) + pub fn dom7(self) -> chords::Chords { + chords::dom7(self) + } + + pub fn dom7b5(self) -> chords::Chords { + chords::dom7b5(self) } - pub fn dominant9(self) -> chords::Dominant9 { - chords::Dominant9::from(self) + pub fn dom7s5(self) -> chords::Chords { + chords::dom7s5(self) + } + + pub fn dom9(self) -> chords::Chords { + chords::dom9(self) + } + + pub fn dom11(self) -> chords::Chords { + chords::dom11(self) + } + + pub fn dom13(self) -> chords::Chords { + chords::dom13(self) + } + + pub fn dominant_chords(self) -> impl Iterator { + chords::dominant_chords(self) } /// Builds a major chord with the root in the current note. @@ -91,11 +125,11 @@ impl Note { /// ``` /// use musika_rs::*; /// - /// let chord = C.major(); + /// let chord = C.maj(); /// println!("{:X}", chord); /// ``` - pub fn major(self) -> chords::Major { - chords::Major::from(self) + pub fn maj(self) -> chords::Chords { + chords::maj(self) } /// Builds a major7 chord with the root in the current note. @@ -104,27 +138,40 @@ impl Note { /// ``` /// use musika_rs::*; /// - /// let chord = C.major7(); + /// let chord = C.maj7(); /// println!("{:X}", chord); /// ``` - pub fn major7(self) -> chords::Major7 { - chords::Major7::from(self) + pub fn maj7(self) -> chords::Chords { + chords::maj7(self) + } + + pub fn maj9(self) -> chords::Chords { + chords::maj9(self) + } + + pub fn maj11(self) -> chords::Chords { + chords::maj11(self) + } + + pub fn maj13(self) -> chords::Chords { + chords::maj13(self) } - pub fn major9(self) -> chords::Major9 { - chords::Major9::from(self) + pub fn major_chords(self) -> impl Iterator { + chords::major_chords(self) } + /// Builds a minor chord with the root in the current note. /// /// # Example /// ``` /// use musika_rs::*; /// - /// let chord = C.minor(); + /// let chord = C.min(); /// println!("{:X}", chord); /// ``` - pub fn minor(self) -> chords::Minor { - chords::Minor::from(self) + pub fn min(self) -> chords::Chords { + chords::min(self) } /// Builds a minor7 chord with the root in the current note. @@ -133,28 +180,31 @@ impl Note { /// ``` /// use musika_rs::*; /// - /// let chord = C.minor7(); + /// let chord = C.min7(); /// println!("{:X}", chord); /// ``` - pub fn minor7(self) -> chords::Minor7 { - chords::Minor7::from(self) + pub fn min7(self) -> chords::Chords { + chords::min7(self) } - pub fn minor9(self) -> chords::Minor9 { - chords::Minor9::from(self) + pub fn min7b5(self) -> chords::Chords { + chords::min7b5(self) } - /// Builds a minor7b5 chord with the root in the current note. - /// - /// # Example - /// ``` - /// use musika_rs::*; - /// - /// let chord = C.minor7b5(); - /// println!("{:X}", chord); - /// ``` - pub fn minor7b5(self) -> chords::Minor7b5 { - chords::Minor7b5::from(self) + pub fn min9(self) -> chords::Chords { + chords::min9(self) + } + + pub fn min11(self) -> chords::Chords { + chords::min11(self) + } + + pub fn min13(self) -> chords::Chords { + chords::min13(self) + } + + pub fn minor_chords(self) -> impl Iterator { + chords::minor_chords(self) } } diff --git a/src/xchords/diminished.rs b/src/xchords/diminished.rs deleted file mode 100644 index 999b882..0000000 --- a/src/xchords/diminished.rs +++ /dev/null @@ -1,32 +0,0 @@ -use super::XChords; -use crate::Note; - -pub fn dim(root: Note) -> XChords { - let steps = [3, 3]; - XChords::diminished_with_steps("dim", root, steps.into_iter()) -} - -pub fn dim7(root: Note) -> XChords { - let steps = [3, 3, 3]; - XChords::diminished_with_steps("dim7", root, steps.into_iter()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::C; - - #[test] - fn test_dim() { - let chord = dim(C); - assert_eq!(format!("{chord:X}"), "Cdim [C, D#, F#]"); - assert_eq!(format!("{chord:x}"), "Cdim [C, Eb, Gb]"); - } - - #[test] - fn test_dim7() { - let chord = dim7(C); - assert_eq!(format!("{chord:X}"), "Cdim7 [C, D#, F#, A]"); - assert_eq!(format!("{chord:x}"), "Cdim7 [C, Eb, Gb, A]"); - } -} diff --git a/src/xchords/major.rs b/src/xchords/major.rs deleted file mode 100644 index ffabd28..0000000 --- a/src/xchords/major.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::Note; - -use super::XChords; - -pub fn maj(root: Note) -> XChords { - let steps = [4, 3]; - XChords::major_with_steps("", root, steps.into_iter()) -} - -pub fn maj7(root: Note) -> XChords { - let steps = [4, 3, 4]; - XChords::major_with_steps("maj7", root, steps.into_iter()) -} - -pub fn maj9(root: Note) -> XChords { - let steps = [4, 3, 4, 3]; - XChords::major_with_steps("maj9", root, steps.into_iter()) -} - -pub fn maj11(root: Note) -> XChords { - let steps = [4, 3, 4, 3, 3]; - XChords::major_with_steps("maj11", root, steps.into_iter()) -} - -pub fn maj13(root: Note) -> XChords { - let steps = [4, 3, 4, 3, 3, 4]; - XChords::major_with_steps("maj13", root, steps.into_iter()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::C; - - #[test] - fn test_maj() { - let chord = maj(C); - assert_eq!(format!("{chord:X}"), "C [C, E, G]") - } - - #[test] - fn test_maj7() { - let chord = maj7(C); - assert_eq!(format!("{chord:X}"), "Cmaj7 [C, E, G, B]") - } - - #[test] - fn test_maj9() { - let chord = maj9(C); - assert_eq!(format!("{chord:X}"), "Cmaj9 [C, E, G, B, D]") - } - - #[test] - fn test_maj11() { - let chord = maj11(C); - assert_eq!(format!("{chord:X}"), "Cmaj11 [C, E, G, B, D, F]") - } - - #[test] - fn test_maj13() { - let chord = maj13(C); - assert_eq!(format!("{chord:X}"), "Cmaj13 [C, E, G, B, D, F, A]") - } -} diff --git a/src/xchords/minor.rs b/src/xchords/minor.rs deleted file mode 100644 index 45781da..0000000 --- a/src/xchords/minor.rs +++ /dev/null @@ -1,80 +0,0 @@ -use super::XChords; -use crate::Note; - -pub fn min(root: Note) -> XChords { - let steps = [3, 4]; - XChords::minor_with_steps("m", root, steps.into_iter()) -} - -pub fn min7(root: Note) -> XChords { - let steps = [3, 4, 3]; - XChords::minor_with_steps("m7", root, steps.into_iter()) -} - -pub fn min7b5(root: Note) -> XChords { - let steps = [3, 3, 4]; - XChords::minor_with_steps("m7(b5)", root, steps.into_iter()) -} - -pub fn min9(root: Note) -> XChords { - let steps = [3, 4, 3, 4]; - XChords::minor_with_steps("m9", root, steps.into_iter()) -} - -pub fn min11(root: Note) -> XChords { - let steps = [3, 4, 3, 4, 3]; - XChords::minor_with_steps("m11", root, steps.into_iter()) -} - -pub fn min13(root: Note) -> XChords { - let steps = [3, 4, 3, 4, 3, 4]; - XChords::minor_with_steps("m13", root, steps.into_iter()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::C; - - #[test] - fn test_min() { - let chord = min(C); - assert_eq!(format!("{chord:X}"), "Cm [C, D#, G]"); - assert_eq!(format!("{chord:x}"), "Cm [C, Eb, G]"); - } - - #[test] - fn test_min7() { - let chord = min7(C); - assert_eq!(format!("{chord:X}"), "Cm7 [C, D#, G, A#]"); - assert_eq!(format!("{chord:x}"), "Cm7 [C, Eb, G, Bb]") - } - - #[test] - fn test_min7b5() { - let chord = min7b5(C); - assert_eq!(format!("{chord:X}"), "Cm7(b5) [C, D#, F#, A#]"); - assert_eq!(format!("{chord:x}"), "Cm7(b5) [C, Eb, Gb, Bb]") - } - - #[test] - fn test_min9() { - let chord = min9(C); - assert_eq!(format!("{chord:X}"), "Cm9 [C, D#, G, A#, D]"); - assert_eq!(format!("{chord:x}"), "Cm9 [C, Eb, G, Bb, D]") - } - - #[test] - fn test_min11() { - let chord = min11(C); - assert_eq!(format!("{chord:X}"), "Cm11 [C, D#, G, A#, D, F]"); - assert_eq!(format!("{chord:x}"), "Cm11 [C, Eb, G, Bb, D, F]") - } - - #[test] - fn test_min13() { - let chord = min13(C); - assert_eq!(format!("{chord:X}"), "Cm13 [C, D#, G, A#, D, F, A]"); - assert_eq!(format!("{chord:x}"), "Cm13 [C, Eb, G, Bb, D, F, A]") - } -} diff --git a/src/xchords/mod.rs b/src/xchords/mod.rs deleted file mode 100644 index e348932..0000000 --- a/src/xchords/mod.rs +++ /dev/null @@ -1,164 +0,0 @@ -use crate::{Note, NoteStepperIterator, Tone}; -use std::fmt::{Display, LowerHex, UpperHex}; - -mod diminished; -mod dominant; -mod major; -mod minor; - -pub use diminished::*; -pub use dominant::*; -pub use major::*; -pub use minor::*; - -pub trait XChord { - fn root(&self) -> Note; - fn notes(&self) -> impl Iterator; - fn as_steps(&self) -> impl Iterator; -} - -pub enum XChords { - Major(&'static str, Vec), - Minor(&'static str, Vec), - Dominant(&'static str, Vec), - Diminished(&'static str, Vec), -} - -impl XChords { - fn major(name: &'static str, notes: impl Iterator) -> Self { - Self::Major(name, notes.collect()) - } - - pub fn major_with_steps( - name: &'static str, - root: Note, - steps: impl Iterator, - ) -> Self - where - Tone: From, - { - let notes = NoteStepperIterator::new(root, steps.into_iter()); - Self::major(name, notes) - } - - fn minor(name: &'static str, notes: impl Iterator) -> Self { - Self::Minor(name, notes.collect()) - } - - pub fn minor_with_steps( - name: &'static str, - root: Note, - steps: impl Iterator, - ) -> Self - where - Tone: From, - { - let notes = NoteStepperIterator::new(root, steps.into_iter()); - Self::minor(name, notes) - } - - fn dominant(name: &'static str, notes: impl Iterator) -> Self { - Self::Dominant(name, notes.collect()) - } - - pub fn dominant_with_steps( - name: &'static str, - root: Note, - steps: impl Iterator, - ) -> Self - where - Tone: From, - { - let notes = NoteStepperIterator::new(root, steps.into_iter()); - Self::dominant(name, notes) - } - - fn diminished(name: &'static str, notes: impl Iterator) -> Self { - Self::Diminished(name, notes.collect()) - } - - pub fn diminished_with_steps( - name: &'static str, - root: Note, - steps: impl Iterator, - ) -> Self - where - Tone: From, - { - let notes = NoteStepperIterator::new(root, steps.into_iter()); - Self::diminished(name, notes) - } - - fn inner_notes(&self) -> &Vec { - match self { - XChords::Major(_, notes) => notes, - XChords::Minor(_, notes) => notes, - XChords::Dominant(_, notes) => notes, - XChords::Diminished(_, notes) => notes, - } - } - - fn inner_name(&self) -> &'static str { - match self { - XChords::Major(name, _) => name, - XChords::Minor(name, _) => name, - XChords::Dominant(name, _) => name, - XChords::Diminished(name, _) => name, - } - } - - const SEPARATOR: &'static str = ", "; - - fn notes_upper_hex(&self) -> String { - self.inner_notes() - .iter() - .map(|n| format!("{n:X}")) - .collect::>() - .join(Self::SEPARATOR) - } - - fn notes_lower_hex(&self) -> String { - self.inner_notes() - .iter() - .map(|n| format!("{n:x}")) - .collect::>() - .join(Self::SEPARATOR) - } -} - -impl XChord for XChords { - fn root(&self) -> Note { - self.inner_notes()[0] - } - - fn notes(&self) -> impl Iterator { - self.inner_notes().iter() - } - - fn as_steps(&self) -> impl Iterator { - self.inner_notes() - .as_slice() - .windows(2) - .map(|notes| notes[0] - notes[1]) - } -} - -impl Display for XChords { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}{}", self.root(), self.inner_name()) - } -} - -impl UpperHex for XChords { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let notes = self.notes_upper_hex(); - write!(f, "{self} [{notes}]") - } -} - -impl LowerHex for XChords { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let notes = self.notes_lower_hex(); - write!(f, "{self} [{notes}]") - } -}