1use crate::{
2 defaults::{FloatType, FractionType, IntegerType},
3 error::{Error, Result},
4};
5
6use fraction::ToPrimitive;
7use std::fmt::{Display, Formatter};
8use std::str::FromStr;
9
10#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[must_use]
18pub enum DurationType {
19 DuplexMaxima,
21 Maxima,
23 Longa,
25 Breve,
27 Whole,
29 Half,
31 Quarter,
33 Eighth,
35 Sixteenth,
37 ThirtySecond,
39 SixtyFourth,
41 HundredTwentyEighth,
43 TwoHundredFiftySixth,
45 FiveHundredTwelfth,
47 TenTwentyFourth,
49 TwentyFortyEighth,
51 Zero,
53}
54
55impl DurationType {
56 pub const ALL: [DurationType; 17] = [
58 Self::DuplexMaxima,
59 Self::Maxima,
60 Self::Longa,
61 Self::Breve,
62 Self::Whole,
63 Self::Half,
64 Self::Quarter,
65 Self::Eighth,
66 Self::Sixteenth,
67 Self::ThirtySecond,
68 Self::SixtyFourth,
69 Self::HundredTwentyEighth,
70 Self::TwoHundredFiftySixth,
71 Self::FiveHundredTwelfth,
72 Self::TenTwentyFourth,
73 Self::TwentyFortyEighth,
74 Self::Zero,
75 ];
76
77 pub fn music21_name(self) -> &'static str {
79 match self {
80 Self::DuplexMaxima => "duplex-maxima",
81 Self::Maxima => "maxima",
82 Self::Longa => "longa",
83 Self::Breve => "breve",
84 Self::Whole => "whole",
85 Self::Half => "half",
86 Self::Quarter => "quarter",
87 Self::Eighth => "eighth",
88 Self::Sixteenth => "16th",
89 Self::ThirtySecond => "32nd",
90 Self::SixtyFourth => "64th",
91 Self::HundredTwentyEighth => "128th",
92 Self::TwoHundredFiftySixth => "256th",
93 Self::FiveHundredTwelfth => "512th",
94 Self::TenTwentyFourth => "1024th",
95 Self::TwentyFortyEighth => "2048th",
96 Self::Zero => "zero",
97 }
98 }
99
100 pub fn ordinal(self) -> Option<usize> {
103 (self != Self::Zero).then(|| Self::ALL.iter().position(|kind| *kind == self))?
104 }
105
106 pub fn next_larger(self) -> Option<DurationType> {
109 let ordinal = self.ordinal()?;
110 Self::ALL.get(ordinal.checked_sub(1)?).copied()
111 }
112
113 pub fn next_smaller(self) -> Option<DurationType> {
116 let ordinal = self.ordinal()?;
117 Self::ALL
118 .get(ordinal + 1)
119 .copied()
120 .filter(|kind| *kind != Self::Zero)
121 }
122
123 pub fn type_number(self) -> Option<FloatType> {
126 (self != Self::Zero).then(|| 4.0 / self.quarter_length())
127 }
128
129 fn title(self) -> String {
130 let name = self.music21_name();
131 if name.starts_with(|c: char| c.is_ascii_digit()) {
132 return name.to_string();
133 }
134 name.split('-')
135 .map(|word| {
136 let mut chars = word.chars();
137 chars
138 .next()
139 .map(|first| first.to_ascii_uppercase().to_string() + chars.as_str())
140 .unwrap_or_default()
141 })
142 .collect::<Vec<_>>()
143 .join("-")
144 }
145
146 pub fn quarter_length(self) -> FloatType {
148 match self {
149 Self::DuplexMaxima => 64.0,
150 Self::Maxima => 32.0,
151 Self::Longa => 16.0,
152 Self::Breve => 8.0,
153 Self::Whole => 4.0,
154 Self::Half => 2.0,
155 Self::Quarter => 1.0,
156 Self::Eighth => 0.5,
157 Self::Sixteenth => 0.25,
158 Self::ThirtySecond => 0.125,
159 Self::SixtyFourth => 0.0625,
160 Self::HundredTwentyEighth => 0.03125,
161 Self::TwoHundredFiftySixth => 0.015625,
162 Self::FiveHundredTwelfth => 0.0078125,
163 Self::TenTwentyFourth => 0.00390625,
164 Self::TwentyFortyEighth => 0.001953125,
165 Self::Zero => 0.0,
166 }
167 }
168
169 pub fn from_music21_name(name: &str) -> Option<Self> {
171 match name {
172 "duplex-maxima" => Some(Self::DuplexMaxima),
173 "maxima" => Some(Self::Maxima),
174 "longa" => Some(Self::Longa),
175 "breve" => Some(Self::Breve),
176 "whole" => Some(Self::Whole),
177 "half" => Some(Self::Half),
178 "quarter" => Some(Self::Quarter),
179 "eighth" => Some(Self::Eighth),
180 "16th" => Some(Self::Sixteenth),
181 "32nd" => Some(Self::ThirtySecond),
182 "64th" => Some(Self::SixtyFourth),
183 "128th" => Some(Self::HundredTwentyEighth),
184 "256th" => Some(Self::TwoHundredFiftySixth),
185 "512th" => Some(Self::FiveHundredTwelfth),
186 "1024th" => Some(Self::TenTwentyFourth),
187 "2048th" => Some(Self::TwentyFortyEighth),
188 "zero" => Some(Self::Zero),
189 _ => None,
190 }
191 }
192
193 pub fn from_quarter_length(quarter_length: FloatType) -> Option<Self> {
195 Self::ALL
196 .into_iter()
197 .find(|candidate| candidate.quarter_length() == quarter_length)
198 }
199
200 pub fn quarter_length_with_dots(self, dots: u32) -> FloatType {
205 self.quarter_length() * (2.0 - (0.5 as FloatType).powi(dots as i32))
206 }
207}
208
209impl Display for DurationType {
210 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
211 f.write_str(self.music21_name())
212 }
213}
214
215impl FromStr for DurationType {
216 type Err = Error;
217
218 fn from_str(value: &str) -> Result<Self> {
219 Self::from_music21_name(value)
220 .ok_or_else(|| Error::Duration(format!("unknown duration type {value:?}")))
221 }
222}
223
224#[derive(Clone, Debug)]
225#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
226#[must_use]
231pub struct Duration {
232 quarter_length: FloatType,
233 #[cfg_attr(feature = "serde", serde(default))]
240 tuplets: Option<Vec<Tuplet>>,
241 #[cfg_attr(feature = "serde", serde(default))]
248 written: Option<Vec<DurationTuple>>,
249 #[cfg_attr(feature = "serde", serde(default))]
251 dot_groups: Option<Vec<u32>>,
252 #[cfg_attr(feature = "serde", serde(default))]
255 unlinked: bool,
256 #[cfg_attr(feature = "serde", serde(default))]
258 grace: Option<Grace>,
259 #[cfg_attr(feature = "serde", serde(default))]
262 said: bool,
263}
264
265#[derive(Clone, Copy, Debug, PartialEq)]
268#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
269pub struct Grace {
270 slash: bool,
271 steal_time_previous: Option<FloatType>,
272 steal_time_following: Option<FloatType>,
273 make_time: bool,
274 #[cfg_attr(feature = "serde", serde(default))]
278 value_unsaid: bool,
279}
280
281impl Default for Grace {
282 fn default() -> Self {
285 Self {
286 slash: true,
287 steal_time_previous: None,
288 steal_time_following: None,
289 make_time: false,
290 value_unsaid: false,
291 }
292 }
293}
294
295impl Grace {
296 pub(crate) fn value_unsaid(&self) -> bool {
299 self.value_unsaid
300 }
301
302 #[cfg(feature = "musicxml")]
303 pub(crate) fn set_value_unsaid(&mut self, unsaid: bool) {
304 self.value_unsaid = unsaid;
305 }
306
307 pub fn new() -> Self {
309 Self::default()
310 }
311
312 pub fn appoggiatura() -> Self {
315 Self {
316 slash: false,
317 make_time: true,
318 ..Self::default()
319 }
320 }
321
322 pub fn slash(&self) -> bool {
324 self.slash
325 }
326
327 pub fn set_slash(&mut self, slash: bool) {
329 self.slash = slash;
330 }
331
332 pub fn steal_time_previous(&self) -> Option<FloatType> {
335 self.steal_time_previous
336 }
337
338 pub fn set_steal_time_previous(&mut self, share: Option<FloatType>) {
340 self.steal_time_previous = share;
341 }
342
343 pub fn steal_time_following(&self) -> Option<FloatType> {
346 self.steal_time_following
347 }
348
349 pub fn set_steal_time_following(&mut self, share: Option<FloatType>) {
351 self.steal_time_following = share;
352 }
353
354 pub fn make_time(&self) -> bool {
357 self.make_time
358 }
359
360 pub fn set_make_time(&mut self, make_time: bool) {
362 self.make_time = make_time;
363 }
364}
365
366#[derive(Clone, Copy, Debug, PartialEq)]
374#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
375#[must_use]
376pub struct DurationTuple {
377 duration_type: Option<DurationType>,
378 dots: u32,
379 quarter_length: FloatType,
380}
381
382impl DurationTuple {
383 pub fn new(duration_type: DurationType, dots: u32) -> Self {
385 Self {
386 duration_type: Some(duration_type),
387 dots,
388 quarter_length: duration_type.quarter_length_with_dots(dots),
389 }
390 }
391
392 pub fn from_quarter_length(quarter_length: FloatType) -> Self {
396 if quarter_length == 0.0 {
397 return Self::new(DurationType::Zero, 0);
398 }
399 match exact_type_and_dots(quarter_length) {
400 Some((duration_type, dots)) => Self::new(duration_type, dots),
401 None => Self {
402 duration_type: None,
403 dots: 0,
404 quarter_length,
405 },
406 }
407 }
408
409 pub fn silenced(self) -> Self {
412 Self {
413 quarter_length: 0.0,
414 ..self
415 }
416 }
417
418 pub fn duration_type(&self) -> Option<DurationType> {
420 self.duration_type
421 }
422
423 pub fn dots(&self) -> u32 {
425 self.dots
426 }
427
428 pub fn quarter_length(&self) -> FloatType {
430 self.quarter_length
431 }
432}
433
434const TUPLET_NUMERATORS: [u32; 5] = [3, 5, 7, 11, 13];
438
439const TUPLET_DOTS: [u32; 2] = [0, 1];
442
443const MAX_TIED_COMPONENTS: usize = 8;
446
447const DENOMINATOR_LIMIT: i128 = 65535;
450
451fn reduce(numerator: &mut i128, denominator: &mut i128) {
453 let divisor = num::integer::gcd(*numerator, *denominator);
454 if divisor > 1 {
455 *numerator /= divisor;
456 *denominator /= divisor;
457 }
458}
459
460pub(crate) fn limited_fraction(value: FloatType, max_denominator: i128) -> Option<(i128, i128)> {
463 if !value.is_finite() || value <= 0.0 {
464 return None;
465 }
466 let mut exact = value;
469 let mut denominator: i128 = 1;
470 for _ in 0..64 {
471 if exact.fract() == 0.0 {
472 break;
473 }
474 exact *= 2.0;
475 denominator = denominator.checked_mul(2)?;
476 }
477 let mut numerator = exact as i128;
478 if exact.fract() != 0.0 {
479 return None;
480 }
481 if denominator <= max_denominator {
482 reduce(&mut numerator, &mut denominator);
483 return Some((numerator, denominator));
484 }
485 let (mut p0, mut q0, mut p1, mut q1) = (0i128, 1i128, 1i128, 0i128);
487 let (mut n, mut d) = (numerator, denominator);
488 loop {
489 let a = n / d;
490 let q2 = q0 + a * q1;
491 if q2 > max_denominator {
492 break;
493 }
494 (p0, q0, p1, q1) = (p1, q1, p0 + a * p1, q2);
495 let next = n - a * d;
496 n = d;
497 d = next;
498 if d == 0 {
499 break;
500 }
501 }
502 if q1 == 0 {
503 return None;
504 }
505 let k = (max_denominator - q0) / q1;
506 let (bound_numerator, bound_denominator) = (p0 + k * p1, q0 + k * q1);
507 let value_as =
508 |numerator: i128, denominator: i128| numerator as FloatType / denominator as FloatType;
509 let one = (value_as(bound_numerator, bound_denominator) - value).abs();
510 let two = (value_as(p1, q1) - value).abs();
511 let (mut numerator, mut denominator) = if one <= two {
512 (bound_numerator, bound_denominator)
513 } else {
514 (p1, q1)
515 };
516 reduce(&mut numerator, &mut denominator);
517 Some((numerator, denominator))
518}
519
520const TUPLET_TOLERANCE: FloatType = 1e-5;
527
528#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
540#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
541#[must_use]
542pub struct Tuplet {
543 actual: u32,
544 normal: u32,
545 duration_actual: Option<(DurationType, u32)>,
547 duration_normal: Option<(DurationType, u32)>,
552 #[cfg_attr(feature = "serde", serde(default))]
555 notation: crate::display::Drawn<TupletNotation>,
556}
557
558#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
560#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
561pub enum TupletType {
562 Start,
564 Stop,
566 StartStop,
568}
569
570impl TupletType {
571 pub fn as_str(self) -> &'static str {
573 match self {
574 Self::Start => "start",
575 Self::Stop => "stop",
576 Self::StartStop => "startStop",
577 }
578 }
579
580 pub fn from_name(name: &str) -> Result<Self> {
582 match name {
583 "start" => Ok(Self::Start),
584 "stop" => Ok(Self::Stop),
585 "startStop" => Ok(Self::StartStop),
586 other => Err(Error::Duration(format!(
587 "Type must be 'start', 'stop', 'startStop', or None, not {other}"
588 ))),
589 }
590 }
591}
592
593#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
596#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
597pub enum TupletBracket {
598 #[default]
600 Bracket,
601 None,
603 Slur,
605}
606
607#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
610#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
611pub enum TupletShow {
612 Number,
614 Type,
616 Both,
618}
619
620impl TupletShow {
621 pub fn as_str(self) -> &'static str {
623 match self {
624 Self::Number => "number",
625 Self::Type => "type",
626 Self::Both => "both",
627 }
628 }
629
630 pub fn from_name(name: &str) -> Result<Self> {
632 match name {
633 "number" => Ok(Self::Number),
634 "type" => Ok(Self::Type),
635 "both" => Ok(Self::Both),
636 other => Err(Error::Duration(format!(
637 "a tuplet shows a number, a type or both, not {other}"
638 ))),
639 }
640 }
641}
642
643#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
647#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
648pub struct TupletNotation {
649 tuplet_type: Option<TupletType>,
650 bracket: TupletBracket,
651 placement: Option<crate::notation::Placement>,
652 actual_show: Option<TupletShow>,
653 normal_show: Option<TupletShow>,
654}
655
656impl Default for TupletNotation {
657 fn default() -> Self {
658 Self {
659 tuplet_type: None,
660 bracket: TupletBracket::Bracket,
661 placement: Some(crate::notation::Placement::Above),
662 actual_show: Some(TupletShow::Number),
663 normal_show: None,
664 }
665 }
666}
667
668const ASSUMED_VALUE: (DurationType, u32) = (DurationType::Eighth, 0);
670
671impl Tuplet {
672 pub fn new(actual: u32, normal: u32, duration_type: DurationType, dots: u32) -> Self {
675 Self {
676 actual,
677 normal,
678 duration_actual: Some((duration_type, dots)),
679 duration_normal: Some((duration_type, dots)),
680 notation: crate::display::Drawn(TupletNotation::default()),
681 }
682 }
683
684 pub fn ratio(actual: u32, normal: u32) -> Self {
695 Self {
696 actual,
697 normal,
698 duration_actual: None,
699 duration_normal: None,
700 notation: crate::display::Drawn(TupletNotation::default()),
701 }
702 }
703
704 pub fn tuplet_type(&self) -> Option<TupletType> {
707 self.notation.0.tuplet_type
708 }
709
710 pub fn set_tuplet_type(&mut self, tuplet_type: Option<TupletType>) {
712 self.notation.0.tuplet_type = tuplet_type;
713 }
714
715 pub fn bracket(&self) -> TupletBracket {
717 self.notation.0.bracket
718 }
719
720 pub fn set_bracket(&mut self, bracket: TupletBracket) {
722 self.notation.0.bracket = bracket;
723 }
724
725 pub fn placement(&self) -> Option<crate::notation::Placement> {
728 self.notation.0.placement
729 }
730
731 pub fn set_placement(&mut self, placement: Option<crate::notation::Placement>) {
733 self.notation.0.placement = placement;
734 }
735
736 pub fn actual_show(&self) -> Option<TupletShow> {
739 self.notation.0.actual_show
740 }
741
742 pub fn set_actual_show(&mut self, show: Option<TupletShow>) {
744 self.notation.0.actual_show = show;
745 }
746
747 pub fn normal_show(&self) -> Option<TupletShow> {
750 self.notation.0.normal_show
751 }
752
753 pub fn set_normal_show(&mut self, show: Option<TupletShow>) {
755 self.notation.0.normal_show = show;
756 }
757
758 pub fn with_normal(mut self, duration_type: DurationType, dots: u32) -> Self {
761 self.duration_normal = Some((duration_type, dots));
762 self
763 }
764
765 pub fn duration_actual(&self) -> Option<(DurationType, u32)> {
768 self.duration_actual
769 }
770
771 pub fn duration_normal(&self) -> Option<(DurationType, u32)> {
774 self.duration_normal
775 }
776
777 pub fn set_duration_actual(&mut self, value: Option<(DurationType, u32)>) {
779 self.duration_actual = value;
780 }
781
782 pub fn set_duration_normal(&mut self, value: Option<(DurationType, u32)>) {
784 self.duration_normal = value;
785 }
786
787 pub fn tuplet_actual(&self) -> (u32, Option<(DurationType, u32)>) {
790 (self.actual, self.duration_actual)
791 }
792
793 pub fn tuplet_normal(&self) -> (u32, Option<(DurationType, u32)>) {
796 (self.normal, self.duration_normal)
797 }
798
799 pub fn set_duration_type(&mut self, duration_type: DurationType, dots: u32) {
802 self.duration_actual = Some((duration_type, dots));
803 self.duration_normal = Some((duration_type, dots));
804 }
805
806 pub fn set_ratio(&mut self, actual: u32, normal: u32) {
809 self.actual = actual;
810 self.normal = normal;
811 }
812
813 pub fn augmented(&self, amount: FloatType) -> Result<Self> {
831 if amount.is_nan() || amount <= 0.0 {
832 return Err(Error::Value(
833 "amountToScale must be greater than zero".to_string(),
834 ));
835 }
836 let scaled = |value: Option<(DurationType, u32)>| -> Result<Option<(DurationType, u32)>> {
837 value
838 .map(|(duration_type, dots)| {
839 let length = duration_type.quarter_length_with_dots(dots) * amount;
840 exact_type_and_dots(length).ok_or_else(|| {
841 Error::Duration(format!("no written value lasts {length} quarter lengths"))
842 })
843 })
844 .transpose()
845 };
846 Ok(Self {
847 duration_actual: scaled(self.duration_actual)?,
848 duration_normal: scaled(self.duration_normal)?,
849 ..*self
850 })
851 }
852
853 pub fn total_tuplet_length(&self) -> FloatType {
856 let (duration_type, dots) = self.duration_normal.unwrap_or(ASSUMED_VALUE);
857 FloatType::from(self.normal) * duration_type.quarter_length_with_dots(dots)
858 }
859
860 pub fn actual(&self) -> u32 {
862 self.actual
863 }
864
865 pub fn normal(&self) -> u32 {
867 self.normal
868 }
869
870 pub fn duration_type(&self) -> DurationType {
872 self.duration_actual.unwrap_or(ASSUMED_VALUE).0
873 }
874
875 pub fn dots(&self) -> u32 {
877 self.duration_actual.unwrap_or(ASSUMED_VALUE).1
878 }
879
880 pub fn normal_duration_type(&self) -> DurationType {
883 self.duration_normal.unwrap_or(ASSUMED_VALUE).0
884 }
885
886 pub fn normal_dots(&self) -> u32 {
888 self.duration_normal.unwrap_or(ASSUMED_VALUE).1
889 }
890
891 pub fn multiplier(&self) -> FractionType {
905 let (actual_type, actual_dots) = self.duration_actual.unwrap_or(ASSUMED_VALUE);
906 let unaltered =
907 FloatType::from(self.actual) * actual_type.quarter_length_with_dots(actual_dots);
908 let (numerator, denominator) =
909 limited_fraction(self.total_tuplet_length() / unaltered, 65535).unwrap_or((0, 1));
910 FractionType::new(
911 IntegerType::try_from(numerator).unwrap_or(IntegerType::MAX),
912 IntegerType::try_from(denominator).unwrap_or(IntegerType::MAX),
913 )
914 }
915
916 pub fn full_name(&self) -> String {
919 match (self.actual, self.normal) {
920 (3, 2) => "Triplet".to_string(),
921 (5, 4 | 2) => "Quintuplet".to_string(),
922 (6, 4) => "Sextuplet".to_string(),
923 (7, 4) => "Septuplet".to_string(),
924 (actual, normal) => format!(
925 "Tuplet of {actual}/{normal}{}",
926 plural_ordinal_abbreviation(normal)
927 ),
928 }
929 }
930}
931
932fn plural_ordinal_abbreviation(value: u32) -> &'static str {
936 if matches!(value % 100, 11..=13) {
937 return "ths";
938 }
939 match value % 10 {
940 1 => "st",
941 2 => "nds",
942 3 => "rds",
943 _ => "ths",
944 }
945}
946
947fn dot_prefix(dots: u32, mensural: bool) -> &'static str {
951 match dots {
952 0 if mensural => "Imperfect ",
953 1 if mensural => "Perfect ",
954 0 => "",
955 1 => "Dotted ",
956 2 => "Double Dotted ",
957 3 => "Triple Dotted ",
958 _ => "Quadruple Dotted ",
959 }
960}
961
962impl Duration {
963 pub fn new(quarter_length: FloatType) -> Result<Self> {
965 if !quarter_length.is_finite() || quarter_length < 0.0 {
966 return Err(Error::Duration(format!(
967 "duration quarter length must be finite and non-negative, got {quarter_length}"
968 )));
969 }
970
971 Ok(Self::of_length(quarter_length))
972 }
973
974 fn of_length(quarter_length: FloatType) -> Self {
976 Self {
977 quarter_length,
978 tuplets: None,
979 written: None,
980 dot_groups: None,
981 unlinked: false,
982 grace: None,
983 said: false,
984 }
985 }
986
987 pub fn grace(&self) -> Option<&Grace> {
991 self.grace.as_ref()
992 }
993
994 pub fn is_grace(&self) -> bool {
996 self.grace.is_some()
997 }
998
999 pub fn set_grace(&mut self, grace: Option<Grace>) {
1002 self.grace = grace;
1003 }
1004
1005 pub fn quarter() -> Self {
1007 Self::from_type(DurationType::Quarter)
1008 }
1009
1010 pub fn half() -> Self {
1012 Self::from_type(DurationType::Half)
1013 }
1014
1015 pub fn whole() -> Self {
1017 Self::from_type(DurationType::Whole)
1018 }
1019
1020 pub fn eighth() -> Self {
1022 Self::from_type(DurationType::Eighth)
1023 }
1024
1025 pub fn from_type(duration_type: DurationType) -> Self {
1027 Self::from_type_with_dots(duration_type, 0)
1028 }
1029
1030 pub fn from_type_with_dots(duration_type: DurationType, dots: u32) -> Self {
1032 Self {
1033 said: true,
1034 ..Self::of_length(duration_type.quarter_length_with_dots(dots))
1035 }
1036 }
1037
1038 pub fn type_and_dots(&self) -> Option<(DurationType, u32)> {
1046 match self.written.as_deref() {
1047 Some([one]) => one
1048 .duration_type
1049 .map(|duration_type| (duration_type, one.dots)),
1050 Some(_) => None,
1051 None => exact_type_and_dots(self.quarter_length),
1052 }
1053 }
1054
1055 pub fn dots(&self) -> u32 {
1058 self.type_and_dots().map_or(0, |(_, dots)| dots)
1059 }
1060
1061 pub fn set_dots(&mut self, dots: u32) -> Result<()> {
1070 let values = self.written_values();
1071 if values.is_empty() {
1072 return Ok(());
1073 }
1074 let dotted = values
1075 .iter()
1076 .map(|value| {
1077 value
1078 .duration_type()
1079 .map(|duration_type| DurationTuple::new(duration_type, dots))
1080 .ok_or_else(|| Error::Duration("Unknown type: inexpressible".to_string()))
1081 })
1082 .collect::<Result<Vec<_>>>()?;
1083 self.set_written_values(dotted);
1084 if self.linked() {
1085 self.set_expression_is_inferred(false);
1086 }
1087 Ok(())
1088 }
1089
1090 pub fn ordinal(&self) -> Option<usize> {
1094 self.type_and_dots()?.0.ordinal()
1095 }
1096
1097 pub fn augment_or_diminish(&self, factor: FloatType) -> Result<Duration> {
1103 if factor.is_nan() || factor <= 0.0 {
1104 return Err(Error::Duration(
1105 "amountToScale must be greater than zero".to_string(),
1106 ));
1107 }
1108 if self.unlinked {
1109 let mut scaled = self.clone();
1110 scaled.tuplets = Some(Vec::new());
1111 scaled.quarter_length = self.quarter_length * factor;
1112 return Ok(scaled);
1113 }
1114 Duration::new(crate::makenotation::op_frac(self.quarter_length * factor))
1115 }
1116
1117 pub fn tuplet(&self) -> Option<Tuplet> {
1126 if self.type_and_dots().is_some() {
1127 return None;
1128 }
1129 quarter_length_to_tuplet(self.quarter_length, 1)
1130 .into_iter()
1131 .next()
1132 }
1133
1134 pub fn tuplets(&self) -> Vec<Tuplet> {
1142 match &self.tuplets {
1143 Some(tuplets) => tuplets.clone(),
1144 None => convert(self.quarter_length, true).1.into_iter().collect(),
1145 }
1146 }
1147
1148 pub(crate) fn said_tuplets(&self) -> Option<&[Tuplet]> {
1151 self.tuplets.as_deref()
1152 }
1153
1154 pub fn aggregate_tuplet_multiplier(&self) -> FractionType {
1161 let ratios: Vec<FractionType> = self.tuplets().iter().map(Tuplet::multiplier).collect();
1162 let exact = ratios
1163 .iter()
1164 .try_fold(FractionType::from(1), |total, ratio| {
1165 fraction::CheckedMul::checked_mul(&total, ratio)
1166 });
1167 if let Some(exact) = exact {
1168 return exact;
1169 }
1170 let product: FloatType = ratios
1171 .iter()
1172 .map(|ratio| ratio.to_f64().unwrap_or(0.0))
1173 .product();
1174 let finest = (FloatType::from(IntegerType::MAX) / product).clamp(1.0, 65535.0) as i128;
1176 let (numerator, denominator) = limited_fraction(product, finest).unwrap_or((0, 1));
1177 FractionType::new(
1178 IntegerType::try_from(numerator).unwrap_or(IntegerType::MAX),
1179 IntegerType::try_from(denominator).unwrap_or(IntegerType::MAX),
1180 )
1181 }
1182
1183 pub fn quarter_length_no_tuplets(&self) -> FloatType {
1186 self.written_values()
1188 .iter()
1189 .fold(0.0, |total, value| total + value.quarter_length)
1190 }
1191
1192 pub fn set_tuplets(&mut self, tuplets: Vec<Tuplet>) {
1196 let written = self.quarter_length_no_tuplets();
1197 self.tuplets = Some(tuplets);
1198 if self.unlinked {
1199 return;
1200 }
1201 self.quarter_length = written * float_from_fraction(self.aggregate_tuplet_multiplier());
1202 }
1203
1204 pub fn append_tuplet(&mut self, tuplet: Tuplet) {
1207 let mut tuplets = self.tuplets();
1208 tuplets.push(tuplet);
1209 self.set_tuplets(tuplets);
1210 }
1211
1212 fn written_quarter_length(&self) -> FloatType {
1220 let Some(tuplets) = &self.tuplets else {
1221 return self.quarter_length;
1222 };
1223 if tuplets.is_empty() {
1224 return self.quarter_length;
1225 }
1226 let multiplier = float_from_fraction(self.aggregate_tuplet_multiplier());
1227 if multiplier == 0.0 {
1228 return self.quarter_length;
1229 }
1230 let written = self.quarter_length / multiplier;
1231 let tolerance = written.abs() * TUPLET_TOLERANCE;
1232 DurationType::ALL
1233 .into_iter()
1234 .flat_map(|duration_type| {
1235 (0..=MAX_DOTS).map(move |dots| duration_type.quarter_length_with_dots(dots))
1236 })
1237 .find(|candidate| (candidate - written).abs() <= tolerance)
1238 .unwrap_or(written)
1239 }
1240
1241 pub fn components(&self) -> Vec<(DurationType, u32)> {
1251 match &self.written {
1252 Some(written) => written
1253 .iter()
1254 .filter_map(|value| Some((value.duration_type?, value.dots)))
1255 .collect(),
1256 None => convert(self.written_quarter_length(), self.tuplets.is_none()).0,
1257 }
1258 }
1259
1260 pub fn written_values(&self) -> Vec<DurationTuple> {
1266 if let Some(written) = &self.written {
1267 return written.clone();
1268 }
1269 let components = self.components();
1270 if components.is_empty() && self.quarter_length != 0.0 {
1271 return vec![DurationTuple::from_quarter_length(
1272 self.written_quarter_length(),
1273 )];
1274 }
1275 components
1276 .into_iter()
1277 .map(|(duration_type, dots)| DurationTuple::new(duration_type, dots))
1278 .collect()
1279 }
1280
1281 fn set_written_values(&mut self, written: Vec<DurationTuple>) {
1284 self.tuplets = Some(self.tuplets());
1286 self.written = Some(written);
1287 self.relength();
1288 }
1289
1290 fn relength(&mut self) {
1293 if self.unlinked {
1294 return;
1295 }
1296 let groups: FloatType = self
1297 .dot_groups
1298 .iter()
1299 .flatten()
1300 .filter(|dots| **dots != 0)
1301 .map(|dots| 2.0 - FloatType::powi(0.5, *dots as i32))
1302 .product();
1303 self.quarter_length = self.quarter_length_no_tuplets()
1304 * float_from_fraction(self.aggregate_tuplet_multiplier())
1305 * groups;
1306 }
1307
1308 pub fn consolidate(&mut self) {
1326 if self.written_values().len() == 1 {
1327 return;
1328 }
1329 let total = self.quarter_length_no_tuplets();
1330 self.set_written_values(vec![DurationTuple::from_quarter_length(total)]);
1331 }
1332
1333 pub fn slice_component_at_position(&mut self, position: FloatType) -> Result<()> {
1340 let written = self.written_values();
1341 let mut start = 0.0;
1342 for (index, value) in written.iter().enumerate() {
1343 let end = start + value.quarter_length;
1344 if position > start && position < end {
1345 let mut sliced = written[..index].to_vec();
1346 sliced.push(DurationTuple::from_quarter_length(position - start));
1347 sliced.push(DurationTuple::from_quarter_length(end - position));
1348 sliced.extend_from_slice(&written[index + 1..]);
1349 self.set_written_values(sliced);
1350 return Ok(());
1351 }
1352 start = end;
1353 }
1354 Err(Error::Duration(
1355 "no slice is possible at this quarter position".to_string(),
1356 ))
1357 }
1358
1359 pub fn set_dot_groups(&mut self, groups: Vec<u32>) {
1364 let undotted = self
1365 .written_values()
1366 .into_iter()
1367 .map(|value| match value.duration_type {
1368 Some(duration_type) => DurationTuple::new(duration_type, 0),
1369 None => value,
1370 })
1371 .collect();
1372 self.dot_groups = Some(groups);
1373 self.set_written_values(undotted);
1374 }
1375
1376 pub fn split_dot_groups(&self) -> Result<Duration> {
1384 let (duration_type, _) = self.type_and_dots().ok_or_else(|| {
1385 Error::Duration("only a single written value has dot groups to split".to_string())
1386 })?;
1387 let groups = self.dot_groups();
1388 let mut values = vec![DurationTuple::new(
1389 duration_type,
1390 groups.first().copied().unwrap_or(0),
1391 )];
1392 for _ in 1..groups.len() {
1393 let smaller: Vec<DurationTuple> = values
1394 .iter()
1395 .map(|value| {
1396 let kind = value.duration_type.unwrap_or(duration_type);
1397 DurationTuple::new(kind.next_smaller().unwrap_or(kind), value.dots)
1398 })
1399 .collect();
1400 values.extend(smaller);
1401 }
1402 let mut split = self.clone();
1403 split.dot_groups = None;
1404 split.set_written_values(values);
1405 Ok(split)
1406 }
1407
1408 pub fn linked(&self) -> bool {
1411 !self.unlinked
1412 }
1413
1414 pub fn set_linked(&mut self, linked: bool) {
1422 if !linked && !self.unlinked {
1423 self.tuplets = Some(self.tuplets());
1424 self.written = Some(self.written_values());
1425 } else if linked && self.unlinked {
1426 self.written = None;
1427 self.tuplets = None;
1428 self.dot_groups = None;
1429 }
1430 self.unlinked = !linked;
1431 }
1432
1433 pub fn grace_duration(&self) -> Duration {
1453 let mut written: Vec<DurationTuple> = self
1454 .written_values()
1455 .into_iter()
1456 .map(DurationTuple::silenced)
1457 .collect();
1458 if written.is_empty() {
1459 written.push(DurationTuple::new(DurationType::Eighth, 0).silenced());
1460 }
1461 Duration {
1462 quarter_length: 0.0,
1463 tuplets: Some(Vec::new()),
1464 written: Some(written),
1465 dot_groups: None,
1466 unlinked: true,
1467 grace: Some(Grace::new()),
1468 said: true,
1469 }
1470 }
1471
1472 pub fn expression_is_inferred(&self) -> bool {
1488 !self.said
1489 }
1490
1491 pub fn set_expression_is_inferred(&mut self, inferred: bool) {
1494 self.said = !inferred;
1495 }
1496
1497 pub(crate) fn grace_duration_as(&self, appoggiatura: bool) -> Duration {
1501 let mut grace = self.grace_duration();
1502 if appoggiatura {
1503 grace.grace = Some(Grace::appoggiatura());
1504 }
1505 grace
1506 }
1507
1508 pub fn is_complex(&self) -> bool {
1511 self.components().len() > 1
1512 }
1513
1514 pub fn dot_groups(&self) -> Vec<u32> {
1518 match &self.dot_groups {
1519 Some(groups) => groups.clone(),
1520 None => vec![self.dots()],
1521 }
1522 }
1523
1524 pub fn clear(&mut self) {
1528 self.tuplets = Some(self.tuplets());
1529 self.written = Some(Vec::new());
1530 self.dot_groups = None;
1531 self.quarter_length = 0.0;
1532 }
1533
1534 pub fn add_duration_tuple(&mut self, duration_type: DurationType, dots: u32) {
1542 let mut written = self.written_values();
1543 written.push(DurationTuple::new(duration_type, dots));
1544 self.set_written_values(written);
1545 }
1546
1547 pub fn component_index_at_qtr_position(&self, position: FloatType) -> Result<usize> {
1552 let components = self.components();
1553 if components.is_empty() {
1554 return Err(Error::Duration(
1555 "Need components to run getComponentIndexAtQtrPosition".to_string(),
1556 ));
1557 }
1558 let total = self.quarter_length_no_tuplets();
1559 if position.is_nan() || position < 0.0 {
1560 return Err(Error::Value(
1561 "position is before the start of the duration".to_string(),
1562 ));
1563 }
1564 if position > total {
1565 return Err(Error::Value(
1566 "position is after the end of the duration".to_string(),
1567 ));
1568 }
1569 if position == total {
1570 return Ok(components.len() - 1);
1571 }
1572 let mut reached = 0.0;
1573 for (index, (duration_type, dots)) in components.iter().enumerate() {
1574 reached += duration_type.quarter_length_with_dots(*dots);
1575 if reached > position {
1576 return Ok(index);
1577 }
1578 }
1579 Ok(components.len() - 1)
1580 }
1581
1582 pub fn component_start_time(&self, index: usize) -> Result<FloatType> {
1586 let components = self.components();
1587 if index >= components.len() {
1588 return Err(Error::Duration(format!(
1589 "invalid component index value {index} submitted; value must be an integer between 0 and {}",
1590 components.len().saturating_sub(1)
1591 )));
1592 }
1593 Ok(components[..index]
1594 .iter()
1595 .map(|(duration_type, dots)| duration_type.quarter_length_with_dots(*dots))
1596 .sum())
1597 }
1598
1599 pub fn full_name(&self) -> String {
1608 let components = self.components();
1609 if components.is_empty() {
1610 return if self.quarter_length == 0.0 {
1611 "Zero Duration (0 total QL)".to_string()
1616 } else {
1617 "Inexpressible".to_string()
1618 };
1619 }
1620 let tuplet = if components.len() == 1 && self.type_and_dots().is_none() {
1624 self.tuplets().first().copied()
1625 } else {
1626 None
1627 };
1628 let names: Vec<String> = components
1629 .iter()
1630 .map(|(duration_type, dots)| {
1631 let mensural = matches!(duration_type, DurationType::Longa | DurationType::Maxima);
1632 let mut name = format!("{}{}", dot_prefix(*dots, mensural), duration_type.title());
1633 if let Some(tuplet) = &tuplet {
1634 name.push(' ');
1635 name.push_str(&tuplet.full_name());
1636 }
1637 if tuplet.is_some() || *dots >= 3 {
1640 name.push_str(&format!(" ({} QL)", mixed_numeral(self.quarter_length)));
1641 }
1642 name
1643 })
1644 .collect();
1645 let mut name = names.join(" tied to ");
1646 if components.len() != 1 {
1647 name.push_str(&format!(
1648 " ({} total QL)",
1649 mixed_numeral(self.quarter_length)
1650 ));
1651 }
1652 name
1653 }
1654
1655 pub fn duration_type(&self) -> Option<DurationType> {
1660 self.type_and_dots().map(|(duration_type, _)| duration_type)
1661 }
1662
1663 pub fn quarter_length(&self) -> FloatType {
1665 self.quarter_length
1666 }
1667
1668 pub fn set_quarter_length(&mut self, quarter_length: FloatType) -> Result<()> {
1673 let mut replacement = Self::new(quarter_length)?;
1674 if self.unlinked {
1675 self.quarter_length = replacement.quarter_length;
1676 } else {
1677 replacement.said = self.said && self.quarter_length == replacement.quarter_length;
1679 *self = replacement;
1680 }
1681 Ok(())
1682 }
1683}
1684
1685const MAX_DOTS: u32 = 4;
1686
1687fn exact_type_and_dots(quarter_length: FloatType) -> Option<(DurationType, u32)> {
1689 DurationType::ALL.into_iter().find_map(|duration_type| {
1690 (0..=MAX_DOTS)
1691 .find(|dots| duration_type.quarter_length_with_dots(*dots) == quarter_length)
1692 .map(|dots| (duration_type, dots))
1693 })
1694}
1695
1696fn float_from_fraction(ratio: FractionType) -> FloatType {
1699 ratio.to_f64().unwrap_or(0.0)
1700}
1701
1702pub fn quarter_length_to_closest_type(quarter_length: FloatType) -> Result<(DurationType, bool)> {
1706 let too_small = || {
1707 Error::Duration(format!(
1708 "cannot return types smaller than 2048th; quarter length was {quarter_length}"
1709 ))
1710 };
1711 if quarter_length.is_nan() || quarter_length <= 0.0 {
1712 return Err(too_small());
1713 }
1714 let note_length = 4.0 / quarter_length;
1715 if let Some(exact) = DurationType::ALL
1716 .into_iter()
1717 .find(|duration_type| duration_type.type_number() == Some(note_length))
1718 {
1719 return Ok((exact, true));
1720 }
1721 let upper_bound = 8.0 / quarter_length;
1722 if let Some(closest) = DurationType::ALL.into_iter().find(|duration_type| {
1723 duration_type
1724 .type_number()
1725 .is_some_and(|number| note_length < number && number < upper_bound)
1726 }) {
1727 return Ok((closest, false));
1728 }
1729 if quarter_length > 128.0 {
1730 return Ok((DurationType::DuplexMaxima, false));
1731 }
1732 Err(too_small())
1733}
1734
1735pub fn quarter_length_to_tuplet(quarter_length: FloatType, max_to_return: usize) -> Vec<Tuplet> {
1747 let mut found = Vec::new();
1748 if quarter_length.is_nan() || quarter_length <= 0.0 || max_to_return == 0 {
1749 return found;
1750 }
1751 let mut values = DurationType::ALL;
1752 values.sort_by(|left, right| {
1753 left.quarter_length()
1754 .partial_cmp(&right.quarter_length())
1755 .unwrap_or(std::cmp::Ordering::Equal)
1756 });
1757 let tolerance = quarter_length * TUPLET_TOLERANCE;
1758 for duration_type in values {
1759 for actual in TUPLET_NUMERATORS {
1760 for normal in 1..actual {
1761 for dots in TUPLET_DOTS {
1762 let candidate = duration_type.quarter_length_with_dots(dots)
1763 * FloatType::from(normal)
1764 / FloatType::from(actual);
1765 if (candidate - quarter_length).abs() <= tolerance {
1766 found.push(Tuplet::new(actual, normal, duration_type, dots));
1767 break;
1768 }
1769 }
1770 }
1771 if found.len() >= max_to_return {
1772 return found;
1773 }
1774 }
1775 }
1776 found
1777}
1778
1779pub fn quarter_length_to_non_power_of_2_tuplet(
1789 quarter_length: FloatType,
1790) -> Option<(Tuplet, DurationType, u32)> {
1791 if quarter_length.is_nan() || quarter_length <= 0.0 {
1792 return None;
1793 }
1794 let (original_actual, original_normal) =
1795 limited_fraction(1.0 / quarter_length, DENOMINATOR_LIMIT)?;
1796 let (mut actual, mut normal) = (original_actual, original_normal);
1797 while actual < normal {
1799 actual *= 2;
1800 reduce(&mut actual, &mut normal);
1801 }
1802 while actual > normal * 2 {
1803 normal *= 2;
1804 reduce(&mut actual, &mut normal);
1805 }
1806 let (written, _) = quarter_length_to_closest_type(quarter_length / normal as FloatType).ok()?;
1807 let inside = (actual as FloatType / normal as FloatType)
1810 / (original_actual as FloatType / original_normal as FloatType);
1811 let (kind, dots) = exact_type_and_dots(inside)?;
1812 Some((
1813 Tuplet::new(
1814 u32::try_from(actual).ok()?,
1815 u32::try_from(normal).ok()?,
1816 written,
1817 0,
1818 ),
1819 kind,
1820 dots,
1821 ))
1822}
1823
1824pub fn quarter_conversion(quarter_length: FloatType) -> (Vec<(DurationType, u32)>, Option<Tuplet>) {
1839 convert(quarter_length, true)
1840}
1841
1842fn convert(
1845 written: FloatType,
1846 look_for_tuplet: bool,
1847) -> (Vec<(DurationType, u32)>, Option<Tuplet>) {
1848 if written == 0.0 {
1851 return (Vec::new(), None);
1852 }
1853 if let Some(value) = exact_type_and_dots(written) {
1854 return (vec![value], None);
1855 }
1856 let Ok((largest, _)) = quarter_length_to_closest_type(written) else {
1860 return (Vec::new(), None);
1861 };
1862 if largest.next_larger().is_none() {
1863 return (Vec::new(), None);
1864 }
1865 if look_for_tuplet && let Some(tuplet) = quarter_length_to_tuplet(written, 1).into_iter().next()
1866 {
1867 return (vec![(tuplet.duration_type(), tuplet.dots())], Some(tuplet));
1868 }
1869 let mut components = vec![(largest, 0)];
1870 let mut remainder = written - largest.quarter_length();
1871 for _ in 0..MAX_TIED_COMPONENTS {
1872 if let Some(rest) = exact_type_and_dots(remainder) {
1873 components.push(rest);
1874 return (components, None);
1875 }
1876 let Ok((next, _)) = quarter_length_to_closest_type(remainder) else {
1877 break;
1878 };
1879 remainder -= next.quarter_length();
1880 components.push((next, 0));
1881 }
1882 match quarter_length_to_non_power_of_2_tuplet(written) {
1883 Some((tuplet, kind, dots)) => (vec![(kind, dots)], Some(tuplet)),
1884 None => (Vec::new(), None),
1885 }
1886}
1887
1888const MAX_MIXED_NUMERAL_DENOMINATOR: u32 = 1024;
1893
1894const MIXED_NUMERAL_TOLERANCE: FloatType = 1e-6;
1897
1898fn mixed_numeral(value: FloatType) -> String {
1901 let whole = value.trunc() as IntegerType;
1902 let remainder = value - value.trunc();
1903 if remainder == 0.0 {
1904 return whole.to_string();
1905 }
1906 let tolerance = MIXED_NUMERAL_TOLERANCE * value.abs().max(1.0);
1912 let fractional = (1..=MAX_MIXED_NUMERAL_DENOMINATOR)
1913 .find_map(|denominator| {
1914 let numerator = (remainder * FloatType::from(denominator)).round();
1915 ((remainder - numerator / FloatType::from(denominator)).abs() <= tolerance)
1916 .then(|| format!("{}/{denominator}", numerator as IntegerType))
1917 })
1918 .unwrap_or_else(|| remainder.to_string());
1919 if whole == 0 {
1920 fractional
1921 } else {
1922 format!("{whole} {fractional}")
1923 }
1924}
1925
1926impl Default for Duration {
1927 fn default() -> Self {
1928 Self::quarter()
1929 }
1930}
1931
1932impl PartialEq for Duration {
1933 fn eq(&self, other: &Self) -> bool {
1934 self.quarter_length == other.quarter_length
1935 }
1936}
1937
1938impl Eq for Duration {}
1939
1940impl std::hash::Hash for Duration {
1942 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1943 ordered_float::OrderedFloat(self.quarter_length).hash(state);
1944 }
1945}
1946
1947impl TryFrom<FloatType> for Duration {
1948 type Error = Error;
1949
1950 fn try_from(value: FloatType) -> Result<Self> {
1951 Self::new(value)
1952 }
1953}
1954
1955impl TryFrom<IntegerType> for Duration {
1956 type Error = Error;
1957
1958 fn try_from(value: IntegerType) -> Result<Self> {
1959 Self::new(value as FloatType)
1960 }
1961}
1962
1963#[cfg(test)]
1964mod tests {
1965
1966 #[test]
1969 fn tuplets_too_fine_to_multiply_exactly_are_multiplied_as_floats() {
1970 use super::{Duration, DurationType, Tuplet};
1971 use fraction::ToPrimitive;
1972
1973 let mut duration = Duration::new(0.25).unwrap();
1974 for normal in [2_147_483_647, 2] {
1975 duration.append_tuplet(Tuplet::new(3, normal, DurationType::Sixteenth, 0));
1976 }
1977 let multiplier = duration.aggregate_tuplet_multiplier().to_f64().unwrap();
1978 let expected = 2_147_483_647.0 / 3.0 * 2.0 / 3.0;
1979 assert!(
1980 (multiplier - expected).abs() / expected < 1e-6,
1981 "{multiplier}"
1982 );
1983 }
1984
1985 #[test]
1986 fn a_type_is_found_from_its_undotted_length_alone() {
1987 assert_eq!(
1988 DurationType::from_quarter_length(2.0),
1989 Some(DurationType::Half)
1990 );
1991 assert_eq!(
1992 DurationType::from_quarter_length(0.125),
1993 Some(DurationType::ThirtySecond)
1994 );
1995 assert_eq!(DurationType::from_quarter_length(3.0), None);
1996 assert_eq!(super::mixed_numeral(2.0 / 3.0), "2/3");
1997 assert_eq!(super::mixed_numeral(1.0 / 3.0 + 1.0), "1 1/3");
1998 }
1999
2000 #[test]
2001 fn a_duration_is_cleared_and_lengthened_a_value_at_a_time() {
2002 let mut duration = Duration::new(1.5).unwrap();
2003 assert_eq!(duration.dot_groups(), [1]);
2004 duration.add_duration_tuple(DurationType::Eighth, 0);
2005 assert_eq!(duration.quarter_length(), 2.0);
2006 duration.clear();
2007 assert_eq!(duration.quarter_length(), 0.0);
2008 assert_eq!(duration.dot_groups(), [0]);
2009 let mut triplet = Duration::new(2.0 / 3.0).unwrap();
2011 triplet.add_duration_tuple(DurationType::Quarter, 0);
2012 assert!((triplet.quarter_length() - 4.0 / 3.0).abs() < 1e-9);
2013 }
2014
2015 #[test]
2017 fn written_values_are_cut_consolidated_split_and_unlinked_as_music21_does() {
2018 let typed = |duration: &Duration| -> Vec<(Option<DurationType>, u32, FloatType)> {
2019 duration
2020 .written_values()
2021 .iter()
2022 .map(|value| (value.duration_type(), value.dots(), value.quarter_length()))
2023 .collect()
2024 };
2025
2026 let mut tied = Duration::new(1.25).unwrap();
2028 tied.slice_component_at_position(0.5).unwrap();
2029 assert_eq!(
2030 tied.components(),
2031 [
2032 (DurationType::Eighth, 0),
2033 (DurationType::Eighth, 0),
2034 (DurationType::Sixteenth, 0)
2035 ]
2036 );
2037 assert_eq!(tied.quarter_length(), 1.25);
2038 let mut dotted = Duration::new(3.0).unwrap();
2039 dotted.slice_component_at_position(1.25).unwrap();
2040 assert_eq!(
2041 typed(&dotted),
2042 [(None, 0, 1.25), (Some(DurationType::Quarter), 2, 1.75)]
2043 );
2044 assert!(
2045 Duration::new(1.25)
2046 .unwrap()
2047 .slice_component_at_position(1.0)
2048 .is_err()
2049 );
2050
2051 let mut long = Duration::quarter();
2053 long.add_duration_tuple(DurationType::Half, 0);
2054 long.add_duration_tuple(DurationType::Half, 0);
2055 assert_eq!(long.components().len(), 3);
2056 assert_eq!(long.duration_type(), None);
2057 long.consolidate();
2058 assert_eq!(typed(&long), [(None, 0, 5.0)]);
2059 assert_eq!(long.quarter_length(), 5.0);
2060 let mut triplets = Duration::new(1.0 / 3.0).unwrap();
2061 triplets.add_duration_tuple(DurationType::Eighth, 0);
2062 triplets.consolidate();
2063 assert_eq!(triplets.components(), [(DurationType::Quarter, 0)]);
2064 assert!((triplets.quarter_length() - 2.0 / 3.0).abs() < 1e-9);
2065
2066 let mut half = Duration::half();
2068 half.set_dot_groups(vec![1, 1]);
2069 assert_eq!(half.quarter_length(), 4.5);
2070 assert_eq!(half.components(), [(DurationType::Half, 0)]);
2071 let split = half.split_dot_groups().unwrap();
2072 assert_eq!(
2073 split.components(),
2074 [(DurationType::Half, 1), (DurationType::Quarter, 1)]
2075 );
2076 assert_eq!(split.quarter_length(), 4.5);
2077 half.set_dot_groups(vec![1, 1, 1]);
2078 assert_eq!(half.quarter_length(), 6.75);
2079 assert_eq!(
2080 half.split_dot_groups().unwrap().components(),
2081 [
2082 (DurationType::Half, 1),
2083 (DurationType::Quarter, 1),
2084 (DurationType::Quarter, 1),
2085 (DurationType::Eighth, 1)
2086 ]
2087 );
2088 assert_eq!(
2089 Duration::new(1.5)
2090 .unwrap()
2091 .split_dot_groups()
2092 .unwrap()
2093 .components(),
2094 [(DurationType::Quarter, 1)]
2095 );
2096
2097 let mut unlinked = Duration::quarter();
2100 assert!(unlinked.linked());
2101 unlinked.set_linked(false);
2102 unlinked.set_quarter_length(3.0).unwrap();
2103 assert_eq!(unlinked.duration_type(), Some(DurationType::Quarter));
2104 assert_eq!(unlinked.quarter_length(), 3.0);
2105 unlinked.set_linked(true);
2106 assert_eq!(unlinked.type_and_dots(), Some((DurationType::Half, 1)));
2107 assert_eq!(unlinked.quarter_length(), 3.0);
2108
2109 let grace = Duration::new(1.25).unwrap().grace_duration();
2111 assert_eq!(
2112 typed(&grace),
2113 [
2114 (Some(DurationType::Quarter), 0, 0.0),
2115 (Some(DurationType::Sixteenth), 0, 0.0)
2116 ]
2117 );
2118 assert!(!grace.linked());
2119 assert_eq!(grace.quarter_length(), 0.0);
2120 let bare = Duration::new(0.0).unwrap().grace_duration();
2121 assert_eq!(bare.duration_type(), Some(DurationType::Eighth));
2122 let appoggiatura = Duration::new(1.0 / 3.0).unwrap().grace_duration();
2123 assert_eq!(appoggiatura.components(), [(DurationType::Eighth, 0)]);
2124 assert!(appoggiatura.tuplets().is_empty());
2125 }
2126
2127 #[test]
2129 fn a_position_within_a_tie_names_the_value_sounding_there() {
2130 let tied = Duration::new(2.5).unwrap();
2131 assert_eq!(tied.components().len(), 2);
2132 assert_eq!(tied.component_index_at_qtr_position(0.0).unwrap(), 0);
2133 assert_eq!(tied.component_index_at_qtr_position(1.5).unwrap(), 0);
2134 assert_eq!(tied.component_index_at_qtr_position(2.0).unwrap(), 1);
2135 assert_eq!(tied.component_index_at_qtr_position(2.5).unwrap(), 1);
2136 assert!(tied.component_index_at_qtr_position(3.0).is_err());
2137 assert!(tied.component_index_at_qtr_position(-1.0).is_err());
2138 assert!(
2139 Duration::new(0.0)
2140 .unwrap()
2141 .component_index_at_qtr_position(0.0)
2142 .is_err()
2143 );
2144 assert_eq!(tied.component_start_time(0).unwrap(), 0.0);
2145 assert_eq!(tied.component_start_time(1).unwrap(), 2.0);
2146 assert!(tied.component_start_time(2).is_err());
2147 }
2148
2149 #[test]
2150 fn a_tuplet_reads_and_writes_both_of_its_sides() {
2151 let mut tuplet = Tuplet::new(3, 2, DurationType::Eighth, 0);
2152 assert_eq!(tuplet.duration_actual(), Some((DurationType::Eighth, 0)));
2153 assert_eq!(tuplet.duration_normal(), Some((DurationType::Eighth, 0)));
2154 assert_eq!(tuplet.tuplet_actual(), (3, Some((DurationType::Eighth, 0))));
2155 assert_eq!(tuplet.tuplet_normal(), (2, Some((DurationType::Eighth, 0))));
2156 tuplet.set_duration_type(DurationType::Quarter, 1);
2157 assert_eq!(tuplet.duration_actual(), Some((DurationType::Quarter, 1)));
2158 assert_eq!(tuplet.normal_duration_type(), DurationType::Quarter);
2159 assert_eq!(tuplet.normal_dots(), 1);
2160 tuplet.set_ratio(5, 4);
2161 assert_eq!((tuplet.actual(), tuplet.normal()), (5, 4));
2162 assert_eq!(tuplet.multiplier(), FractionType::new(4, 5));
2163 }
2164
2165 #[test]
2168 fn a_tuplet_may_leave_its_written_values_unsaid() {
2169 let mut triplet = Tuplet::ratio(3, 2);
2170 assert_eq!(triplet.duration_actual(), None);
2171 assert_eq!(triplet.total_tuplet_length(), 1.0);
2172 assert_eq!(triplet.multiplier(), FractionType::new(2, 3));
2173 triplet.set_duration_type(DurationType::Half, 1);
2174 assert_eq!(triplet.total_tuplet_length(), 6.0);
2175
2176 let mut three_in_one = Tuplet::ratio(3, 1);
2177 three_in_one.set_duration_actual(Some((DurationType::Quarter, 0)));
2178 three_in_one.set_duration_normal(Some((DurationType::Half, 0)));
2179 assert_eq!(three_in_one.multiplier(), FractionType::new(2, 3));
2180 assert_eq!(three_in_one.total_tuplet_length(), 2.0);
2181
2182 let mut six = Tuplet::ratio(6, 2);
2183 six.set_duration_type(DurationType::Eighth, 0);
2184 let halved = six.augmented(0.5).unwrap();
2185 assert_eq!(halved.duration_actual(), Some((DurationType::Sixteenth, 0)));
2186 assert_eq!(halved.multiplier(), FractionType::new(1, 3));
2187 assert!(six.augmented(-1.0).is_err());
2188
2189 let mut five = Tuplet::ratio(5, 2);
2190 five.set_duration_type(DurationType::Half, 0);
2191 five.set_duration_normal(Some((DurationType::Whole, 0)));
2192 assert_eq!(five.total_tuplet_length(), 8.0);
2193 }
2194
2195 #[test]
2198 fn a_length_is_read_as_the_tuplets_and_ties_music21_reads() {
2199 use super::{
2200 Duration, DurationType, quarter_conversion, quarter_length_to_non_power_of_2_tuplet,
2201 quarter_length_to_tuplet,
2202 };
2203
2204 let names = |tuplets: Vec<Tuplet>| -> Vec<String> {
2205 tuplets
2206 .iter()
2207 .map(|tuplet| {
2208 format!(
2209 "{}/{}/{}",
2210 tuplet.actual(),
2211 tuplet.normal(),
2212 tuplet.duration_type().music21_name()
2213 )
2214 })
2215 .collect()
2216 };
2217 assert_eq!(
2218 names(quarter_length_to_tuplet(0.333_333_33, 4)),
2219 ["3/2/eighth", "3/1/quarter"]
2220 );
2221 assert_eq!(
2222 names(quarter_length_to_tuplet(0.20, 4)),
2223 ["5/4/16th", "5/2/eighth", "5/1/quarter"]
2224 );
2225 assert_eq!(
2226 names(quarter_length_to_tuplet(0.333_333_3, 1)),
2227 ["3/2/eighth"]
2228 );
2229 let plain = quarter_length_to_tuplet(1.0, 1);
2232 assert_eq!(names(plain.clone()), ["3/2/quarter"]);
2233 assert_eq!(plain[0].dots(), 1);
2234 assert!(quarter_length_to_tuplet(0.0, 4).is_empty());
2235
2236 let (tuplet, kind, dots) = quarter_length_to_non_power_of_2_tuplet(7.0).unwrap();
2237 assert_eq!(names(vec![tuplet]), ["8/7/quarter"]);
2238 assert_eq!((kind, dots), (DurationType::Breve, 0));
2239 let (tuplet, kind, _) = quarter_length_to_non_power_of_2_tuplet(7.0 / 3.0).unwrap();
2240 assert_eq!(names(vec![tuplet]), ["12/7/16th"]);
2241 assert_eq!(kind, DurationType::Whole);
2242 assert!(quarter_length_to_non_power_of_2_tuplet(0.0).is_none());
2243
2244 let (components, tuplet) = quarter_conversion(2.5);
2245 assert_eq!(
2246 components,
2247 [(DurationType::Half, 0), (DurationType::Eighth, 0)]
2248 );
2249 assert!(tuplet.is_none());
2250 let (components, tuplet) = quarter_conversion(2.0 / 3.0);
2251 assert_eq!(components, [(DurationType::Quarter, 0)]);
2252 assert_eq!(names(tuplet.into_iter().collect()), ["3/2/quarter"]);
2253 let (components, tuplet) = quarter_conversion(3.75);
2254 assert_eq!(components, [(DurationType::Half, 3)]);
2255 assert!(tuplet.is_none());
2256 assert_eq!(quarter_conversion(99.0), (Vec::new(), None));
2257 assert_eq!(quarter_conversion(0.0), (Vec::new(), None));
2258
2259 assert!(Duration::new(2.5).unwrap().is_complex());
2260 assert!(!Duration::new(3.0).unwrap().is_complex());
2261 assert!(!Duration::new(2.0 / 3.0).unwrap().is_complex());
2262 }
2263
2264 #[test]
2265 fn neighbouring_types_match_music21() {
2266 assert_eq!(
2267 DurationType::Quarter.next_larger(),
2268 Some(DurationType::Half)
2269 );
2270 assert_eq!(
2271 DurationType::Quarter.next_smaller(),
2272 Some(DurationType::Eighth)
2273 );
2274 assert_eq!(DurationType::Whole.next_larger(), Some(DurationType::Breve));
2275 assert_eq!(DurationType::Breve.next_larger(), Some(DurationType::Longa));
2276 assert_eq!(
2277 DurationType::Sixteenth.next_smaller(),
2278 Some(DurationType::ThirtySecond)
2279 );
2280 assert_eq!(DurationType::DuplexMaxima.next_larger(), None);
2281 assert_eq!(DurationType::ALL[15].next_smaller(), None);
2282 assert_eq!(DurationType::Zero.next_larger(), None);
2283 assert_eq!(DurationType::Zero.next_smaller(), None);
2284 }
2285
2286 #[test]
2287 fn ordinal_and_scaling_match_music21() {
2288 assert_eq!(DurationType::DuplexMaxima.ordinal(), Some(0));
2289 assert_eq!(DurationType::Quarter.ordinal(), Some(6));
2290 assert_eq!(DurationType::Sixteenth.ordinal(), Some(8));
2291 assert_eq!(DurationType::Zero.ordinal(), None);
2292 assert_eq!(Duration::new(1.5).unwrap().ordinal(), Some(6));
2293 assert_eq!(Duration::new(2.5).unwrap().ordinal(), None);
2294 assert_eq!(Duration::new(0.0).unwrap().ordinal(), None);
2295 assert_eq!(
2296 Duration::new(1.0)
2297 .unwrap()
2298 .augment_or_diminish(2.0)
2299 .unwrap()
2300 .quarter_length(),
2301 2.0
2302 );
2303 assert_eq!(
2304 Duration::new(1.5)
2305 .unwrap()
2306 .augment_or_diminish(0.5)
2307 .unwrap()
2308 .quarter_length(),
2309 0.75
2310 );
2311 assert!(
2312 Duration::new(1.0)
2313 .unwrap()
2314 .augment_or_diminish(0.0)
2315 .is_err()
2316 );
2317 assert!(
2318 Duration::new(1.0)
2319 .unwrap()
2320 .augment_or_diminish(-1.0)
2321 .is_err()
2322 );
2323 let mut unlinked = Duration::new(0.25).unwrap();
2325 unlinked.set_linked(false);
2326 unlinked.set_quarter_length(4.0 / 17.0).unwrap();
2327 let scaled = unlinked.augment_or_diminish(1.5).unwrap();
2328 assert!(!scaled.linked());
2329 assert_eq!(scaled.quarter_length(), 4.0 / 17.0 * 1.5);
2330 assert_ne!(scaled.quarter_length(), 6.0 / 17.0);
2331 assert_eq!(scaled.type_and_dots(), Some((DurationType::Sixteenth, 0)));
2332 }
2333 use super::*;
2334
2335 const MUSIC21_TYPE_TO_DURATION: [(&str, FloatType); 17] = [
2337 ("duplex-maxima", 64.0),
2338 ("maxima", 32.0),
2339 ("longa", 16.0),
2340 ("breve", 8.0),
2341 ("whole", 4.0),
2342 ("half", 2.0),
2343 ("quarter", 1.0),
2344 ("eighth", 0.5),
2345 ("16th", 0.25),
2346 ("32nd", 0.125),
2347 ("64th", 0.0625),
2348 ("128th", 0.03125),
2349 ("256th", 0.015625),
2350 ("512th", 0.0078125),
2351 ("1024th", 0.00390625),
2352 ("2048th", 0.001953125),
2353 ("zero", 0.0),
2354 ];
2355
2356 #[test]
2357 fn dots_types_and_full_names_match_music21() {
2358 let cases = [
2359 (
2360 4.0,
2361 Some((DurationType::Whole, 0)),
2362 "Whole",
2363 (DurationType::Whole, true),
2364 ),
2365 (
2366 2.0,
2367 Some((DurationType::Half, 0)),
2368 "Half",
2369 (DurationType::Half, true),
2370 ),
2371 (
2372 1.0,
2373 Some((DurationType::Quarter, 0)),
2374 "Quarter",
2375 (DurationType::Quarter, true),
2376 ),
2377 (
2378 0.5,
2379 Some((DurationType::Eighth, 0)),
2380 "Eighth",
2381 (DurationType::Eighth, true),
2382 ),
2383 (
2384 0.25,
2385 Some((DurationType::Sixteenth, 0)),
2386 "16th",
2387 (DurationType::Sixteenth, true),
2388 ),
2389 (
2390 3.0,
2391 Some((DurationType::Half, 1)),
2392 "Dotted Half",
2393 (DurationType::Half, false),
2394 ),
2395 (
2396 1.5,
2397 Some((DurationType::Quarter, 1)),
2398 "Dotted Quarter",
2399 (DurationType::Quarter, false),
2400 ),
2401 (
2402 0.75,
2403 Some((DurationType::Eighth, 1)),
2404 "Dotted Eighth",
2405 (DurationType::Eighth, false),
2406 ),
2407 (
2408 6.0,
2409 Some((DurationType::Whole, 1)),
2410 "Dotted Whole",
2411 (DurationType::Whole, false),
2412 ),
2413 (
2414 1.75,
2415 Some((DurationType::Quarter, 2)),
2416 "Double Dotted Quarter",
2417 (DurationType::Quarter, false),
2418 ),
2419 (
2420 0.875,
2421 Some((DurationType::Eighth, 2)),
2422 "Double Dotted Eighth",
2423 (DurationType::Eighth, false),
2424 ),
2425 (
2426 7.0,
2427 Some((DurationType::Whole, 2)),
2428 "Double Dotted Whole",
2429 (DurationType::Whole, false),
2430 ),
2431 (
2432 0.375,
2433 Some((DurationType::Sixteenth, 1)),
2434 "Dotted 16th",
2435 (DurationType::Sixteenth, false),
2436 ),
2437 (
2438 3.5,
2439 Some((DurationType::Half, 2)),
2440 "Double Dotted Half",
2441 (DurationType::Half, false),
2442 ),
2443 (
2444 3.75,
2445 Some((DurationType::Half, 3)),
2446 "Triple Dotted Half (3 3/4 QL)",
2447 (DurationType::Half, false),
2448 ),
2449 (
2450 1.875,
2451 Some((DurationType::Quarter, 3)),
2452 "Triple Dotted Quarter (1 7/8 QL)",
2453 (DurationType::Quarter, false),
2454 ),
2455 (
2456 8.0,
2457 Some((DurationType::Breve, 0)),
2458 "Breve",
2459 (DurationType::Breve, true),
2460 ),
2461 (
2462 16.0,
2463 Some((DurationType::Longa, 0)),
2464 "Imperfect Longa",
2465 (DurationType::Longa, true),
2466 ),
2467 (
2468 24.0,
2469 Some((DurationType::Longa, 1)),
2470 "Perfect Longa",
2471 (DurationType::Longa, false),
2472 ),
2473 ];
2474 for (quarter_length, type_and_dots, full_name, closest) in cases {
2475 let duration = Duration::new(quarter_length).unwrap();
2476 assert_eq!(duration.type_and_dots(), type_and_dots, "{quarter_length}");
2477 assert_eq!(duration.full_name(), full_name, "{quarter_length}");
2478 assert_eq!(
2479 quarter_length_to_closest_type(quarter_length).unwrap(),
2480 closest,
2481 "{quarter_length}"
2482 );
2483 }
2484
2485 let inexact = [
2486 (2.0 / 3.0, DurationType::Eighth),
2487 (1.0 / 3.0, DurationType::Sixteenth),
2488 (4.0 / 3.0, DurationType::Quarter),
2489 (0.2, DurationType::ThirtySecond),
2490 (0.4, DurationType::Sixteenth),
2491 (1.25, DurationType::Quarter),
2492 (5.0, DurationType::Whole),
2493 (2.5, DurationType::Half),
2494 ];
2495 let tuplet_names = [
2500 (2.0 / 3.0, "Quarter Triplet (2/3 QL)"),
2501 (1.0 / 3.0, "Eighth Triplet (1/3 QL)"),
2502 (4.0 / 3.0, "Half Triplet (1 1/3 QL)"),
2503 (0.2, "16th Quintuplet (1/5 QL)"),
2504 (0.4, "Eighth Quintuplet (2/5 QL)"),
2505 ];
2506 let tied = [
2508 (1.25, "Quarter tied to 16th (1 1/4 total QL)"),
2509 (5.0, "Whole tied to Quarter (5 total QL)"),
2510 (2.5, "Half tied to Eighth (2 1/2 total QL)"),
2511 ];
2512 for (quarter_length, closest) in inexact {
2513 let duration = Duration::new(quarter_length).unwrap();
2514 assert_eq!(duration.type_and_dots(), None, "{quarter_length}");
2515 assert_eq!(duration.dots(), 0, "{quarter_length}");
2516 let tuplet_name = tuplet_names
2517 .iter()
2518 .find(|(length, _)| *length == quarter_length);
2519 let tied_name = tied.iter().find(|(length, _)| *length == quarter_length);
2520 let expected = tuplet_name.or(tied_name).map(|(_, name)| *name);
2521 assert_eq!(
2522 duration.full_name(),
2523 expected.expect("every inexact length here is named"),
2524 "{quarter_length}"
2525 );
2526 assert_eq!(
2527 duration.tuplet().is_some(),
2528 tuplet_name.is_some(),
2529 "{quarter_length}"
2530 );
2531 assert_eq!(
2532 duration.components().len(),
2533 if tuplet_name.is_some() { 1 } else { 2 },
2534 "{quarter_length}"
2535 );
2536 assert_eq!(
2537 quarter_length_to_closest_type(quarter_length).unwrap(),
2538 (closest, false),
2539 "{quarter_length}"
2540 );
2541 }
2542
2543 for quarter_length in [0.001, 100.0] {
2545 let duration = Duration::new(quarter_length).unwrap();
2546 assert!(duration.components().is_empty(), "{quarter_length}");
2547 assert_eq!(duration.full_name(), "Inexpressible", "{quarter_length}");
2548 }
2549 let zero = Duration::new(0.0).unwrap();
2550 assert!(zero.components().is_empty());
2551 assert_eq!(zero.full_name(), "Zero Duration (0 total QL)");
2552
2553 let truncated = Duration::new(0.333_333).unwrap();
2556 assert_eq!(truncated.full_name(), "Eighth Triplet (1/3 QL)");
2557
2558 let triplet = Duration::new(2.0 / 3.0).unwrap().tuplet().unwrap();
2559 assert_eq!((triplet.actual(), triplet.normal()), (3, 2));
2560 assert_eq!(triplet.duration_type(), DurationType::Quarter);
2561 assert_eq!(triplet.dots(), 0);
2562 assert_eq!(triplet.full_name(), "Triplet");
2563 assert_eq!(triplet.multiplier(), FractionType::new(2i32, 3i32));
2564
2565 let odd = Tuplet::new(17, 14, DurationType::Quarter, 0);
2567 assert_eq!(odd.full_name(), "Tuplet of 17/14ths");
2568 assert_eq!(odd.total_tuplet_length(), 14.0);
2569 for (actual, normal, name) in [
2571 (40, 21, "Tuplet of 40/21st"),
2572 (3, 1, "Tuplet of 3/1st"),
2573 (12, 11, "Tuplet of 12/11ths"),
2574 (5, 3, "Tuplet of 5/3rds"),
2575 (23, 22, "Tuplet of 23/22nds"),
2576 ] {
2577 let tuplet = Tuplet::new(actual, normal, DurationType::Quarter, 0);
2578 assert_eq!(tuplet.full_name(), name);
2579 }
2580 let across =
2583 Tuplet::new(3, 1, DurationType::Eighth, 0).with_normal(DurationType::Quarter, 0);
2584 assert_eq!(across.total_tuplet_length(), 1.0);
2585 assert_eq!(across.multiplier(), FractionType::new(2i32, 3i32));
2588 assert_eq!(Duration::new(3.75).unwrap().dots(), 3);
2589 assert_eq!(
2590 quarter_length_to_closest_type(200.0).unwrap(),
2591 (DurationType::DuplexMaxima, false)
2592 );
2593 assert!(quarter_length_to_closest_type(0.0).is_err());
2594 assert!(quarter_length_to_closest_type(0.0001).is_err());
2595 }
2596
2597 #[test]
2598 fn duration_types_match_music21s_table() {
2599 assert_eq!(DurationType::ALL.len(), MUSIC21_TYPE_TO_DURATION.len());
2600 for (duration_type, (name, quarter_length)) in
2601 DurationType::ALL.into_iter().zip(MUSIC21_TYPE_TO_DURATION)
2602 {
2603 assert_eq!(duration_type.music21_name(), name);
2604 assert_eq!(duration_type.quarter_length(), quarter_length, "{name}");
2605 assert_eq!(DurationType::from_music21_name(name), Some(duration_type));
2606 }
2607 }
2608
2609 #[test]
2610 fn duration_types_round_trip_through_their_names() {
2611 for duration_type in DurationType::ALL {
2612 let name = duration_type.music21_name();
2613 assert_eq!(name.parse::<DurationType>().unwrap(), duration_type);
2614 assert_eq!(duration_type.to_string(), name);
2615 }
2616 assert!("not-a-duration".parse::<DurationType>().is_err());
2617 }
2618
2619 #[test]
2620 fn each_type_is_half_the_one_before_it() {
2621 let ordered = &DurationType::ALL[..DurationType::ALL.len() - 1];
2623 for pair in ordered.windows(2) {
2624 assert_eq!(
2625 pair[1].quarter_length() * 2.0,
2626 pair[0].quarter_length(),
2627 "{} should be half of {}",
2628 pair[1],
2629 pair[0]
2630 );
2631 }
2632 }
2633
2634 #[test]
2635 fn dots_add_half_of_what_came_before() {
2636 assert_eq!(DurationType::Half.quarter_length_with_dots(0), 2.0);
2637 assert_eq!(DurationType::Half.quarter_length_with_dots(1), 3.0);
2638 assert_eq!(DurationType::Half.quarter_length_with_dots(2), 3.5);
2639 assert_eq!(DurationType::Half.quarter_length_with_dots(3), 3.75);
2640 assert_eq!(DurationType::Quarter.quarter_length_with_dots(1), 1.5);
2641 }
2642
2643 #[test]
2644 fn durations_convert_to_and_from_note_values() {
2645 assert_eq!(
2646 Duration::from_type(DurationType::Whole).quarter_length(),
2647 4.0
2648 );
2649 assert_eq!(
2650 Duration::from_type(DurationType::Whole).duration_type(),
2651 Some(DurationType::Whole)
2652 );
2653 assert_eq!(
2654 Duration::from_type_with_dots(DurationType::Half, 1).quarter_length(),
2655 3.0
2656 );
2657 assert_eq!(
2660 Duration::from_type_with_dots(DurationType::Half, 1).duration_type(),
2661 Some(DurationType::Half)
2662 );
2663 assert_eq!(
2664 Duration::from_type_with_dots(DurationType::Half, 1).dots(),
2665 1
2666 );
2667 assert_eq!(
2668 Duration::new(3.75).unwrap().duration_type(),
2669 Some(DurationType::Half)
2670 );
2671 assert_eq!(Duration::new(3.75).unwrap().dots(), 3);
2672 assert_eq!(Duration::new(1.0 / 3.0).unwrap().duration_type(), None);
2674 }
2675
2676 #[test]
2677 fn the_named_helpers_agree_with_their_types() {
2678 assert_eq!(
2679 Duration::quarter(),
2680 Duration::from_type(DurationType::Quarter)
2681 );
2682 assert_eq!(Duration::half(), Duration::from_type(DurationType::Half));
2683 assert_eq!(Duration::whole(), Duration::from_type(DurationType::Whole));
2684 assert_eq!(
2685 Duration::eighth(),
2686 Duration::from_type(DurationType::Eighth)
2687 );
2688 }
2689
2690 #[test]
2691 fn duration_tracks_quarter_lengths() {
2692 assert_eq!(Duration::quarter().quarter_length(), 1.0);
2693 assert_eq!(Duration::half().quarter_length(), 2.0);
2694 assert_eq!(Duration::whole().quarter_length(), 4.0);
2695 assert_eq!(Duration::eighth().quarter_length(), 0.5);
2696 }
2697
2698 #[test]
2699 fn duration_rejects_invalid_values() {
2700 assert!(Duration::new(-1.0).is_err());
2701 assert!(Duration::new(FloatType::INFINITY).is_err());
2702 }
2703
2704 #[test]
2705 fn a_plain_note_value_is_not_read_as_a_tuplet() {
2706 assert_eq!(Duration::quarter().tuplet(), None);
2709 assert_eq!(Duration::new(3.0).unwrap().tuplet(), None);
2710 assert_eq!(
2711 Duration::new(1.0 / 3.0).unwrap().tuplet(),
2712 Some(Tuplet::new(3, 2, DurationType::Eighth, 0))
2713 );
2714 }
2715
2716 #[test]
2717 fn appending_a_tuplet_shortens_the_length_by_its_ratio() {
2718 let mut duration = Duration::new(1.0).unwrap();
2720 duration.append_tuplet(Tuplet::new(3, 2, DurationType::Quarter, 0));
2721 assert!((duration.quarter_length() - 2.0 / 3.0).abs() < 1e-12);
2722 assert_eq!(duration.quarter_length_no_tuplets(), 1.0);
2723
2724 duration.append_tuplet(Tuplet::new(5, 4, DurationType::Quarter, 0));
2725 assert!((duration.quarter_length() - 8.0 / 15.0).abs() < 1e-12);
2726 assert_eq!(
2727 duration.aggregate_tuplet_multiplier(),
2728 FractionType::new(8, 15)
2729 );
2730 assert_eq!(
2732 duration.components(),
2733 vec![(DurationType::Quarter, 0)],
2734 "the written value stays a quarter inside both tuplets"
2735 );
2736 }
2737
2738 #[test]
2739 fn saying_a_length_is_in_no_tuplet_is_not_the_same_as_saying_nothing() {
2740 let inferred = Duration::new(1.0 / 3.0).unwrap();
2741 assert_eq!(inferred.tuplets().len(), 1);
2742
2743 let mut told = Duration::new(1.0 / 3.0).unwrap();
2744 told.set_tuplets(Vec::new());
2745 assert!(told.tuplets().is_empty());
2746 assert_eq!(told.quarter_length(), 0.5);
2748 }
2749
2750 #[test]
2751 fn setting_a_length_forgets_the_tuplets_it_was_told() {
2752 let mut duration = Duration::new(1.0).unwrap();
2753 duration.append_tuplet(Tuplet::new(3, 2, DurationType::Quarter, 0));
2754 duration.set_quarter_length(2.0).unwrap();
2755 assert!(duration.tuplets().is_empty());
2756 assert_eq!(duration.quarter_length(), 2.0);
2757 }
2758
2759 #[test]
2760 fn duration_supports_conversions_and_updates() {
2761 let mut duration = Duration::try_from(3 as IntegerType).unwrap();
2762 assert_eq!(duration.quarter_length(), 3.0);
2763
2764 duration.set_quarter_length(1.5).unwrap();
2765 assert_eq!(duration, Duration::try_from(1.5).unwrap());
2766 assert!(duration.set_quarter_length(FloatType::NAN).is_err());
2767 }
2768}