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 {
1158 self.tuplets()
1159 .iter()
1160 .map(Tuplet::multiplier)
1161 .fold(FractionType::from(1), |total, ratio| total * ratio)
1162 }
1163
1164 pub fn quarter_length_no_tuplets(&self) -> FloatType {
1167 self.written_values()
1169 .iter()
1170 .fold(0.0, |total, value| total + value.quarter_length)
1171 }
1172
1173 pub fn set_tuplets(&mut self, tuplets: Vec<Tuplet>) {
1177 let written = self.quarter_length_no_tuplets();
1178 self.tuplets = Some(tuplets);
1179 if self.unlinked {
1180 return;
1181 }
1182 self.quarter_length = written * float_from_fraction(self.aggregate_tuplet_multiplier());
1183 }
1184
1185 pub fn append_tuplet(&mut self, tuplet: Tuplet) {
1188 let mut tuplets = self.tuplets();
1189 tuplets.push(tuplet);
1190 self.set_tuplets(tuplets);
1191 }
1192
1193 fn written_quarter_length(&self) -> FloatType {
1201 let Some(tuplets) = &self.tuplets else {
1202 return self.quarter_length;
1203 };
1204 if tuplets.is_empty() {
1205 return self.quarter_length;
1206 }
1207 let multiplier = float_from_fraction(self.aggregate_tuplet_multiplier());
1208 if multiplier == 0.0 {
1209 return self.quarter_length;
1210 }
1211 let written = self.quarter_length / multiplier;
1212 let tolerance = written.abs() * TUPLET_TOLERANCE;
1213 DurationType::ALL
1214 .into_iter()
1215 .flat_map(|duration_type| {
1216 (0..=MAX_DOTS).map(move |dots| duration_type.quarter_length_with_dots(dots))
1217 })
1218 .find(|candidate| (candidate - written).abs() <= tolerance)
1219 .unwrap_or(written)
1220 }
1221
1222 pub fn components(&self) -> Vec<(DurationType, u32)> {
1232 match &self.written {
1233 Some(written) => written
1234 .iter()
1235 .filter_map(|value| Some((value.duration_type?, value.dots)))
1236 .collect(),
1237 None => convert(self.written_quarter_length(), self.tuplets.is_none()).0,
1238 }
1239 }
1240
1241 pub fn written_values(&self) -> Vec<DurationTuple> {
1247 if let Some(written) = &self.written {
1248 return written.clone();
1249 }
1250 let components = self.components();
1251 if components.is_empty() && self.quarter_length != 0.0 {
1252 return vec![DurationTuple::from_quarter_length(
1253 self.written_quarter_length(),
1254 )];
1255 }
1256 components
1257 .into_iter()
1258 .map(|(duration_type, dots)| DurationTuple::new(duration_type, dots))
1259 .collect()
1260 }
1261
1262 fn set_written_values(&mut self, written: Vec<DurationTuple>) {
1265 self.tuplets = Some(self.tuplets());
1267 self.written = Some(written);
1268 self.relength();
1269 }
1270
1271 fn relength(&mut self) {
1274 if self.unlinked {
1275 return;
1276 }
1277 let groups: FloatType = self
1278 .dot_groups
1279 .iter()
1280 .flatten()
1281 .filter(|dots| **dots != 0)
1282 .map(|dots| 2.0 - FloatType::powi(0.5, *dots as i32))
1283 .product();
1284 self.quarter_length = self.quarter_length_no_tuplets()
1285 * float_from_fraction(self.aggregate_tuplet_multiplier())
1286 * groups;
1287 }
1288
1289 pub fn consolidate(&mut self) {
1307 if self.written_values().len() == 1 {
1308 return;
1309 }
1310 let total = self.quarter_length_no_tuplets();
1311 self.set_written_values(vec![DurationTuple::from_quarter_length(total)]);
1312 }
1313
1314 pub fn slice_component_at_position(&mut self, position: FloatType) -> Result<()> {
1321 let written = self.written_values();
1322 let mut start = 0.0;
1323 for (index, value) in written.iter().enumerate() {
1324 let end = start + value.quarter_length;
1325 if position > start && position < end {
1326 let mut sliced = written[..index].to_vec();
1327 sliced.push(DurationTuple::from_quarter_length(position - start));
1328 sliced.push(DurationTuple::from_quarter_length(end - position));
1329 sliced.extend_from_slice(&written[index + 1..]);
1330 self.set_written_values(sliced);
1331 return Ok(());
1332 }
1333 start = end;
1334 }
1335 Err(Error::Duration(
1336 "no slice is possible at this quarter position".to_string(),
1337 ))
1338 }
1339
1340 pub fn set_dot_groups(&mut self, groups: Vec<u32>) {
1345 let undotted = self
1346 .written_values()
1347 .into_iter()
1348 .map(|value| match value.duration_type {
1349 Some(duration_type) => DurationTuple::new(duration_type, 0),
1350 None => value,
1351 })
1352 .collect();
1353 self.dot_groups = Some(groups);
1354 self.set_written_values(undotted);
1355 }
1356
1357 pub fn split_dot_groups(&self) -> Result<Duration> {
1365 let (duration_type, _) = self.type_and_dots().ok_or_else(|| {
1366 Error::Duration("only a single written value has dot groups to split".to_string())
1367 })?;
1368 let groups = self.dot_groups();
1369 let mut values = vec![DurationTuple::new(
1370 duration_type,
1371 groups.first().copied().unwrap_or(0),
1372 )];
1373 for _ in 1..groups.len() {
1374 let smaller: Vec<DurationTuple> = values
1375 .iter()
1376 .map(|value| {
1377 let kind = value.duration_type.unwrap_or(duration_type);
1378 DurationTuple::new(kind.next_smaller().unwrap_or(kind), value.dots)
1379 })
1380 .collect();
1381 values.extend(smaller);
1382 }
1383 let mut split = self.clone();
1384 split.dot_groups = None;
1385 split.set_written_values(values);
1386 Ok(split)
1387 }
1388
1389 pub fn linked(&self) -> bool {
1392 !self.unlinked
1393 }
1394
1395 pub fn set_linked(&mut self, linked: bool) {
1403 if !linked && !self.unlinked {
1404 self.tuplets = Some(self.tuplets());
1405 self.written = Some(self.written_values());
1406 } else if linked && self.unlinked {
1407 self.written = None;
1408 self.tuplets = None;
1409 self.dot_groups = None;
1410 }
1411 self.unlinked = !linked;
1412 }
1413
1414 pub fn grace_duration(&self) -> Duration {
1434 let mut written: Vec<DurationTuple> = self
1435 .written_values()
1436 .into_iter()
1437 .map(DurationTuple::silenced)
1438 .collect();
1439 if written.is_empty() {
1440 written.push(DurationTuple::new(DurationType::Eighth, 0).silenced());
1441 }
1442 Duration {
1443 quarter_length: 0.0,
1444 tuplets: Some(Vec::new()),
1445 written: Some(written),
1446 dot_groups: None,
1447 unlinked: true,
1448 grace: Some(Grace::new()),
1449 said: true,
1450 }
1451 }
1452
1453 pub fn expression_is_inferred(&self) -> bool {
1469 !self.said
1470 }
1471
1472 pub fn set_expression_is_inferred(&mut self, inferred: bool) {
1475 self.said = !inferred;
1476 }
1477
1478 pub(crate) fn grace_duration_as(&self, appoggiatura: bool) -> Duration {
1482 let mut grace = self.grace_duration();
1483 if appoggiatura {
1484 grace.grace = Some(Grace::appoggiatura());
1485 }
1486 grace
1487 }
1488
1489 pub fn is_complex(&self) -> bool {
1492 self.components().len() > 1
1493 }
1494
1495 pub fn dot_groups(&self) -> Vec<u32> {
1499 match &self.dot_groups {
1500 Some(groups) => groups.clone(),
1501 None => vec![self.dots()],
1502 }
1503 }
1504
1505 pub fn clear(&mut self) {
1509 self.tuplets = Some(self.tuplets());
1510 self.written = Some(Vec::new());
1511 self.dot_groups = None;
1512 self.quarter_length = 0.0;
1513 }
1514
1515 pub fn add_duration_tuple(&mut self, duration_type: DurationType, dots: u32) {
1523 let mut written = self.written_values();
1524 written.push(DurationTuple::new(duration_type, dots));
1525 self.set_written_values(written);
1526 }
1527
1528 pub fn component_index_at_qtr_position(&self, position: FloatType) -> Result<usize> {
1533 let components = self.components();
1534 if components.is_empty() {
1535 return Err(Error::Duration(
1536 "Need components to run getComponentIndexAtQtrPosition".to_string(),
1537 ));
1538 }
1539 let total = self.quarter_length_no_tuplets();
1540 if position.is_nan() || position < 0.0 {
1541 return Err(Error::Value(
1542 "position is before the start of the duration".to_string(),
1543 ));
1544 }
1545 if position > total {
1546 return Err(Error::Value(
1547 "position is after the end of the duration".to_string(),
1548 ));
1549 }
1550 if position == total {
1551 return Ok(components.len() - 1);
1552 }
1553 let mut reached = 0.0;
1554 for (index, (duration_type, dots)) in components.iter().enumerate() {
1555 reached += duration_type.quarter_length_with_dots(*dots);
1556 if reached > position {
1557 return Ok(index);
1558 }
1559 }
1560 Ok(components.len() - 1)
1561 }
1562
1563 pub fn component_start_time(&self, index: usize) -> Result<FloatType> {
1567 let components = self.components();
1568 if index >= components.len() {
1569 return Err(Error::Duration(format!(
1570 "invalid component index value {index} submitted; value must be an integer between 0 and {}",
1571 components.len().saturating_sub(1)
1572 )));
1573 }
1574 Ok(components[..index]
1575 .iter()
1576 .map(|(duration_type, dots)| duration_type.quarter_length_with_dots(*dots))
1577 .sum())
1578 }
1579
1580 pub fn full_name(&self) -> String {
1589 let components = self.components();
1590 if components.is_empty() {
1591 return if self.quarter_length == 0.0 {
1592 "Zero Duration (0 total QL)".to_string()
1597 } else {
1598 "Inexpressible".to_string()
1599 };
1600 }
1601 let tuplet = if components.len() == 1 && self.type_and_dots().is_none() {
1605 self.tuplets().first().copied()
1606 } else {
1607 None
1608 };
1609 let names: Vec<String> = components
1610 .iter()
1611 .map(|(duration_type, dots)| {
1612 let mensural = matches!(duration_type, DurationType::Longa | DurationType::Maxima);
1613 let mut name = format!("{}{}", dot_prefix(*dots, mensural), duration_type.title());
1614 if let Some(tuplet) = &tuplet {
1615 name.push(' ');
1616 name.push_str(&tuplet.full_name());
1617 }
1618 if tuplet.is_some() || *dots >= 3 {
1621 name.push_str(&format!(" ({} QL)", mixed_numeral(self.quarter_length)));
1622 }
1623 name
1624 })
1625 .collect();
1626 let mut name = names.join(" tied to ");
1627 if components.len() != 1 {
1628 name.push_str(&format!(
1629 " ({} total QL)",
1630 mixed_numeral(self.quarter_length)
1631 ));
1632 }
1633 name
1634 }
1635
1636 pub fn duration_type(&self) -> Option<DurationType> {
1641 self.type_and_dots().map(|(duration_type, _)| duration_type)
1642 }
1643
1644 pub fn quarter_length(&self) -> FloatType {
1646 self.quarter_length
1647 }
1648
1649 pub fn set_quarter_length(&mut self, quarter_length: FloatType) -> Result<()> {
1654 let mut replacement = Self::new(quarter_length)?;
1655 if self.unlinked {
1656 self.quarter_length = replacement.quarter_length;
1657 } else {
1658 replacement.said = self.said && self.quarter_length == replacement.quarter_length;
1660 *self = replacement;
1661 }
1662 Ok(())
1663 }
1664}
1665
1666const MAX_DOTS: u32 = 4;
1667
1668fn exact_type_and_dots(quarter_length: FloatType) -> Option<(DurationType, u32)> {
1670 DurationType::ALL.into_iter().find_map(|duration_type| {
1671 (0..=MAX_DOTS)
1672 .find(|dots| duration_type.quarter_length_with_dots(*dots) == quarter_length)
1673 .map(|dots| (duration_type, dots))
1674 })
1675}
1676
1677fn float_from_fraction(ratio: FractionType) -> FloatType {
1680 ratio.to_f64().unwrap_or(0.0)
1681}
1682
1683pub fn quarter_length_to_closest_type(quarter_length: FloatType) -> Result<(DurationType, bool)> {
1687 let too_small = || {
1688 Error::Duration(format!(
1689 "cannot return types smaller than 2048th; quarter length was {quarter_length}"
1690 ))
1691 };
1692 if quarter_length.is_nan() || quarter_length <= 0.0 {
1693 return Err(too_small());
1694 }
1695 let note_length = 4.0 / quarter_length;
1696 if let Some(exact) = DurationType::ALL
1697 .into_iter()
1698 .find(|duration_type| duration_type.type_number() == Some(note_length))
1699 {
1700 return Ok((exact, true));
1701 }
1702 let upper_bound = 8.0 / quarter_length;
1703 if let Some(closest) = DurationType::ALL.into_iter().find(|duration_type| {
1704 duration_type
1705 .type_number()
1706 .is_some_and(|number| note_length < number && number < upper_bound)
1707 }) {
1708 return Ok((closest, false));
1709 }
1710 if quarter_length > 128.0 {
1711 return Ok((DurationType::DuplexMaxima, false));
1712 }
1713 Err(too_small())
1714}
1715
1716pub fn quarter_length_to_tuplet(quarter_length: FloatType, max_to_return: usize) -> Vec<Tuplet> {
1728 let mut found = Vec::new();
1729 if quarter_length.is_nan() || quarter_length <= 0.0 || max_to_return == 0 {
1730 return found;
1731 }
1732 let mut values = DurationType::ALL;
1733 values.sort_by(|left, right| {
1734 left.quarter_length()
1735 .partial_cmp(&right.quarter_length())
1736 .unwrap_or(std::cmp::Ordering::Equal)
1737 });
1738 let tolerance = quarter_length * TUPLET_TOLERANCE;
1739 for duration_type in values {
1740 for actual in TUPLET_NUMERATORS {
1741 for normal in 1..actual {
1742 for dots in TUPLET_DOTS {
1743 let candidate = duration_type.quarter_length_with_dots(dots)
1744 * FloatType::from(normal)
1745 / FloatType::from(actual);
1746 if (candidate - quarter_length).abs() <= tolerance {
1747 found.push(Tuplet::new(actual, normal, duration_type, dots));
1748 break;
1749 }
1750 }
1751 }
1752 if found.len() >= max_to_return {
1753 return found;
1754 }
1755 }
1756 }
1757 found
1758}
1759
1760pub fn quarter_length_to_non_power_of_2_tuplet(
1770 quarter_length: FloatType,
1771) -> Option<(Tuplet, DurationType, u32)> {
1772 if quarter_length.is_nan() || quarter_length <= 0.0 {
1773 return None;
1774 }
1775 let (original_actual, original_normal) =
1776 limited_fraction(1.0 / quarter_length, DENOMINATOR_LIMIT)?;
1777 let (mut actual, mut normal) = (original_actual, original_normal);
1778 while actual < normal {
1780 actual *= 2;
1781 reduce(&mut actual, &mut normal);
1782 }
1783 while actual > normal * 2 {
1784 normal *= 2;
1785 reduce(&mut actual, &mut normal);
1786 }
1787 let (written, _) = quarter_length_to_closest_type(quarter_length / normal as FloatType).ok()?;
1788 let inside = (actual as FloatType / normal as FloatType)
1791 / (original_actual as FloatType / original_normal as FloatType);
1792 let (kind, dots) = exact_type_and_dots(inside)?;
1793 Some((
1794 Tuplet::new(
1795 u32::try_from(actual).ok()?,
1796 u32::try_from(normal).ok()?,
1797 written,
1798 0,
1799 ),
1800 kind,
1801 dots,
1802 ))
1803}
1804
1805pub fn quarter_conversion(quarter_length: FloatType) -> (Vec<(DurationType, u32)>, Option<Tuplet>) {
1820 convert(quarter_length, true)
1821}
1822
1823fn convert(
1826 written: FloatType,
1827 look_for_tuplet: bool,
1828) -> (Vec<(DurationType, u32)>, Option<Tuplet>) {
1829 if written == 0.0 {
1832 return (Vec::new(), None);
1833 }
1834 if let Some(value) = exact_type_and_dots(written) {
1835 return (vec![value], None);
1836 }
1837 let Ok((largest, _)) = quarter_length_to_closest_type(written) else {
1841 return (Vec::new(), None);
1842 };
1843 if largest.next_larger().is_none() {
1844 return (Vec::new(), None);
1845 }
1846 if look_for_tuplet && let Some(tuplet) = quarter_length_to_tuplet(written, 1).into_iter().next()
1847 {
1848 return (vec![(tuplet.duration_type(), tuplet.dots())], Some(tuplet));
1849 }
1850 let mut components = vec![(largest, 0)];
1851 let mut remainder = written - largest.quarter_length();
1852 for _ in 0..MAX_TIED_COMPONENTS {
1853 if let Some(rest) = exact_type_and_dots(remainder) {
1854 components.push(rest);
1855 return (components, None);
1856 }
1857 let Ok((next, _)) = quarter_length_to_closest_type(remainder) else {
1858 break;
1859 };
1860 remainder -= next.quarter_length();
1861 components.push((next, 0));
1862 }
1863 match quarter_length_to_non_power_of_2_tuplet(written) {
1864 Some((tuplet, kind, dots)) => (vec![(kind, dots)], Some(tuplet)),
1865 None => (Vec::new(), None),
1866 }
1867}
1868
1869const MAX_MIXED_NUMERAL_DENOMINATOR: u32 = 1024;
1874
1875const MIXED_NUMERAL_TOLERANCE: FloatType = 1e-6;
1878
1879fn mixed_numeral(value: FloatType) -> String {
1882 let whole = value.trunc() as IntegerType;
1883 let remainder = value - value.trunc();
1884 if remainder == 0.0 {
1885 return whole.to_string();
1886 }
1887 let tolerance = MIXED_NUMERAL_TOLERANCE * value.abs().max(1.0);
1893 let fractional = (1..=MAX_MIXED_NUMERAL_DENOMINATOR)
1894 .find_map(|denominator| {
1895 let numerator = (remainder * FloatType::from(denominator)).round();
1896 ((remainder - numerator / FloatType::from(denominator)).abs() <= tolerance)
1897 .then(|| format!("{}/{denominator}", numerator as IntegerType))
1898 })
1899 .unwrap_or_else(|| remainder.to_string());
1900 if whole == 0 {
1901 fractional
1902 } else {
1903 format!("{whole} {fractional}")
1904 }
1905}
1906
1907impl Default for Duration {
1908 fn default() -> Self {
1909 Self::quarter()
1910 }
1911}
1912
1913impl PartialEq for Duration {
1914 fn eq(&self, other: &Self) -> bool {
1915 self.quarter_length == other.quarter_length
1916 }
1917}
1918
1919impl Eq for Duration {}
1920
1921impl std::hash::Hash for Duration {
1923 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1924 ordered_float::OrderedFloat(self.quarter_length).hash(state);
1925 }
1926}
1927
1928impl TryFrom<FloatType> for Duration {
1929 type Error = Error;
1930
1931 fn try_from(value: FloatType) -> Result<Self> {
1932 Self::new(value)
1933 }
1934}
1935
1936impl TryFrom<IntegerType> for Duration {
1937 type Error = Error;
1938
1939 fn try_from(value: IntegerType) -> Result<Self> {
1940 Self::new(value as FloatType)
1941 }
1942}
1943
1944#[cfg(test)]
1945mod tests {
1946
1947 #[test]
1948 fn a_type_is_found_from_its_undotted_length_alone() {
1949 assert_eq!(
1950 DurationType::from_quarter_length(2.0),
1951 Some(DurationType::Half)
1952 );
1953 assert_eq!(
1954 DurationType::from_quarter_length(0.125),
1955 Some(DurationType::ThirtySecond)
1956 );
1957 assert_eq!(DurationType::from_quarter_length(3.0), None);
1958 assert_eq!(super::mixed_numeral(2.0 / 3.0), "2/3");
1959 assert_eq!(super::mixed_numeral(1.0 / 3.0 + 1.0), "1 1/3");
1960 }
1961
1962 #[test]
1963 fn a_duration_is_cleared_and_lengthened_a_value_at_a_time() {
1964 let mut duration = Duration::new(1.5).unwrap();
1965 assert_eq!(duration.dot_groups(), [1]);
1966 duration.add_duration_tuple(DurationType::Eighth, 0);
1967 assert_eq!(duration.quarter_length(), 2.0);
1968 duration.clear();
1969 assert_eq!(duration.quarter_length(), 0.0);
1970 assert_eq!(duration.dot_groups(), [0]);
1971 let mut triplet = Duration::new(2.0 / 3.0).unwrap();
1973 triplet.add_duration_tuple(DurationType::Quarter, 0);
1974 assert!((triplet.quarter_length() - 4.0 / 3.0).abs() < 1e-9);
1975 }
1976
1977 #[test]
1979 fn written_values_are_cut_consolidated_split_and_unlinked_as_music21_does() {
1980 let typed = |duration: &Duration| -> Vec<(Option<DurationType>, u32, FloatType)> {
1981 duration
1982 .written_values()
1983 .iter()
1984 .map(|value| (value.duration_type(), value.dots(), value.quarter_length()))
1985 .collect()
1986 };
1987
1988 let mut tied = Duration::new(1.25).unwrap();
1990 tied.slice_component_at_position(0.5).unwrap();
1991 assert_eq!(
1992 tied.components(),
1993 [
1994 (DurationType::Eighth, 0),
1995 (DurationType::Eighth, 0),
1996 (DurationType::Sixteenth, 0)
1997 ]
1998 );
1999 assert_eq!(tied.quarter_length(), 1.25);
2000 let mut dotted = Duration::new(3.0).unwrap();
2001 dotted.slice_component_at_position(1.25).unwrap();
2002 assert_eq!(
2003 typed(&dotted),
2004 [(None, 0, 1.25), (Some(DurationType::Quarter), 2, 1.75)]
2005 );
2006 assert!(
2007 Duration::new(1.25)
2008 .unwrap()
2009 .slice_component_at_position(1.0)
2010 .is_err()
2011 );
2012
2013 let mut long = Duration::quarter();
2015 long.add_duration_tuple(DurationType::Half, 0);
2016 long.add_duration_tuple(DurationType::Half, 0);
2017 assert_eq!(long.components().len(), 3);
2018 assert_eq!(long.duration_type(), None);
2019 long.consolidate();
2020 assert_eq!(typed(&long), [(None, 0, 5.0)]);
2021 assert_eq!(long.quarter_length(), 5.0);
2022 let mut triplets = Duration::new(1.0 / 3.0).unwrap();
2023 triplets.add_duration_tuple(DurationType::Eighth, 0);
2024 triplets.consolidate();
2025 assert_eq!(triplets.components(), [(DurationType::Quarter, 0)]);
2026 assert!((triplets.quarter_length() - 2.0 / 3.0).abs() < 1e-9);
2027
2028 let mut half = Duration::half();
2030 half.set_dot_groups(vec![1, 1]);
2031 assert_eq!(half.quarter_length(), 4.5);
2032 assert_eq!(half.components(), [(DurationType::Half, 0)]);
2033 let split = half.split_dot_groups().unwrap();
2034 assert_eq!(
2035 split.components(),
2036 [(DurationType::Half, 1), (DurationType::Quarter, 1)]
2037 );
2038 assert_eq!(split.quarter_length(), 4.5);
2039 half.set_dot_groups(vec![1, 1, 1]);
2040 assert_eq!(half.quarter_length(), 6.75);
2041 assert_eq!(
2042 half.split_dot_groups().unwrap().components(),
2043 [
2044 (DurationType::Half, 1),
2045 (DurationType::Quarter, 1),
2046 (DurationType::Quarter, 1),
2047 (DurationType::Eighth, 1)
2048 ]
2049 );
2050 assert_eq!(
2051 Duration::new(1.5)
2052 .unwrap()
2053 .split_dot_groups()
2054 .unwrap()
2055 .components(),
2056 [(DurationType::Quarter, 1)]
2057 );
2058
2059 let mut unlinked = Duration::quarter();
2062 assert!(unlinked.linked());
2063 unlinked.set_linked(false);
2064 unlinked.set_quarter_length(3.0).unwrap();
2065 assert_eq!(unlinked.duration_type(), Some(DurationType::Quarter));
2066 assert_eq!(unlinked.quarter_length(), 3.0);
2067 unlinked.set_linked(true);
2068 assert_eq!(unlinked.type_and_dots(), Some((DurationType::Half, 1)));
2069 assert_eq!(unlinked.quarter_length(), 3.0);
2070
2071 let grace = Duration::new(1.25).unwrap().grace_duration();
2073 assert_eq!(
2074 typed(&grace),
2075 [
2076 (Some(DurationType::Quarter), 0, 0.0),
2077 (Some(DurationType::Sixteenth), 0, 0.0)
2078 ]
2079 );
2080 assert!(!grace.linked());
2081 assert_eq!(grace.quarter_length(), 0.0);
2082 let bare = Duration::new(0.0).unwrap().grace_duration();
2083 assert_eq!(bare.duration_type(), Some(DurationType::Eighth));
2084 let appoggiatura = Duration::new(1.0 / 3.0).unwrap().grace_duration();
2085 assert_eq!(appoggiatura.components(), [(DurationType::Eighth, 0)]);
2086 assert!(appoggiatura.tuplets().is_empty());
2087 }
2088
2089 #[test]
2091 fn a_position_within_a_tie_names_the_value_sounding_there() {
2092 let tied = Duration::new(2.5).unwrap();
2093 assert_eq!(tied.components().len(), 2);
2094 assert_eq!(tied.component_index_at_qtr_position(0.0).unwrap(), 0);
2095 assert_eq!(tied.component_index_at_qtr_position(1.5).unwrap(), 0);
2096 assert_eq!(tied.component_index_at_qtr_position(2.0).unwrap(), 1);
2097 assert_eq!(tied.component_index_at_qtr_position(2.5).unwrap(), 1);
2098 assert!(tied.component_index_at_qtr_position(3.0).is_err());
2099 assert!(tied.component_index_at_qtr_position(-1.0).is_err());
2100 assert!(
2101 Duration::new(0.0)
2102 .unwrap()
2103 .component_index_at_qtr_position(0.0)
2104 .is_err()
2105 );
2106 assert_eq!(tied.component_start_time(0).unwrap(), 0.0);
2107 assert_eq!(tied.component_start_time(1).unwrap(), 2.0);
2108 assert!(tied.component_start_time(2).is_err());
2109 }
2110
2111 #[test]
2112 fn a_tuplet_reads_and_writes_both_of_its_sides() {
2113 let mut tuplet = Tuplet::new(3, 2, DurationType::Eighth, 0);
2114 assert_eq!(tuplet.duration_actual(), Some((DurationType::Eighth, 0)));
2115 assert_eq!(tuplet.duration_normal(), Some((DurationType::Eighth, 0)));
2116 assert_eq!(tuplet.tuplet_actual(), (3, Some((DurationType::Eighth, 0))));
2117 assert_eq!(tuplet.tuplet_normal(), (2, Some((DurationType::Eighth, 0))));
2118 tuplet.set_duration_type(DurationType::Quarter, 1);
2119 assert_eq!(tuplet.duration_actual(), Some((DurationType::Quarter, 1)));
2120 assert_eq!(tuplet.normal_duration_type(), DurationType::Quarter);
2121 assert_eq!(tuplet.normal_dots(), 1);
2122 tuplet.set_ratio(5, 4);
2123 assert_eq!((tuplet.actual(), tuplet.normal()), (5, 4));
2124 assert_eq!(tuplet.multiplier(), FractionType::new(4, 5));
2125 }
2126
2127 #[test]
2130 fn a_tuplet_may_leave_its_written_values_unsaid() {
2131 let mut triplet = Tuplet::ratio(3, 2);
2132 assert_eq!(triplet.duration_actual(), None);
2133 assert_eq!(triplet.total_tuplet_length(), 1.0);
2134 assert_eq!(triplet.multiplier(), FractionType::new(2, 3));
2135 triplet.set_duration_type(DurationType::Half, 1);
2136 assert_eq!(triplet.total_tuplet_length(), 6.0);
2137
2138 let mut three_in_one = Tuplet::ratio(3, 1);
2139 three_in_one.set_duration_actual(Some((DurationType::Quarter, 0)));
2140 three_in_one.set_duration_normal(Some((DurationType::Half, 0)));
2141 assert_eq!(three_in_one.multiplier(), FractionType::new(2, 3));
2142 assert_eq!(three_in_one.total_tuplet_length(), 2.0);
2143
2144 let mut six = Tuplet::ratio(6, 2);
2145 six.set_duration_type(DurationType::Eighth, 0);
2146 let halved = six.augmented(0.5).unwrap();
2147 assert_eq!(halved.duration_actual(), Some((DurationType::Sixteenth, 0)));
2148 assert_eq!(halved.multiplier(), FractionType::new(1, 3));
2149 assert!(six.augmented(-1.0).is_err());
2150
2151 let mut five = Tuplet::ratio(5, 2);
2152 five.set_duration_type(DurationType::Half, 0);
2153 five.set_duration_normal(Some((DurationType::Whole, 0)));
2154 assert_eq!(five.total_tuplet_length(), 8.0);
2155 }
2156
2157 #[test]
2160 fn a_length_is_read_as_the_tuplets_and_ties_music21_reads() {
2161 use super::{
2162 Duration, DurationType, quarter_conversion, quarter_length_to_non_power_of_2_tuplet,
2163 quarter_length_to_tuplet,
2164 };
2165
2166 let names = |tuplets: Vec<Tuplet>| -> Vec<String> {
2167 tuplets
2168 .iter()
2169 .map(|tuplet| {
2170 format!(
2171 "{}/{}/{}",
2172 tuplet.actual(),
2173 tuplet.normal(),
2174 tuplet.duration_type().music21_name()
2175 )
2176 })
2177 .collect()
2178 };
2179 assert_eq!(
2180 names(quarter_length_to_tuplet(0.333_333_33, 4)),
2181 ["3/2/eighth", "3/1/quarter"]
2182 );
2183 assert_eq!(
2184 names(quarter_length_to_tuplet(0.20, 4)),
2185 ["5/4/16th", "5/2/eighth", "5/1/quarter"]
2186 );
2187 assert_eq!(
2188 names(quarter_length_to_tuplet(0.333_333_3, 1)),
2189 ["3/2/eighth"]
2190 );
2191 let plain = quarter_length_to_tuplet(1.0, 1);
2194 assert_eq!(names(plain.clone()), ["3/2/quarter"]);
2195 assert_eq!(plain[0].dots(), 1);
2196 assert!(quarter_length_to_tuplet(0.0, 4).is_empty());
2197
2198 let (tuplet, kind, dots) = quarter_length_to_non_power_of_2_tuplet(7.0).unwrap();
2199 assert_eq!(names(vec![tuplet]), ["8/7/quarter"]);
2200 assert_eq!((kind, dots), (DurationType::Breve, 0));
2201 let (tuplet, kind, _) = quarter_length_to_non_power_of_2_tuplet(7.0 / 3.0).unwrap();
2202 assert_eq!(names(vec![tuplet]), ["12/7/16th"]);
2203 assert_eq!(kind, DurationType::Whole);
2204 assert!(quarter_length_to_non_power_of_2_tuplet(0.0).is_none());
2205
2206 let (components, tuplet) = quarter_conversion(2.5);
2207 assert_eq!(
2208 components,
2209 [(DurationType::Half, 0), (DurationType::Eighth, 0)]
2210 );
2211 assert!(tuplet.is_none());
2212 let (components, tuplet) = quarter_conversion(2.0 / 3.0);
2213 assert_eq!(components, [(DurationType::Quarter, 0)]);
2214 assert_eq!(names(tuplet.into_iter().collect()), ["3/2/quarter"]);
2215 let (components, tuplet) = quarter_conversion(3.75);
2216 assert_eq!(components, [(DurationType::Half, 3)]);
2217 assert!(tuplet.is_none());
2218 assert_eq!(quarter_conversion(99.0), (Vec::new(), None));
2219 assert_eq!(quarter_conversion(0.0), (Vec::new(), None));
2220
2221 assert!(Duration::new(2.5).unwrap().is_complex());
2222 assert!(!Duration::new(3.0).unwrap().is_complex());
2223 assert!(!Duration::new(2.0 / 3.0).unwrap().is_complex());
2224 }
2225
2226 #[test]
2227 fn neighbouring_types_match_music21() {
2228 assert_eq!(
2229 DurationType::Quarter.next_larger(),
2230 Some(DurationType::Half)
2231 );
2232 assert_eq!(
2233 DurationType::Quarter.next_smaller(),
2234 Some(DurationType::Eighth)
2235 );
2236 assert_eq!(DurationType::Whole.next_larger(), Some(DurationType::Breve));
2237 assert_eq!(DurationType::Breve.next_larger(), Some(DurationType::Longa));
2238 assert_eq!(
2239 DurationType::Sixteenth.next_smaller(),
2240 Some(DurationType::ThirtySecond)
2241 );
2242 assert_eq!(DurationType::DuplexMaxima.next_larger(), None);
2243 assert_eq!(DurationType::ALL[15].next_smaller(), None);
2244 assert_eq!(DurationType::Zero.next_larger(), None);
2245 assert_eq!(DurationType::Zero.next_smaller(), None);
2246 }
2247
2248 #[test]
2249 fn ordinal_and_scaling_match_music21() {
2250 assert_eq!(DurationType::DuplexMaxima.ordinal(), Some(0));
2251 assert_eq!(DurationType::Quarter.ordinal(), Some(6));
2252 assert_eq!(DurationType::Sixteenth.ordinal(), Some(8));
2253 assert_eq!(DurationType::Zero.ordinal(), None);
2254 assert_eq!(Duration::new(1.5).unwrap().ordinal(), Some(6));
2255 assert_eq!(Duration::new(2.5).unwrap().ordinal(), None);
2256 assert_eq!(Duration::new(0.0).unwrap().ordinal(), None);
2257 assert_eq!(
2258 Duration::new(1.0)
2259 .unwrap()
2260 .augment_or_diminish(2.0)
2261 .unwrap()
2262 .quarter_length(),
2263 2.0
2264 );
2265 assert_eq!(
2266 Duration::new(1.5)
2267 .unwrap()
2268 .augment_or_diminish(0.5)
2269 .unwrap()
2270 .quarter_length(),
2271 0.75
2272 );
2273 assert!(
2274 Duration::new(1.0)
2275 .unwrap()
2276 .augment_or_diminish(0.0)
2277 .is_err()
2278 );
2279 assert!(
2280 Duration::new(1.0)
2281 .unwrap()
2282 .augment_or_diminish(-1.0)
2283 .is_err()
2284 );
2285 let mut unlinked = Duration::new(0.25).unwrap();
2287 unlinked.set_linked(false);
2288 unlinked.set_quarter_length(4.0 / 17.0).unwrap();
2289 let scaled = unlinked.augment_or_diminish(1.5).unwrap();
2290 assert!(!scaled.linked());
2291 assert_eq!(scaled.quarter_length(), 4.0 / 17.0 * 1.5);
2292 assert_ne!(scaled.quarter_length(), 6.0 / 17.0);
2293 assert_eq!(scaled.type_and_dots(), Some((DurationType::Sixteenth, 0)));
2294 }
2295 use super::*;
2296
2297 const MUSIC21_TYPE_TO_DURATION: [(&str, FloatType); 17] = [
2299 ("duplex-maxima", 64.0),
2300 ("maxima", 32.0),
2301 ("longa", 16.0),
2302 ("breve", 8.0),
2303 ("whole", 4.0),
2304 ("half", 2.0),
2305 ("quarter", 1.0),
2306 ("eighth", 0.5),
2307 ("16th", 0.25),
2308 ("32nd", 0.125),
2309 ("64th", 0.0625),
2310 ("128th", 0.03125),
2311 ("256th", 0.015625),
2312 ("512th", 0.0078125),
2313 ("1024th", 0.00390625),
2314 ("2048th", 0.001953125),
2315 ("zero", 0.0),
2316 ];
2317
2318 #[test]
2319 fn dots_types_and_full_names_match_music21() {
2320 let cases = [
2321 (
2322 4.0,
2323 Some((DurationType::Whole, 0)),
2324 "Whole",
2325 (DurationType::Whole, true),
2326 ),
2327 (
2328 2.0,
2329 Some((DurationType::Half, 0)),
2330 "Half",
2331 (DurationType::Half, true),
2332 ),
2333 (
2334 1.0,
2335 Some((DurationType::Quarter, 0)),
2336 "Quarter",
2337 (DurationType::Quarter, true),
2338 ),
2339 (
2340 0.5,
2341 Some((DurationType::Eighth, 0)),
2342 "Eighth",
2343 (DurationType::Eighth, true),
2344 ),
2345 (
2346 0.25,
2347 Some((DurationType::Sixteenth, 0)),
2348 "16th",
2349 (DurationType::Sixteenth, true),
2350 ),
2351 (
2352 3.0,
2353 Some((DurationType::Half, 1)),
2354 "Dotted Half",
2355 (DurationType::Half, false),
2356 ),
2357 (
2358 1.5,
2359 Some((DurationType::Quarter, 1)),
2360 "Dotted Quarter",
2361 (DurationType::Quarter, false),
2362 ),
2363 (
2364 0.75,
2365 Some((DurationType::Eighth, 1)),
2366 "Dotted Eighth",
2367 (DurationType::Eighth, false),
2368 ),
2369 (
2370 6.0,
2371 Some((DurationType::Whole, 1)),
2372 "Dotted Whole",
2373 (DurationType::Whole, false),
2374 ),
2375 (
2376 1.75,
2377 Some((DurationType::Quarter, 2)),
2378 "Double Dotted Quarter",
2379 (DurationType::Quarter, false),
2380 ),
2381 (
2382 0.875,
2383 Some((DurationType::Eighth, 2)),
2384 "Double Dotted Eighth",
2385 (DurationType::Eighth, false),
2386 ),
2387 (
2388 7.0,
2389 Some((DurationType::Whole, 2)),
2390 "Double Dotted Whole",
2391 (DurationType::Whole, false),
2392 ),
2393 (
2394 0.375,
2395 Some((DurationType::Sixteenth, 1)),
2396 "Dotted 16th",
2397 (DurationType::Sixteenth, false),
2398 ),
2399 (
2400 3.5,
2401 Some((DurationType::Half, 2)),
2402 "Double Dotted Half",
2403 (DurationType::Half, false),
2404 ),
2405 (
2406 3.75,
2407 Some((DurationType::Half, 3)),
2408 "Triple Dotted Half (3 3/4 QL)",
2409 (DurationType::Half, false),
2410 ),
2411 (
2412 1.875,
2413 Some((DurationType::Quarter, 3)),
2414 "Triple Dotted Quarter (1 7/8 QL)",
2415 (DurationType::Quarter, false),
2416 ),
2417 (
2418 8.0,
2419 Some((DurationType::Breve, 0)),
2420 "Breve",
2421 (DurationType::Breve, true),
2422 ),
2423 (
2424 16.0,
2425 Some((DurationType::Longa, 0)),
2426 "Imperfect Longa",
2427 (DurationType::Longa, true),
2428 ),
2429 (
2430 24.0,
2431 Some((DurationType::Longa, 1)),
2432 "Perfect Longa",
2433 (DurationType::Longa, false),
2434 ),
2435 ];
2436 for (quarter_length, type_and_dots, full_name, closest) in cases {
2437 let duration = Duration::new(quarter_length).unwrap();
2438 assert_eq!(duration.type_and_dots(), type_and_dots, "{quarter_length}");
2439 assert_eq!(duration.full_name(), full_name, "{quarter_length}");
2440 assert_eq!(
2441 quarter_length_to_closest_type(quarter_length).unwrap(),
2442 closest,
2443 "{quarter_length}"
2444 );
2445 }
2446
2447 let inexact = [
2448 (2.0 / 3.0, DurationType::Eighth),
2449 (1.0 / 3.0, DurationType::Sixteenth),
2450 (4.0 / 3.0, DurationType::Quarter),
2451 (0.2, DurationType::ThirtySecond),
2452 (0.4, DurationType::Sixteenth),
2453 (1.25, DurationType::Quarter),
2454 (5.0, DurationType::Whole),
2455 (2.5, DurationType::Half),
2456 ];
2457 let tuplet_names = [
2462 (2.0 / 3.0, "Quarter Triplet (2/3 QL)"),
2463 (1.0 / 3.0, "Eighth Triplet (1/3 QL)"),
2464 (4.0 / 3.0, "Half Triplet (1 1/3 QL)"),
2465 (0.2, "16th Quintuplet (1/5 QL)"),
2466 (0.4, "Eighth Quintuplet (2/5 QL)"),
2467 ];
2468 let tied = [
2470 (1.25, "Quarter tied to 16th (1 1/4 total QL)"),
2471 (5.0, "Whole tied to Quarter (5 total QL)"),
2472 (2.5, "Half tied to Eighth (2 1/2 total QL)"),
2473 ];
2474 for (quarter_length, closest) in inexact {
2475 let duration = Duration::new(quarter_length).unwrap();
2476 assert_eq!(duration.type_and_dots(), None, "{quarter_length}");
2477 assert_eq!(duration.dots(), 0, "{quarter_length}");
2478 let tuplet_name = tuplet_names
2479 .iter()
2480 .find(|(length, _)| *length == quarter_length);
2481 let tied_name = tied.iter().find(|(length, _)| *length == quarter_length);
2482 let expected = tuplet_name.or(tied_name).map(|(_, name)| *name);
2483 assert_eq!(
2484 duration.full_name(),
2485 expected.expect("every inexact length here is named"),
2486 "{quarter_length}"
2487 );
2488 assert_eq!(
2489 duration.tuplet().is_some(),
2490 tuplet_name.is_some(),
2491 "{quarter_length}"
2492 );
2493 assert_eq!(
2494 duration.components().len(),
2495 if tuplet_name.is_some() { 1 } else { 2 },
2496 "{quarter_length}"
2497 );
2498 assert_eq!(
2499 quarter_length_to_closest_type(quarter_length).unwrap(),
2500 (closest, false),
2501 "{quarter_length}"
2502 );
2503 }
2504
2505 for quarter_length in [0.001, 100.0] {
2507 let duration = Duration::new(quarter_length).unwrap();
2508 assert!(duration.components().is_empty(), "{quarter_length}");
2509 assert_eq!(duration.full_name(), "Inexpressible", "{quarter_length}");
2510 }
2511 let zero = Duration::new(0.0).unwrap();
2512 assert!(zero.components().is_empty());
2513 assert_eq!(zero.full_name(), "Zero Duration (0 total QL)");
2514
2515 let truncated = Duration::new(0.333_333).unwrap();
2518 assert_eq!(truncated.full_name(), "Eighth Triplet (1/3 QL)");
2519
2520 let triplet = Duration::new(2.0 / 3.0).unwrap().tuplet().unwrap();
2521 assert_eq!((triplet.actual(), triplet.normal()), (3, 2));
2522 assert_eq!(triplet.duration_type(), DurationType::Quarter);
2523 assert_eq!(triplet.dots(), 0);
2524 assert_eq!(triplet.full_name(), "Triplet");
2525 assert_eq!(triplet.multiplier(), FractionType::new(2i32, 3i32));
2526
2527 let odd = Tuplet::new(17, 14, DurationType::Quarter, 0);
2529 assert_eq!(odd.full_name(), "Tuplet of 17/14ths");
2530 assert_eq!(odd.total_tuplet_length(), 14.0);
2531 for (actual, normal, name) in [
2533 (40, 21, "Tuplet of 40/21st"),
2534 (3, 1, "Tuplet of 3/1st"),
2535 (12, 11, "Tuplet of 12/11ths"),
2536 (5, 3, "Tuplet of 5/3rds"),
2537 (23, 22, "Tuplet of 23/22nds"),
2538 ] {
2539 let tuplet = Tuplet::new(actual, normal, DurationType::Quarter, 0);
2540 assert_eq!(tuplet.full_name(), name);
2541 }
2542 let across =
2545 Tuplet::new(3, 1, DurationType::Eighth, 0).with_normal(DurationType::Quarter, 0);
2546 assert_eq!(across.total_tuplet_length(), 1.0);
2547 assert_eq!(across.multiplier(), FractionType::new(2i32, 3i32));
2550 assert_eq!(Duration::new(3.75).unwrap().dots(), 3);
2551 assert_eq!(
2552 quarter_length_to_closest_type(200.0).unwrap(),
2553 (DurationType::DuplexMaxima, false)
2554 );
2555 assert!(quarter_length_to_closest_type(0.0).is_err());
2556 assert!(quarter_length_to_closest_type(0.0001).is_err());
2557 }
2558
2559 #[test]
2560 fn duration_types_match_music21s_table() {
2561 assert_eq!(DurationType::ALL.len(), MUSIC21_TYPE_TO_DURATION.len());
2562 for (duration_type, (name, quarter_length)) in
2563 DurationType::ALL.into_iter().zip(MUSIC21_TYPE_TO_DURATION)
2564 {
2565 assert_eq!(duration_type.music21_name(), name);
2566 assert_eq!(duration_type.quarter_length(), quarter_length, "{name}");
2567 assert_eq!(DurationType::from_music21_name(name), Some(duration_type));
2568 }
2569 }
2570
2571 #[test]
2572 fn duration_types_round_trip_through_their_names() {
2573 for duration_type in DurationType::ALL {
2574 let name = duration_type.music21_name();
2575 assert_eq!(name.parse::<DurationType>().unwrap(), duration_type);
2576 assert_eq!(duration_type.to_string(), name);
2577 }
2578 assert!("not-a-duration".parse::<DurationType>().is_err());
2579 }
2580
2581 #[test]
2582 fn each_type_is_half_the_one_before_it() {
2583 let ordered = &DurationType::ALL[..DurationType::ALL.len() - 1];
2585 for pair in ordered.windows(2) {
2586 assert_eq!(
2587 pair[1].quarter_length() * 2.0,
2588 pair[0].quarter_length(),
2589 "{} should be half of {}",
2590 pair[1],
2591 pair[0]
2592 );
2593 }
2594 }
2595
2596 #[test]
2597 fn dots_add_half_of_what_came_before() {
2598 assert_eq!(DurationType::Half.quarter_length_with_dots(0), 2.0);
2599 assert_eq!(DurationType::Half.quarter_length_with_dots(1), 3.0);
2600 assert_eq!(DurationType::Half.quarter_length_with_dots(2), 3.5);
2601 assert_eq!(DurationType::Half.quarter_length_with_dots(3), 3.75);
2602 assert_eq!(DurationType::Quarter.quarter_length_with_dots(1), 1.5);
2603 }
2604
2605 #[test]
2606 fn durations_convert_to_and_from_note_values() {
2607 assert_eq!(
2608 Duration::from_type(DurationType::Whole).quarter_length(),
2609 4.0
2610 );
2611 assert_eq!(
2612 Duration::from_type(DurationType::Whole).duration_type(),
2613 Some(DurationType::Whole)
2614 );
2615 assert_eq!(
2616 Duration::from_type_with_dots(DurationType::Half, 1).quarter_length(),
2617 3.0
2618 );
2619 assert_eq!(
2622 Duration::from_type_with_dots(DurationType::Half, 1).duration_type(),
2623 Some(DurationType::Half)
2624 );
2625 assert_eq!(
2626 Duration::from_type_with_dots(DurationType::Half, 1).dots(),
2627 1
2628 );
2629 assert_eq!(
2630 Duration::new(3.75).unwrap().duration_type(),
2631 Some(DurationType::Half)
2632 );
2633 assert_eq!(Duration::new(3.75).unwrap().dots(), 3);
2634 assert_eq!(Duration::new(1.0 / 3.0).unwrap().duration_type(), None);
2636 }
2637
2638 #[test]
2639 fn the_named_helpers_agree_with_their_types() {
2640 assert_eq!(
2641 Duration::quarter(),
2642 Duration::from_type(DurationType::Quarter)
2643 );
2644 assert_eq!(Duration::half(), Duration::from_type(DurationType::Half));
2645 assert_eq!(Duration::whole(), Duration::from_type(DurationType::Whole));
2646 assert_eq!(
2647 Duration::eighth(),
2648 Duration::from_type(DurationType::Eighth)
2649 );
2650 }
2651
2652 #[test]
2653 fn duration_tracks_quarter_lengths() {
2654 assert_eq!(Duration::quarter().quarter_length(), 1.0);
2655 assert_eq!(Duration::half().quarter_length(), 2.0);
2656 assert_eq!(Duration::whole().quarter_length(), 4.0);
2657 assert_eq!(Duration::eighth().quarter_length(), 0.5);
2658 }
2659
2660 #[test]
2661 fn duration_rejects_invalid_values() {
2662 assert!(Duration::new(-1.0).is_err());
2663 assert!(Duration::new(FloatType::INFINITY).is_err());
2664 }
2665
2666 #[test]
2667 fn a_plain_note_value_is_not_read_as_a_tuplet() {
2668 assert_eq!(Duration::quarter().tuplet(), None);
2671 assert_eq!(Duration::new(3.0).unwrap().tuplet(), None);
2672 assert_eq!(
2673 Duration::new(1.0 / 3.0).unwrap().tuplet(),
2674 Some(Tuplet::new(3, 2, DurationType::Eighth, 0))
2675 );
2676 }
2677
2678 #[test]
2679 fn appending_a_tuplet_shortens_the_length_by_its_ratio() {
2680 let mut duration = Duration::new(1.0).unwrap();
2682 duration.append_tuplet(Tuplet::new(3, 2, DurationType::Quarter, 0));
2683 assert!((duration.quarter_length() - 2.0 / 3.0).abs() < 1e-12);
2684 assert_eq!(duration.quarter_length_no_tuplets(), 1.0);
2685
2686 duration.append_tuplet(Tuplet::new(5, 4, DurationType::Quarter, 0));
2687 assert!((duration.quarter_length() - 8.0 / 15.0).abs() < 1e-12);
2688 assert_eq!(
2689 duration.aggregate_tuplet_multiplier(),
2690 FractionType::new(8, 15)
2691 );
2692 assert_eq!(
2694 duration.components(),
2695 vec![(DurationType::Quarter, 0)],
2696 "the written value stays a quarter inside both tuplets"
2697 );
2698 }
2699
2700 #[test]
2701 fn saying_a_length_is_in_no_tuplet_is_not_the_same_as_saying_nothing() {
2702 let inferred = Duration::new(1.0 / 3.0).unwrap();
2703 assert_eq!(inferred.tuplets().len(), 1);
2704
2705 let mut told = Duration::new(1.0 / 3.0).unwrap();
2706 told.set_tuplets(Vec::new());
2707 assert!(told.tuplets().is_empty());
2708 assert_eq!(told.quarter_length(), 0.5);
2710 }
2711
2712 #[test]
2713 fn setting_a_length_forgets_the_tuplets_it_was_told() {
2714 let mut duration = Duration::new(1.0).unwrap();
2715 duration.append_tuplet(Tuplet::new(3, 2, DurationType::Quarter, 0));
2716 duration.set_quarter_length(2.0).unwrap();
2717 assert!(duration.tuplets().is_empty());
2718 assert_eq!(duration.quarter_length(), 2.0);
2719 }
2720
2721 #[test]
2722 fn duration_supports_conversions_and_updates() {
2723 let mut duration = Duration::try_from(3 as IntegerType).unwrap();
2724 assert_eq!(duration.quarter_length(), 3.0);
2725
2726 duration.set_quarter_length(1.5).unwrap();
2727 assert_eq!(duration, Duration::try_from(1.5).unwrap());
2728 assert!(duration.set_quarter_length(FloatType::NAN).is_err());
2729 }
2730}