1use crate::defaults::{FloatType, IntegerType};
25use crate::error::{Error, Result};
26use crate::expressions::{Ornament, OrnamentKind};
27use crate::interval::{Interval, IntervalDirection};
28use crate::makenotation::op_frac;
29use crate::note::Note;
30use crate::stream::StreamElement;
31
32pub fn ornament_total_quarter_length(
36 busy: &[StreamElement],
37 simple: &[StreamElement],
38) -> FloatType {
39 match simple.first() {
40 Some(note) => note.quarter_length(),
41 None => op_frac(busy.iter().map(StreamElement::quarter_length).sum()),
42 }
43}
44
45pub fn ornament_note_quarter_length(busy: &[StreamElement], simple: &[StreamElement]) -> FloatType {
48 op_frac(ornament_total_quarter_length(busy, simple) / busy.len() as FloatType)
49}
50
51fn note_of(element: &StreamElement) -> Result<&Note> {
53 match element {
54 StreamElement::Note(note) => Ok(note),
55 other => {
56 let class = match other {
57 StreamElement::Rest(_) => "Rest",
58 StreamElement::Chord(_) => "Chord",
59 StreamElement::Unpitched(_) => "Unpitched",
60 StreamElement::PercussionChord(_) => "PercussionChord",
61 StreamElement::ChordSymbol(_) => "ChordSymbol",
62 _ => "Music21Object",
63 };
64 Err(Error::Analysis(format!(
65 "'{class}' object has no attribute 'pitch'"
66 )))
67 }
68 }
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
73#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
74pub struct TrillRecognizer {
75 pub check_nachschlag: bool,
78 pub acceptable_interval: IntegerType,
81 pub minimum_length_for_nachschlag: usize,
84}
85
86impl Default for TrillRecognizer {
87 fn default() -> Self {
88 Self {
89 check_nachschlag: false,
90 acceptable_interval: 3,
91 minimum_length_for_nachschlag: 5,
92 }
93 }
94}
95
96impl TrillRecognizer {
97 pub fn recognize(
112 &self,
113 busy: &[StreamElement],
114 simple: &[StreamElement],
115 ) -> Result<Option<Ornament>> {
116 if busy.len() <= 2 {
117 return Ok(None);
118 }
119 let (StreamElement::Note(first), StreamElement::Note(second)) = (&busy[0], &busy[1]) else {
120 return Ok(None);
121 };
122 if (first.pitch().midi() - second.pitch().midi()).abs() > self.acceptable_interval {
123 return Ok(None);
124 }
125 let mut alternating = true;
126 let mut reached = 0;
127 for (index, element) in busy.iter().enumerate() {
128 reached = index;
129 let StreamElement::Note(note) = element else {
130 return Ok(None);
131 };
132 let expected = if index % 2 == 0 { first } else { second };
133 if note.pitch() != expected.pitch() {
134 alternating = false;
135 break;
136 }
137 }
138 let nachschlag = if alternating {
139 false
140 } else if !self.check_nachschlag {
141 return Ok(None);
142 } else if busy.len() >= self.minimum_length_for_nachschlag
143 && reached as FloatType >= busy.len() as FloatType / 2.0
144 {
145 true
146 } else {
147 return Ok(None);
148 };
149
150 let (start, end) = match simple.first() {
151 None => (first, second),
152 Some(written) => {
153 let midi = note_of(written)?.pitch().midi();
154 if midi != first.pitch().midi() && midi != second.pitch().midi() {
155 return Ok(None);
156 }
157 if midi == second.pitch().midi() {
158 (second, first)
159 } else {
160 (first, second)
161 }
162 }
163 };
164 let mut trill = Ornament::of_kind(if start.pitch().midi() <= end.pitch().midi() {
165 OrnamentKind::Trill
166 } else {
167 OrnamentKind::InvertedTrill
168 });
169 trill.set_quarter_length(ornament_note_quarter_length(busy, simple));
170 trill.set_nachschlag(nachschlag);
171 if let Some(accidental) = end.pitch().written_accidental() {
172 trill.set_accidental(Some(accidental.clone()))?;
173 }
174 Ok(Some(trill))
175 }
176}
177
178#[derive(Clone, Debug, PartialEq)]
180#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
181pub struct TurnRecognizer {
182 pub acceptable_intervals: Vec<Interval>,
185}
186
187impl Default for TurnRecognizer {
188 fn default() -> Self {
189 Self {
190 acceptable_intervals: ["M2", "M-2", "m2", "m-2", "A2", "A-2"]
191 .iter()
192 .map(|name| Interval::from_name(*name).expect("a second"))
193 .collect(),
194 }
195 }
196}
197
198impl TurnRecognizer {
199 pub fn is_acceptable_interval(&self, step: &Interval) -> bool {
201 self.acceptable_intervals.contains(step)
202 }
203
204 pub fn recognize(
217 &self,
218 busy: &[StreamElement],
219 simple: &[StreamElement],
220 ) -> Result<Option<Ornament>> {
221 if busy.len() != 4 {
222 return Ok(None);
223 }
224 if let Some(written) = simple.first() {
225 let played: FloatType = busy.iter().map(StreamElement::quarter_length).sum();
226 if (written.quarter_length() - played).abs() > 0.1 {
227 return Ok(None);
228 }
229 }
230 let main = note_of(&busy[1])?.pitch().midi();
231 if main != note_of(&busy[3])?.pitch().midi() {
232 return Ok(None);
233 }
234 if let Some(written) = simple.first()
235 && note_of(written)?.pitch().midi() != main
236 {
237 return Ok(None);
238 }
239 let mut steps = Vec::new();
240 for pair in busy.windows(2) {
241 let step =
242 Interval::between_pitches(note_of(&pair[0])?.pitch(), note_of(&pair[1])?.pitch())?;
243 if !self.is_acceptable_interval(&step) {
244 return Ok(None);
245 }
246 steps.push(step);
247 }
248 if steps[0].direction() != steps[1].direction()
249 || steps[1].direction() == steps[2].direction()
250 {
251 return Ok(None);
252 }
253 let mut turn =
254 Ornament::of_kind(if steps[0].direction() == IntervalDirection::Descending {
255 OrnamentKind::Turn
256 } else {
257 OrnamentKind::InvertedTurn
258 });
259 turn.set_quarter_length(ornament_note_quarter_length(busy, simple));
260 Ok(Some(turn))
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267 use crate::duration::Duration;
268 use crate::key::KeySignature;
269 use crate::rest::Rest;
270
271 fn notes(names: &[&str], length: FloatType) -> Vec<StreamElement> {
272 names
273 .iter()
274 .map(|name| {
275 StreamElement::Note(
276 Note::from_name(name)
277 .unwrap()
278 .with_duration(Duration::new(length).unwrap()),
279 )
280 })
281 .collect()
282 }
283
284 fn quarter(name: &str) -> Vec<StreamElement> {
285 notes(&[name], 1.0)
286 }
287
288 #[test]
290 fn turns_are_recognized_as_music21_recognizes_them() {
291 let recognizer = TurnRecognizer::default();
292 let kind = |busy: &[StreamElement], simple: &[StreamElement]| {
293 recognizer
294 .recognize(busy, simple)
295 .unwrap()
296 .map(|turn| turn.kind())
297 };
298 let even = notes(&["G", "F#", "E", "F#"], 0.25);
299 assert_eq!(kind(&even, &[]), Some(OrnamentKind::Turn));
300 assert_eq!(kind(&even, &quarter("F#")), Some(OrnamentKind::Turn));
301 assert_eq!(kind(&even, &quarter("G-")), Some(OrnamentKind::Turn));
302 assert_eq!(kind(&even, &quarter("G")), None);
303 assert_eq!(kind(&even, &quarter("A")), None);
304 let mut rubato = notes(&["G", "F#", "E", "F#"], 0.25);
305 for (element, length) in rubato.iter_mut().zip([0.25, 0.15, 0.2, 0.4]) {
306 if let StreamElement::Note(note) = element {
307 note.set_duration(Duration::new(length).unwrap());
308 }
309 }
310 assert_eq!(kind(&rubato, &[]), Some(OrnamentKind::Turn));
311 assert_eq!(
312 kind(¬es(&["E", "F#", "G", "F#"], 0.25), &[]),
313 Some(OrnamentKind::InvertedTurn)
314 );
315 for wrong in [
316 &["G", "F#", "E", "D"][..],
317 &["E", "G", "A", "G"],
318 &["G", "F#", "G", "F#"],
319 &["G", "F#", "E", "F#", "E"],
320 &["G", "F#", "E"],
321 ] {
322 assert_eq!(kind(¬es(wrong, 0.25), &[]), None, "{wrong:?}");
323 }
324 assert_eq!(
326 kind(¬es(&["G", "F#", "E", "F#"], 1.0), &quarter("F#")),
327 None
328 );
329 }
330
331 #[test]
333 fn trills_are_recognized_as_music21_recognizes_them() {
334 let plain = TrillRecognizer::default();
335 let size = |trill: &Ornament, from: &str| {
336 trill
337 .size(
338 &crate::pitch::Pitch::from_name(from).unwrap(),
339 &KeySignature::new(0),
340 )
341 .unwrap()
342 .directed_name()
343 };
344 let gaga = notes(&["G", "A", "G", "A"], 0.25);
345 let trill = plain.recognize(&gaga, &[]).unwrap().unwrap();
346 assert_eq!(trill.kind(), OrnamentKind::Trill);
347 assert_eq!(size(&trill, "G"), "M2");
348 let trill = plain.recognize(&gaga, &quarter("A")).unwrap().unwrap();
349 assert_eq!(trill.kind(), OrnamentKind::InvertedTrill);
350 assert_eq!(size(&trill, "A"), "M-2");
351 assert!(plain.recognize(&gaga, &quarter("G##")).unwrap().is_some());
352 assert!(plain.recognize(&gaga, &quarter("E")).unwrap().is_none());
353 let mut with_rest = gaga.clone();
354 with_rest[2] = StreamElement::Rest(Rest::new(Duration::new(0.25).unwrap()));
355 assert!(plain.recognize(&with_rest, &[]).unwrap().is_none());
356
357 let a_g = notes(&["A", "G#", "A", "G#", "A"], 0.4);
358 let trill = plain.recognize(&a_g, &[]).unwrap().unwrap();
359 assert_eq!(trill.kind(), OrnamentKind::InvertedTrill);
360 assert_eq!(size(&trill, "A"), "m-2");
361
362 let nachschlag = notes(&["C5", "B", "C5", "B", "C5", "D5", "E5", "F5"], 0.125);
363 assert!(plain.recognize(&nachschlag, &[]).unwrap().is_none());
364 let checking = TrillRecognizer {
365 check_nachschlag: true,
366 ..TrillRecognizer::default()
367 };
368 let trill = checking.recognize(&nachschlag, &[]).unwrap().unwrap();
369 assert!(trill.nachschlag());
370 assert_eq!(size(&trill, "C5"), "m-2");
371
372 assert!(plain.recognize(¬es(&["A"], 0.5), &[]).unwrap().is_none());
373 assert!(
374 plain
375 .recognize(¬es(&["A", "G"], 0.5), &[])
376 .unwrap()
377 .is_none()
378 );
379 assert!(
380 plain
381 .recognize(¬es(&["A", "C", "A", "C"], 0.5), &[])
382 .unwrap()
383 .is_none()
384 );
385 assert!(
386 plain
387 .recognize(¬es(&["F", "E", "F", "G"], 0.5), &[])
388 .unwrap()
389 .is_none()
390 );
391 }
392}