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