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 #[cfg_attr(feature = "serde", serde(default))]
132 hidden: Drawn<bool>,
133}
134
135impl Default for MetronomeMark {
136 fn default() -> Self {
139 Self::build(None, None, Duration::quarter())
140 }
141}
142
143impl MetronomeMark {
144 pub fn new(number: FloatType) -> Self {
147 Self::build(Some(number), None, Duration::quarter())
148 }
149
150 pub fn from_text(text: impl Into<String>) -> Self {
153 Self::build(None, Some(text.into()), Duration::quarter())
154 }
155
156 pub fn with_number_and_text(number: FloatType, text: impl Into<String>) -> Self {
158 Self::build(Some(number), Some(text.into()), Duration::quarter())
159 }
160
161 pub fn with_referent(mut self, referent: Duration) -> Self {
164 self.referent = referent;
165 self
166 }
167
168 fn build(number: Option<FloatType>, text: Option<String>, referent: Duration) -> Self {
169 let number = number.map(whole_where_nearly);
170 let number_implicit = number.is_none();
171 let text_implicit = text.is_none();
172 let number = number.or_else(|| text.as_deref().and_then(default_number_for_text));
173 let text = text.or_else(|| number.and_then(default_text_for_number).map(String::from));
174 Self {
175 number_implicit: number_implicit && number.is_some(),
176 text_implicit: text_implicit && text.is_some(),
177 number,
178 text,
179 referent,
180 number_sounding: None,
181 parentheses: Drawn(false),
182 placement: Drawn(None),
183 hidden: Drawn(false),
184 }
185 }
186
187 pub fn hidden(&self) -> bool {
190 self.hidden.0
191 }
192
193 pub fn set_hidden(&mut self, hidden: bool) {
195 self.hidden.0 = hidden;
196 }
197
198 pub fn parentheses(&self) -> bool {
201 self.parentheses.0
202 }
203
204 pub fn set_parentheses(&mut self, parentheses: bool) {
206 self.parentheses.0 = parentheses;
207 }
208
209 pub fn placement(&self) -> Option<Placement> {
211 self.placement.0
212 }
213
214 pub fn set_placement(&mut self, placement: Option<Placement>) {
216 self.placement.0 = placement;
217 }
218
219 pub fn equivalent_by_referent(&self, referent: Duration) -> MetronomeMark {
223 let number = self.number.map(|number| {
224 convert_tempo_by_referent(
225 number,
226 self.referent.quarter_length(),
227 referent.quarter_length(),
228 )
229 });
230 Self::build(number, self.text.clone(), referent)
231 }
232
233 pub fn maintained_number_with_referent(&self, referent: Duration) -> MetronomeMark {
236 Self::build(self.number, self.text.clone(), referent)
237 }
238
239 pub fn number(&self) -> Option<FloatType> {
241 self.number
242 }
243
244 pub fn set_number(&mut self, number: Option<FloatType>) {
247 let number = number.map(whole_where_nearly);
248 self.number = number;
249 self.number_implicit = false;
250 if self.text.is_none()
251 && let Some(number) = number
252 && let Some(text) = default_text_for_number(number)
253 {
254 self.text = Some(text.to_string());
255 self.text_implicit = true;
256 }
257 }
258
259 pub fn set_text(&mut self, text: impl Into<String>) {
262 let text = text.into();
263 self.text_implicit = false;
264 if self.number.is_none()
265 && let Some(number) = default_number_for_text(&text)
266 {
267 self.number = Some(number);
268 self.number_implicit = true;
269 }
270 self.text = Some(text);
271 }
272
273 pub fn set_referent(&mut self, referent: Duration) {
276 self.referent = referent;
277 }
278
279 pub fn text(&self) -> Option<&str> {
281 self.text.as_deref()
282 }
283
284 pub fn text_expression(&self, return_implicit: bool) -> Option<&str> {
288 if self.text_implicit && !return_implicit {
289 return None;
290 }
291 self.text.as_deref()
292 }
293
294 pub fn referent(&self) -> &Duration {
296 &self.referent
297 }
298
299 pub fn number_implicit(&self) -> bool {
301 self.number_implicit
302 }
303
304 pub fn set_number_implicit(&mut self, implicit: bool) {
308 self.number_implicit = implicit;
309 }
310
311 pub fn text_implicit(&self) -> bool {
313 self.text_implicit
314 }
315
316 pub fn number_sounding(&self) -> Option<FloatType> {
325 self.number_sounding
326 }
327
328 pub fn set_number_sounding(&mut self, number: Option<FloatType>) {
330 self.number_sounding = number;
331 }
332
333 pub fn with_number_sounding(mut self, number: FloatType) -> Self {
339 self.number_sounding = Some(number);
340 self
341 }
342
343 pub fn quarter_bpm(&self) -> Option<FloatType> {
345 self.number
346 .map(|number| convert_tempo_by_referent(number, self.referent.quarter_length(), 1.0))
347 }
348
349 pub fn sounding_quarter_bpm(&self) -> Option<FloatType> {
352 match self.number_sounding {
353 Some(number) => Some(convert_tempo_by_referent(
354 number,
355 self.referent.quarter_length(),
356 1.0,
357 )),
358 None => self.quarter_bpm(),
359 }
360 }
361
362 pub fn seconds_per_quarter(&self) -> Result<FloatType> {
368 let quarter_bpm = self.sounding_quarter_bpm().ok_or_else(|| {
373 Error::Tempo("cannot derive seconds as getQuarterBPM() returns None".to_string())
374 })?;
375 if quarter_bpm == 0.0 {
376 return Err(Error::Tempo(
377 "a tempo of no beats a minute lasts no seconds a beat".to_string(),
378 ));
379 }
380 Ok(60.0 / quarter_bpm)
381 }
382
383 pub fn quarter_length_to_seconds(&self, quarter_length: FloatType) -> Result<FloatType> {
385 Ok(self.seconds_per_quarter()? * quarter_length)
386 }
387
388 pub fn duration_to_seconds(&self, duration: &Duration) -> Result<FloatType> {
390 self.quarter_length_to_seconds(duration.quarter_length())
391 }
392
393 pub fn seconds_to_duration(&self, seconds: FloatType) -> Result<Duration> {
395 if seconds.is_nan() || seconds <= 0.0 {
396 return Err(Error::Tempo(
397 "seconds must be a number greater than zero".to_string(),
398 ));
399 }
400 Duration::new(seconds / self.seconds_per_quarter()?)
401 }
402}
403
404pub fn interpolate_elements(
418 source: &Stream,
419 destination: &mut Stream,
420 start: (FloatType, FloatType),
421 end: (FloatType, FloatType),
422) -> Result<()> {
423 let (start_source, end_source) = (start.0, end.0);
424 interpolated_offset(start_source, start, end)?;
427 for event in source.events() {
428 if event.offset() > start_source && event.offset() < end_source {
429 destination.insert(
430 interpolated_offset(event.offset(), start, end)?,
431 event.element().clone(),
432 );
433 }
434 }
435 Ok(())
436}
437
438pub fn interpolated_offset(
453 offset: FloatType,
454 start: (FloatType, FloatType),
455 end: (FloatType, FloatType),
456) -> Result<FloatType> {
457 let ((start_source, start_destination), (end_source, end_destination)) = (start, end);
458 if end_source == start_source {
459 return Err(Error::Tempo(
460 "the two points stand at the same offset in the source, so nothing lies between them"
461 .to_string(),
462 ));
463 }
464 let scale = (end_destination - start_destination) / (end_source - start_source);
465 Ok(scale * (offset - start_source) + start_destination)
466}
467
468#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
471#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
472pub enum ModulationSide {
473 Old,
475 New,
477}
478
479#[derive(Clone, Debug, PartialEq)]
490#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
491#[must_use]
492pub struct TempoText {
493 text: String,
494 #[cfg_attr(feature = "serde", serde(default))]
495 placement: Drawn<Option<Placement>>,
496}
497
498impl TempoText {
499 pub fn new(text: impl Into<String>) -> Self {
501 Self {
502 text: text.into(),
503 placement: Drawn(None),
504 }
505 }
506
507 pub fn placement(&self) -> Option<Placement> {
509 self.placement.0
510 }
511
512 pub fn set_placement(&mut self, placement: Option<Placement>) {
514 self.placement.0 = placement;
515 }
516
517 pub fn text(&self) -> &str {
519 &self.text
520 }
521
522 pub fn set_text(&mut self, text: impl Into<String>) {
524 self.text = text.into();
525 }
526
527 pub fn metronome_mark(&self) -> MetronomeMark {
529 MetronomeMark::from_text(self.text.clone())
530 }
531
532 pub fn is_common_tempo_text(&self) -> bool {
536 is_common_tempo_text(&self.text)
537 }
538}
539
540pub fn is_common_tempo_text(text: &str) -> bool {
543 let stripped = |value: &str| {
544 value
545 .trim()
546 .chars()
547 .filter(|character| *character != ' ' && *character != '.')
548 .collect::<String>()
549 .to_lowercase()
550 };
551 let text = stripped(text);
552 DEFAULT_TEMPO_VALUES.iter().any(|(candidate, _)| {
553 let candidate = stripped(candidate);
554 text.contains(&candidate) || candidate.contains(&text)
555 })
556}
557
558#[derive(Clone, Debug, PartialEq)]
578#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
579#[must_use]
580pub struct MetricModulation {
581 old: Option<MetronomeMark>,
582 new: Option<MetronomeMark>,
583 classical_style: bool,
584 maintain_beat: bool,
585 transition_symbol: String,
586 arrow_direction: Option<String>,
587 parentheses: bool,
588 #[cfg_attr(feature = "serde", serde(default))]
589 placement: Drawn<Option<Placement>>,
590}
591
592impl Default for MetricModulation {
593 fn default() -> Self {
594 Self::new()
595 }
596}
597
598impl MetricModulation {
599 pub fn new() -> Self {
601 Self {
602 old: None,
603 new: None,
604 classical_style: false,
605 maintain_beat: false,
606 transition_symbol: "=".to_string(),
607 arrow_direction: None,
608 parentheses: false,
609 placement: Drawn(None),
610 }
611 }
612
613 pub fn placement(&self) -> Option<Placement> {
616 self.placement.0
617 }
618
619 pub fn set_placement(&mut self, placement: Option<Placement>) {
621 self.placement.0 = placement;
622 }
623
624 pub fn old_metronome(&self) -> Option<&MetronomeMark> {
626 self.old.as_ref()
627 }
628
629 pub fn new_metronome(&self) -> Option<&MetronomeMark> {
631 self.new.as_ref()
632 }
633
634 pub fn set_old_metronome(&mut self, mark: Option<MetronomeMark>) {
636 self.old = mark;
637 }
638
639 pub fn set_new_metronome(&mut self, mark: Option<MetronomeMark>) {
641 self.new = mark;
642 }
643
644 pub fn old_referent(&self) -> Option<&Duration> {
646 self.old.as_ref().map(MetronomeMark::referent)
647 }
648
649 pub fn new_referent(&self) -> Option<&Duration> {
651 self.new.as_ref().map(MetronomeMark::referent)
652 }
653
654 pub fn set_old_referent(&mut self, referent: Duration, previous: Option<&MetronomeMark>) {
659 self.old = Some(match (&self.old, previous) {
660 (Some(old), _) => old.equivalent_by_referent(referent),
661 (None, Some(previous)) => previous.equivalent_by_referent(referent),
662 (None, None) => MetronomeMark::default().with_referent(referent),
663 });
664 }
665
666 pub fn set_new_referent(&mut self, referent: Duration) {
671 self.new = Some(match (&self.new, &self.old) {
672 (Some(new), _) => new.equivalent_by_referent(referent),
673 (None, Some(old)) => old.maintained_number_with_referent(referent),
674 (None, None) => MetronomeMark::default().with_referent(referent),
675 });
676 }
677
678 pub fn number(&self) -> Option<FloatType> {
680 self.new.as_ref().and_then(MetronomeMark::number)
681 }
682
683 pub fn update_from(&mut self, previous: Option<&MetronomeMark>) {
692 if let Some(previous) = previous {
693 self.old = Some(match &self.old {
694 Some(old) => previous.equivalent_by_referent(old.referent().clone()),
695 None => previous.maintained_number_with_referent(previous.referent().clone()),
696 });
697 }
698 let number = self.old.as_ref().and_then(MetronomeMark::number);
699 if let (Some(new), Some(number)) = (self.new.as_mut(), number) {
700 new.set_number(Some(number));
701 }
702 }
703
704 pub fn set_equality_by_referent(
709 &mut self,
710 side: Option<ModulationSide>,
711 referent: Duration,
712 ) -> Result<()> {
713 self.set_from_other(side, |other| other.equivalent_by_referent(referent))
714 }
715
716 pub fn set_other_by_referent(
720 &mut self,
721 side: Option<ModulationSide>,
722 referent: Duration,
723 ) -> Result<()> {
724 self.set_from_other(side, |other| {
725 other.maintained_number_with_referent(referent)
726 })
727 }
728
729 fn side_to_set(&self, side: Option<ModulationSide>) -> Result<ModulationSide> {
730 side.or(match (&self.old, &self.new) {
731 (None, _) => Some(ModulationSide::Old),
732 (_, None) => Some(ModulationSide::New),
733 _ => None,
734 })
735 .ok_or_else(|| Error::Tempo("cannot set equality for a side of None".to_string()))
736 }
737
738 fn no_other_side() -> Error {
739 Error::Tempo("there is no mark on the other side to take the tempo from".to_string())
740 }
741
742 fn set_from_other(
744 &mut self,
745 side: Option<ModulationSide>,
746 derive: impl FnOnce(&MetronomeMark) -> MetronomeMark,
747 ) -> Result<()> {
748 let (target, other) = match self.side_to_set(side)? {
749 ModulationSide::New => (&mut self.new, &self.old),
750 ModulationSide::Old => (&mut self.old, &self.new),
751 };
752 let other = other.as_ref().ok_or_else(Self::no_other_side)?;
753 *target = Some(derive(other));
754 Ok(())
755 }
756
757 pub fn classical_style(&self) -> bool {
760 self.classical_style
761 }
762
763 pub fn set_classical_style(&mut self, classical: bool) {
765 self.classical_style = classical;
766 }
767
768 pub fn maintain_beat(&self) -> bool {
771 self.maintain_beat
772 }
773
774 pub fn set_maintain_beat(&mut self, maintain: bool) {
776 self.maintain_beat = maintain;
777 }
778
779 pub fn transition_symbol(&self) -> &str {
782 &self.transition_symbol
783 }
784
785 pub fn set_transition_symbol(&mut self, symbol: impl Into<String>) {
787 self.transition_symbol = symbol.into();
788 }
789
790 pub fn arrow_direction(&self) -> Option<&str> {
793 self.arrow_direction.as_deref()
794 }
795
796 pub fn set_arrow_direction(&mut self, direction: Option<String>) {
798 self.arrow_direction = direction;
799 }
800
801 pub fn parentheses(&self) -> bool {
803 self.parentheses
804 }
805
806 pub fn set_parentheses(&mut self, parentheses: bool) {
808 self.parentheses = parentheses;
809 }
810}
811
812#[cfg(test)]
813mod tests {
814 #[test]
815 fn a_number_a_hair_off_a_whole_one_is_that_whole_number() {
816 let mark = super::MetronomeMark::new(110.0)
819 .with_referent(super::Duration::from_type_with_dots(
820 crate::duration::DurationType::Quarter,
821 1,
822 ))
823 .equivalent_by_referent(super::Duration::quarter());
824 assert_eq!(mark.number(), Some(165.0));
825 let dotted = mark.maintained_number_with_referent(super::Duration::from_type_with_dots(
826 crate::duration::DurationType::Quarter,
827 1,
828 ));
829 assert_eq!(dotted.quarter_bpm(), Some(247.5));
830 }
831
832 use super::*;
833
834 #[test]
835 fn a_mark_can_be_rewritten_one_half_at_a_time() {
836 let mut mark = MetronomeMark::from_text("adagio");
837 assert!(mark.number_implicit());
838 mark.set_number(Some(60.0));
839 assert!(!mark.number_implicit());
840 assert_eq!(mark.number(), Some(60.0));
841 mark.set_number_implicit(true);
842 assert!(mark.number_implicit());
843 mark.set_referent(Duration::half());
844 assert_eq!(mark.referent().quarter_length(), 2.0);
845 assert_eq!(mark.quarter_bpm(), Some(120.0));
846
847 let mut numbered = MetronomeMark::new(90.0);
848 assert!(numbered.text_implicit());
849 numbered.set_text("largo");
850 assert!(!numbered.text_implicit());
851 assert_eq!(numbered.text(), Some("largo"));
852 assert_eq!(numbered.number(), Some(90.0));
853 let mut wordless = MetronomeMark::default();
854 wordless.set_number(Some(184.0));
855 assert_eq!(wordless.text(), Some("presto"));
856 assert!(wordless.text_implicit());
857 wordless.set_number(None);
858 assert_eq!(wordless.number(), None);
859 }
860
861 #[test]
863 fn an_implied_word_is_only_answered_when_asked_for() {
864 let presto = MetronomeMark::from_text("presto");
865 assert_eq!(presto.number(), Some(184.0));
866 assert_eq!(presto.text_expression(false), Some("presto"));
867
868 let ninety = MetronomeMark::new(90.0);
869 assert!(ninety.text_implicit());
870 assert_eq!(ninety.text_expression(false), None);
871 assert_eq!(ninety.text_expression(true), Some("maestoso"));
872 }
873
874 #[test]
875 fn a_mark_may_be_played_faster_than_it_is_written() {
876 let playback = MetronomeMark::default().with_number_sounding(168.0);
878 assert_eq!(playback.number_sounding(), Some(168.0));
879 assert_eq!(playback.number(), None);
881 assert_eq!(playback.text(), None);
882 assert_eq!(playback.quarter_bpm(), None);
883 assert_eq!(playback.sounding_quarter_bpm(), Some(168.0));
884
885 let mut written = MetronomeMark::new(60.0).with_referent(Duration::half());
888 assert_eq!(written.quarter_bpm(), Some(120.0));
889 assert_eq!(written.sounding_quarter_bpm(), Some(120.0));
890 written.set_number_sounding(Some(90.0));
891 assert_eq!(written.number(), Some(60.0));
892 assert_eq!(written.quarter_bpm(), Some(120.0));
893 assert_eq!(written.sounding_quarter_bpm(), Some(180.0));
894 }
895
896 #[test]
897 fn referent_changes_match_music21() {
898 let quarter_sixty = MetronomeMark::new(60.0);
899 let eighths = quarter_sixty.equivalent_by_referent(Duration::eighth());
900 assert_eq!(eighths.number(), Some(120.0));
901 assert_eq!(eighths.referent().quarter_length(), 0.5);
902 assert_eq!(eighths.text(), Some("larghetto"));
903 let halves = quarter_sixty.equivalent_by_referent(Duration::half());
904 assert_eq!(halves.number(), Some(30.0));
905
906 let andante = MetronomeMark::with_number_and_text(72.0, "andante")
907 .with_referent(Duration::new(1.5).unwrap());
908 let quarters = andante.equivalent_by_referent(Duration::quarter());
909 assert_eq!(quarters.number(), Some(108.0));
910 assert_eq!(quarters.text(), Some("andante"));
911 assert_eq!(
912 andante.equivalent_by_referent(Duration::half()).number(),
913 Some(54.0)
914 );
915
916 let kept = andante.maintained_number_with_referent(Duration::eighth());
917 assert_eq!(kept.number(), Some(72.0));
918 assert_eq!(kept.referent().quarter_length(), 0.5);
919 assert_eq!(kept.text(), Some("andante"));
920 }
921
922 #[test]
923 fn numbers_imply_the_tempo_words_music21_picks() {
924 let cases = [
925 (120.0, Some("animato")),
926 (60.0, Some("larghetto")),
927 (61.0, Some("larghetto")),
928 (62.0, Some("larghetto")),
929 (63.0, None),
930 (90.0, Some("maestoso")),
931 (92.0, Some("moderate")),
932 (100.0, None),
933 (118.0, Some("animato")),
934 (122.0, Some("animato")),
935 (123.0, None),
936 (16.0, Some("larghissimo")),
937 (12.0, None),
938 (56.5, Some("adagio")),
939 (300.0, None),
940 ];
941 for (number, text) in cases {
942 let mark = MetronomeMark::new(number);
943 assert_eq!(mark.text(), text, "{number}");
944 assert_eq!(mark.text_implicit(), text.is_some(), "{number}");
945 assert!(!mark.number_implicit());
946 }
947 }
948
949 #[test]
950 fn words_imply_their_conventional_numbers() {
951 let cases = [
952 ("allegro", Some(132.0)),
953 ("Allegro", Some(132.0)),
954 ("ALLEGRO ", Some(132.0)),
955 ("très vite", Some(144.0)),
956 ("andante moderato", Some(83.0)),
957 ("unknown words", None),
958 ];
959 for (text, number) in cases {
960 let mark = MetronomeMark::from_text(text);
961 assert_eq!(mark.number(), number, "{text}");
962 assert_eq!(mark.number_implicit(), number.is_some(), "{text}");
963 assert_eq!(mark.text(), Some(text));
964 assert!(!mark.text_implicit());
965 }
966 let both = MetronomeMark::with_number_and_text(120.0, "fast");
967 assert_eq!(both.number(), Some(120.0));
968 assert_eq!(both.text(), Some("fast"));
969 assert!(!both.number_implicit() && !both.text_implicit());
970 }
971
972 #[test]
973 fn referents_convert_to_quarter_bpm_and_seconds() {
974 let quarter = MetronomeMark::new(120.0);
975 assert_eq!(quarter.quarter_bpm(), Some(120.0));
976 assert_eq!(quarter.quarter_length_to_seconds(1.0).unwrap(), 0.5);
977 assert_eq!(
978 quarter.seconds_to_duration(0.75).unwrap().quarter_length(),
979 1.5
980 );
981
982 let half = MetronomeMark::new(60.0).with_referent(Duration::half());
983 assert_eq!(half.quarter_bpm(), Some(120.0));
984 assert_eq!(half.text(), Some("larghetto"));
985 assert_eq!(half.quarter_length_to_seconds(1.0).unwrap(), 0.5);
986 assert_eq!(
987 half.duration_to_seconds(&Duration::new(3.0).unwrap())
988 .unwrap(),
989 1.5
990 );
991 assert_eq!(half.seconds_to_duration(1.0).unwrap().quarter_length(), 2.0);
992
993 let eighth = MetronomeMark::new(120.0).with_referent(Duration::eighth());
994 assert_eq!(eighth.quarter_bpm(), Some(60.0));
995 assert_eq!(eighth.seconds_per_quarter().unwrap(), 1.0);
996
997 let dotted = MetronomeMark::new(56.5).with_referent(Duration::half());
998 assert!((dotted.quarter_bpm().unwrap() - 113.0).abs() < 1e-9);
999
1000 assert_eq!(convert_tempo_by_referent(60.0, 1.0, 2.0), 30.0);
1001 assert_eq!(convert_tempo_by_referent(60.0, 2.0, 1.0), 120.0);
1002
1003 let unknown = MetronomeMark::from_text("unknown words");
1004 assert_eq!(unknown.quarter_bpm(), None);
1005 assert!(unknown.seconds_per_quarter().is_err());
1006
1007 let mut playback = MetronomeMark::from_text("unknown words");
1011 playback.set_number_sounding(Some(96.0));
1012 assert_eq!(playback.number(), None);
1013 assert_eq!(playback.sounding_quarter_bpm(), Some(96.0));
1014 assert!((playback.seconds_per_quarter().expect("seconds") - 0.625).abs() < 1e-12);
1015 assert!(quarter.seconds_to_duration(0.0).is_err());
1016
1017 let motionless = MetronomeMark::new(0.0);
1018 assert_eq!(motionless.quarter_bpm(), Some(0.0));
1019 assert!(motionless.seconds_per_quarter().is_err());
1020 assert!(
1021 motionless
1022 .duration_to_seconds(&Duration::quarter())
1023 .is_err()
1024 );
1025 assert!(motionless.quarter_length_to_seconds(1.0).is_err());
1026 }
1027
1028 #[test]
1030 fn what_lies_between_two_points_keeps_its_place_between_them() {
1031 use crate::{Note, Stream};
1032
1033 let mut source = Stream::new();
1034 for (offset, name) in [
1035 (10.0, "C4"),
1036 (11.0, "D4"),
1037 (12.0, "E4"),
1038 (13.0, "F4"),
1039 (14.0, "G4"),
1040 ] {
1041 source.insert(offset, Note::from_name(name).unwrap());
1042 }
1043 let offsets = |stream: &Stream| -> Vec<FloatType> {
1044 stream.events().iter().map(|event| event.offset()).collect()
1045 };
1046
1047 let mut recording = Stream::new();
1048 interpolate_elements(&source, &mut recording, (10.0, 20.5), (14.0, 25.0)).unwrap();
1049 assert_eq!(offsets(&recording), [21.625, 22.75, 23.875]);
1050
1051 let mut slower = Stream::new();
1052 interpolate_elements(&source, &mut slower, (10.0, 10.1), (14.0, 50.5)).unwrap();
1053 for (found, expected) in offsets(&slower).iter().zip([20.2, 30.3, 40.4]) {
1054 assert!((found - expected).abs() < 1e-9);
1055 }
1056
1057 assert!(interpolate_elements(&source, &mut slower, (10.0, 0.0), (10.0, 4.0)).is_err());
1058 }
1059
1060 #[test]
1061 fn a_metric_modulation_moves_a_number_to_another_note_value() {
1062 use super::{MetricModulation, MetronomeMark, ModulationSide};
1064 use crate::Duration;
1065
1066 let mut modulation = MetricModulation::new();
1068 modulation.set_old_referent(Duration::half(), None);
1069 modulation.set_new_referent(Duration::new(0.25).unwrap());
1070 assert_eq!(modulation.number(), None);
1071
1072 let mut modulation = MetricModulation::new();
1074 modulation.set_old_referent(Duration::quarter(), None);
1075 modulation.set_new_referent(Duration::eighth());
1076 modulation.set_old_metronome(Some(MetronomeMark::new(60.0)));
1077 modulation.update_from(None);
1078 assert_eq!(modulation.number(), Some(60.0));
1079 assert_eq!(
1080 modulation.new_metronome().unwrap().text(),
1081 Some("larghetto")
1082 );
1083
1084 let mut modulation = MetricModulation::new();
1086 modulation.set_new_metronome(Some(MetronomeMark::new(60.0)));
1087 modulation
1088 .set_equality_by_referent(None, Duration::half())
1089 .unwrap();
1090 assert_eq!(modulation.old_metronome().unwrap().number(), Some(30.0));
1091
1092 let mut modulation = MetricModulation::new();
1094 modulation.set_old_metronome(Some(MetronomeMark::new(60.0)));
1095 modulation
1096 .set_other_by_referent(Some(ModulationSide::New), Duration::half())
1097 .unwrap();
1098 assert_eq!(modulation.number(), Some(60.0));
1099 assert_eq!(
1100 modulation.new_metronome().unwrap().quarter_bpm(),
1101 Some(120.0)
1102 );
1103
1104 assert!(
1106 modulation
1107 .set_equality_by_referent(None, Duration::quarter())
1108 .is_err()
1109 );
1110
1111 let mut modulation = MetricModulation::new();
1113 modulation.set_old_referent(Duration::eighth(), None);
1114 modulation.update_from(Some(&MetronomeMark::new(90.0)));
1115 assert_eq!(modulation.old_metronome().unwrap().number(), Some(180.0));
1116 }
1117}