1use crate::{
5 defaults::FloatType,
6 dynamics::SHORT_NAMES,
7 error::{Error, Result},
8 pitch::Pitch,
9 stream::{Stream, StreamElement},
10};
11
12pub fn pitch_to_dynamic(stream: &Stream) -> Result<Vec<(FloatType, usize)>> {
28 let flat = stream.flatten();
29 let events = flat.events();
30 let notes: Vec<(FloatType, Vec<Pitch>)> = events
31 .iter()
32 .filter_map(|event| match event.element() {
33 StreamElement::Note(note) => Some((event.offset(), vec![note.pitch().clone()])),
34 StreamElement::Chord(chord) => Some((event.offset(), chord.pitches())),
35 StreamElement::ChordSymbol(symbol) => {
36 Some((event.offset(), symbol.pitches().unwrap_or_default()))
37 }
38 _ => None,
39 })
40 .collect();
41 if notes.is_empty() {
42 return Err(missing(
43 "(<class 'music21.note.Note'>, <class 'music21.chord.Chord'>)",
44 ));
45 }
46 let dynamics: Vec<(FloatType, &str)> = events
47 .iter()
48 .filter_map(|event| match event.element() {
49 StreamElement::Dynamic(dynamic) => Some((event.offset(), dynamic.value())),
50 _ => None,
51 })
52 .collect();
53 if dynamics.is_empty() {
54 return Err(missing("<class 'music21.dynamics.Dynamic'>"));
55 }
56 let end = Offset::of(flat.end_offset());
57 let mut under: Vec<Vec<usize>> = vec![Vec::new(); notes.len()];
58 for (index, (start, value)) in dynamics.iter().enumerate() {
59 let start = Offset::of(*start);
60 let next = dynamics
61 .get(index + 1)
62 .map_or(end, |(next, _)| Offset::of(*next));
63 let stop = start.plus(next.minus(start).op_frac());
66 let level = SHORT_NAMES
67 .iter()
68 .position(|name| name == value)
69 .ok_or_else(|| Error::Analysis(format!("'{value}' is not in list")))?;
70 for ((offset, _), heard) in notes.iter().zip(&mut under) {
71 let offset = Offset::of(*offset);
72 if start.at_most(offset) && offset.at_most(stop) {
73 heard.push(level);
74 }
75 }
76 }
77 Ok(notes
78 .iter()
79 .zip(&under)
80 .flat_map(|((_, pitches), heard)| {
81 pitches
82 .iter()
83 .flat_map(move |pitch| heard.iter().map(move |level| (pitch.ps(), *level)))
84 })
85 .collect())
86}
87
88pub fn pitch_to_dynamic_counts(stream: &Stream) -> Result<Vec<(FloatType, usize, usize)>> {
95 let mut counts: Vec<(FloatType, usize, usize)> = Vec::new();
96 for (pitch, level) in pitch_to_dynamic(stream)? {
97 match counts
98 .iter_mut()
99 .find(|(known, known_level, _)| *known == pitch && *known_level == level)
100 {
101 Some((_, _, count)) => *count += 1,
102 None => counts.push((pitch, level, 1)),
103 }
104 }
105 Ok(counts)
106}
107
108#[derive(Clone, Copy, Debug)]
112enum Offset {
113 Float(FloatType),
114 Fraction(i128, i128),
115}
116
117fn integer_ratio(value: FloatType) -> (i128, i128) {
120 if value == 0.0 {
121 return (0, 1);
122 }
123 let bits = value.to_bits();
124 let sign = if bits >> 63 == 1 { -1 } else { 1 };
125 let exponent = ((bits >> 52) & 0x7ff) as i32;
126 let mut mantissa = (bits & ((1 << 52) - 1)) as i128;
127 let mut power = if exponent == 0 {
128 -1074
129 } else {
130 mantissa |= 1 << 52;
131 exponent - 1075
132 };
133 while mantissa % 2 == 0 && power < 0 {
134 mantissa /= 2;
135 power += 1;
136 }
137 if power >= 0 {
138 (sign * (mantissa << power), 1)
139 } else {
140 (sign * mantissa, 1 << -power)
141 }
142}
143
144fn gcd(a: i128, b: i128) -> i128 {
145 if b == 0 { a.abs() } else { gcd(b, a % b) }
146}
147
148fn power_of_two(denominator: i128) -> bool {
149 denominator > 0 && denominator & (denominator - 1) == 0
150}
151
152impl Offset {
153 fn of(value: FloatType) -> Self {
156 Self::Float(value).op_frac()
157 }
158
159 fn op_frac(self) -> Self {
163 match self {
164 Self::Float(value) => {
165 let (_, denominator) = integer_ratio(value);
166 if denominator <= 65535 {
167 return self;
168 }
169 match crate::duration::limited_fraction(value, 65535) {
170 Some((numerator, denominator)) if !power_of_two(denominator) => {
171 Self::Fraction(numerator, denominator)
172 }
173 Some((numerator, denominator)) => {
174 Self::Float(numerator as FloatType / denominator as FloatType)
175 }
176 None => self,
177 }
178 }
179 Self::Fraction(numerator, denominator) if power_of_two(denominator) => {
180 Self::Float(numerator as FloatType / denominator as FloatType)
181 }
182 fraction => fraction,
183 }
184 }
185
186 fn as_float(self) -> FloatType {
187 match self {
188 Self::Float(value) => value,
189 Self::Fraction(numerator, denominator) => {
190 numerator as FloatType / denominator as FloatType
191 }
192 }
193 }
194
195 fn plus(self, other: Self) -> Self {
197 match (self, other) {
198 (Self::Fraction(a, b), Self::Fraction(c, d)) => {
199 let (numerator, denominator) = (a * d + c * b, b * d);
200 let divisor = gcd(numerator, denominator);
201 Self::Fraction(numerator / divisor, denominator / divisor)
202 }
203 _ => Self::Float(self.as_float() + other.as_float()),
204 }
205 }
206
207 fn minus(self, other: Self) -> Self {
208 let negated = match other {
209 Self::Float(value) => Self::Float(-value),
210 Self::Fraction(numerator, denominator) => Self::Fraction(-numerator, denominator),
211 };
212 self.plus(negated)
213 }
214
215 fn exact(self) -> (i128, i128) {
217 match self {
218 Self::Float(value) => integer_ratio(value),
219 Self::Fraction(numerator, denominator) => (numerator, denominator),
220 }
221 }
222
223 fn at_most(self, other: Self) -> bool {
225 let ((a, b), (c, d)) = (self.exact(), other.exact());
226 a * d <= c * b
227 }
228}
229
230fn missing(class: &str) -> Error {
231 Error::Analysis(format!(
232 "cannot create correlation: an object that is not found in the Stream: {class}"
233 ))
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use crate::dynamics::Dynamic;
240 use crate::tinynotation::from_tiny_notation;
241
242 #[test]
243 fn pitches_are_paired_with_the_dynamics_they_sound_under() -> Result<()> {
244 let mut line = from_tiny_notation("4/4 c4 d e f")?;
245 line.insert(0.0, Dynamic::new("p"));
246 line.insert(2.0, Dynamic::new("f"));
247 assert_eq!(
250 pitch_to_dynamic(&line)?,
251 [(60.0, 5), (62.0, 5), (64.0, 5), (64.0, 8), (65.0, 8)]
252 );
253 assert_eq!(pitch_to_dynamic_counts(&line)?.len(), 5);
254 assert!(pitch_to_dynamic(&from_tiny_notation("4/4 c4")?).is_err());
255 Ok(())
256 }
257}