redact_composer_musical/note/mod.rs
1use crate::{Interval, PitchClass};
2use std::ops::{Add, AddAssign, Sub, SubAssign};
3
4mod note_name;
5pub use note_name::*;
6
7mod iter;
8pub use iter::*;
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13#[cfg(feature = "redact-composer")]
14use redact_composer_core::{derive::Element, elements::PlayNote};
15
16/// A musical note, corresponding to a specific frequency in 12 tone equal temperament space.
17#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19#[cfg_attr(feature = "redact-composer", derive(Element))]
20pub struct Note(pub u8);
21
22impl Note {
23 /// Returns the note's [`PitchClass`].
24 /// ```
25 /// use redact_composer_musical::{Note, NoteName::C, PitchClass};
26 ///
27 /// assert_eq!(Note::from((C, 4)).pitch_class(), PitchClass(0));
28 /// ```
29 pub fn pitch_class(&self) -> PitchClass {
30 self.0.into()
31 }
32
33 /// Returns the octave number of this note:
34 /// ```
35 /// use redact_composer_musical::{Note, NoteName::{B, C}};
36 ///
37 /// assert_eq!(Note::from((C, 0)).octave(), 0);
38 /// assert_eq!(Note::from((B, 5)).octave(), 5);
39 /// ```
40 pub fn octave(&self) -> i8 {
41 (self.0 / 12) as i8 - 1
42 }
43
44 /// Returns the interval between this note and another.
45 /// ```
46 /// use redact_composer_musical::{Interval, Note, NoteName::C};
47 ///
48 /// assert_eq!(Note::from((C, 3)).interval_with(&Note::from((C, 4))), Interval::Octave);
49 /// ```
50 pub fn interval_with(&self, other_note: &Note) -> Interval {
51 let (lower, higher) = if self.0 <= other_note.0 {
52 (self.0, other_note.0)
53 } else {
54 (other_note.0, self.0)
55 };
56
57 Interval(higher - lower)
58 }
59}
60
61impl From<(NoteName, i8)> for Note {
62 /// ```
63 /// use redact_composer_musical::{Note, NoteName::C};
64 ///
65 /// assert_eq!(Note::from((C, 4)), Note(60));
66 /// ```
67 fn from(value: (NoteName, i8)) -> Self {
68 Note::from((PitchClass::from(value.0), value.1))
69 }
70}
71
72impl From<(PitchClass, i8)> for Note {
73 /// ```
74 /// use redact_composer_musical::{Note, NoteName::C, PitchClass};
75 ///
76 /// let c3 = Note::from((PitchClass(0), 4));
77 /// assert_eq!(c3, Note(60));
78 /// ```
79 fn from(value: (PitchClass, i8)) -> Self {
80 let (note, octave) = (value.0 .0, value.1 + 1);
81 Note(note + 12 * octave as u8)
82 }
83}
84
85impl Add<Interval> for Note {
86 type Output = Note;
87 /// Adds an interval to this note, resulting in another note.
88 /// ```
89 /// use redact_composer_musical::{Interval as I, Note, NoteName::{C, G}};
90 ///
91 /// let c3 = Note::from((C, 4));
92 /// assert_eq!(c3, Note(60));
93 /// assert_eq!(c3 + I::P5, Note::from((G, 4)));
94 /// ```
95 fn add(self, rhs: Interval) -> Self::Output {
96 Note(self.0 + rhs.0)
97 }
98}
99
100impl AddAssign<Interval> for Note {
101 fn add_assign(&mut self, rhs: Interval) {
102 self.0 = self.0 + rhs.0;
103 }
104}
105
106impl Sub<Interval> for Note {
107 type Output = Note;
108 /// Subtracts an interval from this note, resulting in another note.
109 /// ```
110 /// use redact_composer_musical::{Interval as I, Note, NoteName::{C, G}};
111 ///
112 /// let c3 = Note::from((C, 3));
113 /// assert_eq!(c3 - I::P4, Note::from((G, 2)));
114 /// ```
115 fn sub(self, rhs: Interval) -> Self::Output {
116 Note(self.0 - rhs.0)
117 }
118}
119
120impl SubAssign<Interval> for Note {
121 fn sub_assign(&mut self, rhs: Interval) {
122 self.0 = self.0 - rhs.0;
123 }
124}
125
126impl PartialEq<PitchClass> for Note {
127 /// ```
128 /// use redact_composer_musical::{Note, NoteName::C, PitchClass};
129 ///
130 /// assert!(Note::from((C, 3)) == PitchClass::from(C));
131 /// ```
132 fn eq(&self, other: &PitchClass) -> bool {
133 &self.pitch_class() == other
134 }
135}
136
137impl PartialEq<Note> for (NoteName, i8) {
138 /// ```
139 /// use redact_composer_musical::{Note, NoteName::*};
140 /// assert_eq!((C, 4), Note(60));
141 /// ```
142 fn eq(&self, other: &Note) -> bool {
143 Note::from(*self).eq(other)
144 }
145}
146
147impl PartialEq<(NoteName, i8)> for Note {
148 /// ```
149 /// use redact_composer_musical::{Note, NoteName::*};
150 /// assert_eq!(Note(60), (C, 4));
151 /// ```
152 fn eq(&self, other: &(NoteName, i8)) -> bool {
153 self.eq(&Note::from(*other))
154 }
155}
156
157#[cfg(feature = "redact-composer")]
158impl Note {
159 /// Creates a [`PlayNote`] element from this note, which can then be used as a [`Segment`](redact_composer_core::Segment).
160 /// ```
161 /// use redact_composer_core::{elements::PlayNote, timing::Timing, IntoSegment};
162 /// use redact_composer_musical::{Note, NoteName::C};
163 ///
164 /// let expected_play_note = PlayNote { note: 60, velocity: 100};
165 /// let play_note = Note::from((C, 4)).play(100);
166 /// assert_eq!(play_note, expected_play_note);
167 ///
168 ///let segment = play_note.into_segment(0..480);
169 /// assert_eq!(segment.element_as::<PlayNote>(), Some(&expected_play_note));
170 /// assert_eq!(segment.timing, Timing::from(0..480));
171 /// ```
172 pub fn play(&self, velocity: u8) -> PlayNote {
173 PlayNote {
174 note: self.0,
175 velocity,
176 }
177 }
178}