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}
126
127impl Default for TimeSignature {
128 fn default() -> Self {
130 Self::common()
131 }
132}
133
134impl TimeSignature {
135 pub fn new(numerator: UnsignedIntegerType, denominator: UnsignedIntegerType) -> Result<Self> {
140 if numerator == 0 {
141 return Err(Error::Meter(
142 "time signature numerator must be non-zero".to_string(),
143 ));
144 }
145 if denominator == 0 {
146 return Err(Error::Meter(
147 "time signature denominator must be non-zero".to_string(),
148 ));
149 }
150 let mut signature = Self {
151 numerator,
152 denominator,
153 favor_compound: favor_compound(numerator, denominator, None),
154 display_sequence: whole_bar(numerator, denominator)?,
155 beat_sequence: whole_bar(numerator, denominator)?,
156 beam_sequence: whole_bar(numerator, denominator)?,
157 accent_sequence: whole_bar(numerator, denominator)?,
158 symbol: None,
159 symbolize_denominator: false,
160 color: crate::display::Drawn(None),
161 restated: crate::display::Drawn(false),
162 hidden: crate::display::Drawn(false),
163 };
164 signature.set_default_partitions()?;
165 Ok(signature)
166 }
167
168 pub fn symbol(&self) -> Option<&str> {
172 self.symbol.as_deref()
173 }
174
175 pub fn set_symbol(&mut self, symbol: Option<String>) {
177 self.symbol = symbol.filter(|symbol| !symbol.is_empty());
178 }
179
180 pub fn is_restated(&self) -> bool {
190 self.restated.0
191 }
192
193 pub fn set_restated(&mut self, restated: bool) {
195 self.restated.0 = restated;
196 }
197
198 pub fn symbolize_denominator(&self) -> bool {
201 self.symbolize_denominator
202 }
203
204 pub fn set_symbolize_denominator(&mut self, symbolize: bool) {
206 self.symbolize_denominator = symbolize;
207 }
208
209 pub fn color(&self) -> Option<&str> {
212 self.color.0.as_deref()
213 }
214
215 pub fn set_color(&mut self, color: Option<String>) {
217 self.color.0 = color;
218 }
219
220 pub fn is_hidden(&self) -> bool {
224 self.hidden.0
225 }
226
227 pub fn set_hidden(&mut self, hidden: bool) {
229 self.hidden.0 = hidden;
230 }
231
232 #[must_use]
237 pub fn display_sequence(&self) -> &MeterTerminal {
238 &self.display_sequence
239 }
240
241 #[must_use]
244 pub fn beat_sequence(&self) -> &MeterTerminal {
245 &self.beat_sequence
246 }
247
248 #[must_use]
251 pub fn beam_sequence(&self) -> &MeterTerminal {
252 &self.beam_sequence
253 }
254
255 #[must_use]
259 pub fn accent_sequence(&self) -> &MeterTerminal {
260 &self.accent_sequence
261 }
262
263 #[must_use]
266 pub fn favors_compound(&self) -> bool {
267 self.favor_compound
268 }
269
270 pub fn beat_sequence_mut(&mut self) -> &mut MeterTerminal {
273 &mut self.beat_sequence
274 }
275
276 pub fn beam_sequence_mut(&mut self) -> &mut MeterTerminal {
278 &mut self.beam_sequence
279 }
280
281 pub fn accent_sequence_mut(&mut self) -> &mut MeterTerminal {
283 &mut self.accent_sequence
284 }
285
286 pub fn display_sequence_mut(&mut self) -> &mut MeterTerminal {
288 &mut self.display_sequence
289 }
290
291 pub fn set_display(&mut self, value: &str) -> Result<()> {
299 let parts = Self::parts(value)?;
300 let denominator = parts
301 .iter()
302 .map(|(_, part)| *part)
303 .max()
304 .unwrap_or(self.denominator);
305 let numerator = parts
306 .iter()
307 .map(|(count, part)| count * (denominator / part))
308 .sum();
309 let mut display = whole_bar(numerator, denominator)?;
310 if (display.quarter_length() - self.bar_quarter_length()).abs() > OFFSET_TOLERANCE {
311 return Err(Error::Meter(format!(
312 "cannot write a {} bar as {value:?}: that is {} quarter lengths, not {}",
313 self.ratio_string(),
314 display.quarter_length(),
315 self.bar_quarter_length()
316 )));
317 }
318 if parts.len() > 1 {
319 let written: Vec<String> = parts
320 .iter()
321 .map(|(count, part)| format!("{count}/{part}"))
322 .collect();
323 let borrowed: Vec<&str> = written.iter().map(String::as_str).collect();
324 display.partition_by_parts(&borrowed)?;
325 }
326 self.display_sequence = display;
327 Ok(())
328 }
329
330 pub fn divide_beats(&mut self, count: UnsignedIntegerType) -> Result<()> {
337 if count == 0 {
338 return Err(Error::Meter(
339 "a bar cannot be divided into no parts".to_string(),
340 ));
341 }
342 let mut divided = whole_bar(self.numerator, self.denominator)?;
343 divided.partition_by_count(count as usize, false)?;
344 self.beat_sequence = divided.clone();
347 self.accent_sequence = divided.clone();
348 self.beam_sequence = divided;
349 Ok(())
350 }
351
352 pub fn set_beat_count(&mut self, count: UnsignedIntegerType) -> Result<()> {
359 if count == 0 {
360 return Err(Error::Meter(
361 "a bar cannot be counted in no beats".to_string(),
362 ));
363 }
364 let mut beats = MeterTerminal::new(self.numerator, self.denominator)?;
365 beats.partition_by_count(count as usize, false)?;
366 if beats.len() > 1 {
367 let _ = beats.subdivide_partitions_equal(None);
368 }
369 self.beat_sequence = beats;
370 Ok(())
371 }
372
373 fn set_default_partitions(&mut self) -> Result<()> {
377 self.set_default_beam_partitions()?;
378 self.set_default_beat_partitions()?;
379 self.set_default_accent_weights();
380 Ok(())
381 }
382
383 fn set_default_beam_partitions(&mut self) -> Result<()> {
392 let numerator = self.numerator;
393 let denominator = self.denominator;
394 if self.beam_sequence.summed_numerator() {
395 return Ok(());
396 }
397 if (denominator == 8 && matches!(numerator, 1..=3))
398 || (denominator == 16 && matches!(numerator, 1..=5))
399 || (denominator == 32 && matches!(numerator, 1..=11))
400 {
401 return Ok(());
402 }
403 match numerator {
404 2..=4 => {
405 self.beam_sequence
406 .partition_by_count(numerator as usize, true)?;
407 if denominator == 4 {
408 for part in self.beam_sequence.parts_mut() {
409 part.subdivide(2)?;
410 }
411 }
412 }
413 5 => {
414 self.beam_sequence.partition_by_list(&[2, 3])?;
415 if denominator == 4 {
416 for (part, count) in self.beam_sequence.parts_mut().iter_mut().zip([2, 3]) {
417 part.subdivide(count)?;
418 }
419 }
420 }
421 7 => self.beam_sequence.partition_by_count(3, true)?,
422 6 | 9 | 12 | 15 | 18 | 21 => {
423 let threes = vec![3; (numerator / 3) as usize];
424 self.beam_sequence.partition_by_list(&threes)?;
425 }
426 _ => {}
427 }
428 Ok(())
429 }
430
431 fn set_default_beat_partitions(&mut self) -> Result<()> {
438 let numerator = self.numerator;
439 let compound = self.favor_compound;
440 if self.display_sequence.len() == 1 {
441 match numerator {
442 2 => self.beat_sequence.partition_by_count(2, true)?,
443 6 if compound => self.beat_sequence.partition_by_count(2, true)?,
444 3 if compound => self.beat_sequence.partition_by_count(1, true)?,
445 3 => self.beat_sequence.partition_by_list(&[1, 1, 1])?,
446 9 if compound => self.beat_sequence.partition_by_list(&[3, 3, 3])?,
447 4 => self.beat_sequence.partition_by_count(4, true)?,
448 12 if compound => self.beat_sequence.partition_by_count(4, true)?,
449 other if other >= 15 && other.is_multiple_of(3) && compound => {
450 let threes = vec![3; (other / 3) as usize];
451 self.beat_sequence.partition_by_list(&threes)?;
452 }
453 other => self
454 .beat_sequence
455 .partition_by_count(other as usize, true)?,
456 }
457 } else {
458 let written: Vec<String> = self
459 .display_sequence
460 .parts()
461 .iter()
462 .map(|part| format!("{}/{}", part.numerator(), part.denominator()))
463 .collect();
464 let borrowed: Vec<&str> = written.iter().map(String::as_str).collect();
465 self.beat_sequence.partition_by_parts(&borrowed)?;
466 }
467 if self.beat_sequence.len() > 1 {
468 match self.beat_sequence.subdivide_partitions_equal(None) {
471 Ok(()) => {}
472 Err(error) if self.denominator >= 128 => {
473 let _ = error;
474 }
475 Err(error) => return Err(error),
476 }
477 }
478 Ok(())
479 }
480
481 fn set_default_accent_weights(&mut self) {
485 let weights = self.default_accent_weights();
486 let ones = vec![1; weights.len()];
487 if self.accent_sequence.partition_by_list(&ones).is_err() {
488 return;
489 }
490 for (part, weight) in self.accent_sequence.parts_mut().iter_mut().zip(weights) {
491 part.set_weight(weight);
492 }
493 }
494
495 pub fn from_ratio_string(ratio: &str) -> Result<Self> {
504 let parts = Self::parts(ratio)?;
505 let word = division_word(ratio);
506 let summed = ratio.split('+').any(|part| !part.contains('/'));
507 let denominator = parts[0].1;
508 if parts.iter().all(|(_, part)| *part == denominator) {
509 let numerator = parts.iter().map(|(count, _)| count).sum();
510 let mut signature = Self::new(numerator, denominator)?;
511 signature.write_as(&parts, word, summed)?;
512 return Ok(signature);
513 }
514 let denominator = parts
517 .iter()
518 .map(|(_, part)| *part)
519 .max()
520 .unwrap_or(denominator);
521 let numerator = parts
522 .iter()
523 .map(|(count, part)| count * (denominator / part))
524 .sum();
525 let mut signature = Self::new(numerator, denominator)?;
526 signature.write_as(&parts, word, summed)?;
527 Ok(signature)
528 }
529
530 fn write_as(
534 &mut self,
535 parts: &[(UnsignedIntegerType, UnsignedIntegerType)],
536 word: Option<&str>,
537 summed: bool,
538 ) -> Result<()> {
539 self.favor_compound = favor_compound(self.numerator, self.denominator, word);
540 self.beat_sequence = whole_bar(self.numerator, self.denominator)?;
541 self.beam_sequence = whole_bar(self.numerator, self.denominator)?;
542 self.accent_sequence = whole_bar(self.numerator, self.denominator)?;
543 if parts.len() > 1 {
544 let written = parts
551 .iter()
552 .map(|(count, part)| format!("{count}/{part}"))
553 .collect::<Vec<_>>()
554 .join("+");
555 let loaded = MeterTerminal::from_partition_string(&written)?;
556 self.display_sequence = loaded.clone();
557 self.beat_sequence = loaded.clone();
558 self.beam_sequence = loaded;
559 }
560 for sequence in [
563 &mut self.display_sequence,
564 &mut self.beat_sequence,
565 &mut self.beam_sequence,
566 &mut self.accent_sequence,
567 ] {
568 sequence.set_summed_numerator(summed);
569 }
570 self.set_default_partitions()
571 }
572
573 #[must_use]
577 pub fn summed_numerator(&self) -> bool {
578 self.display_sequence.summed_numerator()
579 }
580
581 pub fn set_summed_numerator(&mut self, summed: bool) {
584 self.display_sequence.set_summed_numerator(summed);
585 }
586
587 pub fn parts(ratio: &str) -> Result<Vec<(UnsignedIntegerType, UnsignedIntegerType)>> {
596 let unworded = without_division_word(ratio);
597 let written = unworded.trim();
598 if written.is_empty() {
599 return Err(Error::Meter("a time signature says nothing".to_string()));
600 }
601 let mut pre: Vec<(UnsignedIntegerType, Option<UnsignedIntegerType>)> = Vec::new();
602 for part in written.split('+') {
603 let part = part.trim();
604 match part.split_once('/') {
605 Some((numerator, denominator)) => pre.push((
606 Self::count(numerator, "numerator", written)?,
607 Some(Self::count(denominator, "denominator", written)?),
608 )),
609 None => pre.push((Self::count(part, "numerator", written)?, None)),
610 }
611 }
612 let mut out = Vec::with_capacity(pre.len());
613 for (index, (numerator, denominator)) in pre.iter().enumerate() {
614 let denominator = match denominator {
615 Some(denominator) => *denominator,
616 None => pre[index + 1..]
617 .iter()
618 .find_map(|(_, later)| *later)
619 .ok_or_else(|| {
620 Error::Meter(format!(
621 "cannot match a denominator to every numerator in {written:?}"
622 ))
623 })?,
624 };
625 out.push((*numerator, denominator));
626 }
627 Ok(out)
628 }
629
630 fn count(part: &str, label: &str, ratio: &str) -> Result<UnsignedIntegerType> {
633 let digits = part
634 .trim()
635 .rsplit(|character: char| character.is_whitespace())
636 .next()
637 .unwrap_or("")
638 .trim();
639 digits
640 .parse::<UnsignedIntegerType>()
641 .map_err(|_| Error::Meter(format!("cannot read a {label} from {part:?} in {ratio:?}")))
642 }
643
644 pub fn common() -> Self {
646 Self::new(4, 4).expect("4/4 is a meter")
647 }
648
649 pub fn cut() -> Self {
651 Self::new(2, 2).expect("2/2 is a meter")
652 }
653
654 pub fn numerator(&self) -> UnsignedIntegerType {
656 self.numerator
657 }
658
659 pub fn denominator(&self) -> UnsignedIntegerType {
661 self.denominator
662 }
663
664 pub fn ratio_string(&self) -> String {
666 if self.display_sequence.len() > 1 {
671 return self.display_sequence.partition_display();
672 }
673 format!("{}/{}", self.numerator, self.denominator)
674 }
675
676 pub fn bar_quarter_length(&self) -> FloatType {
678 FloatType::from(self.numerator) * 4.0 / FloatType::from(self.denominator)
679 }
680
681 pub fn bar_duration(&self) -> Duration {
683 Duration::new(self.bar_quarter_length())
684 .expect("a non-zero numerator and denominator give a positive finite bar length")
685 }
686
687 pub fn offset_in_bar(&self, element_offset: FloatType, meter_offset: FloatType) -> FloatType {
701 let bar = self.bar_quarter_length();
702
703 if element_offset + meter_offset < bar {
706 return element_offset;
707 }
708 (element_offset - meter_offset).rem_euclid(bar)
709 }
710
711 pub fn beat_count(&self) -> UnsignedIntegerType {
717 self.beat_sequence.len() as UnsignedIntegerType
718 }
719
720 pub fn beat_count_name(&self) -> String {
724 partition_name(self.beat_count() as usize)
725 }
726
727 pub fn beat_quarter_length(&self) -> Result<FloatType> {
734 let spans = self.beat_spans();
735 let first = spans[0].1 - spans[0].0;
736 if spans
737 .iter()
738 .any(|(start, end)| ((end - start) - first).abs() > OFFSET_TOLERANCE)
739 {
740 let lengths: Vec<FloatType> = spans.iter().map(|(s, e)| e - s).collect();
741 return Err(Error::Meter(format!("non-uniform beat unit: {lengths:?}")));
742 }
743 Ok(first)
744 }
745
746 pub fn beat_duration(&self) -> Result<Duration> {
748 Duration::new(self.beat_quarter_length()?)
749 }
750
751 pub fn beat_division(&self) -> BeatDivision {
753 let parts = self.beat_sequence.parts();
754 if parts.len() <= 1 {
755 return BeatDivision::Other;
756 }
757 let mut counts = parts.iter().map(MeterTerminal::len);
758 let Some(first) = counts.next() else {
759 return BeatDivision::Other;
760 };
761 if first == 0 || !counts.all(|count| count == first) {
762 return BeatDivision::Other;
765 }
766 match first {
767 2 => BeatDivision::Simple,
768 3 => BeatDivision::Compound,
769 _ => BeatDivision::Other,
770 }
771 }
772
773 pub fn beat_division_count(&self) -> UnsignedIntegerType {
775 self.beat_division().count()
776 }
777
778 pub fn is_compound(&self) -> bool {
780 self.beat_division() == BeatDivision::Compound
781 }
782
783 pub fn classification(&self) -> String {
785 format!(
786 "{} {}",
787 self.beat_division().music21_name(),
788 self.beat_count_name()
789 )
790 }
791
792 pub fn beat_division_count_name(&self) -> &'static str {
795 self.beat_division().music21_name()
796 }
797
798 pub fn ratio_equal(&self, other: &TimeSignature) -> bool {
801 self.numerator == other.numerator && self.denominator == other.denominator
802 }
803
804 pub fn beat_length_to_quarter_length_ratio(&self) -> FloatType {
807 4.0 / FloatType::from(self.denominator)
808 }
809
810 pub fn quarter_length_to_beat_length_ratio(&self) -> FloatType {
813 FloatType::from(self.denominator) / 4.0
814 }
815
816 pub fn beat_division_quarter_lengths(&self) -> Result<Vec<FloatType>> {
829 let beat_sequence = self.beat_sequence();
830 let beats = match beat_sequence.parts() {
831 [] => std::slice::from_ref(beat_sequence),
832 beats => beats,
833 };
834
835 let divisions = |beat: &MeterTerminal| -> Vec<FloatType> {
838 match beat.parts() {
839 [] => vec![beat.quarter_length()],
840 parts => parts.iter().map(MeterTerminal::quarter_length).collect(),
841 }
842 };
843
844 let every: Vec<FloatType> = beats.iter().flat_map(divisions).collect();
845 if every.windows(2).any(|pair| pair[0] != pair[1]) {
846 return Err(Error::Meter(format!(
847 "non uniform beat division: {every:?}"
848 )));
849 }
850 Ok(beats.first().map(divisions).unwrap_or_default())
851 }
852
853 pub fn beat_division_durations(&self) -> Result<Vec<Duration>> {
856 self.beat_division_quarter_lengths()?
857 .into_iter()
858 .map(Duration::new)
859 .collect()
860 }
861
862 pub fn beat_sub_division_durations(&self) -> Result<Vec<Duration>> {
865 let mut out = Vec::new();
866 for quarter_length in self.beat_division_quarter_lengths()? {
867 let half = Duration::new(quarter_length / 2.0)?;
868 out.push(half.clone());
869 out.push(half);
870 }
871 Ok(out)
872 }
873
874 pub fn offset_from_beat(&self, beat: FloatType) -> Result<FloatType> {
878 let whole = beat.floor();
879 if !beat.is_finite() || whole < 1.0 || whole > FloatType::from(self.beat_count()) {
880 return Err(Error::Meter(format!(
881 "requested beat value ({beat}) not found in the {} beats of {}",
882 self.beat_count(),
883 self.ratio_string()
884 )));
885 }
886 let (start, end) = self.beat_spans()[whole as usize - 1];
887 let fraction = snapped_fraction(beat - whole);
888 Ok(start + fraction * (end - start))
889 }
890
891 pub fn beat_progress(&self, offset: FloatType) -> Result<(UnsignedIntegerType, FloatType)> {
894 let index = self.beat_index(offset)?;
895 let (start, _) = self.beat_spans()[index];
896 Ok((index as UnsignedIntegerType + 1, offset - start))
897 }
898
899 pub fn beat_proportion(&self, offset: FloatType) -> Result<FloatType> {
903 let (beat, progress) = self.beat_progress(offset)?;
904 let (start, end) = self.beat_spans()[beat as usize - 1];
905 Ok(crate::makenotation::op_frac(
906 FloatType::from(beat) + progress / (end - start),
907 ))
908 }
909
910 pub fn beat_proportion_string(&self, offset: FloatType) -> Result<String> {
915 let (beat, progress) = self.beat_progress(offset)?;
916 let (start, end) = self.beat_spans()[beat as usize - 1];
917 let proportion = progress / (end - start);
918 if proportion == 0.0 {
919 return Ok(beat.to_string());
920 }
921 let (numerator, denominator) = closest_fraction(proportion, 16);
922 Ok(format!("{beat} {numerator}/{denominator}"))
923 }
924
925 fn beat_spans(&self) -> Vec<(FloatType, FloatType)> {
929 let mut spans = Vec::new();
930 let mut position = 0.0;
931 for part in self.beat_sequence.parts() {
932 let end = position + part.quarter_length();
933 spans.push((position, end));
934 position = end;
935 }
936 if spans.is_empty() {
937 spans.push((0.0, self.bar_quarter_length()));
938 }
939 spans
940 }
941
942 pub fn beat_duration_at(&self, offset: FloatType) -> Result<Duration> {
948 let index = self.beat_index(offset)?;
949 let (start, end) = self.beat_spans()[index];
950 Duration::new(end - start)
951 }
952
953 fn beat_index(&self, offset: FloatType) -> Result<usize> {
955 if !offset.is_finite() || offset < 0.0 || offset >= self.bar_quarter_length() {
956 return Err(Error::Meter(format!(
957 "offset {offset} is outside a {} bar of {} quarter lengths",
958 self.ratio_string(),
959 self.bar_quarter_length()
960 )));
961 }
962 let spans = self.beat_spans();
963 for (index, (start, end)) in spans.iter().enumerate() {
964 let _ = start;
965 if offset < end - OFFSET_TOLERANCE {
966 return Ok(index);
967 }
968 }
969 Ok(spans.len() - 1)
970 }
971
972 pub fn beat_offsets(&self) -> Vec<FloatType> {
974 self.beat_spans()
975 .into_iter()
976 .map(|(start, _)| start)
977 .collect()
978 }
979
980 pub fn beat_at_offset(&self, offset: FloatType) -> Result<UnsignedIntegerType> {
985 if !offset.is_finite() || offset < 0.0 || offset >= self.bar_quarter_length() {
986 return Err(Error::Meter(format!(
987 "offset {offset} is outside a {} bar of {} quarter lengths",
988 self.ratio_string(),
989 self.bar_quarter_length()
990 )));
991 }
992 Ok(self.beat_index(offset)? as UnsignedIntegerType + 1)
993 }
994}
995
996fn closest_fraction(value: FloatType, max_denominator: u32) -> (u32, u32) {
1000 let mut best = (value.round() as u32, 1);
1001 let mut best_error = (value - value.round()).abs();
1002 for denominator in 2..=max_denominator {
1003 let numerator = (value * FloatType::from(denominator)).round();
1004 let error = (value - numerator / FloatType::from(denominator)).abs();
1005 if error < best_error {
1006 best = (numerator as u32, denominator);
1007 best_error = error;
1008 }
1009 }
1010 best
1011}
1012
1013impl std::fmt::Display for TimeSignature {
1014 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1015 f.write_str(&self.ratio_string())
1016 }
1017}
1018
1019const OFFSET_TOLERANCE: FloatType = 1e-9;
1022
1023const SHORTEST_PARTITION: FloatType = 4.0 / 128.0;
1025
1026const LONGEST_PARTITION: FloatType = 4.0;
1028
1029impl TimeSignature {
1030 fn accent_hierarchy(
1042 &self,
1043 ) -> (
1044 UnsignedIntegerType,
1045 UnsignedIntegerType,
1046 UnsignedIntegerType,
1047 ) {
1048 let beats = self.beat_count();
1049 let first = if beats > 1 { beats } else { self.numerator };
1050 let top = match first {
1051 1 | 2 | 4 | 8 | 16 | 32 => 2,
1052 3 => 3,
1053 other => other,
1054 };
1055 let (span, unit) = split_span(self.numerator, self.denominator, top);
1056 let second = default_division(span, unit);
1057 let (span, unit) = split_span(span, unit, second);
1058 let third = default_division(span, unit);
1059 let partition = self.bar_quarter_length() / FloatType::from(top * second * third);
1064 if !(SHORTEST_PARTITION..=LONGEST_PARTITION).contains(&partition) {
1065 return (1, 1, 1);
1066 }
1067 (top, second, third)
1068 }
1069
1070 pub fn accent_partition_quarter_length(&self) -> FloatType {
1073 let parts = self.accent_sequence.parts();
1074 if parts.len() > 1 {
1075 return parts[0].quarter_length();
1076 }
1077 let (top, second, third) = self.accent_hierarchy();
1078 self.bar_quarter_length() / FloatType::from(top * second * third)
1079 }
1080
1081 pub fn accent_weights(&self) -> Vec<FloatType> {
1086 let carried: Vec<FloatType> = self
1087 .accent_sequence
1088 .parts()
1089 .iter()
1090 .map(MeterTerminal::weight)
1091 .collect();
1092 if carried.len() > 1 {
1093 return carried;
1094 }
1095 self.default_accent_weights()
1096 }
1097
1098 fn default_accent_weights(&self) -> Vec<FloatType> {
1103 if !self.beat_sequence.is_uniform_partition(0)
1104 && let Some(weights) = self.accent_weights_from_the_beats()
1105 {
1106 return weights;
1107 }
1108 let (top, second, third) = self.accent_hierarchy();
1109 let count = top * second * third;
1110 (0..count)
1111 .map(|index| {
1112 let depth = 1
1113 + UnsignedIntegerType::from(index.is_multiple_of(third))
1114 + UnsignedIntegerType::from(index.is_multiple_of(second * third))
1115 + UnsignedIntegerType::from(index == 0);
1116 FloatType::from(2u32.pow(depth - 1)) / 8.0
1117 })
1118 .collect()
1119 }
1120
1121 fn accent_weights_from_the_beats(&self) -> Option<Vec<FloatType>> {
1131 let mut nested = MeterTerminal::new(self.numerator, self.denominator).ok()?;
1132 nested
1133 .subdivide_nested_hierarchy_by(3, &self.beat_sequence, true)
1134 .ok()?;
1135 let count = nested.flattened().len();
1136 let step = self.bar_quarter_length() / FloatType::from(u32::try_from(count).ok()?);
1137 let depths: Vec<usize> = (0..count)
1138 .map(|index| {
1139 let offset = step * FloatType::from(index as u32);
1140 nested.offset_to_depth(offset, sequence::OffsetAlign::Quantize)
1141 })
1142 .collect::<Result<_>>()
1143 .ok()?;
1144 let deepest = depths.iter().copied().max()?;
1145 Some(
1146 depths
1147 .into_iter()
1148 .map(|depth| {
1149 FloatType::from(2u32.pow(depth as u32 - 1))
1150 / FloatType::from(2u32.pow(deepest as u32 - 1))
1151 })
1152 .collect(),
1153 )
1154 }
1155
1156 pub fn accent(&self, offset: FloatType) -> bool {
1160 let partition = self.accent_partition_quarter_length();
1161 let index = (offset / partition).round();
1162 index >= 0.0
1163 && index < FloatType::from(self.accent_weights().len() as u32)
1164 && (offset - index * partition).abs() < OFFSET_TOLERANCE
1165 }
1166
1167 pub fn accent_weight(&self, offset: FloatType) -> Result<FloatType> {
1171 self.accent_weight_with(offset, false, false)
1172 }
1173
1174 pub fn accent_weight_at_level(
1177 &self,
1178 offset: FloatType,
1179 level: usize,
1180 force_position_match: bool,
1181 permit_meter_modulus: bool,
1182 ) -> Result<FloatType> {
1183 let bar = self.bar_quarter_length();
1184 let offset = if permit_meter_modulus {
1185 offset.rem_euclid(bar)
1186 } else {
1187 offset
1188 };
1189 let offset = offset_within(offset, bar)?;
1190 let terminals = self.accent_sequence.level_list(level, true);
1191 if terminals.len() <= 1 {
1192 return self.accent_weight_with(offset, force_position_match, permit_meter_modulus);
1193 }
1194 let spans = self.accent_sequence.level_span(level);
1195 let smallest = terminals
1196 .iter()
1197 .map(MeterTerminal::weight)
1198 .fold(FloatType::INFINITY, FloatType::min);
1199 for (index, (start, end)) in spans.iter().enumerate() {
1200 if offset < end - OFFSET_TOLERANCE {
1201 if force_position_match && (offset - start).abs() >= OFFSET_TOLERANCE {
1202 return Ok(smallest * 0.5);
1203 }
1204 return Ok(terminals[index].weight());
1205 }
1206 }
1207 Ok(terminals[terminals.len() - 1].weight())
1208 }
1209
1210 pub fn set_accent_weight(&mut self, weights: &[FloatType], level: usize) -> Result<()> {
1213 self.accent_sequence.set_level_weight(level, weights)
1214 }
1215
1216 pub fn accent_weight_with(
1221 &self,
1222 offset: FloatType,
1223 force_position_match: bool,
1224 permit_meter_modulus: bool,
1225 ) -> Result<FloatType> {
1226 let bar = self.bar_quarter_length();
1227 let offset = if permit_meter_modulus {
1228 offset.rem_euclid(bar)
1229 } else {
1230 offset
1231 };
1232 let offset = offset_within(offset, bar)?;
1233 let weights = self.accent_weights();
1234 let partition = self.accent_partition_quarter_length();
1235 let index = ((offset + OFFSET_TOLERANCE) / partition).floor() as usize;
1236 let index = index.min(weights.len() - 1);
1237 if force_position_match
1238 && (offset - index as FloatType * partition).abs() >= OFFSET_TOLERANCE
1239 {
1240 let smallest = weights
1241 .iter()
1242 .copied()
1243 .fold(FloatType::INFINITY, FloatType::min);
1244 return Ok(smallest * 0.5);
1245 }
1246 Ok(weights[index])
1247 }
1248
1249 pub fn average_beat_strength(&self, stream: &crate::Stream, notes_only: bool) -> FloatType {
1256 let bar = self.bar_quarter_length();
1257 let offsets: Vec<FloatType> = stream
1258 .events()
1259 .iter()
1260 .filter(|event| !notes_only || crate::makenotation::is_not_rest(event.element()))
1261 .map(crate::stream::StreamEvent::offset)
1262 .collect();
1263 if offsets.is_empty() {
1264 return 0.0;
1265 }
1266 let total: FloatType = offsets
1267 .iter()
1268 .map(|offset| {
1269 self.accent_weight_with(offset.rem_euclid(bar), true, false)
1270 .unwrap_or(0.0)
1271 })
1272 .sum();
1273 total / offsets.len() as FloatType
1274 }
1275
1276 pub fn beat_depth(&self, offset: FloatType) -> Result<u8> {
1282 let bar = self.bar_quarter_length();
1283 if offset.is_nan() || offset < 0.0 || offset >= bar {
1284 return Err(Error::Meter(format!(
1285 "cannot access from qLenPos {}",
1286 offset_repr(offset)
1287 )));
1288 }
1289 let depth = self
1290 .beat_sequence
1291 .offset_to_depth(offset, crate::meter::OffsetAlign::Quantize)?;
1292 Ok(depth as u8)
1293 }
1294}
1295
1296#[must_use]
1303pub fn snapped_fraction(value: FloatType) -> FloatType {
1304 const GRAIN: FloatType = 1e-2;
1305 let thirds = [1.0 / 3.0, 2.0 / 3.0, 1.0 / 6.0, 5.0 / 6.0];
1306 for candidate in thirds {
1307 if (value - candidate).abs() <= GRAIN {
1308 return candidate;
1309 }
1310 }
1311 value
1312}
1313
1314#[derive(Clone, Copy, Debug, PartialEq)]
1321pub struct BeamedNote {
1322 pub offset: FloatType,
1324 pub quarter_length: FloatType,
1326 pub duration_type: DurationType,
1328 pub sounds: bool,
1330}
1331
1332impl TimeSignature {
1333 pub fn beams_for(
1344 &self,
1345 notes: &[BeamedNote],
1346 measure_start_offset: FloatType,
1347 measure_padding: Option<FloatType>,
1348 ) -> Result<Vec<Option<Beams>>> {
1349 if notes.len() <= 1 {
1350 return Ok(notes.iter().map(|_| None).collect());
1351 }
1352
1353 let mut beamed: Vec<Option<Beams>> = notes
1356 .iter()
1357 .map(|note| Beams::naive(Some(note.duration_type), note.sounds))
1358 .collect();
1359 crate::notation::remove_sandwiched_unbeamables(&mut beamed);
1360
1361 for depth in 0..Beams::LEVELS {
1362 for index in 0..notes.len() {
1363 self.fix_one_beam(
1364 &mut beamed,
1365 notes,
1366 index,
1367 depth,
1368 measure_start_offset,
1369 measure_padding,
1370 )?;
1371 }
1372 }
1373
1374 crate::notation::sanitize_partial_beams(&mut beamed);
1375 crate::notation::merge_connecting_partial_beams(&mut beamed);
1376 Ok(beamed)
1377 }
1378
1379 fn fix_one_beam(
1382 &self,
1383 beamed: &mut [Option<Beams>],
1384 notes: &[BeamedNote],
1385 index: usize,
1386 depth: usize,
1387 measure_start_offset: FloatType,
1388 measure_padding: Option<FloatType>,
1389 ) -> Result<()> {
1390 let beam_number = depth as u32 + 1;
1391 let carries = |beams: Option<&Beams>| {
1392 beams.is_some_and(|beams| {
1393 beams
1394 .numbers()
1395 .into_iter()
1396 .flatten()
1397 .any(|n| n == beam_number)
1398 })
1399 };
1400 if !carries(beamed[index].as_ref()) {
1401 return Ok(());
1402 }
1403
1404 let note = notes[index];
1405 let start = note.offset + measure_start_offset;
1406 let end = start + note.quarter_length;
1407 let start_next = end;
1408
1409 let is_first = index == 0;
1410 let is_last = index + 1 == notes.len();
1411
1412 let previous_is_none = is_first || beamed[index - 1].is_none();
1415 let next_is_none = is_last || beamed[index + 1].is_none();
1416 let previous_carries = !is_first && carries(beamed[index - 1].as_ref());
1417 let next_carries = !is_last && carries(beamed[index + 1].as_ref());
1418 let previous_broke = !is_first
1419 && beamed[index - 1].as_ref().is_some_and(|beams| {
1420 beams.by_number(beam_number).is_some_and(|beam| {
1421 matches!(beam.beam_type(), Some(BeamType::Stop))
1422 || (matches!(beam.beam_type(), Some(BeamType::PartialBeam))
1423 && beam.direction() == Some(crate::notation::BeamDirection::Left))
1424 })
1425 });
1426
1427 let archetype = self.beam_sequence.level(depth, true)?;
1428 let (span_start, span_end) = archetype.offset_to_span(start, false)?;
1429 let span_next_start = if next_is_none {
1430 0.0
1431 } else {
1432 archetype.offset_to_span(start_next, false)?.0
1433 };
1434
1435 let same = |a: FloatType, b: FloatType| (a - b).abs() < OFFSET_TOLERANCE;
1436
1437 if same(end, span_end)
1439 && (same(start, span_start) || (previous_is_none && beam_number == 1))
1440 {
1441 beamed[index] = None;
1442 return Ok(());
1443 }
1444
1445 let ends_the_measure = is_last && measure_padding.is_none_or(|padding| padding == 0.0);
1446
1447 let (beam_type, direction) = if is_first && measure_start_offset == 0.0 {
1448 if next_is_none || !next_carries {
1449 (
1450 BeamType::PartialBeam,
1451 Some(crate::notation::BeamDirection::Right),
1452 )
1453 } else {
1454 (BeamType::Start, None)
1455 }
1456 } else if ends_the_measure {
1457 if previous_is_none || !previous_carries {
1458 (
1459 BeamType::PartialBeam,
1460 Some(crate::notation::BeamDirection::Left),
1461 )
1462 } else {
1463 (BeamType::Stop, None)
1464 }
1465 } else if previous_is_none || !previous_carries {
1466 if beam_number == 1 && next_is_none {
1468 beamed[index] = None;
1469 return Ok(());
1470 } else if (next_is_none && beam_number > 1) || start_next >= span_end - OFFSET_TOLERANCE
1471 {
1472 (
1476 BeamType::PartialBeam,
1477 Some(crate::notation::BeamDirection::Left),
1478 )
1479 } else if next_is_none || !next_carries {
1480 (
1481 BeamType::PartialBeam,
1482 Some(crate::notation::BeamDirection::Right),
1483 )
1484 } else {
1485 (BeamType::Start, None)
1486 }
1487 } else if previous_broke {
1488 if next_is_none {
1489 (
1490 BeamType::PartialBeam,
1491 Some(crate::notation::BeamDirection::Left),
1492 )
1493 } else if next_carries {
1494 (BeamType::Start, None)
1495 } else {
1496 (
1497 BeamType::PartialBeam,
1498 Some(crate::notation::BeamDirection::Right),
1499 )
1500 }
1501 } else if next_is_none || !next_carries {
1502 (BeamType::Stop, None)
1503 } else if start_next < span_end - OFFSET_TOLERANCE {
1504 (BeamType::Continue, None)
1505 } else if start_next >= span_next_start - OFFSET_TOLERANCE {
1506 (BeamType::Stop, None)
1507 } else {
1508 return Err(Error::Meter("cannot match beamType".to_string()));
1509 };
1510
1511 if let Some(beams) = beamed[index].as_mut() {
1512 beams.set_by_number(beam_number, beam_type, direction)?;
1513 }
1514 Ok(())
1515 }
1516}
1517
1518fn whole_bar(
1522 numerator: UnsignedIntegerType,
1523 denominator: UnsignedIntegerType,
1524) -> Result<MeterTerminal> {
1525 let mut bar = MeterTerminal::new(numerator, denominator)?;
1526 let whole = format!("{numerator}/{denominator}");
1527 bar.partition_by_parts(&[whole.as_str()])?;
1528 Ok(bar)
1529}
1530
1531fn division_word(ratio: &str) -> Option<&'static str> {
1535 let lower = ratio.to_ascii_lowercase();
1536 if lower.contains("slow") {
1537 Some("slow")
1538 } else if lower.contains("fast") {
1539 Some("fast")
1540 } else {
1541 None
1542 }
1543}
1544
1545fn without_division_word(ratio: &str) -> String {
1547 let mut out = ratio.to_string();
1548 for word in ["slow", "fast"] {
1549 while let Some(at) = out.to_ascii_lowercase().find(word) {
1550 out.replace_range(at..at + word.len(), " ");
1551 }
1552 }
1553 out
1554}
1555
1556fn favor_compound(
1562 numerator: UnsignedIntegerType,
1563 denominator: UnsignedIntegerType,
1564 word: Option<&str>,
1565) -> bool {
1566 match word {
1567 Some("slow") => false,
1568 Some("fast") => true,
1569 _ => !(numerator == 3 && denominator < 8),
1570 }
1571}
1572
1573pub fn best_time_signature(measure: &crate::Stream) -> Result<TimeSignature> {
1585 let elements = measure.recurse();
1586 let sum = elements
1587 .iter()
1588 .filter(|(_, element)| element.as_stream().is_none())
1589 .map(|(offset, element)| offset + element.quarter_length())
1590 .fold(0.0, FloatType::max);
1591
1592 let mut min_dur = 4.0;
1594 let mut min_dots = 0;
1595 for (_, element) in &elements {
1596 let sounds =
1597 element.is_note_or_chord() || matches!(element, crate::stream::StreamElement::Rest(_));
1598 let quarter_length = element.quarter_length();
1599 if sounds && quarter_length != 0.0 && quarter_length < min_dur && is_binary(quarter_length)
1600 {
1601 min_dur = quarter_length;
1602 min_dots = element.duration().map_or(0, Duration::dots);
1603 }
1604 }
1605
1606 let (numerator, denominator) = if is_binary(sum) {
1607 binary_signature(sum, min_dur, min_dots)?
1608 } else {
1609 let (numerator, denominator) = crate::duration::limited_fraction(sum, 65535)
1610 .ok_or_else(|| Error::Meter("Cannot find a good match for this measure".to_string()))?;
1611 (
1612 numerator as UnsignedIntegerType,
1613 denominator as UnsignedIntegerType,
1614 )
1615 };
1616 let (numerator, denominator) = simplified_signature(numerator, denominator);
1617
1618 let strength =
1619 |ratio: (UnsignedIntegerType, UnsignedIntegerType)| -> Result<(TimeSignature, FloatType)> {
1620 let signature = TimeSignature::new(ratio.0, ratio.1)?;
1621 let strength = signature.average_beat_strength(measure, true);
1622 Ok((signature, strength))
1623 };
1624 match (numerator, denominator) {
1625 (3, 4) => {
1627 let (three_four, simple) = strength((3, 4))?;
1628 let (six_eight, compound) = strength((6, 8))?;
1629 Ok(if simple <= compound {
1630 six_eight
1631 } else {
1632 three_four
1633 })
1634 }
1635 (6, 4) => {
1637 let (six_four, first) = strength((6, 4))?;
1638 let (twelve_eight, second) = strength((12, 8))?;
1639 let (three_two, third) = strength((3, 2))?;
1640 let most = first.max(second).max(third);
1641 Ok(if most == first {
1642 six_four
1643 } else if most == third {
1644 three_two
1645 } else {
1646 twelve_eight
1647 })
1648 }
1649 _ => TimeSignature::new(numerator, denominator),
1650 }
1651}
1652
1653fn binary_signature(
1658 sum: FloatType,
1659 min_dur: FloatType,
1660 min_dots: u32,
1661) -> Result<(UnsignedIntegerType, UnsignedIntegerType)> {
1662 let no_match = || Error::Meter("Cannot find a good match for this measure".to_string());
1663 let smallest_type = crate::duration::DurationType::from_music21_name("128th")
1664 .expect("music21 names the 128th note");
1665 let limit = smallest_type.quarter_length();
1666 let dot_multiplier =
1667 FloatType::from(2u32.pow(min_dots + 1) - 1) / FloatType::from(2u32.pow(min_dots));
1668
1669 let mut min_test = min_dur;
1670 let mut remaining = 10;
1671 while remaining > 0 {
1672 let parts = sum / min_test;
1673 if parts.floor() == parts || min_test <= limit {
1674 break;
1675 }
1676 min_test /= 2.0 * dot_multiplier;
1677 remaining -= 1;
1678 }
1679 let mut remaining = 10;
1680 while remaining > 0 {
1681 if min_test < limit {
1682 min_test = limit;
1683 break;
1684 }
1685 let (duration_type, matched) =
1686 crate::duration::quarter_length_to_closest_type(min_test).map_err(|_| no_match())?;
1687 if matched || duration_type == smallest_type {
1688 break;
1689 }
1690 min_test /= 2.0 * dot_multiplier;
1691 remaining -= 1;
1692 }
1693 let (duration_type, matched) =
1694 crate::duration::quarter_length_to_closest_type(min_test).map_err(|_| no_match())?;
1695 if !matched {
1696 return Err(Error::Meter(format!(
1697 "cannot find a type for denominator {min_test}"
1698 )));
1699 }
1700 let mut float_denominator = duration_type.type_number().unwrap_or(1.0);
1701 let mut multiplier = 1.0;
1702 let mut numerator_float = 0.0;
1703 while remaining > 0 {
1704 numerator_float = multiplier * sum / min_test;
1705 if numerator_float == numerator_float.floor() {
1706 break;
1707 }
1708 multiplier *= 2.0;
1709 remaining -= 1;
1710 }
1711 float_denominator *= multiplier;
1712 let numerator = numerator_float as UnsignedIntegerType;
1713 let denominator = float_denominator as UnsignedIntegerType;
1714 let divisor = num::integer::gcd(numerator, denominator).max(1);
1715 Ok((numerator / divisor, denominator / divisor))
1716}
1717
1718fn simplified_signature(
1721 numerator: UnsignedIntegerType,
1722 denominator: UnsignedIntegerType,
1723) -> (UnsignedIntegerType, UnsignedIntegerType) {
1724 if numerator == denominator && !matches!(numerator, 2 | 4) {
1725 (4, 4)
1726 } else if numerator != denominator && denominator == 1 {
1727 (numerator * 4, 4)
1728 } else if numerator != denominator && denominator == 2 {
1729 (numerator * 2, 4)
1730 } else {
1731 (numerator, denominator)
1732 }
1733}
1734
1735fn is_binary(quarter_length: FloatType) -> bool {
1739 (quarter_length * 32768.0).fract() == 0.0
1740}
1741
1742fn split_span(
1745 count: UnsignedIntegerType,
1746 unit: UnsignedIntegerType,
1747 parts: UnsignedIntegerType,
1748) -> (UnsignedIntegerType, UnsignedIntegerType) {
1749 if count.is_multiple_of(parts) {
1750 (count / parts, unit)
1751 } else {
1752 (count, unit * parts)
1753 }
1754}
1755
1756fn default_division(count: UnsignedIntegerType, unit: UnsignedIntegerType) -> UnsignedIntegerType {
1760 let _ = unit;
1761 if count > 3 && count.is_multiple_of(3) {
1762 count / 3
1763 } else if count == 1 || count.is_multiple_of(2) {
1764 2
1765 } else {
1766 count
1767 }
1768}
1769
1770pub(crate) fn offset_repr(offset: FloatType) -> String {
1773 if offset.fract() == 0.0 {
1774 format!("{offset:.1}")
1775 } else {
1776 offset.to_string()
1777 }
1778}
1779
1780fn offset_within(offset: FloatType, length: FloatType) -> Result<FloatType> {
1783 if offset.is_nan() || offset < 0.0 || offset >= length {
1784 return Err(Error::Meter(format!(
1785 "cannot access from qLenPos {} where total duration is {}",
1786 offset_repr(offset),
1787 offset_repr(length)
1788 )));
1789 }
1790 Ok(offset)
1791}
1792
1793#[cfg(test)]
1794mod tests {
1795
1796 #[test]
1797 fn an_offset_past_the_bar_wraps_from_where_the_meter_stands() {
1798 let three = TimeSignature::from_ratio_string("3/4").unwrap();
1800 assert_eq!(three.offset_in_bar(2.0, 0.0), 2.0);
1801 assert_eq!(three.offset_in_bar(4.0, 0.0), 1.0);
1802 let five = TimeSignature::from_ratio_string("5/4").unwrap();
1803 assert_eq!(five.offset_in_bar(3.0, 0.0), 3.0);
1804 assert_eq!(five.offset_in_bar(5.0, 0.0), 0.0);
1805
1806 assert_eq!(three.offset_in_bar(1.5, 1.0), 1.5);
1808 assert_eq!(three.offset_in_bar(2.5, 1.0), 1.5);
1809 }
1810
1811 #[test]
1812 fn beat_divisions_are_read_off_the_beats_as_music21_reads_them() {
1813 let cases: [(&str, &[FloatType], &[FloatType]); 9] = [
1816 ("2+3/8", &[0.5, 0.5], &[0.25; 4]),
1817 ("3+3+2/8", &[0.5; 3], &[0.25; 6]),
1818 ("5/8", &[0.25, 0.25], &[0.125; 4]),
1819 ("7/8", &[0.25, 0.25], &[0.125; 4]),
1820 ("4/4", &[0.5, 0.5], &[0.25; 4]),
1821 ("6/8", &[0.5; 3], &[0.25; 6]),
1822 ("5/4", &[0.5, 0.5], &[0.25; 4]),
1823 ("9/8", &[0.5; 3], &[0.25; 6]),
1824 ("3/2", &[1.0, 1.0], &[0.5; 4]),
1825 ];
1826 for (ratio, divisions, subdivisions) in cases {
1827 let meter = TimeSignature::from_ratio_string(ratio).unwrap();
1828 let lengths = |durations: Vec<Duration>| -> Vec<FloatType> {
1829 durations.iter().map(Duration::quarter_length).collect()
1830 };
1831 assert_eq!(
1832 lengths(meter.beat_division_durations().unwrap()),
1833 divisions,
1834 "{ratio}"
1835 );
1836 assert_eq!(
1837 lengths(meter.beat_sub_division_durations().unwrap()),
1838 subdivisions,
1839 "{ratio}"
1840 );
1841 }
1842
1843 for ratio in ["3/4+3/8", "2/4+3/8", "1/4+1/8"] {
1845 let meter = TimeSignature::from_ratio_string(ratio).unwrap();
1846 assert!(meter.beat_division_durations().is_err(), "{ratio}");
1847 }
1848 }
1849 #[test]
1853 fn a_meter_is_read_as_music21_writes_it() {
1854 use crate::meter::TimeSignature;
1855
1856 let plain = TimeSignature::from_ratio_string("6/8").unwrap();
1860 let slow = TimeSignature::from_ratio_string("slow 6/8").unwrap();
1861 assert_eq!(TimeSignature::from_ratio_string("fast 6/8").unwrap(), plain);
1862 assert_ne!(slow, plain);
1863 assert_eq!(
1864 (slow.numerator(), slow.denominator()),
1865 (plain.numerator(), plain.denominator())
1866 );
1867 assert_eq!(plain.beat_sequence().len(), 2);
1868 assert_eq!(slow.beat_sequence().len(), 6);
1869
1870 let additive = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
1871 assert_eq!((additive.numerator(), additive.denominator()), (5, 8));
1872 assert_eq!(
1873 TimeSignature::parts("3/8+2/8").unwrap(),
1874 vec![(3, 8), (2, 8)]
1875 );
1876 assert_eq!(TimeSignature::parts("3+2/8").unwrap(), vec![(3, 8), (2, 8)]);
1878 assert_eq!(
1879 TimeSignature::parts("3+2+5/8+3/4").unwrap(),
1880 vec![(3, 8), (2, 8), (5, 8), (3, 4)]
1881 );
1882 let mixed = TimeSignature::from_ratio_string("3/8+1/4").unwrap();
1884 assert_eq!((mixed.numerator(), mixed.denominator()), (5, 8));
1885
1886 assert!(TimeSignature::parts("3+2+5").is_err());
1887 assert!(TimeSignature::from_ratio_string("").is_err());
1888 assert!(TimeSignature::from_ratio_string("3.0/4.0").is_err());
1889 }
1890
1891 #[test]
1892 fn a_summed_numerator_is_beamed_as_it_was_written() {
1893 use crate::meter::TimeSignature;
1894
1895 let expected = [
1900 ("3+2/8", true, "{3/8+2/8}"),
1901 ("3/8+2/8", false, "{2/8+3/8}"),
1902 ("2+3/8", true, "{2/8+3/8}"),
1903 ("3+2+2/8", true, "{3/8+2/8+2/8}"),
1904 ("3/8+2/8+2/8", false, "{2/8+2/8+3/8}"),
1905 ("2+2+3/16", true, "{2/16+2/16+3/16}"),
1906 ("3/16+2/16", false, "{3/16+2/16}"),
1907 ("6/8", false, "{3/8+3/8}"),
1908 ];
1909 for (written, summed, beams) in expected {
1910 let signature = TimeSignature::from_ratio_string(written).unwrap();
1911 assert_eq!(signature.summed_numerator(), summed, "{written}");
1912 assert_eq!(signature.beam_sequence().to_string(), beams, "{written}");
1913 }
1914
1915 let mut signature = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
1916 signature.set_summed_numerator(true);
1917 assert!(signature.summed_numerator());
1918 assert!(signature.display_sequence().summed_numerator());
1919 }
1920
1921 #[test]
1922 fn the_sequences_are_partitioned_as_music21_partitions_them() {
1923 use crate::meter::TimeSignature;
1924
1925 let expected = [
1928 ("1/4", "{1/4}", "{1/4}"),
1929 ("2/4", "{{1/8+1/8}+{1/8+1/8}}", "{{1/8+1/8}+{1/8+1/8}}"),
1930 (
1931 "3/4",
1932 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1933 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1934 ),
1935 (
1936 "4/4",
1937 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1938 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1939 ),
1940 (
1941 "5/4",
1942 "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1943 "{{1/4+1/4}+{1/4+1/4+1/4}}",
1944 ),
1945 ("6/4", "{{1/4+1/4+1/4}+{1/4+1/4+1/4}}", "{3/4+3/4}"),
1946 ("2/2", "{{1/4+1/4}+{1/4+1/4}}", "{1/2+1/2}"),
1947 ("3/2", "{{1/4+1/4}+{1/4+1/4}+{1/4+1/4}}", "{1/2+1/2+1/2}"),
1948 ("3/8", "{3/8}", "{3/8}"),
1949 (
1950 "5/8",
1951 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
1952 "{2/8+3/8}",
1953 ),
1954 ("6/8", "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}", "{3/8+3/8}"),
1955 (
1956 "9/8",
1957 "{{1/8+1/8+1/8}+{1/8+1/8+1/8}+{1/8+1/8+1/8}}",
1958 "{3/8+3/8+3/8}",
1959 ),
1960 (
1961 "12/8",
1962 "{{1/8+1/8+1/8}+{1/8+1/8+1/8}+{1/8+1/8+1/8}+{1/8+1/8+1/8}}",
1963 "{3/8+3/8+3/8+3/8}",
1964 ),
1965 (
1966 "5/16",
1967 "{{1/32+1/32}+{1/32+1/32}+{1/32+1/32}+{1/32+1/32}+{1/32+1/32}}",
1968 "{5/16}",
1969 ),
1970 (
1971 "slow 6/8",
1972 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
1973 "{3/8+3/8}",
1974 ),
1975 ("fast 6/8", "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}", "{3/8+3/8}"),
1976 ("6/8 fast", "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}", "{3/8+3/8}"),
1977 (
1978 "6/8 Slow",
1979 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
1980 "{3/8+3/8}",
1981 ),
1982 ("3/8+2/8", "{{1/8+1/8+1/8}+{1/8+1/8}}", "{2/8+3/8}"),
1983 ("2/4+3/8", "{{1/4+1/4}+{1/8+1/8+1/8}}", "{2/8+2/8+3/8}"),
1984 ];
1985 for (ratio, beats, beams) in expected {
1986 let signature = TimeSignature::from_ratio_string(ratio).unwrap();
1987 assert_eq!(
1988 signature.beat_sequence().to_string(),
1989 beats,
1990 "beats of {ratio}"
1991 );
1992 assert_eq!(
1993 signature.beam_sequence().to_string(),
1994 beams,
1995 "beams of {ratio}"
1996 );
1997 }
1998 }
1999
2000 #[test]
2002 fn accent_weights_and_beat_depths_follow_the_default_hierarchy() {
2003 let weights = |ratio: &str| {
2004 TimeSignature::from_ratio_string(ratio)
2005 .unwrap()
2006 .accent_weights()
2007 };
2008 assert_eq!(
2009 weights("4/4"),
2010 [1.0, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125]
2011 );
2012 assert_eq!(
2013 weights("6/8"),
2014 [
2015 1.0, 0.125, 0.25, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125, 0.25, 0.125
2016 ]
2017 );
2018 assert_eq!(
2019 weights("12/8"),
2020 [
2021 1.0, 0.125, 0.125, 0.25, 0.125, 0.125, 0.5, 0.125, 0.125, 0.25, 0.125, 0.125
2022 ]
2023 );
2024 assert_eq!(weights("3/4").len(), 12);
2025 assert_eq!(weights("24/8").len(), 24);
2026 assert_eq!(
2027 TimeSignature::new(1, 4)
2028 .unwrap()
2029 .accent_partition_quarter_length(),
2030 0.125
2031 );
2032
2033 let three_four = TimeSignature::new(3, 4).unwrap();
2034 let read: Vec<FloatType> = (0..3)
2035 .map(|beat| three_four.accent_weight(FloatType::from(beat)).unwrap())
2036 .collect();
2037 assert_eq!(read, [1.0, 0.5, 0.5]);
2038 let beyond = three_four.accent_weight(3.0).unwrap_err().to_string();
2039 assert!(beyond.ends_with("cannot access from qLenPos 3.0 where total duration is 3.0"));
2040 assert_eq!(weights("16/1"), [1.0]);
2043 assert_eq!(weights("1/32"), [1.0]);
2044 assert_eq!(
2045 TimeSignature::new(24, 4)
2046 .unwrap()
2047 .accent_partition_quarter_length(),
2048 1.0
2049 );
2050 assert_eq!(
2051 three_four.accent_weight_with(4.0, false, true).unwrap(),
2052 0.5
2053 );
2054 assert_eq!(
2055 three_four.accent_weight_with(0.1, true, false).unwrap(),
2056 0.0625
2057 );
2058 assert_eq!(
2059 three_four.accent_weight_with(0.1, false, false).unwrap(),
2060 1.0
2061 );
2062 assert!(three_four.accent(2.0));
2063 assert!(!three_four.accent(0.1));
2064 assert!(!three_four.accent(3.0));
2065
2066 assert_eq!(three_four.beat_depth(0.0).unwrap(), 2);
2067 assert_eq!(three_four.beat_depth(0.25).unwrap(), 2);
2068 assert_eq!(three_four.beat_depth(0.5).unwrap(), 1);
2069 assert_eq!(three_four.beat_depth(1.0).unwrap(), 2);
2070 assert!(three_four.beat_depth(3.0).is_err());
2071 assert_eq!(
2072 TimeSignature::new(3, 8).unwrap().beat_depth(0.5).unwrap(),
2073 1
2074 );
2075 assert_eq!(
2076 TimeSignature::new(6, 8).unwrap().beat_depth(1.0).unwrap(),
2077 1
2078 );
2079 assert_eq!(
2080 TimeSignature::new(6, 8).unwrap().beat_depth(1.5).unwrap(),
2081 2
2082 );
2083 }
2084
2085 #[test]
2087 fn the_best_time_signature_fits_what_the_measure_holds() {
2088 use crate::{Note, Pitch, Stream};
2089
2090 let measure = |lengths: &[FloatType]| {
2091 let mut stream = Stream::new();
2092 for length in lengths {
2093 let mut note = Note::from_pitch(Pitch::from_name("C4").unwrap());
2094 note.set_duration(Duration::new(*length).unwrap());
2095 stream.push(note);
2096 }
2097 stream
2098 };
2099 let best = |lengths: &[FloatType]| {
2100 best_time_signature(&measure(lengths))
2101 .unwrap()
2102 .ratio_string()
2103 };
2104 assert_eq!(best(&[1.0, 1.0, 0.5, 0.5]), "3/4");
2105 assert_eq!(best(&[0.75, 0.25, 0.5, 0.75, 0.25, 0.5]), "6/8");
2106 assert_eq!(best(&[2.0, 2.0, 2.0]), "3/2");
2107 assert_eq!(best(&[0.75, 0.25, 0.5, 0.75, 0.25, 0.5, 1.5, 1.5]), "12/8");
2108 assert_eq!(best(&[1.0, 2.0, 1.0, 2.0]), "6/4");
2109 assert_eq!(best(&[1.0, 0.375]), "11/32");
2110 assert_eq!(best(&[3.5, 5.5]), "9/4");
2111 assert_eq!(best(&[1.0, 1.0, 1.0, 1.0]), "4/4");
2112 assert_eq!(best(&[4.0]), "4/4");
2113 assert_eq!(best(&[1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0, 1.0]), "2/4");
2115 }
2116
2117 #[test]
2120 fn beat_strength_is_averaged_over_a_stream() {
2121 use crate::{Note, Pitch, Stream};
2122
2123 let mut stream = Stream::new();
2124 for (name, length) in [("C4", 1.0), ("D4", 1.0), ("E4", 0.5), ("F4", 0.5)] {
2125 let mut note = Note::from_pitch(Pitch::from_name(name).unwrap());
2126 note.set_duration(Duration::new(length).unwrap());
2127 stream.push(note);
2128 }
2129 assert_eq!(
2130 TimeSignature::new(6, 8)
2131 .unwrap()
2132 .average_beat_strength(&stream, true),
2133 0.4375
2134 );
2135 assert_eq!(
2136 TimeSignature::new(3, 4)
2137 .unwrap()
2138 .average_beat_strength(&stream, true),
2139 0.5625
2140 );
2141 stream.insert(0.0, TimeSignature::new(3, 4).unwrap());
2142 stream.insert(0.0, TimeSignature::new(6, 8).unwrap());
2143 assert_eq!(
2144 TimeSignature::new(6, 8)
2145 .unwrap()
2146 .average_beat_strength(&stream, false),
2147 0.625
2148 );
2149 assert_eq!(
2150 TimeSignature::new(4, 4)
2151 .unwrap()
2152 .average_beat_strength(&Stream::new(), true),
2153 0.0
2154 );
2155 }
2156
2157 #[test]
2162 fn a_measure_weighs_its_own_notes_and_not_its_voices() {
2163 use crate::stream::StreamKind;
2164 use crate::{Note, Pitch, Stream};
2165
2166 let mut voice = Stream::with_kind(StreamKind::Voice);
2167 for (name, length) in [("C5", 2.0), ("B4", 1.0)] {
2168 let mut note = Note::from_pitch(Pitch::from_name(name).unwrap());
2169 note.set_duration(Duration::new(length).unwrap());
2170 voice.push(note);
2171 }
2172 let mut plain = voice.clone();
2173 plain.set_kind(StreamKind::Measure);
2174 let mut voiced = Stream::with_kind(StreamKind::Measure);
2175 voiced.insert(0.0, voice);
2176
2177 let three_four = TimeSignature::new(3, 4).unwrap();
2178 assert_eq!(three_four.average_beat_strength(&voiced, true), 0.0);
2179 assert_eq!(best_time_signature(&plain).unwrap().ratio_string(), "3/4");
2180 assert_eq!(best_time_signature(&voiced).unwrap().ratio_string(), "6/8");
2181 }
2182
2183 #[test]
2184 fn a_signature_reports_its_two_numbers_and_its_bar() {
2185 let six_eight = TimeSignature::new(6, 8).unwrap();
2186 assert_eq!((six_eight.numerator(), six_eight.denominator()), (6, 8));
2187 assert_eq!(six_eight.bar_duration().quarter_length(), 3.0);
2188 }
2189
2190 #[test]
2191 fn division_helpers_match_music21() {
2192 let quarter_lengths = |durations: Vec<Duration>| -> Vec<FloatType> {
2193 durations.into_iter().map(|d| d.quarter_length()).collect()
2194 };
2195 let cases: [(&str, FloatType, &str, &[FloatType], usize); 7] = [
2196 ("4/4", 1.0, "Simple", &[0.5, 0.5], 4),
2197 ("6/8", 0.5, "Compound", &[0.5, 0.5, 0.5], 6),
2198 ("2/2", 2.0, "Simple", &[1.0, 1.0], 4),
2199 ("3/8", 0.5, "Other", &[1.5], 2),
2200 ("7/8", 0.5, "Simple", &[0.25, 0.25], 4),
2201 ("12/16", 0.25, "Compound", &[0.25, 0.25, 0.25], 6),
2202 ("1/4", 1.0, "Other", &[1.0], 2),
2203 ];
2204 for (ratio, beat_to_quarter, division_name, divisions, sub_divisions) in cases {
2205 let ts = ts(ratio);
2206 assert_eq!(
2207 ts.beat_length_to_quarter_length_ratio(),
2208 beat_to_quarter,
2209 "{ratio}"
2210 );
2211 assert_eq!(
2212 ts.quarter_length_to_beat_length_ratio(),
2213 1.0 / beat_to_quarter,
2214 "{ratio}"
2215 );
2216 assert_eq!(ts.beat_division_count_name(), division_name, "{ratio}");
2217 assert_eq!(
2218 quarter_lengths(ts.beat_division_durations().unwrap()),
2219 divisions,
2220 "{ratio}"
2221 );
2222 let subs = quarter_lengths(ts.beat_sub_division_durations().unwrap());
2223 assert_eq!(subs.len(), sub_divisions, "{ratio}");
2224 assert!(subs.iter().all(|ql| *ql == divisions[0] / 2.0), "{ratio}");
2225 }
2226 assert!(ts("4/4").ratio_equal(&ts("4/4")));
2227 assert!(!ts("4/4").ratio_equal(&ts("2/2")));
2228 }
2229
2230 #[test]
2231 fn beat_positions_match_music21() {
2232 let common = ts("4/4");
2233 let cases: [(FloatType, UnsignedIntegerType, FloatType, FloatType, &str); 7] = [
2234 (0.0, 1, 0.0, 1.0, "1"),
2235 (0.5, 1, 0.5, 1.5, "1 1/2"),
2236 (1.25, 2, 0.25, 2.25, "2 1/4"),
2237 (2.75, 3, 0.75, 3.75, "3 3/4"),
2238 (3.0, 4, 0.0, 4.0, "4"),
2239 (3.75, 4, 0.75, 4.75, "4 3/4"),
2240 (3.9, 4, 0.9, 4.9, "4 9/10"),
2241 ];
2242 for (offset, beat, progress, proportion, text) in cases {
2243 let (actual_beat, actual_progress) = common.beat_progress(offset).unwrap();
2244 assert_eq!(actual_beat, beat, "{offset}");
2245 assert!((actual_progress - progress).abs() < 1e-9, "{offset}");
2246 assert!(
2247 (common.beat_proportion(offset).unwrap() - proportion).abs() < 1e-9,
2248 "{offset}"
2249 );
2250 assert_eq!(
2251 common.beat_proportion_string(offset).unwrap(),
2252 text,
2253 "{offset}"
2254 );
2255 }
2256
2257 let compound = ts("6/8");
2258 assert_eq!(compound.beat_proportion_string(0.5).unwrap(), "1 1/3");
2259 assert_eq!(compound.beat_proportion_string(1.0).unwrap(), "1 2/3");
2260 assert_eq!(compound.beat_proportion_string(2.5).unwrap(), "2 2/3");
2261 assert!((compound.beat_proportion(2.0).unwrap() - 7.0 / 3.0).abs() < 1e-9);
2262 assert_eq!(ts("3/8").beat_proportion_string(1.0).unwrap(), "1 2/3");
2263 assert!(common.beat_progress(4.0).is_err());
2264 }
2265
2266 #[test]
2267 fn offset_from_beat_matches_music21() {
2268 let common = ts("4/4");
2269 for (beat, offset) in [
2270 (1.0, 0.0),
2271 (1.5, 0.5),
2272 (2.5, 1.5),
2273 (3.25, 2.25),
2274 (4.75, 3.75),
2275 ] {
2276 assert_eq!(common.offset_from_beat(beat).unwrap(), offset, "{beat}");
2277 }
2278 assert!(common.offset_from_beat(5.0).is_err());
2279 assert!(common.offset_from_beat(0.5).is_err());
2280 let compound = ts("6/8");
2281 assert_eq!(compound.offset_from_beat(1.5).unwrap(), 0.75);
2282 assert_eq!(compound.offset_from_beat(2.5).unwrap(), 2.25);
2283 assert!((compound.offset_from_beat(2.999).unwrap() - 2.9985).abs() < 1e-9);
2284 assert_eq!(ts("3/8").offset_from_beat(1.5).unwrap(), 0.75);
2285 }
2286
2287 #[test]
2288 fn closest_fraction_limits_the_denominator_like_python() {
2289 assert_eq!(closest_fraction(0.5, 16), (1, 2));
2290 assert_eq!(closest_fraction(1.0 / 3.0, 16), (1, 3));
2291 assert_eq!(closest_fraction(0.9, 16), (9, 10));
2292 assert_eq!(closest_fraction(0.75, 16), (3, 4));
2293 assert_eq!(closest_fraction(0.1234, 16), (1, 8));
2294 }
2295 use super::*;
2296
2297 fn ts(ratio: &str) -> TimeSignature {
2298 TimeSignature::from_ratio_string(ratio).expect("valid time signature")
2299 }
2300
2301 #[test]
2302 fn common_and_cut_time_match_their_ratios() {
2303 assert_eq!(TimeSignature::common().ratio_string(), "4/4");
2304 assert_eq!(TimeSignature::cut().ratio_string(), "2/2");
2305 assert_eq!(TimeSignature::default(), TimeSignature::common());
2306 }
2307
2308 #[test]
2309 fn bar_and_beat_lengths_follow_the_ratio() {
2310 assert_eq!(ts("4/4").bar_quarter_length(), 4.0);
2311 assert_eq!(ts("5/16").bar_quarter_length(), 1.25);
2312 assert_eq!(ts("3/8").bar_quarter_length(), 1.5);
2313 assert_eq!(ts("6/8").beat_quarter_length().unwrap(), 1.5);
2314 assert_eq!(ts("4/4").beat_quarter_length().unwrap(), 1.0);
2315 assert_eq!(ts("2/2").beat_duration().unwrap().quarter_length(), 2.0);
2316 }
2317
2318 #[test]
2319 fn compound_meters_beat_in_threes() {
2320 for (ratio, beats, division) in [
2321 ("6/8", 2, BeatDivision::Compound),
2322 ("9/8", 3, BeatDivision::Compound),
2323 ("12/8", 4, BeatDivision::Compound),
2324 ("15/8", 5, BeatDivision::Compound),
2325 ("18/8", 6, BeatDivision::Compound),
2326 ("24/8", 8, BeatDivision::Compound),
2327 ] {
2328 assert_eq!(ts(ratio).beat_count(), beats, "{ratio}");
2329 assert_eq!(ts(ratio).beat_division(), division, "{ratio}");
2330 assert!(ts(ratio).is_compound(), "{ratio}");
2331 }
2332 }
2333
2334 #[test]
2335 fn three_is_the_one_denominator_sensitive_numerator() {
2336 assert_eq!(ts("3/2").beat_count(), 3);
2338 assert_eq!(ts("3/4").beat_count(), 3);
2339 assert_eq!(ts("3/8").beat_count(), 1);
2340 assert_eq!(ts("3/16").beat_count(), 1);
2341 assert_eq!(ts("3/32").beat_count(), 1);
2342 assert_eq!(ts("3/3").beat_count(), 3);
2345 assert_eq!(ts("3/6").beat_count(), 3);
2346 assert_eq!(ts("3/12").beat_count(), 1);
2347 for denominator in [2, 4, 8, 16] {
2349 assert_eq!(TimeSignature::new(6, denominator).unwrap().beat_count(), 2);
2350 assert_eq!(TimeSignature::new(5, denominator).unwrap().beat_count(), 5);
2351 }
2352 }
2353
2354 #[test]
2355 fn a_bar_written_in_unequal_parts_is_counted_along_its_own_beats() {
2356 use crate::meter::TimeSignature;
2357
2358 let mixed = TimeSignature::from_ratio_string("2/4+3/8").unwrap();
2361 assert_eq!(mixed.bar_quarter_length(), 3.5);
2362 assert_eq!(mixed.beat_count(), 2);
2363 assert_eq!(mixed.beat_offsets(), vec![0.0, 2.0]);
2364 assert_eq!(mixed.beat_at_offset(2.5).unwrap(), 2);
2365 assert_eq!(mixed.beat_duration_at(0.0).unwrap().quarter_length(), 2.0);
2366 assert_eq!(mixed.beat_duration_at(2.5).unwrap().quarter_length(), 1.5);
2367 assert_eq!(mixed.offset_from_beat(2.0).unwrap(), 2.0);
2368 assert_eq!(mixed.offset_from_beat(1.5).unwrap(), 1.0);
2371 assert_eq!(mixed.beat_proportion_string(2.5).unwrap(), "2 1/3");
2372 assert_eq!(mixed.beat_depth(0.0).unwrap(), 2);
2373
2374 let other = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2375 assert_eq!(other.beat_offsets(), vec![0.0, 1.5]);
2376 assert_eq!(other.offset_from_beat(2.0).unwrap(), 1.5);
2377 assert_eq!(other.offset_from_beat(1.5).unwrap(), 0.75);
2378 assert!(other.beat_proportion_string(2.5).is_err());
2380
2381 let common = TimeSignature::common();
2383 assert_eq!(common.beat_offsets(), vec![0.0, 1.0, 2.0, 3.0]);
2384 assert_eq!(common.offset_from_beat(2.0).unwrap(), 1.0);
2385 assert_eq!(common.offset_from_beat(1.5).unwrap(), 0.5);
2386 assert_eq!(common.beat_proportion_string(2.5).unwrap(), "3 1/2");
2387 }
2388
2389 #[test]
2390 fn a_bar_whose_beats_differ_has_no_one_beat_length() {
2391 use crate::meter::TimeSignature;
2392
2393 let mixed = TimeSignature::from_ratio_string("2/4+3/8").unwrap();
2396 assert!(mixed.beat_quarter_length().is_err());
2397 assert!(mixed.beat_duration().is_err());
2398 assert!(mixed.beat_division_durations().is_err());
2399 assert_eq!(mixed.beat_division_count(), 1);
2400 assert_eq!(mixed.beat_division_count_name(), "Other");
2401 assert_eq!(mixed.classification(), "Other Duple");
2402
2403 let other = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2404 assert!(other.beat_quarter_length().is_err());
2405 assert_eq!(other.beat_division_count(), 1);
2406 assert_eq!(other.classification(), "Other Duple");
2407
2408 assert_eq!(TimeSignature::common().beat_quarter_length().unwrap(), 1.0);
2410 assert_eq!(
2411 TimeSignature::from_ratio_string("6/8")
2412 .unwrap()
2413 .beat_quarter_length()
2414 .unwrap(),
2415 1.5
2416 );
2417 }
2418
2419 #[test]
2420 fn weights_a_caller_sets_are_the_weights_read_back() {
2421 use crate::meter::TimeSignature;
2422
2423 let mut common = TimeSignature::common();
2424 let default = common.accent_weights();
2425 assert_eq!(default[0], 1.0);
2426
2427 common.set_accent_weight(&[0.8, 0.2], 0).unwrap();
2429 let set = common.accent_weights();
2430 assert_eq!(set.len(), default.len());
2431 assert!((set[0] - 0.8).abs() < 1e-9);
2432 assert!((set[1] - 0.2).abs() < 1e-9);
2433 assert!((set[2] - 0.8).abs() < 1e-9);
2434
2435 assert!((common.accent_weight(0.0).unwrap() - 0.8).abs() < 1e-9);
2437
2438 assert_eq!(TimeSignature::common().accent_weights()[0], 1.0);
2440 }
2441
2442 #[test]
2443 fn a_bar_can_be_counted_in_a_different_number_of_beats() {
2444 use crate::meter::TimeSignature;
2445
2446 for (ratio, count, partitioned) in [
2448 (
2449 "6/8",
2450 6,
2451 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
2452 ),
2453 ("6/8", 2, "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}"),
2454 ("4/4", 2, "{{1/4+1/4}+{1/4+1/4}}"),
2455 ("3/4", 1, "{3/4}"),
2456 (
2457 "2/4",
2458 4,
2459 "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
2460 ),
2461 ] {
2462 let mut signature = TimeSignature::from_ratio_string(ratio).unwrap();
2463 signature.set_beat_count(count).unwrap();
2464 assert_eq!(
2465 signature.beat_sequence().to_string(),
2466 partitioned,
2467 "{ratio} in {count}"
2468 );
2469 assert_eq!(signature.beat_count(), count, "{ratio} in {count}");
2470 }
2471 }
2472
2473 #[test]
2474 fn a_bar_can_be_written_a_different_way_than_it_is_counted() {
2475 use crate::meter::TimeSignature;
2476
2477 let mut signature = TimeSignature::from_ratio_string("3/4").unwrap();
2480 let counted = signature.beat_sequence().to_string();
2481 signature.set_display("2/8+2/8+2/8").unwrap();
2482 assert_eq!(signature.display_sequence().to_string(), "{2/8+2/8+2/8}");
2483 assert_eq!(
2484 signature.display_sequence().partition_display(),
2485 "2/8+2/8+2/8"
2486 );
2487 assert_eq!(signature.beat_sequence().to_string(), counted);
2489 assert_eq!(signature.beat_count(), 3);
2490 assert!(signature.set_display("2/4").is_err());
2496
2497 let mut common = TimeSignature::common();
2499 common
2500 .beat_sequence_mut()
2501 .partition_by_count(2, true)
2502 .unwrap();
2503 assert_eq!(common.beat_count(), 2);
2504 }
2505
2506 #[test]
2507 fn the_divisions_a_meter_is_built_with_partition_its_beats() {
2508 use crate::meter::TimeSignature;
2509
2510 for (ratio, divisions, partitioned) in [
2512 ("3/4", 1, "{3/4}"),
2513 ("6/8", 2, "{3/8+3/8}"),
2514 ("4/4", 2, "{1/2+1/2}"),
2515 ("3/4", 3, "{1/4+1/4+1/4}"),
2516 ] {
2517 let mut signature = TimeSignature::from_ratio_string(ratio).unwrap();
2518 signature.divide_beats(divisions).unwrap();
2519 assert_eq!(
2520 signature.beat_sequence().to_string(),
2521 partitioned,
2522 "{ratio} in {divisions}"
2523 );
2524 }
2525
2526 let mut counted = TimeSignature::from_ratio_string("6/8").unwrap();
2528 counted.set_beat_count(2).unwrap();
2529 assert_eq!(
2530 counted.beat_sequence().to_string(),
2531 "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}"
2532 );
2533 }
2534
2535 #[test]
2536 fn a_fractional_beat_is_read_as_the_fraction_it_suggests() {
2537 use crate::meter::TimeSignature;
2538
2539 let compound = TimeSignature::from_ratio_string("6/8").unwrap();
2544 assert!((compound.offset_from_beat(2.33).unwrap() - 2.0).abs() < 1e-9);
2545 assert!((compound.offset_from_beat(2.66).unwrap() - 2.5).abs() < 1e-9);
2546 assert!((compound.offset_from_beat(1.66).unwrap() - 1.0).abs() < 1e-9);
2547
2548 let simple = TimeSignature::common();
2551 let third = 1.0 / 3.0;
2552 assert!((simple.offset_from_beat(2.33).unwrap() - (1.0 + third)).abs() < 1e-9);
2553 assert!((simple.offset_from_beat(2.66).unwrap() - (1.0 + 2.0 * third)).abs() < 1e-9);
2554 assert!((simple.offset_from_beat(1.66).unwrap() - (2.0 * third)).abs() < 1e-9);
2555
2556 let mut divided = TimeSignature::common();
2558 divided.divide_beats(4).unwrap();
2559 assert_eq!(divided.beat_sequence().to_string(), "{1/4+1/4+1/4+1/4}");
2560 assert_eq!(divided.accent_sequence().to_string(), "{1/4+1/4+1/4+1/4}");
2561 assert_eq!(divided.beam_sequence().to_string(), "{1/4+1/4+1/4+1/4}");
2562 assert_eq!(divided.display_sequence().to_string(), "{4/4}");
2563
2564 divided.set_accent_weight(&[0.8, 0.2], 0).unwrap();
2566 let weights = divided.accent_weights();
2567 assert_eq!(weights.len(), 4);
2568 assert!((weights[0] - 0.8).abs() < 1e-9);
2569 assert!((weights[1] - 0.2).abs() < 1e-9);
2570 }
2571
2572 #[test]
2573 fn classification_joins_division_and_count() {
2574 assert_eq!(ts("4/4").classification(), "Simple Quadruple");
2575 assert_eq!(ts("6/8").classification(), "Compound Duple");
2576 assert_eq!(ts("3/8").classification(), "Other Single");
2577 assert_eq!(ts("5/4").classification(), "Simple Quintuple");
2578 assert_eq!(ts("13/8").classification(), "Simple 13-uple");
2579 assert_eq!(ts("21/16").classification(), "Compound Septuple");
2580 }
2581
2582 #[test]
2583 fn beat_offsets_partition_the_bar() {
2584 assert_eq!(ts("4/4").beat_offsets(), [0.0, 1.0, 2.0, 3.0]);
2585 assert_eq!(ts("6/8").beat_offsets(), [0.0, 1.5]);
2586 assert_eq!(ts("5/8").beat_offsets(), [0.0, 0.5, 1.0, 1.5, 2.0]);
2587 }
2588
2589 #[test]
2590 fn beat_at_offset_is_one_based_and_bounded() {
2591 assert_eq!(ts("4/4").beat_at_offset(1.5).unwrap(), 2);
2592 assert_eq!(ts("6/8").beat_at_offset(1.5).unwrap(), 2);
2593 assert_eq!(ts("5/8").beat_at_offset(1.5).unwrap(), 4);
2594 assert_eq!(ts("4/4").beat_at_offset(0.0).unwrap(), 1);
2595 assert!(ts("4/4").beat_at_offset(4.0).is_err());
2596 assert!(ts("4/4").beat_at_offset(-0.5).is_err());
2597 assert!(ts("4/4").beat_at_offset(FloatType::NAN).is_err());
2598 }
2599
2600 #[test]
2601 fn irrational_denominators_are_accepted_as_music21_accepts_them() {
2602 let four_three = ts("4/3");
2603 assert!((four_three.bar_quarter_length() - 16.0 / 3.0).abs() < 1e-12);
2604 assert_eq!(four_three.beat_count(), 4);
2605 }
2606
2607 #[test]
2608 fn malformed_ratios_error_instead_of_panicking() {
2609 for ratio in [
2610 "", "4", "4/", "/4", "4/4/4", "x/4", "4/x", "0/4", "4/0", "-1/4",
2611 ] {
2612 assert!(
2613 TimeSignature::from_ratio_string(ratio).is_err(),
2614 "{ratio:?} should not parse"
2615 );
2616 }
2617 }
2618
2619 #[test]
2620 fn display_is_the_ratio_string() {
2621 assert_eq!(ts("7/8").to_string(), "7/8");
2622 }
2623
2624 #[test]
2625 fn a_meter_says_how_it_was_written() {
2626 use crate::meter::TimeSignature;
2627
2628 let written = TimeSignature::from_ratio_string("2/4+3/8").unwrap();
2630 assert_eq!(written.ratio_string(), "2/4+3/8");
2631 assert_eq!(written.to_string(), "2/4+3/8");
2632 assert_eq!((written.numerator(), written.denominator()), (7, 8));
2634
2635 let two_three = TimeSignature::from_ratio_string("2/8+3/8").unwrap();
2638 let three_two = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2639 assert_eq!(two_three.ratio_string(), "2/8+3/8");
2640 assert_eq!(three_two.ratio_string(), "3/8+2/8");
2641 assert_ne!(two_three, three_two);
2642 assert!(two_three.ratio_equal(&three_two));
2645
2646 let again = TimeSignature::from_ratio_string(&written.ratio_string()).unwrap();
2648 assert_eq!(again.ratio_string(), written.ratio_string());
2649
2650 let slow = TimeSignature::from_ratio_string("slow 6/8").unwrap();
2653 let fast = TimeSignature::from_ratio_string("6/8").unwrap();
2654 assert_eq!(slow.ratio_string(), "6/8");
2655 assert_eq!(fast.ratio_string(), "6/8");
2656 assert_ne!(slow, fast);
2657
2658 assert_eq!(TimeSignature::common().ratio_string(), "4/4");
2660 }
2661}