1pub mod sequence;
17
18pub use sequence::{MeterTerminal, OffsetAlign};
19
20use crate::defaults::{FloatType, UnsignedIntegerType};
21use crate::duration::Duration;
22use crate::duration::DurationType;
23use crate::error::{Error, Result};
24use crate::notation::{BeamType, Beams};
25
26#[must_use]
30pub fn partition_name(count: usize) -> String {
31 BEAT_COUNT_NAMES
32 .get(count)
33 .map_or_else(|| format!("{count}-uple"), |name| (*name).to_string())
34}
35
36const BEAT_COUNT_NAMES: [&str; 9] = [
40 "Empty",
41 "Single",
42 "Duple",
43 "Triple",
44 "Quadruple",
45 "Quintuple",
46 "Sextuple",
47 "Septuple",
48 "Octuple",
49];
50
51#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56pub enum BeatDivision {
57 Other,
60 Simple,
62 Compound,
64}
65
66impl BeatDivision {
67 pub fn music21_name(&self) -> &'static str {
69 match self {
70 Self::Other => "Other",
71 Self::Simple => "Simple",
72 Self::Compound => "Compound",
73 }
74 }
75
76 pub fn count(&self) -> UnsignedIntegerType {
81 match self {
82 Self::Other => 1,
83 Self::Simple => 2,
84 Self::Compound => 3,
85 }
86 }
87}
88
89#[derive(Clone, Debug, PartialEq)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
104#[must_use]
105pub struct TimeSignature {
106 numerator: UnsignedIntegerType,
107 denominator: UnsignedIntegerType,
108 favor_compound: bool,
109 display_sequence: MeterTerminal,
110 beat_sequence: MeterTerminal,
111 beam_sequence: MeterTerminal,
112 accent_sequence: MeterTerminal,
113 #[cfg_attr(feature = "serde", serde(default))]
115 symbol: Option<String>,
116 #[cfg_attr(feature = "serde", serde(default))]
117 symbolize_denominator: bool,
118 #[cfg_attr(feature = "serde", serde(default))]
119 color: crate::display::Color,
120 #[cfg_attr(feature = "serde", serde(default))]
122 restated: crate::display::Drawn<bool>,
123 #[cfg_attr(feature = "serde", serde(default))]
124 hidden: crate::display::Drawn<bool>,
125 #[cfg_attr(feature = "serde", serde(default))]
128 overridden_bar_duration: Option<Duration>,
129}
130
131impl Default for TimeSignature {
132 fn default() -> Self {
134 Self::common()
135 }
136}
137
138pub const MAX_NUMERATOR: UnsignedIntegerType = 10_000;
142
143impl TimeSignature {
144 pub fn new(numerator: UnsignedIntegerType, denominator: UnsignedIntegerType) -> Result<Self> {
150 if numerator == 0 {
151 return Err(Error::Meter(
152 "time signature numerator must be non-zero".to_string(),
153 ));
154 }
155 if denominator == 0 {
156 return Err(Error::Meter(
157 "time signature denominator must be non-zero".to_string(),
158 ));
159 }
160 if numerator > MAX_NUMERATOR {
161 return Err(Error::Meter(format!(
162 "time signature numerator {numerator} is more than {MAX_NUMERATOR}"
163 )));
164 }
165 let mut signature = Self {
166 numerator,
167 denominator,
168 favor_compound: favor_compound(numerator, denominator, None),
169 display_sequence: whole_bar(numerator, denominator)?,
170 beat_sequence: whole_bar(numerator, denominator)?,
171 beam_sequence: whole_bar(numerator, denominator)?,
172 accent_sequence: whole_bar(numerator, denominator)?,
173 symbol: None,
174 symbolize_denominator: false,
175 color: crate::display::Drawn(None),
176 restated: crate::display::Drawn(false),
177 hidden: crate::display::Drawn(false),
178 overridden_bar_duration: None,
179 };
180 signature.set_default_partitions()?;
181 Ok(signature)
182 }
183
184 pub fn symbol(&self) -> Option<&str> {
188 self.symbol.as_deref()
189 }
190
191 pub fn set_symbol(&mut self, symbol: Option<String>) {
193 self.symbol = symbol.filter(|symbol| !symbol.is_empty());
194 }
195
196 pub fn is_restated(&self) -> bool {
206 self.restated.0
207 }
208
209 pub fn set_restated(&mut self, restated: bool) {
211 self.restated.0 = restated;
212 }
213
214 pub fn symbolize_denominator(&self) -> bool {
217 self.symbolize_denominator
218 }
219
220 pub fn set_symbolize_denominator(&mut self, symbolize: bool) {
222 self.symbolize_denominator = symbolize;
223 }
224
225 pub fn color(&self) -> Option<&str> {
228 self.color.0.as_deref()
229 }
230
231 pub fn set_color(&mut self, color: Option<String>) {
233 self.color.0 = color;
234 }
235
236 pub fn is_hidden(&self) -> bool {
240 self.hidden.0
241 }
242
243 pub fn set_hidden(&mut self, hidden: bool) {
245 self.hidden.0 = hidden;
246 }
247
248 #[must_use]
253 pub fn display_sequence(&self) -> &MeterTerminal {
254 &self.display_sequence
255 }
256
257 #[must_use]
260 pub fn beat_sequence(&self) -> &MeterTerminal {
261 &self.beat_sequence
262 }
263
264 #[must_use]
267 pub fn beam_sequence(&self) -> &MeterTerminal {
268 &self.beam_sequence
269 }
270
271 #[must_use]
275 pub fn accent_sequence(&self) -> &MeterTerminal {
276 &self.accent_sequence
277 }
278
279 #[must_use]
282 pub fn favors_compound(&self) -> bool {
283 self.favor_compound
284 }
285
286 pub fn set_beat_sequence(&mut self, sequence: MeterTerminal) {
289 self.beat_sequence = sequence;
290 }
291
292 pub fn set_beam_sequence(&mut self, sequence: MeterTerminal) {
295 self.beam_sequence = sequence;
296 }
297
298 pub fn set_accent_sequence(&mut self, sequence: MeterTerminal) {
301 self.accent_sequence = sequence;
302 }
303
304 pub fn set_display_sequence(&mut self, sequence: MeterTerminal) {
307 self.display_sequence = sequence;
308 }
309
310 pub fn set_display(&mut self, value: &str) -> Result<()> {
318 let parts = Self::parts(value)?;
319 let denominator = parts
320 .iter()
321 .map(|(_, part)| *part)
322 .max()
323 .unwrap_or(self.denominator);
324 let numerator = parts
325 .iter()
326 .fold(0, |sum: UnsignedIntegerType, (count, part)| {
327 sum.saturating_add(count.saturating_mul(denominator / part))
328 });
329 let display = whole_bar(numerator, denominator)?;
330 if (display.quarter_length() - self.bar_quarter_length()).abs() > OFFSET_TOLERANCE {
331 return Err(Error::Meter(format!(
332 "cannot write a {} bar as {value:?}: that is {} quarter lengths, not {}",
333 self.ratio_string(),
334 display.quarter_length(),
335 self.bar_quarter_length()
336 )));
337 }
338 if parts.len() > 1 {
339 let written: Vec<String> = parts
340 .iter()
341 .map(|(count, part)| format!("{count}/{part}"))
342 .collect();
343 let borrowed: Vec<&str> = written.iter().map(String::as_str).collect();
344 self.display_sequence = display.partitioned_by_parts(&borrowed)?;
345 return Ok(());
346 }
347 self.display_sequence = display;
348 Ok(())
349 }
350
351 pub fn divide_beats(&mut self, count: UnsignedIntegerType) -> Result<()> {
358 if count == 0 {
359 return Err(Error::Meter(
360 "a bar cannot be divided into no parts".to_string(),
361 ));
362 }
363 let divided = whole_bar(self.numerator, self.denominator)?
364 .partitioned_by_count(count as usize, false)?;
365 self.beat_sequence = divided.clone();
368 self.accent_sequence = divided.clone();
369 self.beam_sequence = divided;
370 Ok(())
371 }
372
373 pub fn set_beat_count(&mut self, count: UnsignedIntegerType) -> Result<()> {
380 if count == 0 {
381 return Err(Error::Meter(
382 "a bar cannot be counted in no beats".to_string(),
383 ));
384 }
385 let mut beats = MeterTerminal::new(self.numerator, self.denominator)?
386 .partitioned_by_count(count as usize, false)?;
387 if beats.len() > 1 {
388 beats = beats.subdivided_partitions_equal(None)?;
389 }
390 self.beat_sequence = beats;
391 Ok(())
392 }
393
394 fn set_default_partitions(&mut self) -> Result<()> {
398 self.set_default_beam_partitions()?;
399 self.set_default_beat_partitions()?;
400 self.set_default_accent_weights();
401 Ok(())
402 }
403
404 fn set_default_beam_partitions(&mut self) -> Result<()> {
413 let numerator = self.numerator;
414 let denominator = self.denominator;
415 if self.beam_sequence.summed_numerator() {
416 return Ok(());
417 }
418 if (denominator == 8 && matches!(numerator, 1..=3))
419 || (denominator == 16 && matches!(numerator, 1..=5))
420 || (denominator == 32 && matches!(numerator, 1..=11))
421 {
422 return Ok(());
423 }
424 let beams = &self.beam_sequence;
425 self.beam_sequence = match numerator {
426 2..=4 => {
427 let mut beams = beams.partitioned_by_count(numerator as usize, false)?;
428 if denominator == 4 {
429 for index in 0..beams.len() {
430 let divided = beams.parts()[index].subdivide_by_count(2)?;
431 beams = beams.set_index(index, divided)?;
432 }
433 }
434 beams
435 }
436 5 => {
437 let mut beams = beams.partitioned_by_list(&[2, 3])?;
438 if denominator == 4 {
439 for (index, count) in [2, 3].into_iter().enumerate() {
440 let divided = beams.parts()[index].subdivide_by_count(count)?;
441 beams = beams.set_index(index, divided)?;
442 }
443 }
444 beams
445 }
446 7 => beams.partitioned_by_count(3, false)?,
447 other if other >= 6 && other.is_multiple_of(3) => {
449 beams.partitioned_by_list(&vec![3; (other / 3) as usize])?
450 }
451 _ => return Ok(()),
452 };
453 Ok(())
454 }
455
456 fn set_default_beat_partitions(&mut self) -> Result<()> {
463 let numerator = self.numerator;
464 let compound = self.favor_compound;
465 let beats = &self.beat_sequence;
466 self.beat_sequence = if self.display_sequence.len() == 1 {
467 match numerator {
468 2 => beats.partitioned_by_count(2, false)?,
469 6 if compound => beats.partitioned_by_count(2, false)?,
470 3 if compound => beats.partitioned_by_count(1, false)?,
471 3 => beats.partitioned_by_list(&[1, 1, 1])?,
472 9 if compound => beats.partitioned_by_list(&[3, 3, 3])?,
473 4 => beats.partitioned_by_count(4, false)?,
474 12 if compound => beats.partitioned_by_count(4, false)?,
475 other if other >= 15 && other.is_multiple_of(3) && compound => {
476 beats.partitioned_by_list(&vec![3; (other / 3) as usize])?
477 }
478 other => beats.partitioned_by_count(other as usize, false)?,
479 }
480 } else {
481 let written: Vec<String> = self
482 .display_sequence
483 .parts()
484 .iter()
485 .map(|part| format!("{}/{}", part.numerator(), part.denominator()))
486 .collect();
487 let borrowed: Vec<&str> = written.iter().map(String::as_str).collect();
488 beats.partitioned_by_parts(&borrowed)?
489 };
490 if self.beat_sequence.len() > 1 {
491 match self.beat_sequence.subdivided_partitions_equal(None) {
494 Ok(divided) => self.beat_sequence = divided,
495 Err(error) if self.denominator >= 128 => {
496 let _ = error;
497 }
498 Err(error) => return Err(error),
499 }
500 }
501 Ok(())
502 }
503
504 fn set_default_accent_weights(&mut self) {
508 let weights = self.default_accent_weights();
509 let ones = vec![1; weights.len()];
510 let Ok(partitioned) = self.accent_sequence.partitioned_by_list(&ones) else {
511 return;
512 };
513 let weighed: Vec<MeterTerminal> = partitioned
514 .parts()
515 .iter()
516 .zip(weights)
517 .map(|(part, weight)| part.with_weight(weight))
518 .collect();
519 if let Ok(sequence) =
520 MeterTerminal::sequence(partitioned.numerator(), partitioned.denominator(), weighed)
521 {
522 self.accent_sequence = sequence.with_summed_numerator(partitioned.summed_numerator());
523 }
524 }
525
526 pub fn from_ratio_string(ratio: &str) -> Result<Self> {
535 let parts = Self::parts(ratio)?;
536 if parts.iter().any(|(_, part)| *part == 0) {
537 return Err(Error::Meter(format!(
538 "time signature denominator must be non-zero: {ratio}"
539 )));
540 }
541 let word = division_word(ratio);
542 let summed = ratio.split('+').any(|part| !part.contains('/'));
543 let denominator = parts[0].1;
544 if parts.iter().all(|(_, part)| *part == denominator) {
545 let numerator = parts
546 .iter()
547 .fold(0, |sum: UnsignedIntegerType, (count, _)| {
548 sum.saturating_add(*count)
549 });
550 let mut signature = Self::new(numerator, denominator)?;
551 signature.write_as(&parts, word, summed)?;
552 return Ok(signature);
553 }
554 let denominator = parts
557 .iter()
558 .map(|(_, part)| *part)
559 .max()
560 .unwrap_or(denominator);
561 let numerator = parts
562 .iter()
563 .fold(0, |sum: UnsignedIntegerType, (count, part)| {
564 sum.saturating_add(count.saturating_mul(denominator / part))
565 });
566 let mut signature = Self::new(numerator, denominator)?;
567 signature.write_as(&parts, word, summed)?;
568 Ok(signature)
569 }
570
571 fn write_as(
575 &mut self,
576 parts: &[(UnsignedIntegerType, UnsignedIntegerType)],
577 word: Option<&str>,
578 summed: bool,
579 ) -> Result<()> {
580 self.favor_compound = favor_compound(self.numerator, self.denominator, word);
581 self.beat_sequence = whole_bar(self.numerator, self.denominator)?;
582 self.beam_sequence = whole_bar(self.numerator, self.denominator)?;
583 self.accent_sequence = whole_bar(self.numerator, self.denominator)?;
584 if parts.len() > 1 {
585 let written = parts
592 .iter()
593 .map(|(count, part)| format!("{count}/{part}"))
594 .collect::<Vec<_>>()
595 .join("+");
596 let loaded = MeterTerminal::from_partition_string(&written)?;
597 self.display_sequence = loaded.clone();
598 self.beat_sequence = loaded.clone();
599 self.beam_sequence = loaded;
600 }
601 for sequence in [
604 &mut self.display_sequence,
605 &mut self.beat_sequence,
606 &mut self.beam_sequence,
607 &mut self.accent_sequence,
608 ] {
609 *sequence = sequence.with_summed_numerator(summed);
610 }
611 self.set_default_partitions()
612 }
613
614 #[must_use]
618 pub fn summed_numerator(&self) -> bool {
619 self.display_sequence.summed_numerator()
620 }
621
622 pub fn set_summed_numerator(&mut self, summed: bool) {
625 self.display_sequence = self.display_sequence.with_summed_numerator(summed);
626 }
627
628 pub fn parts(ratio: &str) -> Result<Vec<(UnsignedIntegerType, UnsignedIntegerType)>> {
637 let unworded = without_division_word(ratio);
638 let written = unworded.trim();
639 if written.is_empty() {
640 return Err(Error::Meter("a time signature says nothing".to_string()));
641 }
642 let mut pre: Vec<(UnsignedIntegerType, Option<UnsignedIntegerType>)> = Vec::new();
643 for part in written.split('+') {
644 let part = part.trim();
645 match part.split_once('/') {
646 Some((numerator, denominator)) => pre.push((
647 Self::count(numerator, "numerator", written)?,
648 Some(Self::count(denominator, "denominator", written)?),
649 )),
650 None => pre.push((Self::count(part, "numerator", written)?, None)),
651 }
652 }
653 let mut out = Vec::with_capacity(pre.len());
654 for (index, (numerator, denominator)) in pre.iter().enumerate() {
655 let denominator = match denominator {
656 Some(denominator) => *denominator,
657 None => pre[index + 1..]
658 .iter()
659 .find_map(|(_, later)| *later)
660 .ok_or_else(|| {
661 Error::Meter(format!(
662 "cannot match a denominator to every numerator in {written:?}"
663 ))
664 })?,
665 };
666 out.push((*numerator, denominator));
667 }
668 Ok(out)
669 }
670
671 fn count(part: &str, label: &str, ratio: &str) -> Result<UnsignedIntegerType> {
674 let digits = part
675 .trim()
676 .rsplit(|character: char| character.is_whitespace())
677 .next()
678 .unwrap_or("")
679 .trim();
680 digits
681 .parse::<UnsignedIntegerType>()
682 .map_err(|_| Error::Meter(format!("cannot read a {label} from {part:?} in {ratio:?}")))
683 }
684
685 pub fn common() -> Self {
687 Self::new(4, 4).expect("4/4 is a meter")
688 }
689
690 pub fn cut() -> Self {
692 Self::new(2, 2).expect("2/2 is a meter")
693 }
694
695 pub fn numerator(&self) -> UnsignedIntegerType {
697 self.numerator
698 }
699
700 pub fn denominator(&self) -> UnsignedIntegerType {
702 self.denominator
703 }
704
705 pub fn ratio_string(&self) -> String {
707 if self.display_sequence.len() > 1 {
712 return self.display_sequence.partition_display();
713 }
714 format!("{}/{}", self.numerator, self.denominator)
715 }
716
717 pub fn bar_quarter_length(&self) -> FloatType {
719 FloatType::from(self.numerator) * 4.0 / FloatType::from(self.denominator)
720 }
721
722 pub fn bar_duration(&self) -> Duration {
726 if let Some(written) = &self.overridden_bar_duration {
727 return written.clone();
728 }
729 Duration::new(self.bar_quarter_length())
730 .expect("a non-zero numerator and denominator give a positive finite bar length")
731 }
732
733 pub fn set_bar_duration(&mut self, written: Option<Duration>) {
746 self.overridden_bar_duration = written;
747 }
748
749 pub fn written_bar_duration(&self) -> Option<&Duration> {
752 self.overridden_bar_duration.as_ref()
753 }
754
755 pub fn offset_in_bar(&self, element_offset: FloatType, meter_offset: FloatType) -> FloatType {
769 let bar = self.bar_duration().quarter_length();
772
773 let op_frac = crate::makenotation::op_frac;
778 if op_frac(element_offset + meter_offset) < bar {
779 return element_offset;
780 }
781 op_frac(op_frac(element_offset - meter_offset).rem_euclid(bar))
782 }
783
784 pub fn beat_count(&self) -> UnsignedIntegerType {
790 self.beat_sequence.len() as UnsignedIntegerType
791 }
792
793 pub fn beat_count_name(&self) -> String {
797 partition_name(self.beat_count() as usize)
798 }
799
800 pub fn beat_quarter_length(&self) -> FloatType {
804 let (start, end) = self.beat_spans()[0];
805 end - start
806 }
807
808 pub fn beat_duration(&self) -> Duration {
810 Duration::new(self.beat_quarter_length())
811 .expect("a beat of a meter is a positive finite length")
812 }
813
814 pub fn beat_division(&self) -> BeatDivision {
816 let parts = self.beat_sequence.parts();
817 if parts.len() <= 1 {
818 return BeatDivision::Other;
819 }
820 let mut counts = parts.iter().map(MeterTerminal::len);
821 let Some(first) = counts.next() else {
822 return BeatDivision::Other;
823 };
824 if first == 0 || !counts.all(|count| count == first) {
825 return BeatDivision::Other;
828 }
829 match first {
830 2 => BeatDivision::Simple,
831 3 => BeatDivision::Compound,
832 _ => BeatDivision::Other,
833 }
834 }
835
836 pub fn beat_division_count(&self) -> UnsignedIntegerType {
838 self.beat_division().count()
839 }
840
841 pub fn is_compound(&self) -> bool {
843 self.beat_division() == BeatDivision::Compound
844 }
845
846 pub fn classification(&self) -> String {
848 format!(
849 "{} {}",
850 self.beat_division().music21_name(),
851 self.beat_count_name()
852 )
853 }
854
855 pub fn beat_division_count_name(&self) -> &'static str {
858 self.beat_division().music21_name()
859 }
860
861 pub fn ratio_equal(&self, other: &TimeSignature) -> bool {
864 self.numerator == other.numerator && self.denominator == other.denominator
865 }
866
867 pub fn beat_length_to_quarter_length_ratio(&self) -> FloatType {
870 4.0 / FloatType::from(self.denominator)
871 }
872
873 pub fn quarter_length_to_beat_length_ratio(&self) -> FloatType {
876 FloatType::from(self.denominator) / 4.0
877 }
878
879 pub fn beat_division_quarter_lengths(&self) -> Result<Vec<FloatType>> {
892 let beat_sequence = self.beat_sequence();
893 let beats = match beat_sequence.parts() {
894 [] => std::slice::from_ref(beat_sequence),
895 beats => beats,
896 };
897
898 let divisions = |beat: &MeterTerminal| -> Vec<FloatType> {
901 match beat.parts() {
902 [] => vec![beat.quarter_length()],
903 parts => parts.iter().map(MeterTerminal::quarter_length).collect(),
904 }
905 };
906
907 let every: Vec<FloatType> = beats.iter().flat_map(divisions).collect();
908 if every.windows(2).any(|pair| pair[0] != pair[1]) {
909 return Err(Error::Meter(format!(
910 "non uniform beat division: {every:?}"
911 )));
912 }
913 Ok(beats.first().map(divisions).unwrap_or_default())
914 }
915
916 pub fn beat_division_durations(&self) -> Result<Vec<Duration>> {
919 self.beat_division_quarter_lengths()?
920 .into_iter()
921 .map(Duration::new)
922 .collect()
923 }
924
925 pub fn beat_sub_division_durations(&self) -> Result<Vec<Duration>> {
928 let mut out = Vec::new();
929 for quarter_length in self.beat_division_quarter_lengths()? {
930 let half = Duration::new(quarter_length / 2.0)?;
931 out.push(half.clone());
932 out.push(half);
933 }
934 Ok(out)
935 }
936
937 pub fn offset_from_beat(&self, beat: FloatType) -> Result<FloatType> {
941 let whole = beat.floor();
942 if !beat.is_finite() || whole < 1.0 || whole > FloatType::from(self.beat_count()) {
943 return Err(Error::Meter(format!(
944 "requested beat value ({beat}) not found in the {} beats of {}",
945 self.beat_count(),
946 self.ratio_string()
947 )));
948 }
949 let (start, end) = self.beat_spans()[whole as usize - 1];
950 let fraction = snapped_fraction(beat - whole);
951 Ok(start + fraction * (end - start))
952 }
953
954 pub fn beat_progress(&self, offset: FloatType) -> Result<(UnsignedIntegerType, FloatType)> {
957 let index = self.beat_index(offset)?;
958 let (start, _) = self.beat_spans()[index];
959 Ok((
961 index as UnsignedIntegerType + 1,
962 crate::makenotation::op_frac(offset - start),
963 ))
964 }
965
966 pub fn beat_proportion(&self, offset: FloatType) -> Result<FloatType> {
970 let (beat, progress) = self.beat_progress(offset)?;
971 let (start, end) = self.beat_spans()[beat as usize - 1];
972 Ok(crate::makenotation::op_frac(
973 FloatType::from(beat) + progress / (end - start),
974 ))
975 }
976
977 pub fn beat_proportion_string(&self, offset: FloatType) -> Result<String> {
982 let (beat, progress) = self.beat_progress(offset)?;
983 let (start, end) = self.beat_spans()[beat as usize - 1];
984 let proportion = progress / (end - start);
985 if proportion == 0.0 {
986 return Ok(beat.to_string());
987 }
988 let (numerator, denominator) = closest_fraction(proportion, 16);
989 Ok(format!("{beat} {numerator}/{denominator}"))
990 }
991
992 fn beat_spans(&self) -> Vec<(FloatType, FloatType)> {
996 let mut spans = Vec::new();
997 let mut position = 0.0;
998 for part in self.beat_sequence.parts() {
999 let end = position + part.quarter_length();
1000 spans.push((position, end));
1001 position = end;
1002 }
1003 if spans.is_empty() {
1004 spans.push((0.0, self.bar_quarter_length()));
1005 }
1006 spans
1007 }
1008
1009 pub fn beat_duration_at(&self, offset: FloatType) -> Result<Duration> {
1015 let index = self.beat_index(offset)?;
1016 let (start, end) = self.beat_spans()[index];
1017 Duration::new(end - start)
1018 }
1019
1020 fn beat_index(&self, offset: FloatType) -> Result<usize> {
1022 if !offset.is_finite() || offset < 0.0 || offset >= self.bar_quarter_length() {
1023 return Err(Error::Meter(format!(
1024 "offset {offset} is outside a {} bar of {} quarter lengths",
1025 self.ratio_string(),
1026 self.bar_quarter_length()
1027 )));
1028 }
1029 let spans = self.beat_spans();
1030 for (index, (start, end)) in spans.iter().enumerate() {
1031 let _ = start;
1032 if offset < end - OFFSET_TOLERANCE {
1033 return Ok(index);
1034 }
1035 }
1036 Ok(spans.len() - 1)
1037 }
1038
1039 pub fn beat_offsets(&self) -> Vec<FloatType> {
1041 self.beat_spans()
1042 .into_iter()
1043 .map(|(start, _)| start)
1044 .collect()
1045 }
1046
1047 pub fn beat_at_offset(&self, offset: FloatType) -> Result<UnsignedIntegerType> {
1052 if !offset.is_finite() || offset < 0.0 || offset >= self.bar_quarter_length() {
1053 return Err(Error::Meter(format!(
1054 "offset {offset} is outside a {} bar of {} quarter lengths",
1055 self.ratio_string(),
1056 self.bar_quarter_length()
1057 )));
1058 }
1059 Ok(self.beat_index(offset)? as UnsignedIntegerType + 1)
1060 }
1061}
1062
1063fn closest_fraction(value: FloatType, max_denominator: u32) -> (u32, u32) {
1067 let mut best = (value.round() as u32, 1);
1068 let mut best_error = (value - value.round()).abs();
1069 for denominator in 2..=max_denominator {
1070 let numerator = (value * FloatType::from(denominator)).round();
1071 let error = (value - numerator / FloatType::from(denominator)).abs();
1072 if error < best_error {
1073 best = (numerator as u32, denominator);
1074 best_error = error;
1075 }
1076 }
1077 best
1078}
1079
1080impl std::fmt::Display for TimeSignature {
1081 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1082 f.write_str(&self.ratio_string())
1083 }
1084}
1085
1086const OFFSET_TOLERANCE: FloatType = 1e-9;
1089
1090const SHORTEST_PARTITION: FloatType = 4.0 / 128.0;
1092
1093const LONGEST_PARTITION: FloatType = 4.0;
1095
1096impl TimeSignature {
1097 fn accent_hierarchy(
1109 &self,
1110 ) -> (
1111 UnsignedIntegerType,
1112 UnsignedIntegerType,
1113 UnsignedIntegerType,
1114 ) {
1115 let beats = self.beat_count();
1116 let first = if beats > 1 { beats } else { self.numerator };
1117 let top = match first {
1118 1 | 2 | 4 | 8 | 16 | 32 => 2,
1119 3 => 3,
1120 other => other,
1121 };
1122 let Some((span, unit)) = split_span(self.numerator, self.denominator, top) else {
1125 return (1, 1, 1);
1126 };
1127 let second = default_division(span, unit);
1128 let Some((span, unit)) = split_span(span, unit, second) else {
1129 return (1, 1, 1);
1130 };
1131 let third = default_division(span, unit);
1132 let partition = self.bar_quarter_length() / FloatType::from(top * second * third);
1137 if !(SHORTEST_PARTITION..=LONGEST_PARTITION).contains(&partition) {
1138 return (1, 1, 1);
1139 }
1140 (top, second, third)
1141 }
1142
1143 pub fn accent_partition_quarter_length(&self) -> FloatType {
1146 let parts = self.accent_sequence.parts();
1147 if parts.len() > 1 {
1148 return parts[0].quarter_length();
1149 }
1150 let (top, second, third) = self.accent_hierarchy();
1151 self.bar_quarter_length() / FloatType::from(top * second * third)
1152 }
1153
1154 pub fn accent_weights(&self) -> Vec<FloatType> {
1159 let carried: Vec<FloatType> = self
1160 .accent_sequence
1161 .parts()
1162 .iter()
1163 .map(MeterTerminal::weight)
1164 .collect();
1165 if carried.len() > 1 {
1166 return carried;
1167 }
1168 self.default_accent_weights()
1169 }
1170
1171 fn default_accent_weights(&self) -> Vec<FloatType> {
1176 if !self.beat_sequence.is_uniform_partition(0)
1177 && let Some(weights) = self.accent_weights_from_the_beats()
1178 {
1179 return weights;
1180 }
1181 let (top, second, third) = self.accent_hierarchy();
1182 let count = top * second * third;
1183 (0..count)
1184 .map(|index| {
1185 let depth = 1
1186 + UnsignedIntegerType::from(index.is_multiple_of(third))
1187 + UnsignedIntegerType::from(index.is_multiple_of(second * third))
1188 + UnsignedIntegerType::from(index == 0);
1189 FloatType::from(2u32.pow(depth - 1)) / 8.0
1190 })
1191 .collect()
1192 }
1193
1194 fn accent_weights_from_the_beats(&self) -> Option<Vec<FloatType>> {
1204 let nested = MeterTerminal::new(self.numerator, self.denominator)
1205 .ok()?
1206 .subdivided_nested_hierarchy_by(3, &self.beat_sequence, true)
1207 .ok()?;
1208 let levels = nested.depth();
1209 if levels == 0 {
1210 return None;
1211 }
1212 let starts: Vec<Vec<FloatType>> = (0..levels)
1213 .map(|level| {
1214 nested
1215 .level_span(level)
1216 .into_iter()
1217 .map(|(start, _)| start)
1218 .collect()
1219 })
1220 .collect();
1221 let depths: Vec<usize> = starts[levels - 1]
1224 .iter()
1225 .map(|start| {
1226 starts
1227 .iter()
1228 .filter(|level| {
1229 level
1230 .iter()
1231 .any(|other| (other - start).abs() < OFFSET_TOLERANCE)
1232 })
1233 .count()
1234 })
1235 .collect();
1236 let deepest = depths.iter().copied().max()?;
1237 Some(
1238 depths
1239 .into_iter()
1240 .map(|depth| {
1241 FloatType::from(2u32.pow(depth as u32 - 1))
1242 / FloatType::from(2u32.pow(deepest as u32 - 1))
1243 })
1244 .collect(),
1245 )
1246 }
1247
1248 pub fn accent(&self, offset: FloatType) -> bool {
1252 let parts = match self.accent_sequence.parts() {
1253 [] => std::slice::from_ref(&self.accent_sequence),
1254 parts => parts,
1255 };
1256 let mut position = 0.0;
1257 for part in parts {
1258 if (position - offset).abs() < OFFSET_TOLERANCE {
1259 return true;
1260 }
1261 position += part.quarter_length();
1262 }
1263 false
1264 }
1265
1266 pub fn accent_weight(&self, offset: FloatType) -> Result<FloatType> {
1270 self.accent_weight_with(offset, false, false)
1271 }
1272
1273 pub fn accent_weight_at_level(
1276 &self,
1277 offset: FloatType,
1278 level: usize,
1279 force_position_match: bool,
1280 permit_meter_modulus: bool,
1281 ) -> Result<FloatType> {
1282 let bar = self.bar_quarter_length();
1283 let offset = if permit_meter_modulus {
1284 offset.rem_euclid(bar)
1285 } else {
1286 offset
1287 };
1288 let offset = offset_within(offset, bar)?;
1289 let terminals = self.accent_sequence.level_list(level, true);
1290 if terminals.len() <= 1 {
1291 return self.accent_weight_with(offset, force_position_match, permit_meter_modulus);
1292 }
1293 let spans = self.accent_sequence.level_span(level);
1294 let smallest = terminals
1295 .iter()
1296 .map(MeterTerminal::weight)
1297 .fold(FloatType::INFINITY, FloatType::min);
1298 for (index, (start, end)) in spans.iter().enumerate() {
1299 if offset < end - OFFSET_TOLERANCE {
1300 if force_position_match && (offset - start).abs() >= OFFSET_TOLERANCE {
1301 return Ok(smallest * 0.5);
1302 }
1303 return Ok(terminals[index].weight());
1304 }
1305 }
1306 Ok(terminals[terminals.len() - 1].weight())
1307 }
1308
1309 pub fn set_accent_weight(&mut self, weights: &[FloatType], level: usize) -> Result<()> {
1312 self.accent_sequence = self.accent_sequence.with_level_weight(level, weights)?;
1313 Ok(())
1314 }
1315
1316 pub fn accent_weight_with(
1321 &self,
1322 offset: FloatType,
1323 force_position_match: bool,
1324 permit_meter_modulus: bool,
1325 ) -> Result<FloatType> {
1326 let bar = self.bar_quarter_length();
1327 let offset = if permit_meter_modulus {
1328 offset.rem_euclid(bar)
1329 } else {
1330 offset
1331 };
1332 let offset = offset_within(offset, bar)?;
1333 let weights = self.accent_weights();
1334 let partition = self.accent_partition_quarter_length();
1335 let index = ((offset + OFFSET_TOLERANCE) / partition).floor() as usize;
1336 let index = index.min(weights.len() - 1);
1337 if force_position_match
1338 && (offset - index as FloatType * partition).abs() >= OFFSET_TOLERANCE
1339 {
1340 let smallest = weights
1341 .iter()
1342 .copied()
1343 .fold(FloatType::INFINITY, FloatType::min);
1344 return Ok(smallest * 0.5);
1345 }
1346 Ok(weights[index])
1347 }
1348
1349 pub fn average_beat_strength(&self, stream: &crate::Stream, notes_only: bool) -> FloatType {
1356 let bar = self.bar_quarter_length();
1357 let offsets: Vec<FloatType> = stream
1358 .events()
1359 .iter()
1360 .filter(|event| !notes_only || crate::makenotation::is_not_rest(event.element()))
1361 .map(crate::stream::StreamEvent::offset)
1362 .collect();
1363 if offsets.is_empty() {
1364 return 0.0;
1365 }
1366 let total: FloatType = offsets
1367 .iter()
1368 .map(|offset| {
1369 self.accent_weight_with(offset.rem_euclid(bar), true, false)
1370 .unwrap_or(0.0)
1371 })
1372 .sum();
1373 total / offsets.len() as FloatType
1374 }
1375
1376 pub fn beat_depth(&self, offset: FloatType) -> Result<u8> {
1382 let bar = self.bar_quarter_length();
1383 if offset.is_nan() || offset < 0.0 || offset >= bar {
1384 return Err(Error::Meter(format!(
1385 "cannot access from qLenPos {}",
1386 offset_repr(offset)
1387 )));
1388 }
1389 let depth = self
1390 .beat_sequence
1391 .offset_to_depth(offset, crate::meter::OffsetAlign::Quantize)?;
1392 Ok(depth as u8)
1393 }
1394}
1395
1396#[must_use]
1403pub fn snapped_fraction(value: FloatType) -> FloatType {
1404 const GRAIN: FloatType = 1e-2;
1405 let thirds = [1.0 / 3.0, 2.0 / 3.0, 1.0 / 6.0, 5.0 / 6.0];
1406 for candidate in thirds {
1407 if (value - candidate).abs() <= GRAIN {
1408 return candidate;
1409 }
1410 }
1411 value
1412}
1413
1414#[derive(Clone, Copy, Debug, PartialEq)]
1421pub struct BeamedNote {
1422 pub offset: FloatType,
1424 pub quarter_length: FloatType,
1426 pub duration_type: DurationType,
1428 pub sounds: bool,
1430}
1431
1432impl TimeSignature {
1433 pub fn beams_for(
1444 &self,
1445 notes: &[BeamedNote],
1446 measure_start_offset: FloatType,
1447 measure_padding: Option<FloatType>,
1448 ) -> Result<Vec<Option<Beams>>> {
1449 if notes.len() <= 1 {
1450 return Ok(notes.iter().map(|_| None).collect());
1451 }
1452
1453 let mut beamed: Vec<Option<Beams>> = notes
1456 .iter()
1457 .map(|note| Beams::naive(Some(note.duration_type), note.sounds))
1458 .collect();
1459 crate::notation::remove_sandwiched_unbeamables(&mut beamed);
1460
1461 for depth in 0..Beams::LEVELS {
1462 for index in 0..notes.len() {
1463 self.fix_one_beam(
1464 &mut beamed,
1465 notes,
1466 index,
1467 depth,
1468 measure_start_offset,
1469 measure_padding,
1470 )?;
1471 }
1472 }
1473
1474 crate::notation::sanitize_partial_beams(&mut beamed);
1475 crate::notation::merge_connecting_partial_beams(&mut beamed);
1476 Ok(beamed)
1477 }
1478
1479 fn fix_one_beam(
1482 &self,
1483 beamed: &mut [Option<Beams>],
1484 notes: &[BeamedNote],
1485 index: usize,
1486 depth: usize,
1487 measure_start_offset: FloatType,
1488 measure_padding: Option<FloatType>,
1489 ) -> Result<()> {
1490 let beam_number = depth as u32 + 1;
1491 let carries = |beams: Option<&Beams>| {
1492 beams.is_some_and(|beams| {
1493 beams
1494 .numbers()
1495 .into_iter()
1496 .flatten()
1497 .any(|n| n == beam_number)
1498 })
1499 };
1500 if !carries(beamed[index].as_ref()) {
1501 return Ok(());
1502 }
1503
1504 let note = notes[index];
1505 let start = note.offset + measure_start_offset;
1506 let end = start + note.quarter_length;
1507 let start_next = end;
1508
1509 let is_first = index == 0;
1510 let is_last = index + 1 == notes.len();
1511
1512 let previous_is_none = is_first || beamed[index - 1].is_none();
1515 let next_is_none = is_last || beamed[index + 1].is_none();
1516 let previous_carries = !is_first && carries(beamed[index - 1].as_ref());
1517 let next_carries = !is_last && carries(beamed[index + 1].as_ref());
1518 let previous_broke = !is_first
1519 && beamed[index - 1].as_ref().is_some_and(|beams| {
1520 beams.by_number(beam_number).is_some_and(|beam| {
1521 matches!(beam.beam_type(), Some(BeamType::Stop))
1522 || (matches!(beam.beam_type(), Some(BeamType::PartialBeam))
1523 && beam.direction() == Some(crate::notation::BeamDirection::Left))
1524 })
1525 });
1526
1527 let archetype = self.beam_sequence.level(depth, true)?;
1528 let (span_start, span_end) = archetype.offset_to_span(start, false)?;
1529 let span_next_start = if next_is_none {
1530 0.0
1531 } else {
1532 archetype.offset_to_span(start_next, false)?.0
1533 };
1534
1535 let same = |a: FloatType, b: FloatType| (a - b).abs() < OFFSET_TOLERANCE;
1536
1537 if same(end, span_end)
1539 && (same(start, span_start) || (previous_is_none && beam_number == 1))
1540 {
1541 beamed[index] = None;
1542 return Ok(());
1543 }
1544
1545 let ends_the_measure = is_last && measure_padding.is_none_or(|padding| padding == 0.0);
1546
1547 let (beam_type, direction) = if is_first && measure_start_offset == 0.0 {
1548 if next_is_none || !next_carries {
1549 (
1550 BeamType::PartialBeam,
1551 Some(crate::notation::BeamDirection::Right),
1552 )
1553 } else {
1554 (BeamType::Start, None)
1555 }
1556 } else if ends_the_measure {
1557 if previous_is_none || !previous_carries {
1558 (
1559 BeamType::PartialBeam,
1560 Some(crate::notation::BeamDirection::Left),
1561 )
1562 } else {
1563 (BeamType::Stop, None)
1564 }
1565 } else if previous_is_none || !previous_carries {
1566 if beam_number == 1 && next_is_none {
1568 beamed[index] = None;
1569 return Ok(());
1570 } else if (next_is_none && beam_number > 1) || start_next >= span_end - OFFSET_TOLERANCE
1571 {
1572 (
1576 BeamType::PartialBeam,
1577 Some(crate::notation::BeamDirection::Left),
1578 )
1579 } else if next_is_none || !next_carries {
1580 (
1581 BeamType::PartialBeam,
1582 Some(crate::notation::BeamDirection::Right),
1583 )
1584 } else {
1585 (BeamType::Start, None)
1586 }
1587 } else if previous_broke {
1588 if next_is_none {
1589 (
1590 BeamType::PartialBeam,
1591 Some(crate::notation::BeamDirection::Left),
1592 )
1593 } else if next_carries {
1594 (BeamType::Start, None)
1595 } else {
1596 (
1597 BeamType::PartialBeam,
1598 Some(crate::notation::BeamDirection::Right),
1599 )
1600 }
1601 } else if next_is_none || !next_carries {
1602 (BeamType::Stop, None)
1603 } else if start_next < span_end - OFFSET_TOLERANCE {
1604 (BeamType::Continue, None)
1605 } else if start_next >= span_next_start - OFFSET_TOLERANCE {
1606 (BeamType::Stop, None)
1607 } else {
1608 return Err(Error::Meter("cannot match beamType".to_string()));
1609 };
1610
1611 if let Some(beams) = beamed[index].as_mut() {
1612 beams.set_by_number(beam_number, beam_type, direction)?;
1613 }
1614 Ok(())
1615 }
1616}
1617
1618fn whole_bar(
1622 numerator: UnsignedIntegerType,
1623 denominator: UnsignedIntegerType,
1624) -> Result<MeterTerminal> {
1625 let whole = format!("{numerator}/{denominator}");
1626 MeterTerminal::new(numerator, denominator)?.partitioned_by_parts(&[whole.as_str()])
1627}
1628
1629fn division_word(ratio: &str) -> Option<&'static str> {
1633 let lower = ratio.to_ascii_lowercase();
1634 if lower.contains("slow") {
1635 Some("slow")
1636 } else if lower.contains("fast") {
1637 Some("fast")
1638 } else {
1639 None
1640 }
1641}
1642
1643fn without_division_word(ratio: &str) -> String {
1645 let mut out = ratio.to_string();
1646 for word in ["slow", "fast"] {
1647 while let Some(at) = out.to_ascii_lowercase().find(word) {
1648 out.replace_range(at..at + word.len(), " ");
1649 }
1650 }
1651 out
1652}
1653
1654fn favor_compound(
1660 numerator: UnsignedIntegerType,
1661 denominator: UnsignedIntegerType,
1662 word: Option<&str>,
1663) -> bool {
1664 match word {
1665 Some("slow") => false,
1666 Some("fast") => true,
1667 _ => !(numerator == 3 && denominator < 8),
1668 }
1669}
1670
1671pub fn best_time_signature(measure: &crate::Stream) -> Result<TimeSignature> {
1683 let elements = measure.recurse();
1684 let sum = elements
1685 .iter()
1686 .filter(|(_, element)| element.as_stream().is_none())
1687 .map(|(offset, element)| offset + element.quarter_length())
1688 .fold(0.0, FloatType::max);
1689
1690 let mut min_dur = 4.0;
1692 let mut min_dots = 0;
1693 for (_, element) in &elements {
1694 let sounds =
1695 element.is_note_or_chord() || matches!(element, crate::stream::StreamElement::Rest(_));
1696 let quarter_length = element.quarter_length();
1697 if sounds && quarter_length != 0.0 && quarter_length < min_dur && is_binary(quarter_length)
1698 {
1699 min_dur = quarter_length;
1700 min_dots = element.duration().map_or(0, Duration::dots);
1701 }
1702 }
1703
1704 let (numerator, denominator) = if is_binary(sum) {
1705 binary_signature(sum, min_dur, min_dots)?
1706 } else {
1707 let (numerator, denominator) = crate::duration::limited_fraction(sum, 65535)
1708 .ok_or_else(|| Error::Meter("Cannot find a good match for this measure".to_string()))?;
1709 (
1710 numerator as UnsignedIntegerType,
1711 denominator as UnsignedIntegerType,
1712 )
1713 };
1714 let (numerator, denominator) = simplified_signature(numerator, denominator)
1715 .ok_or_else(|| Error::Meter("Cannot find a good match for this measure".to_string()))?;
1716
1717 let strength =
1718 |ratio: (UnsignedIntegerType, UnsignedIntegerType)| -> Result<(TimeSignature, FloatType)> {
1719 let signature = TimeSignature::new(ratio.0, ratio.1)?;
1720 let strength = signature.average_beat_strength(measure, true);
1721 Ok((signature, strength))
1722 };
1723 match (numerator, denominator) {
1724 (3, 4) => {
1726 let (three_four, simple) = strength((3, 4))?;
1727 let (six_eight, compound) = strength((6, 8))?;
1728 Ok(if simple <= compound {
1729 six_eight
1730 } else {
1731 three_four
1732 })
1733 }
1734 (6, 4) => {
1736 let (six_four, first) = strength((6, 4))?;
1737 let (twelve_eight, second) = strength((12, 8))?;
1738 let (three_two, third) = strength((3, 2))?;
1739 let most = first.max(second).max(third);
1740 Ok(if most == first {
1741 six_four
1742 } else if most == third {
1743 three_two
1744 } else {
1745 twelve_eight
1746 })
1747 }
1748 _ => TimeSignature::new(numerator, denominator),
1749 }
1750}
1751
1752fn binary_signature(
1757 sum: FloatType,
1758 min_dur: FloatType,
1759 min_dots: u32,
1760) -> Result<(UnsignedIntegerType, UnsignedIntegerType)> {
1761 let no_match = || Error::Meter("Cannot find a good match for this measure".to_string());
1762 let smallest_type = crate::duration::DurationType::from_music21_name("128th")
1763 .expect("music21 names the 128th note");
1764 let limit = smallest_type.quarter_length();
1765 let dot_multiplier =
1766 FloatType::from(2u32.pow(min_dots + 1) - 1) / FloatType::from(2u32.pow(min_dots));
1767
1768 let mut min_test = min_dur;
1769 let mut remaining = 10;
1770 while remaining > 0 {
1771 let parts = sum / min_test;
1772 if parts.floor() == parts || min_test <= limit {
1773 break;
1774 }
1775 min_test /= 2.0 * dot_multiplier;
1776 remaining -= 1;
1777 }
1778 let mut remaining = 10;
1779 while remaining > 0 {
1780 if min_test < limit {
1781 min_test = limit;
1782 break;
1783 }
1784 let (duration_type, matched) =
1785 crate::duration::quarter_length_to_closest_type(min_test).map_err(|_| no_match())?;
1786 if matched || duration_type == smallest_type {
1787 break;
1788 }
1789 min_test /= 2.0 * dot_multiplier;
1790 remaining -= 1;
1791 }
1792 let (duration_type, matched) =
1793 crate::duration::quarter_length_to_closest_type(min_test).map_err(|_| no_match())?;
1794 if !matched {
1795 return Err(Error::Meter(format!(
1796 "cannot find a type for denominator {min_test}"
1797 )));
1798 }
1799 let mut float_denominator = duration_type.type_number().unwrap_or(1.0);
1800 let mut multiplier = 1.0;
1801 let mut numerator_float = 0.0;
1802 while remaining > 0 {
1803 numerator_float = multiplier * sum / min_test;
1804 if numerator_float == numerator_float.floor() {
1805 break;
1806 }
1807 multiplier *= 2.0;
1808 remaining -= 1;
1809 }
1810 float_denominator *= multiplier;
1811 let numerator = numerator_float as UnsignedIntegerType;
1812 let denominator = float_denominator as UnsignedIntegerType;
1813 let divisor = num::integer::gcd(numerator, denominator).max(1);
1814 Ok((numerator / divisor, denominator / divisor))
1815}
1816
1817fn simplified_signature(
1821 numerator: UnsignedIntegerType,
1822 denominator: UnsignedIntegerType,
1823) -> Option<(UnsignedIntegerType, UnsignedIntegerType)> {
1824 if numerator == denominator && !matches!(numerator, 2 | 4) {
1825 Some((4, 4))
1826 } else if numerator != denominator && denominator == 1 {
1827 Some((numerator.checked_mul(4)?, 4))
1828 } else if numerator != denominator && denominator == 2 {
1829 Some((numerator.checked_mul(2)?, 4))
1830 } else {
1831 Some((numerator, denominator))
1832 }
1833}
1834
1835fn is_binary(quarter_length: FloatType) -> bool {
1839 (quarter_length * 32768.0).fract() == 0.0
1840}
1841
1842fn split_span(
1846 count: UnsignedIntegerType,
1847 unit: UnsignedIntegerType,
1848 parts: UnsignedIntegerType,
1849) -> Option<(UnsignedIntegerType, UnsignedIntegerType)> {
1850 if count.is_multiple_of(parts) {
1851 Some((count / parts, unit))
1852 } else {
1853 Some((count, unit.checked_mul(parts)?))
1854 }
1855}
1856
1857fn default_division(count: UnsignedIntegerType, unit: UnsignedIntegerType) -> UnsignedIntegerType {
1861 let _ = unit;
1862 if count > 3 && count.is_multiple_of(3) {
1863 count / 3
1864 } else if count == 1 || count.is_multiple_of(2) {
1865 2
1866 } else {
1867 count
1868 }
1869}
1870
1871pub(crate) fn offset_repr(offset: FloatType) -> String {
1874 if offset.fract() == 0.0 {
1875 format!("{offset:.1}")
1876 } else {
1877 offset.to_string()
1878 }
1879}
1880
1881fn offset_within(offset: FloatType, length: FloatType) -> Result<FloatType> {
1884 if offset.is_nan() || offset < 0.0 || offset >= length {
1885 return Err(Error::Meter(format!(
1886 "cannot access from qLenPos {} where total duration is {}",
1887 offset_repr(offset),
1888 offset_repr(length)
1889 )));
1890 }
1891 Ok(offset)
1892}
1893
1894#[cfg(test)]
1895mod tests {
1896
1897 #[test]
1898 fn a_meter_counting_more_beats_than_the_limit_is_refused() {
1899 use super::{MAX_NUMERATOR, TimeSignature};
1900
1901 assert!(TimeSignature::new(MAX_NUMERATOR, 4).is_ok());
1902 assert!(TimeSignature::new(MAX_NUMERATOR + 1, 4).is_err());
1903 assert!(TimeSignature::from_ratio_string("99999999/1").is_err());
1904 assert!(TimeSignature::from_ratio_string("6000/4+6000/4").is_err());
1905 assert!(TimeSignature::from_ratio_string("4000000000/4+4000000000/4").is_err());
1906 assert!(TimeSignature::from_ratio_string("4000000000/4+1/8").is_err());
1907 }
1908
1909 #[test]
1910 fn a_meter_with_a_part_over_nought_is_refused() {
1911 use super::TimeSignature;
1912
1913 assert!(TimeSignature::from_ratio_string("3/8+2/0").is_err());
1914 assert!(TimeSignature::from_ratio_string("3/0+2/8").is_err());
1915 }
1916
1917 #[test]
1918 fn a_meter_of_the_shortest_units_is_one_accent() {
1919 use super::TimeSignature;
1920
1921 let signature = TimeSignature::new(1, 1 << 31).unwrap();
1922 assert_eq!(signature.accent_weights(), [1.0]);
1923 }
1924
1925 #[test]
1926 fn an_offset_past_the_bar_wraps_from_where_the_meter_stands() {
1927 let three = TimeSignature::from_ratio_string("3/4").unwrap();
1929 assert_eq!(three.offset_in_bar(2.0, 0.0), 2.0);
1930 assert_eq!(three.offset_in_bar(4.0, 0.0), 1.0);
1931 let five = TimeSignature::from_ratio_string("5/4").unwrap();
1932 assert_eq!(five.offset_in_bar(3.0, 0.0), 3.0);
1933 assert_eq!(five.offset_in_bar(5.0, 0.0), 0.0);
1934
1935 assert_eq!(three.offset_in_bar(1.5, 1.0), 1.5);
1937 assert_eq!(three.offset_in_bar(2.5, 1.0), 1.5);
1938 }
1939
1940 #[test]
1941 fn beat_divisions_are_read_off_the_beats_as_music21_reads_them() {
1942 let cases: [(&str, &[FloatType], &[FloatType]); 9] = [
1945 ("2+3/8", &[0.5, 0.5], &[0.25; 4]),
1946 ("3+3+2/8", &[0.5; 3], &[0.25; 6]),
1947 ("5/8", &[0.25, 0.25], &[0.125; 4]),
1948 ("7/8", &[0.25, 0.25], &[0.125; 4]),
1949 ("4/4", &[0.5, 0.5], &[0.25; 4]),
1950 ("6/8", &[0.5; 3], &[0.25; 6]),
1951 ("5/4", &[0.5, 0.5], &[0.25; 4]),
1952 ("9/8", &[0.5; 3], &[0.25; 6]),
1953 ("3/2", &[1.0, 1.0], &[0.5; 4]),
1954 ];
1955 for (ratio, divisions, subdivisions) in cases {
1956 let meter = TimeSignature::from_ratio_string(ratio).unwrap();
1957 let lengths = |durations: Vec<Duration>| -> Vec<FloatType> {
1958 durations.iter().map(Duration::quarter_length).collect()
1959 };
1960 assert_eq!(
1961 lengths(meter.beat_division_durations().unwrap()),
1962 divisions,
1963 "{ratio}"
1964 );
1965 assert_eq!(
1966 lengths(meter.beat_sub_division_durations().unwrap()),
1967 subdivisions,
1968 "{ratio}"
1969 );
1970 }
1971
1972 for ratio in ["3/4+3/8", "2/4+3/8", "1/4+1/8"] {
1974 let meter = TimeSignature::from_ratio_string(ratio).unwrap();
1975 assert!(meter.beat_division_durations().is_err(), "{ratio}");
1976 }
1977 }
1978 #[test]
1982 fn a_meter_is_read_as_music21_writes_it() {
1983 use crate::meter::TimeSignature;
1984
1985 let plain = TimeSignature::from_ratio_string("6/8").unwrap();
1989 let slow = TimeSignature::from_ratio_string("slow 6/8").unwrap();
1990 assert_eq!(TimeSignature::from_ratio_string("fast 6/8").unwrap(), plain);
1991 assert_ne!(slow, plain);
1992 assert_eq!(
1993 (slow.numerator(), slow.denominator()),
1994 (plain.numerator(), plain.denominator())
1995 );
1996 assert_eq!(plain.beat_sequence().len(), 2);
1997 assert_eq!(slow.beat_sequence().len(), 6);
1998
1999 let additive = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2000 assert_eq!((additive.numerator(), additive.denominator()), (5, 8));
2001 assert_eq!(
2002 TimeSignature::parts("3/8+2/8").unwrap(),
2003 vec![(3, 8), (2, 8)]
2004 );
2005 assert_eq!(TimeSignature::parts("3+2/8").unwrap(), vec![(3, 8), (2, 8)]);
2007 assert_eq!(
2008 TimeSignature::parts("3+2+5/8+3/4").unwrap(),
2009 vec![(3, 8), (2, 8), (5, 8), (3, 4)]
2010 );
2011 let mixed = TimeSignature::from_ratio_string("3/8+1/4").unwrap();
2013 assert_eq!((mixed.numerator(), mixed.denominator()), (5, 8));
2014
2015 assert!(TimeSignature::parts("3+2+5").is_err());
2016 assert!(TimeSignature::from_ratio_string("").is_err());
2017 assert!(TimeSignature::from_ratio_string("3.0/4.0").is_err());
2018 }
2019
2020 #[test]
2021 fn a_summed_numerator_is_beamed_as_it_was_written() {
2022 use crate::meter::TimeSignature;
2023
2024 let expected = [
2029 ("3+2/8", true, "{3/8+2/8}"),
2030 ("3/8+2/8", false, "{2/8+3/8}"),
2031 ("2+3/8", true, "{2/8+3/8}"),
2032 ("3+2+2/8", true, "{3/8+2/8+2/8}"),
2033 ("3/8+2/8+2/8", false, "{2/8+2/8+3/8}"),
2034 ("2+2+3/16", true, "{2/16+2/16+3/16}"),
2035 ("3/16+2/16", false, "{3/16+2/16}"),
2036 ("6/8", false, "{3/8+3/8}"),
2037 ];
2038 for (written, summed, beams) in expected {
2039 let signature = TimeSignature::from_ratio_string(written).unwrap();
2040 assert_eq!(signature.summed_numerator(), summed, "{written}");
2041 assert_eq!(signature.beam_sequence().to_string(), beams, "{written}");
2042 }
2043
2044 let mut signature = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2045 signature.set_summed_numerator(true);
2046 assert!(signature.summed_numerator());
2047 assert!(signature.display_sequence().summed_numerator());
2048 }
2049
2050 #[test]
2051 fn the_sequences_are_partitioned_as_music21_partitions_them() {
2052 use crate::meter::TimeSignature;
2053
2054 let expected = [
2057 ("1/4", "{1/4}", "{1/4}"),
2058 ("2/4", "{{1/8+1/8}+{1/8+1/8}}", "{{1/8+1/8}+{1/8+1/8}}"),
2059 (
2060 "3/4",
2061 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
2062 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
2063 ),
2064 (
2065 "4/4",
2066 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
2067 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
2068 ),
2069 (
2070 "5/4",
2071 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
2072 "{{1/4+1/4}+{1/4+1/4+1/4}}",
2073 ),
2074 ("6/4", "{{1/4+1/4+1/4}+{1/4+1/4+1/4}}", "{3/4+3/4}"),
2075 ("2/2", "{{1/4+1/4}+{1/4+1/4}}", "{1/2+1/2}"),
2076 ("3/2", "{{1/4+1/4}+{1/4+1/4}+{1/4+1/4}}", "{1/2+1/2+1/2}"),
2077 ("3/8", "{3/8}", "{3/8}"),
2078 (
2079 "5/8",
2080 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
2081 "{2/8+3/8}",
2082 ),
2083 ("6/8", "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}", "{3/8+3/8}"),
2084 (
2085 "9/8",
2086 "{{1/8+1/8+1/8}+{1/8+1/8+1/8}+{1/8+1/8+1/8}}",
2087 "{3/8+3/8+3/8}",
2088 ),
2089 (
2090 "12/8",
2091 "{{1/8+1/8+1/8}+{1/8+1/8+1/8}+{1/8+1/8+1/8}+{1/8+1/8+1/8}}",
2092 "{3/8+3/8+3/8+3/8}",
2093 ),
2094 (
2095 "5/16",
2096 "{{1/32+1/32}+{1/32+1/32}+{1/32+1/32}+{1/32+1/32}+{1/32+1/32}}",
2097 "{5/16}",
2098 ),
2099 (
2100 "slow 6/8",
2101 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
2102 "{3/8+3/8}",
2103 ),
2104 ("fast 6/8", "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}", "{3/8+3/8}"),
2105 ("6/8 fast", "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}", "{3/8+3/8}"),
2106 (
2107 "6/8 Slow",
2108 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
2109 "{3/8+3/8}",
2110 ),
2111 ("3/8+2/8", "{{1/8+1/8+1/8}+{1/8+1/8}}", "{2/8+3/8}"),
2112 ("2/4+3/8", "{{1/4+1/4}+{1/8+1/8+1/8}}", "{2/8+2/8+3/8}"),
2113 ];
2114 for (ratio, beats, beams) in expected {
2115 let signature = TimeSignature::from_ratio_string(ratio).unwrap();
2116 assert_eq!(
2117 signature.beat_sequence().to_string(),
2118 beats,
2119 "beats of {ratio}"
2120 );
2121 assert_eq!(
2122 signature.beam_sequence().to_string(),
2123 beams,
2124 "beams of {ratio}"
2125 );
2126 }
2127 }
2128
2129 #[test]
2131 fn accent_weights_and_beat_depths_follow_the_default_hierarchy() {
2132 let weights = |ratio: &str| {
2133 TimeSignature::from_ratio_string(ratio)
2134 .unwrap()
2135 .accent_weights()
2136 };
2137 assert_eq!(
2138 weights("4/4"),
2139 [1.0, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125]
2140 );
2141 assert_eq!(
2142 weights("6/8"),
2143 [
2144 1.0, 0.125, 0.25, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125, 0.25, 0.125
2145 ]
2146 );
2147 assert_eq!(
2148 weights("12/8"),
2149 [
2150 1.0, 0.125, 0.125, 0.25, 0.125, 0.125, 0.5, 0.125, 0.125, 0.25, 0.125, 0.125
2151 ]
2152 );
2153 assert_eq!(weights("3/4").len(), 12);
2154 assert_eq!(weights("24/8").len(), 24);
2155 assert_eq!(
2156 TimeSignature::new(1, 4)
2157 .unwrap()
2158 .accent_partition_quarter_length(),
2159 0.125
2160 );
2161
2162 let three_four = TimeSignature::new(3, 4).unwrap();
2163 let read: Vec<FloatType> = (0..3)
2164 .map(|beat| three_four.accent_weight(FloatType::from(beat)).unwrap())
2165 .collect();
2166 assert_eq!(read, [1.0, 0.5, 0.5]);
2167 let beyond = three_four.accent_weight(3.0).unwrap_err().to_string();
2168 assert!(beyond.ends_with("cannot access from qLenPos 3.0 where total duration is 3.0"));
2169 assert_eq!(weights("16/1"), [1.0]);
2172 assert_eq!(weights("1/32"), [1.0]);
2173 assert_eq!(
2174 TimeSignature::new(24, 4)
2175 .unwrap()
2176 .accent_partition_quarter_length(),
2177 1.0
2178 );
2179 assert_eq!(
2180 three_four.accent_weight_with(4.0, false, true).unwrap(),
2181 0.5
2182 );
2183 assert_eq!(
2184 three_four.accent_weight_with(0.1, true, false).unwrap(),
2185 0.0625
2186 );
2187 assert_eq!(
2188 three_four.accent_weight_with(0.1, false, false).unwrap(),
2189 1.0
2190 );
2191 assert!(three_four.accent(2.0));
2192 assert!(!three_four.accent(0.1));
2193 assert!(!three_four.accent(3.0));
2194
2195 assert_eq!(three_four.beat_depth(0.0).unwrap(), 2);
2196 assert_eq!(three_four.beat_depth(0.25).unwrap(), 2);
2197 assert_eq!(three_four.beat_depth(0.5).unwrap(), 1);
2198 assert_eq!(three_four.beat_depth(1.0).unwrap(), 2);
2199 assert!(three_four.beat_depth(3.0).is_err());
2200 assert_eq!(
2201 TimeSignature::new(3, 8).unwrap().beat_depth(0.5).unwrap(),
2202 1
2203 );
2204 assert_eq!(
2205 TimeSignature::new(6, 8).unwrap().beat_depth(1.0).unwrap(),
2206 1
2207 );
2208 assert_eq!(
2209 TimeSignature::new(6, 8).unwrap().beat_depth(1.5).unwrap(),
2210 2
2211 );
2212 }
2213
2214 #[test]
2216 fn the_best_time_signature_fits_what_the_measure_holds() {
2217 use crate::{Note, Pitch, Stream};
2218
2219 let measure = |lengths: &[FloatType]| {
2220 let mut stream = Stream::new();
2221 for length in lengths {
2222 let mut note = Note::from_pitch(Pitch::from_name("C4").unwrap());
2223 note.set_duration(Duration::new(*length).unwrap());
2224 stream.push(note);
2225 }
2226 stream
2227 };
2228 let best = |lengths: &[FloatType]| {
2229 best_time_signature(&measure(lengths))
2230 .unwrap()
2231 .ratio_string()
2232 };
2233 assert_eq!(best(&[1.0, 1.0, 0.5, 0.5]), "3/4");
2234 assert_eq!(best(&[0.75, 0.25, 0.5, 0.75, 0.25, 0.5]), "6/8");
2235 assert_eq!(best(&[2.0, 2.0, 2.0]), "3/2");
2236 assert_eq!(best(&[0.75, 0.25, 0.5, 0.75, 0.25, 0.5, 1.5, 1.5]), "12/8");
2237 assert_eq!(best(&[1.0, 2.0, 1.0, 2.0]), "6/4");
2238 assert_eq!(best(&[1.0, 0.375]), "11/32");
2239 assert_eq!(best(&[3.5, 5.5]), "9/4");
2240 assert_eq!(best(&[1.0, 1.0, 1.0, 1.0]), "4/4");
2241 assert_eq!(best(&[4.0]), "4/4");
2242 assert_eq!(best(&[1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0, 1.0]), "2/4");
2244 }
2245
2246 #[test]
2247 fn a_measure_too_long_for_any_signature_has_none() {
2248 use crate::{Note, Pitch, Stream};
2249
2250 let mut note = Note::from_pitch(Pitch::from_name("C4").unwrap());
2251 note.set_duration(Duration::new(4_294_967_296.0).unwrap());
2252 let mut measure = Stream::new();
2253 measure.push(note);
2254 assert!(best_time_signature(&measure).is_err());
2255 }
2256
2257 #[test]
2260 fn beat_strength_is_averaged_over_a_stream() {
2261 use crate::{Note, Pitch, Stream};
2262
2263 let mut stream = Stream::new();
2264 for (name, length) in [("C4", 1.0), ("D4", 1.0), ("E4", 0.5), ("F4", 0.5)] {
2265 let mut note = Note::from_pitch(Pitch::from_name(name).unwrap());
2266 note.set_duration(Duration::new(length).unwrap());
2267 stream.push(note);
2268 }
2269 assert_eq!(
2270 TimeSignature::new(6, 8)
2271 .unwrap()
2272 .average_beat_strength(&stream, true),
2273 0.4375
2274 );
2275 assert_eq!(
2276 TimeSignature::new(3, 4)
2277 .unwrap()
2278 .average_beat_strength(&stream, true),
2279 0.5625
2280 );
2281 stream.insert(0.0, TimeSignature::new(3, 4).unwrap());
2282 stream.insert(0.0, TimeSignature::new(6, 8).unwrap());
2283 assert_eq!(
2284 TimeSignature::new(6, 8)
2285 .unwrap()
2286 .average_beat_strength(&stream, false),
2287 0.625
2288 );
2289 assert_eq!(
2290 TimeSignature::new(4, 4)
2291 .unwrap()
2292 .average_beat_strength(&Stream::new(), true),
2293 0.0
2294 );
2295 }
2296
2297 #[test]
2302 fn a_measure_weighs_its_own_notes_and_not_its_voices() {
2303 use crate::stream::StreamKind;
2304 use crate::{Note, Pitch, Stream};
2305
2306 let mut voice = Stream::with_kind(StreamKind::Voice);
2307 for (name, length) in [("C5", 2.0), ("B4", 1.0)] {
2308 let mut note = Note::from_pitch(Pitch::from_name(name).unwrap());
2309 note.set_duration(Duration::new(length).unwrap());
2310 voice.push(note);
2311 }
2312 let mut plain = voice.clone();
2313 plain.set_kind(StreamKind::Measure);
2314 let mut voiced = Stream::with_kind(StreamKind::Measure);
2315 voiced.insert(0.0, voice);
2316
2317 let three_four = TimeSignature::new(3, 4).unwrap();
2318 assert_eq!(three_four.average_beat_strength(&voiced, true), 0.0);
2319 assert_eq!(best_time_signature(&plain).unwrap().ratio_string(), "3/4");
2320 assert_eq!(best_time_signature(&voiced).unwrap().ratio_string(), "6/8");
2321 }
2322
2323 #[test]
2324 fn a_signature_reports_its_two_numbers_and_its_bar() {
2325 let six_eight = TimeSignature::new(6, 8).unwrap();
2326 assert_eq!((six_eight.numerator(), six_eight.denominator()), (6, 8));
2327 assert_eq!(six_eight.bar_duration().quarter_length(), 3.0);
2328 }
2329
2330 #[test]
2333 fn beat_progress_is_read_through_op_frac() {
2334 let six_eight = TimeSignature::new(6, 8).unwrap();
2335 let (beat, progress) = six_eight.beat_progress(5.0 / 3.0).unwrap();
2336 assert_eq!(beat, 2);
2337 assert_eq!(progress, crate::makenotation::op_frac(1.0 / 6.0));
2338 }
2339
2340 #[test]
2341 fn a_written_bar_length_stands_over_the_meters_own() {
2342 let mut three_eight = TimeSignature::new(3, 8).unwrap();
2343 three_eight.set_bar_duration(Some(Duration::new(1.75).unwrap()));
2344 assert_eq!(three_eight.bar_duration().quarter_length(), 1.75);
2345 assert_eq!(three_eight.bar_quarter_length(), 1.5);
2347
2348 three_eight.set_bar_duration(None);
2349 assert_eq!(three_eight.bar_duration().quarter_length(), 1.5);
2350 }
2351
2352 #[test]
2353 fn division_helpers_match_music21() {
2354 let quarter_lengths = |durations: Vec<Duration>| -> Vec<FloatType> {
2355 durations.into_iter().map(|d| d.quarter_length()).collect()
2356 };
2357 let cases: [(&str, FloatType, &str, &[FloatType], usize); 7] = [
2358 ("4/4", 1.0, "Simple", &[0.5, 0.5], 4),
2359 ("6/8", 0.5, "Compound", &[0.5, 0.5, 0.5], 6),
2360 ("2/2", 2.0, "Simple", &[1.0, 1.0], 4),
2361 ("3/8", 0.5, "Other", &[1.5], 2),
2362 ("7/8", 0.5, "Simple", &[0.25, 0.25], 4),
2363 ("12/16", 0.25, "Compound", &[0.25, 0.25, 0.25], 6),
2364 ("1/4", 1.0, "Other", &[1.0], 2),
2365 ];
2366 for (ratio, beat_to_quarter, division_name, divisions, sub_divisions) in cases {
2367 let ts = ts(ratio);
2368 assert_eq!(
2369 ts.beat_length_to_quarter_length_ratio(),
2370 beat_to_quarter,
2371 "{ratio}"
2372 );
2373 assert_eq!(
2374 ts.quarter_length_to_beat_length_ratio(),
2375 1.0 / beat_to_quarter,
2376 "{ratio}"
2377 );
2378 assert_eq!(ts.beat_division_count_name(), division_name, "{ratio}");
2379 assert_eq!(
2380 quarter_lengths(ts.beat_division_durations().unwrap()),
2381 divisions,
2382 "{ratio}"
2383 );
2384 let subs = quarter_lengths(ts.beat_sub_division_durations().unwrap());
2385 assert_eq!(subs.len(), sub_divisions, "{ratio}");
2386 assert!(subs.iter().all(|ql| *ql == divisions[0] / 2.0), "{ratio}");
2387 }
2388 assert!(ts("4/4").ratio_equal(&ts("4/4")));
2389 assert!(!ts("4/4").ratio_equal(&ts("2/2")));
2390 }
2391
2392 #[test]
2393 fn beat_positions_match_music21() {
2394 let common = ts("4/4");
2395 let cases: [(FloatType, UnsignedIntegerType, FloatType, FloatType, &str); 7] = [
2396 (0.0, 1, 0.0, 1.0, "1"),
2397 (0.5, 1, 0.5, 1.5, "1 1/2"),
2398 (1.25, 2, 0.25, 2.25, "2 1/4"),
2399 (2.75, 3, 0.75, 3.75, "3 3/4"),
2400 (3.0, 4, 0.0, 4.0, "4"),
2401 (3.75, 4, 0.75, 4.75, "4 3/4"),
2402 (3.9, 4, 0.9, 4.9, "4 9/10"),
2403 ];
2404 for (offset, beat, progress, proportion, text) in cases {
2405 let (actual_beat, actual_progress) = common.beat_progress(offset).unwrap();
2406 assert_eq!(actual_beat, beat, "{offset}");
2407 assert!((actual_progress - progress).abs() < 1e-9, "{offset}");
2408 assert!(
2409 (common.beat_proportion(offset).unwrap() - proportion).abs() < 1e-9,
2410 "{offset}"
2411 );
2412 assert_eq!(
2413 common.beat_proportion_string(offset).unwrap(),
2414 text,
2415 "{offset}"
2416 );
2417 }
2418
2419 let compound = ts("6/8");
2420 assert_eq!(compound.beat_proportion_string(0.5).unwrap(), "1 1/3");
2421 assert_eq!(compound.beat_proportion_string(1.0).unwrap(), "1 2/3");
2422 assert_eq!(compound.beat_proportion_string(2.5).unwrap(), "2 2/3");
2423 assert!((compound.beat_proportion(2.0).unwrap() - 7.0 / 3.0).abs() < 1e-9);
2424 assert_eq!(ts("3/8").beat_proportion_string(1.0).unwrap(), "1 2/3");
2425 assert!(common.beat_progress(4.0).is_err());
2426 }
2427
2428 #[test]
2429 fn offset_from_beat_matches_music21() {
2430 let common = ts("4/4");
2431 for (beat, offset) in [
2432 (1.0, 0.0),
2433 (1.5, 0.5),
2434 (2.5, 1.5),
2435 (3.25, 2.25),
2436 (4.75, 3.75),
2437 ] {
2438 assert_eq!(common.offset_from_beat(beat).unwrap(), offset, "{beat}");
2439 }
2440 assert!(common.offset_from_beat(5.0).is_err());
2441 assert!(common.offset_from_beat(0.5).is_err());
2442 let compound = ts("6/8");
2443 assert_eq!(compound.offset_from_beat(1.5).unwrap(), 0.75);
2444 assert_eq!(compound.offset_from_beat(2.5).unwrap(), 2.25);
2445 assert!((compound.offset_from_beat(2.999).unwrap() - 2.9985).abs() < 1e-9);
2446 assert_eq!(ts("3/8").offset_from_beat(1.5).unwrap(), 0.75);
2447 }
2448
2449 #[test]
2450 fn closest_fraction_limits_the_denominator_like_python() {
2451 assert_eq!(closest_fraction(0.5, 16), (1, 2));
2452 assert_eq!(closest_fraction(1.0 / 3.0, 16), (1, 3));
2453 assert_eq!(closest_fraction(0.9, 16), (9, 10));
2454 assert_eq!(closest_fraction(0.75, 16), (3, 4));
2455 assert_eq!(closest_fraction(0.1234, 16), (1, 8));
2456 }
2457 use super::*;
2458
2459 fn ts(ratio: &str) -> TimeSignature {
2460 TimeSignature::from_ratio_string(ratio).expect("valid time signature")
2461 }
2462
2463 #[test]
2464 fn common_and_cut_time_match_their_ratios() {
2465 assert_eq!(TimeSignature::common().ratio_string(), "4/4");
2466 assert_eq!(TimeSignature::cut().ratio_string(), "2/2");
2467 assert_eq!(TimeSignature::default(), TimeSignature::common());
2468 }
2469
2470 #[test]
2471 fn bar_and_beat_lengths_follow_the_ratio() {
2472 assert_eq!(ts("4/4").bar_quarter_length(), 4.0);
2473 assert_eq!(ts("5/16").bar_quarter_length(), 1.25);
2474 assert_eq!(ts("3/8").bar_quarter_length(), 1.5);
2475 assert_eq!(ts("6/8").beat_quarter_length(), 1.5);
2476 assert_eq!(ts("4/4").beat_quarter_length(), 1.0);
2477 assert_eq!(ts("2/2").beat_duration().quarter_length(), 2.0);
2478 }
2479
2480 #[test]
2481 fn compound_meters_beat_in_threes() {
2482 for (ratio, beats, division) in [
2483 ("6/8", 2, BeatDivision::Compound),
2484 ("9/8", 3, BeatDivision::Compound),
2485 ("12/8", 4, BeatDivision::Compound),
2486 ("15/8", 5, BeatDivision::Compound),
2487 ("18/8", 6, BeatDivision::Compound),
2488 ("24/8", 8, BeatDivision::Compound),
2489 ] {
2490 assert_eq!(ts(ratio).beat_count(), beats, "{ratio}");
2491 assert_eq!(ts(ratio).beat_division(), division, "{ratio}");
2492 assert!(ts(ratio).is_compound(), "{ratio}");
2493 }
2494 }
2495
2496 #[test]
2497 fn three_is_the_one_denominator_sensitive_numerator() {
2498 assert_eq!(ts("3/2").beat_count(), 3);
2500 assert_eq!(ts("3/4").beat_count(), 3);
2501 assert_eq!(ts("3/8").beat_count(), 1);
2502 assert_eq!(ts("3/16").beat_count(), 1);
2503 assert_eq!(ts("3/32").beat_count(), 1);
2504 assert_eq!(ts("3/3").beat_count(), 3);
2507 assert_eq!(ts("3/6").beat_count(), 3);
2508 assert_eq!(ts("3/12").beat_count(), 1);
2509 for denominator in [2, 4, 8, 16] {
2511 assert_eq!(TimeSignature::new(6, denominator).unwrap().beat_count(), 2);
2512 assert_eq!(TimeSignature::new(5, denominator).unwrap().beat_count(), 5);
2513 }
2514 }
2515
2516 #[test]
2517 fn a_bar_written_in_unequal_parts_is_counted_along_its_own_beats() {
2518 use crate::meter::TimeSignature;
2519
2520 let mixed = TimeSignature::from_ratio_string("2/4+3/8").unwrap();
2523 assert_eq!(mixed.bar_quarter_length(), 3.5);
2524 assert_eq!(mixed.beat_count(), 2);
2525 assert_eq!(mixed.beat_offsets(), vec![0.0, 2.0]);
2526 assert_eq!(mixed.beat_at_offset(2.5).unwrap(), 2);
2527 assert_eq!(mixed.beat_duration_at(0.0).unwrap().quarter_length(), 2.0);
2528 assert_eq!(mixed.beat_duration_at(2.5).unwrap().quarter_length(), 1.5);
2529 assert_eq!(mixed.offset_from_beat(2.0).unwrap(), 2.0);
2530 assert_eq!(mixed.offset_from_beat(1.5).unwrap(), 1.0);
2533 assert_eq!(mixed.beat_proportion_string(2.5).unwrap(), "2 1/3");
2534 assert_eq!(mixed.beat_depth(0.0).unwrap(), 2);
2535
2536 let other = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2537 assert_eq!(other.beat_offsets(), vec![0.0, 1.5]);
2538 assert_eq!(other.offset_from_beat(2.0).unwrap(), 1.5);
2539 assert_eq!(other.offset_from_beat(1.5).unwrap(), 0.75);
2540 assert!(other.beat_proportion_string(2.5).is_err());
2542
2543 let common = TimeSignature::common();
2545 assert_eq!(common.beat_offsets(), vec![0.0, 1.0, 2.0, 3.0]);
2546 assert_eq!(common.offset_from_beat(2.0).unwrap(), 1.0);
2547 assert_eq!(common.offset_from_beat(1.5).unwrap(), 0.5);
2548 assert_eq!(common.beat_proportion_string(2.5).unwrap(), "3 1/2");
2549 }
2550
2551 #[test]
2552 fn a_bar_whose_beats_differ_answers_its_first_beat_length() {
2553 use crate::meter::TimeSignature;
2554
2555 let mixed = TimeSignature::from_ratio_string("2/4+3/8").unwrap();
2558 assert_eq!(mixed.beat_quarter_length(), 2.0);
2559 assert_eq!(mixed.beat_duration().quarter_length(), 2.0);
2560 assert!(mixed.beat_division_durations().is_err());
2561 assert_eq!(mixed.beat_division_count(), 1);
2562 assert_eq!(mixed.beat_division_count_name(), "Other");
2563 assert_eq!(mixed.classification(), "Other Duple");
2564
2565 let other = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2566 assert_eq!(other.beat_quarter_length(), 1.5);
2567 assert_eq!(other.beat_division_count(), 1);
2568 assert_eq!(other.classification(), "Other Duple");
2569
2570 assert_eq!(TimeSignature::common().beat_quarter_length(), 1.0);
2572 assert_eq!(
2573 TimeSignature::from_ratio_string("6/8")
2574 .unwrap()
2575 .beat_quarter_length(),
2576 1.5
2577 );
2578 }
2579
2580 #[test]
2581 fn weights_a_caller_sets_are_the_weights_read_back() {
2582 use crate::meter::TimeSignature;
2583
2584 let mut common = TimeSignature::common();
2585 let default = common.accent_weights();
2586 assert_eq!(default[0], 1.0);
2587
2588 common.set_accent_weight(&[0.8, 0.2], 0).unwrap();
2590 let set = common.accent_weights();
2591 assert_eq!(set.len(), default.len());
2592 assert!((set[0] - 0.8).abs() < 1e-9);
2593 assert!((set[1] - 0.2).abs() < 1e-9);
2594 assert!((set[2] - 0.8).abs() < 1e-9);
2595
2596 assert!((common.accent_weight(0.0).unwrap() - 0.8).abs() < 1e-9);
2598
2599 assert_eq!(TimeSignature::common().accent_weights()[0], 1.0);
2601 }
2602
2603 #[test]
2604 fn a_bar_can_be_counted_in_a_different_number_of_beats() {
2605 use crate::meter::TimeSignature;
2606
2607 for (ratio, count, partitioned) in [
2609 (
2610 "6/8",
2611 6,
2612 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
2613 ),
2614 ("6/8", 2, "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}"),
2615 ("4/4", 2, "{{1/4+1/4}+{1/4+1/4}}"),
2616 ("3/4", 1, "{3/4}"),
2617 (
2618 "2/4",
2619 4,
2620 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
2621 ),
2622 ] {
2623 let mut signature = TimeSignature::from_ratio_string(ratio).unwrap();
2624 signature.set_beat_count(count).unwrap();
2625 assert_eq!(
2626 signature.beat_sequence().to_string(),
2627 partitioned,
2628 "{ratio} in {count}"
2629 );
2630 assert_eq!(signature.beat_count(), count, "{ratio} in {count}");
2631 }
2632 }
2633
2634 #[test]
2635 fn a_bar_can_be_written_a_different_way_than_it_is_counted() {
2636 use crate::meter::TimeSignature;
2637
2638 let mut signature = TimeSignature::from_ratio_string("3/4").unwrap();
2641 let counted = signature.beat_sequence().to_string();
2642 signature.set_display("2/8+2/8+2/8").unwrap();
2643 assert_eq!(signature.display_sequence().to_string(), "{2/8+2/8+2/8}");
2644 assert_eq!(
2645 signature.display_sequence().partition_display(),
2646 "2/8+2/8+2/8"
2647 );
2648 assert_eq!(signature.beat_sequence().to_string(), counted);
2650 assert_eq!(signature.beat_count(), 3);
2651 assert!(signature.set_display("2/4").is_err());
2657
2658 let mut common = TimeSignature::common();
2660 common.set_beat_sequence(
2661 common
2662 .beat_sequence()
2663 .partitioned_by_count(2, true)
2664 .unwrap(),
2665 );
2666 assert_eq!(common.beat_count(), 2);
2667 }
2668
2669 #[test]
2670 fn the_divisions_a_meter_is_built_with_partition_its_beats() {
2671 use crate::meter::TimeSignature;
2672
2673 for (ratio, divisions, partitioned) in [
2675 ("3/4", 1, "{3/4}"),
2676 ("6/8", 2, "{3/8+3/8}"),
2677 ("4/4", 2, "{1/2+1/2}"),
2678 ("3/4", 3, "{1/4+1/4+1/4}"),
2679 ] {
2680 let mut signature = TimeSignature::from_ratio_string(ratio).unwrap();
2681 signature.divide_beats(divisions).unwrap();
2682 assert_eq!(
2683 signature.beat_sequence().to_string(),
2684 partitioned,
2685 "{ratio} in {divisions}"
2686 );
2687 }
2688
2689 let mut counted = TimeSignature::from_ratio_string("6/8").unwrap();
2691 counted.set_beat_count(2).unwrap();
2692 assert_eq!(
2693 counted.beat_sequence().to_string(),
2694 "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}"
2695 );
2696 }
2697
2698 #[test]
2699 fn a_fractional_beat_is_read_as_the_fraction_it_suggests() {
2700 use crate::meter::TimeSignature;
2701
2702 let compound = TimeSignature::from_ratio_string("6/8").unwrap();
2707 assert!((compound.offset_from_beat(2.33).unwrap() - 2.0).abs() < 1e-9);
2708 assert!((compound.offset_from_beat(2.66).unwrap() - 2.5).abs() < 1e-9);
2709 assert!((compound.offset_from_beat(1.66).unwrap() - 1.0).abs() < 1e-9);
2710
2711 let simple = TimeSignature::common();
2714 let third = 1.0 / 3.0;
2715 assert!((simple.offset_from_beat(2.33).unwrap() - (1.0 + third)).abs() < 1e-9);
2716 assert!((simple.offset_from_beat(2.66).unwrap() - (1.0 + 2.0 * third)).abs() < 1e-9);
2717 assert!((simple.offset_from_beat(1.66).unwrap() - (2.0 * third)).abs() < 1e-9);
2718
2719 let mut divided = TimeSignature::common();
2721 divided.divide_beats(4).unwrap();
2722 assert_eq!(divided.beat_sequence().to_string(), "{1/4+1/4+1/4+1/4}");
2723 assert_eq!(divided.accent_sequence().to_string(), "{1/4+1/4+1/4+1/4}");
2724 assert_eq!(divided.beam_sequence().to_string(), "{1/4+1/4+1/4+1/4}");
2725 assert_eq!(divided.display_sequence().to_string(), "{4/4}");
2726
2727 divided.set_accent_weight(&[0.8, 0.2], 0).unwrap();
2729 let weights = divided.accent_weights();
2730 assert_eq!(weights.len(), 4);
2731 assert!((weights[0] - 0.8).abs() < 1e-9);
2732 assert!((weights[1] - 0.2).abs() < 1e-9);
2733 }
2734
2735 #[test]
2736 fn classification_joins_division_and_count() {
2737 assert_eq!(ts("4/4").classification(), "Simple Quadruple");
2738 assert_eq!(ts("6/8").classification(), "Compound Duple");
2739 assert_eq!(ts("3/8").classification(), "Other Single");
2740 assert_eq!(ts("5/4").classification(), "Simple Quintuple");
2741 assert_eq!(ts("13/8").classification(), "Simple 13-uple");
2742 assert_eq!(ts("21/16").classification(), "Compound Septuple");
2743 }
2744
2745 #[test]
2746 fn beat_offsets_partition_the_bar() {
2747 assert_eq!(ts("4/4").beat_offsets(), [0.0, 1.0, 2.0, 3.0]);
2748 assert_eq!(ts("6/8").beat_offsets(), [0.0, 1.5]);
2749 assert_eq!(ts("5/8").beat_offsets(), [0.0, 0.5, 1.0, 1.5, 2.0]);
2750 }
2751
2752 #[test]
2753 fn beat_at_offset_is_one_based_and_bounded() {
2754 assert_eq!(ts("4/4").beat_at_offset(1.5).unwrap(), 2);
2755 assert_eq!(ts("6/8").beat_at_offset(1.5).unwrap(), 2);
2756 assert_eq!(ts("5/8").beat_at_offset(1.5).unwrap(), 4);
2757 assert_eq!(ts("4/4").beat_at_offset(0.0).unwrap(), 1);
2758 assert!(ts("4/4").beat_at_offset(4.0).is_err());
2759 assert!(ts("4/4").beat_at_offset(-0.5).is_err());
2760 assert!(ts("4/4").beat_at_offset(FloatType::NAN).is_err());
2761 }
2762
2763 #[test]
2764 fn irrational_denominators_are_accepted_as_music21_accepts_them() {
2765 let four_three = ts("4/3");
2766 assert!((four_three.bar_quarter_length() - 16.0 / 3.0).abs() < 1e-12);
2767 assert_eq!(four_three.beat_count(), 4);
2768 }
2769
2770 #[test]
2771 fn malformed_ratios_error_instead_of_panicking() {
2772 for ratio in [
2773 "", "4", "4/", "/4", "4/4/4", "x/4", "4/x", "0/4", "4/0", "-1/4",
2774 ] {
2775 assert!(
2776 TimeSignature::from_ratio_string(ratio).is_err(),
2777 "{ratio:?} should not parse"
2778 );
2779 }
2780 }
2781
2782 #[test]
2783 fn display_is_the_ratio_string() {
2784 assert_eq!(ts("7/8").to_string(), "7/8");
2785 }
2786
2787 #[test]
2788 fn a_meter_says_how_it_was_written() {
2789 use crate::meter::TimeSignature;
2790
2791 let written = TimeSignature::from_ratio_string("2/4+3/8").unwrap();
2793 assert_eq!(written.ratio_string(), "2/4+3/8");
2794 assert_eq!(written.to_string(), "2/4+3/8");
2795 assert_eq!((written.numerator(), written.denominator()), (7, 8));
2797
2798 let two_three = TimeSignature::from_ratio_string("2/8+3/8").unwrap();
2801 let three_two = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2802 assert_eq!(two_three.ratio_string(), "2/8+3/8");
2803 assert_eq!(three_two.ratio_string(), "3/8+2/8");
2804 assert_ne!(two_three, three_two);
2805 assert!(two_three.ratio_equal(&three_two));
2808
2809 let again = TimeSignature::from_ratio_string(&written.ratio_string()).unwrap();
2811 assert_eq!(again.ratio_string(), written.ratio_string());
2812
2813 let slow = TimeSignature::from_ratio_string("slow 6/8").unwrap();
2816 let fast = TimeSignature::from_ratio_string("6/8").unwrap();
2817 assert_eq!(slow.ratio_string(), "6/8");
2818 assert_eq!(fast.ratio_string(), "6/8");
2819 assert_ne!(slow, fast);
2820
2821 assert_eq!(TimeSignature::common().ratio_string(), "4/4");
2823 }
2824}