rust_music/chord.rs
1use crate::errors::ChordError;
2use crate::num::u7;
3use crate::Note;
4use crate::Result;
5
6/// Describes a set of notes played simultaneously
7#[derive(Debug, Default, Clone, PartialEq)]
8pub struct Chord {
9 /// `rhythm` is the length in beats that the `Chord` will take
10 /// in a `Phrase`.
11 ///
12 /// It cannot be longer than the longest `Note` of the `Chord`,
13 /// but it can be longer than some of the notes.
14 ///
15 /// It can be shorter than some (or all) of the notes.
16 /// This allows to play notes in the `Phrase` that start
17 /// while the previous `Chord` is still playing.
18 /// This also works with a `Chord` of one `Note`, to enable
19 /// these trailing notes with unique notes as well.
20 rhythm: f64,
21
22 /// The notes of the `Chord`. They can have different rhythm values.
23 /// This allows to stop some of the notes of the Chord before some others.
24 notes: Vec<Note>,
25}
26
27impl Chord {
28 /// Returns a new Chord based on the given rhythm value and notes
29 ///
30 /// # Arguments
31 ///
32 /// * `rhythm`: duration in beats that the `Chord` will take in a phrase.
33 /// Some notes of the chord can last longer, but the next entry added to the
34 /// phrase will start after the end of this `rhythm` value.
35 /// * `notes`: list of notes in the `Chord` (len must be 1 or more)
36 ///
37 /// # Errors
38 ///
39 /// * `ChordError::EmptyChord` if notes vec is empty
40 /// * `ChordError::RhythmTooLong` if `rhythm` is longer than the longest note
41 pub fn new(rhythm: f64, notes: Vec<Note>) -> Result<Self> {
42 if notes.is_empty() {
43 return Err(ChordError::EmptyChord.into());
44 }
45 let maxr = notes
46 .iter()
47 .map(Note::rhythm)
48 // .filter_map(|v| if v.is_nan() { None } else { Some(v) })
49 .fold(f64::NEG_INFINITY, f64::max);
50 if maxr < rhythm {
51 return Err(ChordError::RhythmTooLong.into());
52 }
53 Ok(Chord { rhythm, notes })
54 }
55
56 /// Returns a new Chord based on the given rhythm value, note pitches, and dynamic.
57 ///
58 /// # Arguments
59 ///
60 /// * `rhythm`: duration in beats of the `Chord` and all the notes it contains
61 /// * `pitches`: list of the pitches of the notes of the `Chord`
62 /// * `dynamic`: dynamic that each note in the `Chord` will take
63 pub fn from_pitches(rhythm: f64, dynamic: u7, pitches: &[u7]) -> Result<Self> {
64 let notes: Result<Vec<_>> =
65 Note::new_sequence(rhythm, dynamic, pitches.iter().copied()).collect();
66 Self::new(rhythm, notes?)
67 }
68
69 /// Returns the rhythm value of the `Chord`
70 pub fn rhythm(&self) -> f64 {
71 self.rhythm
72 }
73
74 /// Returns the notes of the `Chord`
75 pub fn notes(&self) -> &[Note] {
76 self.notes.as_slice()
77 }
78}