music21_rs/scale/
diatonicscale.rs1use crate::{chord::Chord, defaults::IntegerType, error::Result, pitch::Pitch};
2
3use super::concretescale::ConcreteScale;
4
5#[derive(Clone, Debug)]
6pub struct DiatonicScale {
8 concrete: ConcreteScale,
9 mode: String,
10}
11
12impl DiatonicScale {
13 pub(crate) fn new(tonic: Pitch, sharps: IntegerType, mode: &str) -> Self {
14 Self {
15 concrete: ConcreteScale::new(tonic, sharps),
16 mode: mode.to_string(),
17 }
18 }
19
20 pub fn mode(&self) -> &str {
22 &self.mode
23 }
24
25 pub fn tonic(&self) -> &Pitch {
27 self.concrete.tonic()
28 }
29
30 pub fn pitch_from_degree(&self, degree: usize) -> Result<Pitch> {
32 self.concrete.pitch_from_degree(degree)
33 }
34
35 pub fn pitches(&self) -> Result<Vec<Pitch>> {
37 self.concrete.pitches()
38 }
39
40 pub fn triad_from_degree(&self, degree: usize) -> Result<Chord> {
42 let notes = vec![
43 self.pitch_from_degree(degree)?,
44 self.pitch_from_degree(degree + 2)?,
45 self.pitch_from_degree(degree + 4)?,
46 ];
47 Chord::new(notes.as_slice())
48 }
49
50 pub fn seventh_chord_from_degree(&self, degree: usize) -> Result<Chord> {
52 let notes = vec![
53 self.pitch_from_degree(degree)?,
54 self.pitch_from_degree(degree + 2)?,
55 self.pitch_from_degree(degree + 4)?,
56 self.pitch_from_degree(degree + 6)?,
57 ];
58 Chord::new(notes.as_slice())
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 fn pitch(name: &str) -> Pitch {
67 Pitch::new(
68 Some(name.to_string()),
69 None,
70 None,
71 Option::<IntegerType>::None,
72 Option::<IntegerType>::None,
73 None,
74 None,
75 None,
76 None,
77 )
78 .expect("valid pitch")
79 }
80
81 #[test]
82 fn diatonic_scale_degree_lookup() {
83 let scale = DiatonicScale::new(pitch("A4"), 0, "minor");
84 assert_eq!(scale.pitch_from_degree(1).unwrap().name_with_octave(), "A4");
85 assert_eq!(scale.pitch_from_degree(3).unwrap().name_with_octave(), "C5");
86 assert_eq!(scale.pitch_from_degree(7).unwrap().name_with_octave(), "G5");
87 }
88
89 #[test]
90 fn diatonic_scale_degree_chords() {
91 let scale = DiatonicScale::new(pitch("C4"), 0, "major");
92 assert_eq!(
93 scale.triad_from_degree(1).unwrap().pitched_common_name(),
94 "C-major triad"
95 );
96 assert_eq!(
97 scale
98 .seventh_chord_from_degree(5)
99 .unwrap()
100 .pitched_common_name(),
101 "G-dominant seventh chord"
102 );
103 }
104}