1use crate::{
4 defaults::FloatType,
5 display::Drawn,
6 duration::Duration,
7 error::{Error, Result},
8 notation::Placement,
9 stream::Stream,
10};
11
12pub const DEFAULT_TEMPO_VALUES: [(&str, FloatType); 30] = [
15 ("larghissimo", 16.0),
16 ("largamente", 32.0),
17 ("grave", 40.0),
18 ("molto adagio", 40.0),
19 ("largo", 46.0),
20 ("lento", 52.0),
21 ("adagio", 56.0),
22 ("slow", 56.0),
23 ("langsam", 56.0),
24 ("larghetto", 60.0),
25 ("adagietto", 66.0),
26 ("andante", 72.0),
27 ("andantino", 80.0),
28 ("andante moderato", 83.0),
29 ("maestoso", 88.0),
30 ("moderato", 92.0),
31 ("moderate", 92.0),
32 ("allegretto", 108.0),
33 ("animato", 120.0),
34 ("allegro moderato", 128.0),
35 ("allegro", 132.0),
36 ("fast", 132.0),
37 ("schnell", 132.0),
38 ("allegrissimo", 140.0),
39 ("molto allegro", 144.0),
40 ("très vite", 144.0),
41 ("vivace", 160.0),
42 ("vivacissimo", 168.0),
43 ("presto", 184.0),
44 ("prestissimo", 208.0),
45];
46
47pub fn convert_tempo_by_referent(
52 number: FloatType,
53 source_quarter_length: FloatType,
54 destination_quarter_length: FloatType,
55) -> FloatType {
56 let seconds_per_source_beat = 60.0 / number;
57 let seconds_per_quarter = seconds_per_source_beat / source_quarter_length;
58 60.0 / (seconds_per_quarter * destination_quarter_length)
59}
60
61fn whole_where_nearly(number: FloatType) -> FloatType {
66 let rounded = number.round_ties_even();
67 if (rounded - number).abs() < 1e-6 {
68 rounded
69 } else {
70 number
71 }
72}
73
74pub fn default_text_for_number(number: FloatType) -> Option<&'static str> {
78 let mut sorted = DEFAULT_TEMPO_VALUES;
79 sorted.sort_by(|left, right| {
80 left.1
81 .partial_cmp(&right.1)
82 .unwrap_or(std::cmp::Ordering::Equal)
83 .then_with(|| left.0.cmp(right.0))
84 });
85 sorted
86 .iter()
87 .find(|(_, value)| (value - 2.0..=value + 2.0).contains(&number))
88 .map(|(text, _)| *text)
89}
90
91pub fn default_number_for_text(text: &str) -> Option<FloatType> {
94 let lowered = text.to_lowercase();
95 let lookup = |candidate: &str| {
96 DEFAULT_TEMPO_VALUES
97 .iter()
98 .find(|(name, _)| *name == candidate)
99 .map(|(_, value)| *value)
100 };
101 lookup(&lowered).or_else(|| lookup(text)).or_else(|| {
102 text.split(' ')
103 .filter_map(|word| lookup(&word.to_lowercase()))
104 .next_back()
105 })
106}
107
108#[derive(Clone, Debug, PartialEq)]
114#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
115#[must_use]
116pub struct MetronomeMark {
117 number: Option<FloatType>,
118 text: Option<String>,
119 referent: Duration,
120 number_implicit: bool,
121 text_implicit: bool,
122 #[cfg_attr(feature = "serde", serde(default))]
124 number_sounding: Option<FloatType>,
125 #[cfg_attr(feature = "serde", serde(default))]
126 parentheses: Drawn<bool>,
127 #[cfg_attr(feature = "serde", serde(default))]
128 placement: Drawn<Option<Placement>>,
129}
130
131impl Default for MetronomeMark {
132 fn default() -> Self {
135 Self::build(None, None, Duration::quarter())
136 }
137}
138
139impl MetronomeMark {
140 pub fn new(number: FloatType) -> Self {
143 Self::build(Some(number), None, Duration::quarter())
144 }
145
146 pub fn from_text(text: impl Into<String>) -> Self {
149 Self::build(None, Some(text.into()), Duration::quarter())
150 }
151
152 pub fn with_number_and_text(number: FloatType, text: impl Into<String>) -> Self {
154 Self::build(Some(number), Some(text.into()), Duration::quarter())
155 }
156
157 pub fn with_referent(mut self, referent: Duration) -> Self {
160 self.referent = referent;
161 self
162 }
163
164 fn build(number: Option<FloatType>, text: Option<String>, referent: Duration) -> Self {
165 let number = number.map(whole_where_nearly);
166 let number_implicit = number.is_none();
167 let text_implicit = text.is_none();
168 let number = number.or_else(|| text.as_deref().and_then(default_number_for_text));
169 let text = text.or_else(|| number.and_then(default_text_for_number).map(String::from));
170 Self {
171 number_implicit: number_implicit && number.is_some(),
172 text_implicit: text_implicit && text.is_some(),
173 number,
174 text,
175 referent,
176 number_sounding: None,
177 parentheses: Drawn(false),
178 placement: Drawn(None),
179 }
180 }
181
182 pub fn parentheses(&self) -> bool {
185 self.parentheses.0
186 }
187
188 pub fn set_parentheses(&mut self, parentheses: bool) {
190 self.parentheses.0 = parentheses;
191 }
192
193 pub fn placement(&self) -> Option<Placement> {
195 self.placement.0
196 }
197
198 pub fn set_placement(&mut self, placement: Option<Placement>) {
200 self.placement.0 = placement;
201 }
202
203 pub fn equivalent_by_referent(&self, referent: Duration) -> MetronomeMark {
207 let number = self.number.map(|number| {
208 convert_tempo_by_referent(
209 number,
210 self.referent.quarter_length(),
211 referent.quarter_length(),
212 )
213 });
214 Self::build(number, self.text.clone(), referent)
215 }
216
217 pub fn maintained_number_with_referent(&self, referent: Duration) -> MetronomeMark {
220 Self::build(self.number, self.text.clone(), referent)
221 }
222
223 pub fn number(&self) -> Option<FloatType> {
225 self.number
226 }
227
228 pub fn set_number(&mut self, number: Option<FloatType>) {
231 let number = number.map(whole_where_nearly);
232 self.number = number;
233 self.number_implicit = false;
234 if self.text.is_none()
235 && let Some(number) = number
236 && let Some(text) = default_text_for_number(number)
237 {
238 self.text = Some(text.to_string());
239 self.text_implicit = true;
240 }
241 }
242
243 pub fn set_text(&mut self, text: impl Into<String>) {
246 let text = text.into();
247 self.text_implicit = false;
248 if self.number.is_none()
249 && let Some(number) = default_number_for_text(&text)
250 {
251 self.number = Some(number);
252 self.number_implicit = true;
253 }
254 self.text = Some(text);
255 }
256
257 pub fn set_referent(&mut self, referent: Duration) {
260 self.referent = referent;
261 }
262
263 pub fn text(&self) -> Option<&str> {
265 self.text.as_deref()
266 }
267
268 pub fn text_expression(&self, return_implicit: bool) -> Option<&str> {
272 if self.text_implicit && !return_implicit {
273 return None;
274 }
275 self.text.as_deref()
276 }
277
278 pub fn referent(&self) -> &Duration {
280 &self.referent
281 }
282
283 pub fn number_implicit(&self) -> bool {
285 self.number_implicit
286 }
287
288 pub fn set_number_implicit(&mut self, implicit: bool) {
292 self.number_implicit = implicit;
293 }
294
295 pub fn text_implicit(&self) -> bool {
297 self.text_implicit
298 }
299
300 pub fn number_sounding(&self) -> Option<FloatType> {
309 self.number_sounding
310 }
311
312 pub fn set_number_sounding(&mut self, number: Option<FloatType>) {
314 self.number_sounding = number;
315 }
316
317 pub fn with_number_sounding(mut self, number: FloatType) -> Self {
323 self.number_sounding = Some(number);
324 self
325 }
326
327 pub fn quarter_bpm(&self) -> Option<FloatType> {
329 self.number
330 .map(|number| convert_tempo_by_referent(number, self.referent.quarter_length(), 1.0))
331 }
332
333 pub fn sounding_quarter_bpm(&self) -> Option<FloatType> {
336 match self.number_sounding {
337 Some(number) => Some(convert_tempo_by_referent(
338 number,
339 self.referent.quarter_length(),
340 1.0,
341 )),
342 None => self.quarter_bpm(),
343 }
344 }
345
346 pub fn seconds_per_quarter(&self) -> Result<FloatType> {
352 let quarter_bpm = self.sounding_quarter_bpm().ok_or_else(|| {
357 Error::Tempo("cannot derive seconds as getQuarterBPM() returns None".to_string())
358 })?;
359 if quarter_bpm == 0.0 {
360 return Err(Error::Tempo(
361 "a tempo of no beats a minute lasts no seconds a beat".to_string(),
362 ));
363 }
364 Ok(60.0 / quarter_bpm)
365 }
366
367 pub fn quarter_length_to_seconds(&self, quarter_length: FloatType) -> Result<FloatType> {
369 Ok(self.seconds_per_quarter()? * quarter_length)
370 }
371
372 pub fn duration_to_seconds(&self, duration: &Duration) -> Result<FloatType> {
374 self.quarter_length_to_seconds(duration.quarter_length())
375 }
376
377 pub fn seconds_to_duration(&self, seconds: FloatType) -> Result<Duration> {
379 if seconds.is_nan() || seconds <= 0.0 {
380 return Err(Error::Tempo(
381 "seconds must be a number greater than zero".to_string(),
382 ));
383 }
384 Duration::new(seconds / self.seconds_per_quarter()?)
385 }
386}
387
388pub fn interpolate_elements(
402 source: &Stream,
403 destination: &mut Stream,
404 start: (FloatType, FloatType),
405 end: (FloatType, FloatType),
406) -> Result<()> {
407 let (start_source, end_source) = (start.0, end.0);
408 interpolated_offset(start_source, start, end)?;
411 for event in source.events() {
412 if event.offset() > start_source && event.offset() < end_source {
413 destination.insert(
414 interpolated_offset(event.offset(), start, end)?,
415 event.element().clone(),
416 );
417 }
418 }
419 Ok(())
420}
421
422pub fn interpolated_offset(
437 offset: FloatType,
438 start: (FloatType, FloatType),
439 end: (FloatType, FloatType),
440) -> Result<FloatType> {
441 let ((start_source, start_destination), (end_source, end_destination)) = (start, end);
442 if end_source == start_source {
443 return Err(Error::Tempo(
444 "the two points stand at the same offset in the source, so nothing lies between them"
445 .to_string(),
446 ));
447 }
448 let scale = (end_destination - start_destination) / (end_source - start_source);
449 Ok(scale * (offset - start_source) + start_destination)
450}
451
452#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
455#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
456pub enum ModulationSide {
457 Old,
459 New,
461}
462
463#[derive(Clone, Debug, PartialEq)]
474#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
475#[must_use]
476pub struct TempoText {
477 text: String,
478 #[cfg_attr(feature = "serde", serde(default))]
479 placement: Drawn<Option<Placement>>,
480}
481
482impl TempoText {
483 pub fn new(text: impl Into<String>) -> Self {
485 Self {
486 text: text.into(),
487 placement: Drawn(None),
488 }
489 }
490
491 pub fn placement(&self) -> Option<Placement> {
493 self.placement.0
494 }
495
496 pub fn set_placement(&mut self, placement: Option<Placement>) {
498 self.placement.0 = placement;
499 }
500
501 pub fn text(&self) -> &str {
503 &self.text
504 }
505
506 pub fn set_text(&mut self, text: impl Into<String>) {
508 self.text = text.into();
509 }
510
511 pub fn metronome_mark(&self) -> MetronomeMark {
513 MetronomeMark::from_text(self.text.clone())
514 }
515
516 pub fn is_common_tempo_text(&self) -> bool {
520 is_common_tempo_text(&self.text)
521 }
522}
523
524pub fn is_common_tempo_text(text: &str) -> bool {
527 let stripped = |value: &str| {
528 value
529 .trim()
530 .chars()
531 .filter(|character| *character != ' ' && *character != '.')
532 .collect::<String>()
533 .to_lowercase()
534 };
535 let text = stripped(text);
536 DEFAULT_TEMPO_VALUES.iter().any(|(candidate, _)| {
537 let candidate = stripped(candidate);
538 text.contains(&candidate) || candidate.contains(&text)
539 })
540}
541
542#[derive(Clone, Debug, PartialEq)]
562#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
563#[must_use]
564pub struct MetricModulation {
565 old: Option<MetronomeMark>,
566 new: Option<MetronomeMark>,
567 classical_style: bool,
568 maintain_beat: bool,
569 transition_symbol: String,
570 arrow_direction: Option<String>,
571 parentheses: bool,
572 #[cfg_attr(feature = "serde", serde(default))]
573 placement: Drawn<Option<Placement>>,
574}
575
576impl Default for MetricModulation {
577 fn default() -> Self {
578 Self::new()
579 }
580}
581
582impl MetricModulation {
583 pub fn new() -> Self {
585 Self {
586 old: None,
587 new: None,
588 classical_style: false,
589 maintain_beat: false,
590 transition_symbol: "=".to_string(),
591 arrow_direction: None,
592 parentheses: false,
593 placement: Drawn(None),
594 }
595 }
596
597 pub fn placement(&self) -> Option<Placement> {
600 self.placement.0
601 }
602
603 pub fn set_placement(&mut self, placement: Option<Placement>) {
605 self.placement.0 = placement;
606 }
607
608 pub fn old_metronome(&self) -> Option<&MetronomeMark> {
610 self.old.as_ref()
611 }
612
613 pub fn new_metronome(&self) -> Option<&MetronomeMark> {
615 self.new.as_ref()
616 }
617
618 pub fn set_old_metronome(&mut self, mark: Option<MetronomeMark>) {
620 self.old = mark;
621 }
622
623 pub fn set_new_metronome(&mut self, mark: Option<MetronomeMark>) {
625 self.new = mark;
626 }
627
628 pub fn old_referent(&self) -> Option<&Duration> {
630 self.old.as_ref().map(MetronomeMark::referent)
631 }
632
633 pub fn new_referent(&self) -> Option<&Duration> {
635 self.new.as_ref().map(MetronomeMark::referent)
636 }
637
638 pub fn set_old_referent(&mut self, referent: Duration, previous: Option<&MetronomeMark>) {
643 self.old = Some(match (&self.old, previous) {
644 (Some(old), _) => old.equivalent_by_referent(referent),
645 (None, Some(previous)) => previous.equivalent_by_referent(referent),
646 (None, None) => MetronomeMark::default().with_referent(referent),
647 });
648 }
649
650 pub fn set_new_referent(&mut self, referent: Duration) {
655 self.new = Some(match (&self.new, &self.old) {
656 (Some(new), _) => new.equivalent_by_referent(referent),
657 (None, Some(old)) => old.maintained_number_with_referent(referent),
658 (None, None) => MetronomeMark::default().with_referent(referent),
659 });
660 }
661
662 pub fn number(&self) -> Option<FloatType> {
664 self.new.as_ref().and_then(MetronomeMark::number)
665 }
666
667 pub fn update_from(&mut self, previous: Option<&MetronomeMark>) {
676 if let Some(previous) = previous {
677 self.old = Some(match &self.old {
678 Some(old) => previous.equivalent_by_referent(old.referent().clone()),
679 None => previous.maintained_number_with_referent(previous.referent().clone()),
680 });
681 }
682 let number = self.old.as_ref().and_then(MetronomeMark::number);
683 if let (Some(new), Some(number)) = (self.new.as_mut(), number) {
684 new.set_number(Some(number));
685 }
686 }
687
688 pub fn set_equality_by_referent(
693 &mut self,
694 side: Option<ModulationSide>,
695 referent: Duration,
696 ) -> Result<()> {
697 self.set_from_other(side, |other| other.equivalent_by_referent(referent))
698 }
699
700 pub fn set_other_by_referent(
704 &mut self,
705 side: Option<ModulationSide>,
706 referent: Duration,
707 ) -> Result<()> {
708 self.set_from_other(side, |other| {
709 other.maintained_number_with_referent(referent)
710 })
711 }
712
713 fn side_to_set(&self, side: Option<ModulationSide>) -> Result<ModulationSide> {
714 side.or(match (&self.old, &self.new) {
715 (None, _) => Some(ModulationSide::Old),
716 (_, None) => Some(ModulationSide::New),
717 _ => None,
718 })
719 .ok_or_else(|| Error::Tempo("cannot set equality for a side of None".to_string()))
720 }
721
722 fn no_other_side() -> Error {
723 Error::Tempo("there is no mark on the other side to take the tempo from".to_string())
724 }
725
726 fn set_from_other(
728 &mut self,
729 side: Option<ModulationSide>,
730 derive: impl FnOnce(&MetronomeMark) -> MetronomeMark,
731 ) -> Result<()> {
732 let (target, other) = match self.side_to_set(side)? {
733 ModulationSide::New => (&mut self.new, &self.old),
734 ModulationSide::Old => (&mut self.old, &self.new),
735 };
736 let other = other.as_ref().ok_or_else(Self::no_other_side)?;
737 *target = Some(derive(other));
738 Ok(())
739 }
740
741 pub fn classical_style(&self) -> bool {
744 self.classical_style
745 }
746
747 pub fn set_classical_style(&mut self, classical: bool) {
749 self.classical_style = classical;
750 }
751
752 pub fn maintain_beat(&self) -> bool {
755 self.maintain_beat
756 }
757
758 pub fn set_maintain_beat(&mut self, maintain: bool) {
760 self.maintain_beat = maintain;
761 }
762
763 pub fn transition_symbol(&self) -> &str {
766 &self.transition_symbol
767 }
768
769 pub fn set_transition_symbol(&mut self, symbol: impl Into<String>) {
771 self.transition_symbol = symbol.into();
772 }
773
774 pub fn arrow_direction(&self) -> Option<&str> {
777 self.arrow_direction.as_deref()
778 }
779
780 pub fn set_arrow_direction(&mut self, direction: Option<String>) {
782 self.arrow_direction = direction;
783 }
784
785 pub fn parentheses(&self) -> bool {
787 self.parentheses
788 }
789
790 pub fn set_parentheses(&mut self, parentheses: bool) {
792 self.parentheses = parentheses;
793 }
794}
795
796#[cfg(test)]
797mod tests {
798 #[test]
799 fn a_number_a_hair_off_a_whole_one_is_that_whole_number() {
800 let mark = super::MetronomeMark::new(110.0)
803 .with_referent(super::Duration::from_type_with_dots(
804 crate::duration::DurationType::Quarter,
805 1,
806 ))
807 .equivalent_by_referent(super::Duration::quarter());
808 assert_eq!(mark.number(), Some(165.0));
809 let dotted = mark.maintained_number_with_referent(super::Duration::from_type_with_dots(
810 crate::duration::DurationType::Quarter,
811 1,
812 ));
813 assert_eq!(dotted.quarter_bpm(), Some(247.5));
814 }
815
816 use super::*;
817
818 #[test]
819 fn a_mark_can_be_rewritten_one_half_at_a_time() {
820 let mut mark = MetronomeMark::from_text("adagio");
821 assert!(mark.number_implicit());
822 mark.set_number(Some(60.0));
823 assert!(!mark.number_implicit());
824 assert_eq!(mark.number(), Some(60.0));
825 mark.set_number_implicit(true);
826 assert!(mark.number_implicit());
827 mark.set_referent(Duration::half());
828 assert_eq!(mark.referent().quarter_length(), 2.0);
829 assert_eq!(mark.quarter_bpm(), Some(120.0));
830
831 let mut numbered = MetronomeMark::new(90.0);
832 assert!(numbered.text_implicit());
833 numbered.set_text("largo");
834 assert!(!numbered.text_implicit());
835 assert_eq!(numbered.text(), Some("largo"));
836 assert_eq!(numbered.number(), Some(90.0));
837 let mut wordless = MetronomeMark::default();
838 wordless.set_number(Some(184.0));
839 assert_eq!(wordless.text(), Some("presto"));
840 assert!(wordless.text_implicit());
841 wordless.set_number(None);
842 assert_eq!(wordless.number(), None);
843 }
844
845 #[test]
847 fn an_implied_word_is_only_answered_when_asked_for() {
848 let presto = MetronomeMark::from_text("presto");
849 assert_eq!(presto.number(), Some(184.0));
850 assert_eq!(presto.text_expression(false), Some("presto"));
851
852 let ninety = MetronomeMark::new(90.0);
853 assert!(ninety.text_implicit());
854 assert_eq!(ninety.text_expression(false), None);
855 assert_eq!(ninety.text_expression(true), Some("maestoso"));
856 }
857
858 #[test]
859 fn a_mark_may_be_played_faster_than_it_is_written() {
860 let playback = MetronomeMark::default().with_number_sounding(168.0);
862 assert_eq!(playback.number_sounding(), Some(168.0));
863 assert_eq!(playback.number(), None);
865 assert_eq!(playback.text(), None);
866 assert_eq!(playback.quarter_bpm(), None);
867 assert_eq!(playback.sounding_quarter_bpm(), Some(168.0));
868
869 let mut written = MetronomeMark::new(60.0).with_referent(Duration::half());
872 assert_eq!(written.quarter_bpm(), Some(120.0));
873 assert_eq!(written.sounding_quarter_bpm(), Some(120.0));
874 written.set_number_sounding(Some(90.0));
875 assert_eq!(written.number(), Some(60.0));
876 assert_eq!(written.quarter_bpm(), Some(120.0));
877 assert_eq!(written.sounding_quarter_bpm(), Some(180.0));
878 }
879
880 #[test]
881 fn referent_changes_match_music21() {
882 let quarter_sixty = MetronomeMark::new(60.0);
883 let eighths = quarter_sixty.equivalent_by_referent(Duration::eighth());
884 assert_eq!(eighths.number(), Some(120.0));
885 assert_eq!(eighths.referent().quarter_length(), 0.5);
886 assert_eq!(eighths.text(), Some("larghetto"));
887 let halves = quarter_sixty.equivalent_by_referent(Duration::half());
888 assert_eq!(halves.number(), Some(30.0));
889
890 let andante = MetronomeMark::with_number_and_text(72.0, "andante")
891 .with_referent(Duration::new(1.5).unwrap());
892 let quarters = andante.equivalent_by_referent(Duration::quarter());
893 assert_eq!(quarters.number(), Some(108.0));
894 assert_eq!(quarters.text(), Some("andante"));
895 assert_eq!(
896 andante.equivalent_by_referent(Duration::half()).number(),
897 Some(54.0)
898 );
899
900 let kept = andante.maintained_number_with_referent(Duration::eighth());
901 assert_eq!(kept.number(), Some(72.0));
902 assert_eq!(kept.referent().quarter_length(), 0.5);
903 assert_eq!(kept.text(), Some("andante"));
904 }
905
906 #[test]
907 fn numbers_imply_the_tempo_words_music21_picks() {
908 let cases = [
909 (120.0, Some("animato")),
910 (60.0, Some("larghetto")),
911 (61.0, Some("larghetto")),
912 (62.0, Some("larghetto")),
913 (63.0, None),
914 (90.0, Some("maestoso")),
915 (92.0, Some("moderate")),
916 (100.0, None),
917 (118.0, Some("animato")),
918 (122.0, Some("animato")),
919 (123.0, None),
920 (16.0, Some("larghissimo")),
921 (12.0, None),
922 (56.5, Some("adagio")),
923 (300.0, None),
924 ];
925 for (number, text) in cases {
926 let mark = MetronomeMark::new(number);
927 assert_eq!(mark.text(), text, "{number}");
928 assert_eq!(mark.text_implicit(), text.is_some(), "{number}");
929 assert!(!mark.number_implicit());
930 }
931 }
932
933 #[test]
934 fn words_imply_their_conventional_numbers() {
935 let cases = [
936 ("allegro", Some(132.0)),
937 ("Allegro", Some(132.0)),
938 ("ALLEGRO ", Some(132.0)),
939 ("très vite", Some(144.0)),
940 ("andante moderato", Some(83.0)),
941 ("unknown words", None),
942 ];
943 for (text, number) in cases {
944 let mark = MetronomeMark::from_text(text);
945 assert_eq!(mark.number(), number, "{text}");
946 assert_eq!(mark.number_implicit(), number.is_some(), "{text}");
947 assert_eq!(mark.text(), Some(text));
948 assert!(!mark.text_implicit());
949 }
950 let both = MetronomeMark::with_number_and_text(120.0, "fast");
951 assert_eq!(both.number(), Some(120.0));
952 assert_eq!(both.text(), Some("fast"));
953 assert!(!both.number_implicit() && !both.text_implicit());
954 }
955
956 #[test]
957 fn referents_convert_to_quarter_bpm_and_seconds() {
958 let quarter = MetronomeMark::new(120.0);
959 assert_eq!(quarter.quarter_bpm(), Some(120.0));
960 assert_eq!(quarter.quarter_length_to_seconds(1.0).unwrap(), 0.5);
961 assert_eq!(
962 quarter.seconds_to_duration(0.75).unwrap().quarter_length(),
963 1.5
964 );
965
966 let half = MetronomeMark::new(60.0).with_referent(Duration::half());
967 assert_eq!(half.quarter_bpm(), Some(120.0));
968 assert_eq!(half.text(), Some("larghetto"));
969 assert_eq!(half.quarter_length_to_seconds(1.0).unwrap(), 0.5);
970 assert_eq!(
971 half.duration_to_seconds(&Duration::new(3.0).unwrap())
972 .unwrap(),
973 1.5
974 );
975 assert_eq!(half.seconds_to_duration(1.0).unwrap().quarter_length(), 2.0);
976
977 let eighth = MetronomeMark::new(120.0).with_referent(Duration::eighth());
978 assert_eq!(eighth.quarter_bpm(), Some(60.0));
979 assert_eq!(eighth.seconds_per_quarter().unwrap(), 1.0);
980
981 let dotted = MetronomeMark::new(56.5).with_referent(Duration::half());
982 assert!((dotted.quarter_bpm().unwrap() - 113.0).abs() < 1e-9);
983
984 assert_eq!(convert_tempo_by_referent(60.0, 1.0, 2.0), 30.0);
985 assert_eq!(convert_tempo_by_referent(60.0, 2.0, 1.0), 120.0);
986
987 let unknown = MetronomeMark::from_text("unknown words");
988 assert_eq!(unknown.quarter_bpm(), None);
989 assert!(unknown.seconds_per_quarter().is_err());
990
991 let mut playback = MetronomeMark::from_text("unknown words");
995 playback.set_number_sounding(Some(96.0));
996 assert_eq!(playback.number(), None);
997 assert_eq!(playback.sounding_quarter_bpm(), Some(96.0));
998 assert!((playback.seconds_per_quarter().expect("seconds") - 0.625).abs() < 1e-12);
999 assert!(quarter.seconds_to_duration(0.0).is_err());
1000
1001 let motionless = MetronomeMark::new(0.0);
1002 assert_eq!(motionless.quarter_bpm(), Some(0.0));
1003 assert!(motionless.seconds_per_quarter().is_err());
1004 assert!(
1005 motionless
1006 .duration_to_seconds(&Duration::quarter())
1007 .is_err()
1008 );
1009 assert!(motionless.quarter_length_to_seconds(1.0).is_err());
1010 }
1011
1012 #[test]
1014 fn what_lies_between_two_points_keeps_its_place_between_them() {
1015 use crate::{Note, Stream};
1016
1017 let mut source = Stream::new();
1018 for (offset, name) in [
1019 (10.0, "C4"),
1020 (11.0, "D4"),
1021 (12.0, "E4"),
1022 (13.0, "F4"),
1023 (14.0, "G4"),
1024 ] {
1025 source.insert(offset, Note::from_name(name).unwrap());
1026 }
1027 let offsets = |stream: &Stream| -> Vec<FloatType> {
1028 stream.events().iter().map(|event| event.offset()).collect()
1029 };
1030
1031 let mut recording = Stream::new();
1032 interpolate_elements(&source, &mut recording, (10.0, 20.5), (14.0, 25.0)).unwrap();
1033 assert_eq!(offsets(&recording), [21.625, 22.75, 23.875]);
1034
1035 let mut slower = Stream::new();
1036 interpolate_elements(&source, &mut slower, (10.0, 10.1), (14.0, 50.5)).unwrap();
1037 for (found, expected) in offsets(&slower).iter().zip([20.2, 30.3, 40.4]) {
1038 assert!((found - expected).abs() < 1e-9);
1039 }
1040
1041 assert!(interpolate_elements(&source, &mut slower, (10.0, 0.0), (10.0, 4.0)).is_err());
1042 }
1043
1044 #[test]
1045 fn a_metric_modulation_moves_a_number_to_another_note_value() {
1046 use super::{MetricModulation, MetronomeMark, ModulationSide};
1048 use crate::Duration;
1049
1050 let mut modulation = MetricModulation::new();
1052 modulation.set_old_referent(Duration::half(), None);
1053 modulation.set_new_referent(Duration::new(0.25).unwrap());
1054 assert_eq!(modulation.number(), None);
1055
1056 let mut modulation = MetricModulation::new();
1058 modulation.set_old_referent(Duration::quarter(), None);
1059 modulation.set_new_referent(Duration::eighth());
1060 modulation.set_old_metronome(Some(MetronomeMark::new(60.0)));
1061 modulation.update_from(None);
1062 assert_eq!(modulation.number(), Some(60.0));
1063 assert_eq!(
1064 modulation.new_metronome().unwrap().text(),
1065 Some("larghetto")
1066 );
1067
1068 let mut modulation = MetricModulation::new();
1070 modulation.set_new_metronome(Some(MetronomeMark::new(60.0)));
1071 modulation
1072 .set_equality_by_referent(None, Duration::half())
1073 .unwrap();
1074 assert_eq!(modulation.old_metronome().unwrap().number(), Some(30.0));
1075
1076 let mut modulation = MetricModulation::new();
1078 modulation.set_old_metronome(Some(MetronomeMark::new(60.0)));
1079 modulation
1080 .set_other_by_referent(Some(ModulationSide::New), Duration::half())
1081 .unwrap();
1082 assert_eq!(modulation.number(), Some(60.0));
1083 assert_eq!(
1084 modulation.new_metronome().unwrap().quarter_bpm(),
1085 Some(120.0)
1086 );
1087
1088 assert!(
1090 modulation
1091 .set_equality_by_referent(None, Duration::quarter())
1092 .is_err()
1093 );
1094
1095 let mut modulation = MetricModulation::new();
1097 modulation.set_old_referent(Duration::eighth(), None);
1098 modulation.update_from(Some(&MetronomeMark::new(90.0)));
1099 assert_eq!(modulation.old_metronome().unwrap().number(), Some(180.0));
1100 }
1101}