rust_music_theory/interval/
interval.rs1use crate::interval::errors::IntervalError;
2use crate::note::{Note, PitchClass};
3use strum_macros::Display;
4
5#[derive(Display, Debug, Copy, Clone)]
7pub enum Quality {
8 Perfect,
10 Major,
11 Minor,
12 Augmented,
13 Diminished,
14}
15
16#[derive(Display, Debug, Copy, Clone)]
18pub enum Number {
19 Unison,
20 Second,
21 Third,
22 Fourth,
23 Fifth,
24 Sixth,
25 Seventh,
26 Octave,
27}
28
29#[derive(Display, Debug, Copy, Clone)]
31pub enum Step {
32 Half,
34 Whole,
36 Tritone,
38}
39
40#[derive(Debug, Copy, Clone)]
42pub struct Interval {
43 pub semitone_count: u8,
45 pub quality: Quality,
47 pub number: Number,
49 pub step: Option<Step>,
51}
52
53impl Interval {
54 pub fn new(semitone_count: u8, quality: Quality, number: Number, step: Option<Step>) -> Self {
56 Interval {
57 semitone_count,
58 quality,
59 number,
60 step,
61 }
62 }
63
64 pub fn from_semitones(semi_tones: &[u8]) -> Result<Vec<Self>, IntervalError> {
70 let mut intervals: Vec<Interval> = vec![];
71
72 if semi_tones.is_empty() {
73 return Err(IntervalError::InvalidInterval);
74 }
75
76 for i in semi_tones {
77 let interval = Self::from_semitone(*i)?;
78 intervals.push(interval);
79 }
80
81 Ok(intervals)
82 }
83
84 pub fn from_semitone(sc: u8) -> Result<Self, IntervalError> {
90 let (number, quality, mut step): (Number, Quality, Option<Step>);
91 step = None;
92
93 match sc {
94 0 => {
95 number = Number::Unison;
96 quality = Quality::Perfect;
97 }
98 1 => {
99 number = Number::Second;
100 quality = Quality::Minor;
101 step = Some(Step::Half);
102 }
103 2 => {
104 number = Number::Second;
105 quality = Quality::Major;
106 step = Some(Step::Whole);
107 }
108 3 => {
109 number = Number::Third;
110 quality = Quality::Minor;
111 }
112 4 => {
113 number = Number::Third;
114 quality = Quality::Major;
115 }
116 5 => {
117 number = Number::Fourth;
118 quality = Quality::Perfect;
119 }
120 6 => {
121 number = Number::Fifth;
122 quality = Quality::Diminished;
123 step = Some(Step::Tritone);
124 }
125 7 => {
126 number = Number::Fifth;
127 quality = Quality::Perfect;
128 }
129 8 => {
130 number = Number::Sixth;
131 quality = Quality::Minor;
132 }
133 9 => {
134 number = Number::Sixth;
135 quality = Quality::Major;
136 }
137 10 => {
138 number = Number::Seventh;
139 quality = Quality::Minor;
140 }
141 11 => {
142 number = Number::Seventh;
143 quality = Quality::Major;
144 }
145 12 => {
146 number = Number::Octave;
147 quality = Quality::Perfect;
148 }
149 _ => {
150 return Err(IntervalError::InvalidInterval);
151 }
152 };
153
154 Ok(Interval {
155 semitone_count: sc,
156 number,
157 quality,
158 step,
159 })
160 }
161
162 pub fn invert(interval: &Self) -> Result<Self, IntervalError> {
165 if interval.semitone_count == 12 {
166 Self::from_semitone(12)
167 } else {
168 let adjusted = (12 + (12i16 - interval.semitone_count as i16)) % 12;
169 Self::from_semitone(adjusted as u8)
170 }
171 }
172
173 pub fn second_note_from(self, first_note: Note) -> Note {
175 let pitch_class = PitchClass::from_interval(first_note.pitch_class, self);
176 let octave = first_note.octave;
177 let excess_octave = (first_note.pitch_class as u8 + self.semitone_count) / 12;
178
179 Note {
180 octave: octave + excess_octave,
181 pitch_class,
182 }
183 }
184
185 pub fn second_note_down_from(self, first_note: Note) -> Note {
187 let pitch_class = PitchClass::from_interval_down(first_note.pitch_class, self);
188 let octave = first_note.octave;
189 let raw_diff = first_note.pitch_class as i16 - self.semitone_count as i16;
190 let excess_octave = (raw_diff / -12) + if raw_diff < 0 { 1 } else { 0 };
191
192 Note {
193 octave: octave - excess_octave as u8,
194 pitch_class,
195 }
196 }
197
198 pub fn to_notes(root: Note, intervals: impl IntoIterator<Item = Interval>) -> Vec<Note> {
200 let mut notes = vec![root];
201
202 for interval in intervals {
203 let last_note = notes.last().unwrap();
204 let interval_first_note = Note::new(last_note.pitch_class, last_note.octave);
205 let interval_second_note = interval.second_note_from(interval_first_note);
206 notes.push(interval_second_note);
207 }
208
209 notes
210 }
211
212 pub fn to_notes_reverse(
214 root: Note,
215 intervals: impl IntoIterator<Item = Interval>,
216 ) -> Vec<Note> {
217 let mut notes = vec![root];
218
219 let reversed = intervals
220 .into_iter()
221 .collect::<Vec<Interval>>()
222 .into_iter()
223 .rev();
224 for interval in reversed {
225 let last_note = notes.last().unwrap();
226 let interval_first_note = Note::new(last_note.pitch_class, last_note.octave);
227 let interval_second_note = interval.second_note_down_from(interval_first_note);
228 notes.push(interval_second_note);
229 }
230
231 notes
232 }
233}
234
235impl Default for Interval {
236 fn default() -> Self {
237 Interval {
238 semitone_count: 0,
239 quality: Quality::Major,
240 number: Number::Unison,
241 step: None,
242 }
243 }
244}