Skip to main content

music21_rs/meter/
mod.rs

1//! Time signatures, ported from music21's `meter` package.
2//!
3//! [`TimeSignature`] covers the part of music21's meter handling that is a pure
4//! function of the numerator and denominator: how long a bar is, how many beats
5//! it carries, how long each beat is, and how that beat subdivides.
6//!
7//! music21 derives all of this from a `MeterSequence` partition tree, and so
8//! does this: a meter carries the four sequences music21 hangs off one — the
9//! display, the beats, the beams and the accents — and answers from them.
10//! [`TimeSignature::beams_for`] beams a run of notes against the beam
11//! sequence, and the weights come off the accent sequence, so a caller who
12//! sets them reads their own back. The partition rule is music21's
13//! `_setDefaultBeatPartitions`, verified
14//! against upstream by the `meter_parity` fixture.
15
16pub 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/// Names music21 gives a partition count, indexed by the count itself.
27///
28/// Index 0 is music21's `Empty`, which a valid time signature never reaches.
29const BEAT_COUNT_NAMES: [&str; 9] = [
30    "Empty",
31    "Single",
32    "Duple",
33    "Triple",
34    "Quadruple",
35    "Quintuple",
36    "Sextuple",
37    "Septuple",
38    "Octuple",
39];
40
41/// How the beat of a meter subdivides.
42///
43/// Mirrors music21's `beatDivisionCountName`.
44#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub enum BeatDivision {
47    /// A single-beat meter, which music21 reports as `Other` rather than as a
48    /// division — there is no lower level to divide.
49    Other,
50    /// Beats divide in two.
51    Simple,
52    /// Beats divide in three.
53    Compound,
54}
55
56impl BeatDivision {
57    /// Returns music21's name for this division.
58    pub fn music21_name(&self) -> &'static str {
59        match self {
60            Self::Other => "Other",
61            Self::Simple => "Simple",
62            Self::Compound => "Compound",
63        }
64    }
65
66    /// Returns the number of divisions in one beat.
67    ///
68    /// Matches music21's `beatDivisionCount`, which reports `1` rather than
69    /// raising for a single-beat meter.
70    pub fn count(&self) -> UnsignedIntegerType {
71        match self {
72            Self::Other => 1,
73            Self::Simple => 2,
74            Self::Compound => 3,
75        }
76    }
77}
78
79/// A time signature, such as `4/4` or `6/8`.
80///
81/// ```
82/// use music21_rs::TimeSignature;
83///
84/// let six_eight = TimeSignature::from_ratio_string("6/8")?;
85/// assert_eq!(six_eight.beat_count(), 2);
86/// assert_eq!(six_eight.beat_quarter_length()?, 1.5);
87/// assert_eq!(six_eight.classification(), "Compound Duple");
88/// # Ok::<(), music21_rs::Error>(())
89/// ```
90/// Not `Copy`, `Eq` or `Hash`: a meter is about to carry the partitions it
91/// is felt in, and those weigh their parts in floats.
92#[derive(Clone, Debug, PartialEq)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
94#[must_use]
95pub struct TimeSignature {
96    numerator: UnsignedIntegerType,
97    denominator: UnsignedIntegerType,
98    favor_compound: bool,
99    display_sequence: MeterTerminal,
100    beat_sequence: MeterTerminal,
101    beam_sequence: MeterTerminal,
102    accent_sequence: MeterTerminal,
103}
104
105impl Default for TimeSignature {
106    /// Returns `4/4`, matching music21's default `TimeSignature()`.
107    fn default() -> Self {
108        Self::common()
109    }
110}
111
112impl TimeSignature {
113    /// Creates a time signature from a numerator and denominator.
114    ///
115    /// Both must be non-zero. The denominator need not be a power of two —
116    /// music21 accepts irrational meters such as `4/3`, and so does this.
117    pub fn new(numerator: UnsignedIntegerType, denominator: UnsignedIntegerType) -> Result<Self> {
118        if numerator == 0 {
119            return Err(Error::Meter(
120                "time signature numerator must be non-zero".to_string(),
121            ));
122        }
123        if denominator == 0 {
124            return Err(Error::Meter(
125                "time signature denominator must be non-zero".to_string(),
126            ));
127        }
128        let mut signature = Self {
129            numerator,
130            denominator,
131            favor_compound: favor_compound(numerator, denominator, None),
132            display_sequence: whole_bar(numerator, denominator)?,
133            beat_sequence: whole_bar(numerator, denominator)?,
134            beam_sequence: whole_bar(numerator, denominator)?,
135            accent_sequence: whole_bar(numerator, denominator)?,
136        };
137        signature.set_default_partitions()?;
138        Ok(signature)
139    }
140
141    /// How the bar is written, before anything divides it: music21's
142    /// `displaySequence`. A meter written additively as `"3/8+2/8"` is two
143    /// parts here, and that is what makes the beats fall where they are
144    /// written rather than where the numerator alone would put them.
145    #[must_use]
146    pub fn display_sequence(&self) -> &MeterTerminal {
147        &self.display_sequence
148    }
149
150    /// How the bar is counted: music21's `beatSequence`, one part per beat,
151    /// each divided again into what the beat is felt in.
152    #[must_use]
153    pub fn beat_sequence(&self) -> &MeterTerminal {
154        &self.beat_sequence
155    }
156
157    /// How the bar is beamed: music21's `beamSequence`, the groups a run of
158    /// short notes is written in.
159    #[must_use]
160    pub fn beam_sequence(&self) -> &MeterTerminal {
161        &self.beam_sequence
162    }
163
164    /// How the bar is weighted: music21's `accentSequence`, one part per
165    /// accent partition, each carrying the weight [`Self::accent_weights`]
166    /// gives it.
167    #[must_use]
168    pub fn accent_sequence(&self) -> &MeterTerminal {
169        &self.accent_sequence
170    }
171
172    /// Whether the bar is felt in compound beats: music21's `favorCompound`,
173    /// which is what counts a `6/8` in two and a `slow 6/8` in six.
174    #[must_use]
175    pub fn favors_compound(&self) -> bool {
176        self.favor_compound
177    }
178
179    /// The beats, to partition: music21 divides a meter by reaching into the
180    /// sequence it carries, as `ts.beatSequence.partition(2)`.
181    pub fn beat_sequence_mut(&mut self) -> &mut MeterTerminal {
182        &mut self.beat_sequence
183    }
184
185    /// The beams, to partition.
186    pub fn beam_sequence_mut(&mut self) -> &mut MeterTerminal {
187        &mut self.beam_sequence
188    }
189
190    /// The accent partitions, to partition or to weigh.
191    pub fn accent_sequence_mut(&mut self) -> &mut MeterTerminal {
192        &mut self.accent_sequence
193    }
194
195    /// How the bar is written, to partition.
196    pub fn display_sequence_mut(&mut self) -> &mut MeterTerminal {
197        &mut self.display_sequence
198    }
199
200    /// Writes the bar a different way without changing what it counts:
201    /// music21's `setDisplay`.
202    ///
203    /// A bar of `3/4` set to display as `"2/8+2/8+2/8"` is still three
204    /// quarters long and still counted in three; what changes is how it is
205    /// written. The value has to come to the same length as the bar, since a
206    /// bar cannot be written as something it is not.
207    pub fn set_display(&mut self, value: &str) -> Result<()> {
208        let parts = Self::parts(value)?;
209        let denominator = parts
210            .iter()
211            .map(|(_, part)| *part)
212            .max()
213            .unwrap_or(self.denominator);
214        let numerator = parts
215            .iter()
216            .map(|(count, part)| count * (denominator / part))
217            .sum();
218        let mut display = whole_bar(numerator, denominator)?;
219        if (display.quarter_length() - self.bar_quarter_length()).abs() > OFFSET_TOLERANCE {
220            return Err(Error::Meter(format!(
221                "cannot write a {} bar as {value:?}: that is {} quarter lengths, not {}",
222                self.ratio_string(),
223                display.quarter_length(),
224                self.bar_quarter_length()
225            )));
226        }
227        if parts.len() > 1 {
228            let written: Vec<String> = parts
229                .iter()
230                .map(|(count, part)| format!("{count}/{part}"))
231                .collect();
232            let borrowed: Vec<&str> = written.iter().map(String::as_str).collect();
233            display.partition_by_parts(&borrowed)?;
234        }
235        self.display_sequence = display;
236        Ok(())
237    }
238
239    /// Divides the beats into `count` parts, leaving each part undivided:
240    /// the `divisions` a meter is built with.
241    ///
242    /// This is not [`Self::set_beat_count`]: music21 partitions the beats
243    /// here and stops, so `TimeSignature("6/8", 2)` is `{3/8+3/8}` where
244    /// counting a `6/8` in two gives `{{1/8+1/8+1/8}+{1/8+1/8+1/8}}`.
245    pub fn divide_beats(&mut self, count: UnsignedIntegerType) -> Result<()> {
246        if count == 0 {
247            return Err(Error::Meter(
248                "a bar cannot be divided into no parts".to_string(),
249            ));
250        }
251        let mut divided = whole_bar(self.numerator, self.denominator)?;
252        divided.partition_by_count(count as usize, false)?;
253        // music21 divides the beats, the accents and the beams alike here,
254        // and leaves the display saying what the bar was written as.
255        self.beat_sequence = divided.clone();
256        self.accent_sequence = divided.clone();
257        self.beam_sequence = divided;
258        Ok(())
259    }
260
261    /// Counts the bar in a different number of beats: music21's settable
262    /// `beatCount`.
263    ///
264    /// The beats are repartitioned and each divided again, so a `6/8`
265    /// counted in six is six eighths rather than two dotted quarters. A
266    /// count the bar cannot be divided into is an error.
267    pub fn set_beat_count(&mut self, count: UnsignedIntegerType) -> Result<()> {
268        if count == 0 {
269            return Err(Error::Meter(
270                "a bar cannot be counted in no beats".to_string(),
271            ));
272        }
273        let mut beats = MeterTerminal::new(self.numerator, self.denominator)?;
274        beats.partition_by_count(count as usize, false)?;
275        if beats.len() > 1 {
276            let _ = beats.subdivide_partitions_equal(None);
277        }
278        self.beat_sequence = beats;
279        Ok(())
280    }
281
282    /// Builds the beam, beat and accent partitions the way music21 does when
283    /// nobody has said how: `_setDefaultBeamPartitions`,
284    /// `_setDefaultBeatPartitions` and `_setDefaultAccentWeights`.
285    fn set_default_partitions(&mut self) -> Result<()> {
286        self.set_default_beam_partitions()?;
287        self.set_default_beat_partitions()?;
288        self.set_default_accent_weights();
289        Ok(())
290    }
291
292    /// music21's `_setDefaultBeamPartitions`: a short bar of short notes is
293    /// beamed all together, and anything else in the groups its numerator is
294    /// felt in.
295    ///
296    /// A meter written additively is beamed by these rules too — music21
297    /// beams `"3/8+2/8"` as `{2/8+3/8}`, by the rule for a five, and not in
298    /// the parts it was written in.
299    fn set_default_beam_partitions(&mut self) -> Result<()> {
300        let numerator = self.numerator;
301        let denominator = self.denominator;
302        if (denominator == 8 && matches!(numerator, 1..=3))
303            || (denominator == 16 && matches!(numerator, 1..=5))
304            || (denominator == 32 && matches!(numerator, 1..=11))
305        {
306            return Ok(());
307        }
308        match numerator {
309            2..=4 => {
310                self.beam_sequence
311                    .partition_by_count(numerator as usize, true)?;
312                if denominator == 4 {
313                    for part in self.beam_sequence.parts_mut() {
314                        part.subdivide(2)?;
315                    }
316                }
317            }
318            5 => {
319                self.beam_sequence.partition_by_list(&[2, 3])?;
320                if denominator == 4 {
321                    for (part, count) in self.beam_sequence.parts_mut().iter_mut().zip([2, 3]) {
322                        part.subdivide(count)?;
323                    }
324                }
325            }
326            7 => self.beam_sequence.partition_by_count(3, true)?,
327            6 | 9 | 12 | 15 | 18 | 21 => {
328                let threes = vec![3; (numerator / 3) as usize];
329                self.beam_sequence.partition_by_list(&threes)?;
330            }
331            _ => {}
332        }
333        Ok(())
334    }
335
336    /// music21's `_setDefaultBeatPartitions`: the top-level count of the bar,
337    /// then each beat divided into what it is felt in.
338    ///
339    /// A meter written additively is counted in the parts it was written in,
340    /// each keeping the note it was written in — `"2/4+3/8"` is counted
341    /// `{{1/4+1/4}+{1/8+1/8+1/8}}`.
342    fn set_default_beat_partitions(&mut self) -> Result<()> {
343        let numerator = self.numerator;
344        let compound = self.favor_compound;
345        if self.display_sequence.len() == 1 {
346            match numerator {
347                2 => self.beat_sequence.partition_by_count(2, true)?,
348                6 if compound => self.beat_sequence.partition_by_count(2, true)?,
349                3 if compound => self.beat_sequence.partition_by_count(1, true)?,
350                3 => self.beat_sequence.partition_by_list(&[1, 1, 1])?,
351                9 if compound => self.beat_sequence.partition_by_list(&[3, 3, 3])?,
352                4 => self.beat_sequence.partition_by_count(4, true)?,
353                12 if compound => self.beat_sequence.partition_by_count(4, true)?,
354                other if other >= 15 && other.is_multiple_of(3) && compound => {
355                    let threes = vec![3; (other / 3) as usize];
356                    self.beat_sequence.partition_by_list(&threes)?;
357                }
358                other => self
359                    .beat_sequence
360                    .partition_by_count(other as usize, true)?,
361            }
362        } else {
363            let written: Vec<String> = self
364                .display_sequence
365                .parts()
366                .iter()
367                .map(|part| format!("{}/{}", part.numerator(), part.denominator()))
368                .collect();
369            let borrowed: Vec<&str> = written.iter().map(String::as_str).collect();
370            self.beat_sequence.partition_by_parts(&borrowed)?;
371        }
372        if self.beat_sequence.len() > 1 {
373            // music21 forgives a bar written in notes shorter than a 128th
374            // for being too short to divide again, and nothing else.
375            match self.beat_sequence.subdivide_partitions_equal(None) {
376                Ok(()) => {}
377                Err(error) if self.denominator >= 128 => {
378                    let _ = error;
379                }
380                Err(error) => return Err(error),
381            }
382        }
383        Ok(())
384    }
385
386    /// music21's `_setDefaultAccentWeights`, read off the weights this meter
387    /// already gives its accent partitions rather than derived a second time
388    /// from a nested hierarchy: one part per weight, carrying that weight.
389    fn set_default_accent_weights(&mut self) {
390        let weights = self.default_accent_weights();
391        let ones = vec![1; weights.len()];
392        if self.accent_sequence.partition_by_list(&ones).is_err() {
393            return;
394        }
395        for (part, weight) in self.accent_sequence.parts_mut().iter_mut().zip(weights) {
396            part.set_weight(weight);
397        }
398    }
399
400    /// Parses a `"numerator/denominator"` string such as `"6/8"`.
401    ///
402    /// music21 writes more than a bare ratio here. A word before the ratio
403    /// says how the beat is felt — `"slow 6/8"` is counted in six and
404    /// `"fast 6/8"` in two — and a meter can be written additively, as
405    /// `"3/8+2/8"` or `"3+2/8"`, where a numerator with no denominator of
406    /// its own takes the next one written. What the parts add up to is the
407    /// meter this returns; how they are grouped is [`Self::parts`].
408    pub fn from_ratio_string(ratio: &str) -> Result<Self> {
409        let parts = Self::parts(ratio)?;
410        let word = division_word(ratio);
411        let denominator = parts[0].1;
412        if parts.iter().all(|(_, part)| *part == denominator) {
413            let numerator = parts.iter().map(|(count, _)| count).sum();
414            let mut signature = Self::new(numerator, denominator)?;
415            signature.write_as(&parts, word)?;
416            return Ok(signature);
417        }
418        // Parts measured in different notes: what they come to together is
419        // the meter, over the shortest note any of them is written in.
420        let denominator = parts
421            .iter()
422            .map(|(_, part)| *part)
423            .max()
424            .unwrap_or(denominator);
425        let numerator = parts
426            .iter()
427            .map(|(count, part)| count * (denominator / part))
428            .sum();
429        let mut signature = Self::new(numerator, denominator)?;
430        signature.write_as(&parts, word)?;
431        Ok(signature)
432    }
433
434    /// Records how the meter was written — the parts it was written in, and
435    /// the word saying how it is counted — and rebuilds what depends on
436    /// either.
437    fn write_as(
438        &mut self,
439        parts: &[(UnsignedIntegerType, UnsignedIntegerType)],
440        word: Option<&str>,
441    ) -> Result<()> {
442        self.favor_compound = favor_compound(self.numerator, self.denominator, word);
443        if parts.len() > 1 {
444            let written: Vec<String> = parts
445                .iter()
446                .map(|(count, part)| format!("{count}/{part}"))
447                .collect();
448            let borrowed: Vec<&str> = written.iter().map(String::as_str).collect();
449            self.display_sequence.partition_by_parts(&borrowed)?;
450        }
451        self.beat_sequence = whole_bar(self.numerator, self.denominator)?;
452        self.beam_sequence = whole_bar(self.numerator, self.denominator)?;
453        self.accent_sequence = whole_bar(self.numerator, self.denominator)?;
454        self.set_default_partitions()
455    }
456
457    /// The `(numerator, denominator)` pairs a meter string is written in,
458    /// in the order written: music21's `slashMixedToFraction`.
459    ///
460    /// `"3/8+2/8"` is two parts and `"6/8"` is one. A part written as a bare
461    /// numerator takes the denominator of the next part that has one, which
462    /// is how `"3+2/8"` is two eighth-note parts; a string whose last part
463    /// says no denominator at all says nothing about how it is measured, and
464    /// is refused.
465    pub fn parts(ratio: &str) -> Result<Vec<(UnsignedIntegerType, UnsignedIntegerType)>> {
466        let written = ratio.trim();
467        if written.is_empty() {
468            return Err(Error::Meter("a time signature says nothing".to_string()));
469        }
470        let mut pre: Vec<(UnsignedIntegerType, Option<UnsignedIntegerType>)> = Vec::new();
471        for part in written.split('+') {
472            let part = part.trim();
473            match part.split_once('/') {
474                Some((numerator, denominator)) => pre.push((
475                    Self::count(numerator, "numerator", written)?,
476                    Some(Self::count(denominator, "denominator", written)?),
477                )),
478                None => pre.push((Self::count(part, "numerator", written)?, None)),
479            }
480        }
481        let mut out = Vec::with_capacity(pre.len());
482        for (index, (numerator, denominator)) in pre.iter().enumerate() {
483            let denominator = match denominator {
484                Some(denominator) => *denominator,
485                None => pre[index + 1..]
486                    .iter()
487                    .find_map(|(_, later)| *later)
488                    .ok_or_else(|| {
489                        Error::Meter(format!(
490                            "cannot match a denominator to every numerator in {written:?}"
491                        ))
492                    })?,
493            };
494            out.push((*numerator, denominator));
495        }
496        Ok(out)
497    }
498
499    /// One number out of a meter string, with the word music21 allows before
500    /// it — `"slow 6"` is six — taken off first.
501    fn count(part: &str, label: &str, ratio: &str) -> Result<UnsignedIntegerType> {
502        let digits = part
503            .trim()
504            .rsplit(|character: char| character.is_whitespace())
505            .next()
506            .unwrap_or("")
507            .trim();
508        digits
509            .parse::<UnsignedIntegerType>()
510            .map_err(|_| Error::Meter(format!("cannot read a {label} from {part:?} in {ratio:?}")))
511    }
512
513    /// Returns common time, `4/4`.
514    pub fn common() -> Self {
515        Self::new(4, 4).expect("4/4 is a meter")
516    }
517
518    /// Returns cut time, `2/2`.
519    pub fn cut() -> Self {
520        Self::new(2, 2).expect("2/2 is a meter")
521    }
522
523    /// Returns the numerator.
524    pub fn numerator(&self) -> UnsignedIntegerType {
525        self.numerator
526    }
527
528    /// Returns the denominator.
529    pub fn denominator(&self) -> UnsignedIntegerType {
530        self.denominator
531    }
532
533    /// Returns the `"numerator/denominator"` spelling.
534    pub fn ratio_string(&self) -> String {
535        // music21 reads this off the display sequence, so a bar written
536        // additively says what it was written as. The parts come back in
537        // the order they were written, which is what makes `2/8+3/8` and
538        // `3/8+2/8` different meters rather than two spellings of `5/8`.
539        if self.display_sequence.len() > 1 {
540            return self.display_sequence.partition_display();
541        }
542        format!("{}/{}", self.numerator, self.denominator)
543    }
544
545    /// Returns the length of one bar in quarter lengths.
546    pub fn bar_quarter_length(&self) -> FloatType {
547        FloatType::from(self.numerator) * 4.0 / FloatType::from(self.denominator)
548    }
549
550    /// Returns the length of one bar as a [`Duration`].
551    pub fn bar_duration(&self) -> Duration {
552        Duration::new(self.bar_quarter_length())
553            .expect("a non-zero numerator and denominator give a positive finite bar length")
554    }
555
556    /// Returns how many beats one bar carries.
557    ///
558    /// This is music21's `beatCount`, which follows the numerator rather than
559    /// the denominator — except at `3`, where `3/4` is three beats but `3/8` is
560    /// one.
561    pub fn beat_count(&self) -> UnsignedIntegerType {
562        self.beat_sequence.len() as UnsignedIntegerType
563    }
564
565    /// Returns music21's name for the beat count, such as `"Duple"`.
566    ///
567    /// Counts above eight are spelled as `"<n>-uple"`, as music21 does.
568    pub fn beat_count_name(&self) -> String {
569        let count = self.beat_count();
570        BEAT_COUNT_NAMES
571            .get(count as usize)
572            .map_or_else(|| format!("{count}-uple"), |name| (*name).to_string())
573    }
574
575    /// Returns the length of one beat in quarter lengths.
576    ///
577    /// Every meter this type can express has a uniform beat, so unlike
578    /// music21's `beatDuration` this never fails. music21 only reports a
579    /// non-uniform beat for a hand-partitioned `MeterSequence`, which has no
580    /// counterpart here.
581    pub fn beat_quarter_length(&self) -> Result<FloatType> {
582        let spans = self.beat_spans();
583        let first = spans[0].1 - spans[0].0;
584        if spans
585            .iter()
586            .any(|(start, end)| ((end - start) - first).abs() > OFFSET_TOLERANCE)
587        {
588            let lengths: Vec<FloatType> = spans.iter().map(|(s, e)| e - s).collect();
589            return Err(Error::Meter(format!("non-uniform beat unit: {lengths:?}")));
590        }
591        Ok(first)
592    }
593
594    /// Returns the length of one beat as a [`Duration`].
595    pub fn beat_duration(&self) -> Result<Duration> {
596        Duration::new(self.beat_quarter_length()?)
597    }
598
599    /// Returns how the beat subdivides.
600    pub fn beat_division(&self) -> BeatDivision {
601        let parts = self.beat_sequence.parts();
602        if parts.len() <= 1 {
603            return BeatDivision::Other;
604        }
605        let mut counts = parts.iter().map(MeterTerminal::len);
606        let Some(first) = counts.next() else {
607            return BeatDivision::Other;
608        };
609        if first == 0 || !counts.all(|count| count == first) {
610            // The beats of a bar written additively need not divide alike;
611            // music21 reports such a meter as Other and counts one.
612            return BeatDivision::Other;
613        }
614        match first {
615            2 => BeatDivision::Simple,
616            3 => BeatDivision::Compound,
617            _ => BeatDivision::Other,
618        }
619    }
620
621    /// Returns the number of divisions in one beat.
622    pub fn beat_division_count(&self) -> UnsignedIntegerType {
623        self.beat_division().count()
624    }
625
626    /// Returns `true` when beats divide in three.
627    pub fn is_compound(&self) -> bool {
628        self.beat_division() == BeatDivision::Compound
629    }
630
631    /// Returns music21's `classification`, such as `"Compound Duple"`.
632    pub fn classification(&self) -> String {
633        format!(
634            "{} {}",
635            self.beat_division().music21_name(),
636            self.beat_count_name()
637        )
638    }
639
640    /// Returns music21's `beatDivisionCountName`: `Simple`, `Compound` or
641    /// `Other`.
642    pub fn beat_division_count_name(&self) -> &'static str {
643        self.beat_division().music21_name()
644    }
645
646    /// Returns whether two time signatures have the same numerator and
647    /// denominator: music21's `ratioEqual`, so `4/4` and `2/2` differ.
648    pub fn ratio_equal(&self, other: &TimeSignature) -> bool {
649        self.numerator == other.numerator && self.denominator == other.denominator
650    }
651
652    /// Returns how many quarter lengths one unit of the denominator lasts:
653    /// `0.5` in `6/8`, `2.0` in `2/2`.
654    pub fn beat_length_to_quarter_length_ratio(&self) -> FloatType {
655        4.0 / FloatType::from(self.denominator)
656    }
657
658    /// Returns how many denominator units make one quarter length, the
659    /// inverse of [`Self::beat_length_to_quarter_length_ratio`].
660    pub fn quarter_length_to_beat_length_ratio(&self) -> FloatType {
661        FloatType::from(self.denominator) / 4.0
662    }
663
664    /// Returns the quarter length of each division of one beat, in order:
665    /// two eighths in `4/4`, three in `6/8`, the whole dotted-quarter beat
666    /// in `3/8` where the beat does not divide.
667    pub fn beat_division_quarter_lengths(&self) -> Result<Vec<FloatType>> {
668        let count = self.beat_division_count().max(1);
669        let beat = self.beat_quarter_length()?;
670        Ok(vec![beat / FloatType::from(count); count as usize])
671    }
672
673    /// Returns [`Self::beat_division_quarter_lengths`] as durations: music21's
674    /// `beatDivisionDurations`.
675    pub fn beat_division_durations(&self) -> Result<Vec<Duration>> {
676        self.beat_division_quarter_lengths()?
677            .into_iter()
678            .map(Duration::new)
679            .collect()
680    }
681
682    /// Returns each division of the beat halved: music21's
683    /// `beatSubDivisionDurations`, four sixteenths in `4/4`.
684    pub fn beat_sub_division_durations(&self) -> Result<Vec<Duration>> {
685        let mut out = Vec::new();
686        for quarter_length in self.beat_division_quarter_lengths()? {
687            let half = Duration::new(quarter_length / 2.0)?;
688            out.push(half.clone());
689            out.push(half);
690        }
691        Ok(out)
692    }
693
694    /// Returns the quarter-length offset of a one-based, possibly fractional
695    /// beat: music21's `getOffsetFromBeat`, so beat `2.5` of `4/4` is `1.5`
696    /// and beat `1.5` of `6/8` is `0.75`. A beat past the bar is an error.
697    pub fn offset_from_beat(&self, beat: FloatType) -> Result<FloatType> {
698        let whole = beat.floor();
699        if !beat.is_finite() || whole < 1.0 || whole > FloatType::from(self.beat_count()) {
700            return Err(Error::Meter(format!(
701                "requested beat value ({beat}) not found in the {} beats of {}",
702                self.beat_count(),
703                self.ratio_string()
704            )));
705        }
706        let (start, end) = self.beat_spans()[whole as usize - 1];
707        let fraction = snapped_fraction(beat - whole);
708        Ok(start + fraction * (end - start))
709    }
710
711    /// Returns the one-based beat containing `offset` and how far into that
712    /// beat it lies, in quarter lengths: music21's `getBeatProgress`.
713    pub fn beat_progress(&self, offset: FloatType) -> Result<(UnsignedIntegerType, FloatType)> {
714        let index = self.beat_index(offset)?;
715        let (start, _) = self.beat_spans()[index];
716        Ok((index as UnsignedIntegerType + 1, offset - start))
717    }
718
719    /// Returns the position within the bar as a fractional beat: music21's
720    /// `getBeatProportion`, `2.5` for the second eighth of beat two in `4/4`
721    /// and `1.333…` for the second eighth of `6/8`.
722    pub fn beat_proportion(&self, offset: FloatType) -> Result<FloatType> {
723        let (beat, progress) = self.beat_progress(offset)?;
724        let (start, end) = self.beat_spans()[beat as usize - 1];
725        Ok(FloatType::from(beat) + progress / (end - start))
726    }
727
728    /// Returns [`Self::beat_proportion`] the way music21's
729    /// `getBeatProportionStr` writes it: the beat alone on the beat, otherwise
730    /// the beat and the fraction of it elapsed, `2 1/2`, with the fraction's
731    /// denominator limited to 16.
732    pub fn beat_proportion_string(&self, offset: FloatType) -> Result<String> {
733        let (beat, progress) = self.beat_progress(offset)?;
734        let (start, end) = self.beat_spans()[beat as usize - 1];
735        let proportion = progress / (end - start);
736        if proportion == 0.0 {
737            return Ok(beat.to_string());
738        }
739        let (numerator, denominator) = closest_fraction(proportion, 16);
740        Ok(format!("{beat} {numerator}/{denominator}"))
741    }
742
743    /// Where each beat starts and ends, in quarter lengths from the start of
744    /// the bar. The beats of a meter written additively are not all the same
745    /// length, so this is read off the beat sequence rather than divided out.
746    fn beat_spans(&self) -> Vec<(FloatType, FloatType)> {
747        let mut spans = Vec::new();
748        let mut position = 0.0;
749        for part in self.beat_sequence.parts() {
750            let end = position + part.quarter_length();
751            spans.push((position, end));
752            position = end;
753        }
754        if spans.is_empty() {
755            spans.push((0.0, self.bar_quarter_length()));
756        }
757        spans
758    }
759
760    /// How long the beat holding an offset is: music21's `getBeatDuration`.
761    ///
762    /// A meter written additively answers differently along the bar — the
763    /// first beat of `2/4+3/8` is two quarters long and the second is three
764    /// eighths.
765    pub fn beat_duration_at(&self, offset: FloatType) -> Result<Duration> {
766        let index = self.beat_index(offset)?;
767        let (start, end) = self.beat_spans()[index];
768        Duration::new(end - start)
769    }
770
771    /// Which beat holds an offset, counted from nought.
772    fn beat_index(&self, offset: FloatType) -> Result<usize> {
773        if !offset.is_finite() || offset < 0.0 || offset >= self.bar_quarter_length() {
774            return Err(Error::Meter(format!(
775                "offset {offset} is outside a {} bar of {} quarter lengths",
776                self.ratio_string(),
777                self.bar_quarter_length()
778            )));
779        }
780        let spans = self.beat_spans();
781        for (index, (start, end)) in spans.iter().enumerate() {
782            let _ = start;
783            if offset < end - OFFSET_TOLERANCE {
784                return Ok(index);
785            }
786        }
787        Ok(spans.len() - 1)
788    }
789
790    /// Returns the quarter-length offset of each beat within one bar.
791    pub fn beat_offsets(&self) -> Vec<FloatType> {
792        self.beat_spans()
793            .into_iter()
794            .map(|(start, _)| start)
795            .collect()
796    }
797
798    /// Returns the one-based beat containing `offset` quarter lengths into a bar.
799    ///
800    /// Matches music21's `getBeat`. Offsets at or beyond the end of the bar are
801    /// rejected rather than wrapping.
802    pub fn beat_at_offset(&self, offset: FloatType) -> Result<UnsignedIntegerType> {
803        if !offset.is_finite() || offset < 0.0 || offset >= self.bar_quarter_length() {
804            return Err(Error::Meter(format!(
805                "offset {offset} is outside a {} bar of {} quarter lengths",
806                self.ratio_string(),
807                self.bar_quarter_length()
808            )));
809        }
810        Ok(self.beat_index(offset)? as UnsignedIntegerType + 1)
811    }
812}
813
814/// The fraction closest to `value` with a denominator no larger than
815/// `max_denominator`, as Python's `Fraction.limit_denominator` finds it for
816/// the proportions music21 prints. Ties go to the smaller denominator.
817fn closest_fraction(value: FloatType, max_denominator: u32) -> (u32, u32) {
818    let mut best = (value.round() as u32, 1);
819    let mut best_error = (value - value.round()).abs();
820    for denominator in 2..=max_denominator {
821        let numerator = (value * FloatType::from(denominator)).round();
822        let error = (value - numerator / FloatType::from(denominator)).abs();
823        if error < best_error {
824            best = (numerator as u32, denominator);
825            best_error = error;
826        }
827    }
828    best
829}
830
831impl std::fmt::Display for TimeSignature {
832    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
833        f.write_str(&self.ratio_string())
834    }
835}
836
837/// How far an offset may sit from a partition boundary and still be read as
838/// on it.
839const OFFSET_TOLERANCE: FloatType = 1e-9;
840
841/// The shortest accent partition music21 can write, a 128th note.
842const SHORTEST_PARTITION: FloatType = 4.0 / 128.0;
843
844/// The longest, a whole note.
845const LONGEST_PARTITION: FloatType = 4.0;
846
847impl TimeSignature {
848    /// How music21's default accent hierarchy divides the bar, three levels
849    /// deep: how many parts the bar divides into, how many each of those
850    /// divides into, and how many each of those divides into.
851    ///
852    /// music21 builds the hierarchy by subdividing a `MeterSequence`. The top
853    /// level takes the beat count, except that one, two, four, eight, sixteen
854    /// and thirty-two beats divide in two and three beats divide in three; a
855    /// single beat takes the numerator's place in that rule. Below that, each
856    /// span divides the way music21 divides a span by default: two or more
857    /// triples into its triples, an even count of anything in two, a single
858    /// unit in two, and an odd count into its units.
859    fn accent_hierarchy(
860        &self,
861    ) -> (
862        UnsignedIntegerType,
863        UnsignedIntegerType,
864        UnsignedIntegerType,
865    ) {
866        let beats = self.beat_count();
867        let first = if beats > 1 { beats } else { self.numerator };
868        let top = match first {
869            1 | 2 | 4 | 8 | 16 | 32 => 2,
870            3 => 3,
871            other => other,
872        };
873        let (span, unit) = split_span(self.numerator, self.denominator, top);
874        let second = default_division(span, unit);
875        let (span, unit) = split_span(span, unit, second);
876        let third = default_division(span, unit);
877        // music21 partitions the accent sequence into that many equal parts
878        // written as whole numbers of a note value, and gives up — leaving
879        // the bar as one partition — when a part would be longer than a
880        // whole note or shorter than a 128th.
881        let partition = self.bar_quarter_length() / FloatType::from(top * second * third);
882        if !(SHORTEST_PARTITION..=LONGEST_PARTITION).contains(&partition) {
883            return (1, 1, 1);
884        }
885        (top, second, third)
886    }
887
888    /// The length of one partition of music21's default accent hierarchy, the
889    /// finest level the accent weights are given at.
890    pub fn accent_partition_quarter_length(&self) -> FloatType {
891        let parts = self.accent_sequence.parts();
892        if parts.len() > 1 {
893            return parts[0].quarter_length();
894        }
895        let (top, second, third) = self.accent_hierarchy();
896        self.bar_quarter_length() / FloatType::from(top * second * third)
897    }
898
899    /// The accent weight of every partition of the bar, music21's default
900    /// `accentSequence`: `1.0` on the downbeat, halving with every level of
901    /// the hierarchy a partition's start is not a boundary of, so `4/4` reads
902    /// `1.0, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125`.
903    pub fn accent_weights(&self) -> Vec<FloatType> {
904        let carried: Vec<FloatType> = self
905            .accent_sequence
906            .parts()
907            .iter()
908            .map(MeterTerminal::weight)
909            .collect();
910        if carried.len() > 1 {
911            return carried;
912        }
913        self.default_accent_weights()
914    }
915
916    /// The weights music21's default hierarchy gives the bar, which is what
917    /// the accent sequence is built carrying. Kept apart from
918    /// [`Self::accent_weights`], which reads the sequence, so that weights a
919    /// caller has set are the ones read back.
920    fn default_accent_weights(&self) -> Vec<FloatType> {
921        let (top, second, third) = self.accent_hierarchy();
922        let count = top * second * third;
923        (0..count)
924            .map(|index| {
925                let depth = 1
926                    + UnsignedIntegerType::from(index.is_multiple_of(third))
927                    + UnsignedIntegerType::from(index.is_multiple_of(second * third))
928                    + UnsignedIntegerType::from(index == 0);
929                FloatType::from(2u32.pow(depth - 1)) / 8.0
930            })
931            .collect()
932    }
933
934    /// Whether an offset in quarter lengths starts an accent partition:
935    /// music21's `getAccent`, which is false for any offset off the grid,
936    /// beyond the bar included.
937    pub fn accent(&self, offset: FloatType) -> bool {
938        let partition = self.accent_partition_quarter_length();
939        let index = (offset / partition).round();
940        index >= 0.0
941            && index < FloatType::from(self.accent_weights().len() as u32)
942            && (offset - index * partition).abs() < OFFSET_TOLERANCE
943    }
944
945    /// The accent weight at an offset in quarter lengths: music21's
946    /// `getAccentWeight`, the weight of the partition the offset falls in.
947    /// An offset outside the bar is an error.
948    pub fn accent_weight(&self, offset: FloatType) -> Result<FloatType> {
949        self.accent_weight_with(offset, false, false)
950    }
951
952    /// The accent weight at an offset, read at a level of the accent
953    /// sequence: music21's `getAccentWeight` with its `level`.
954    pub fn accent_weight_at_level(
955        &self,
956        offset: FloatType,
957        level: usize,
958        force_position_match: bool,
959        permit_meter_modulus: bool,
960    ) -> Result<FloatType> {
961        let bar = self.bar_quarter_length();
962        let offset = if permit_meter_modulus {
963            offset.rem_euclid(bar)
964        } else {
965            offset
966        };
967        if offset.is_nan() || offset < 0.0 || offset >= bar {
968            return Err(Error::Meter(format!(
969                "cannot access from qLenPos {} where total duration is {}",
970                offset_repr(offset),
971                offset_repr(bar)
972            )));
973        }
974        let terminals = self.accent_sequence.level_list(level, true);
975        if terminals.len() <= 1 {
976            return self.accent_weight_with(offset, force_position_match, permit_meter_modulus);
977        }
978        let spans = self.accent_sequence.level_span(level);
979        let smallest = terminals
980            .iter()
981            .map(MeterTerminal::weight)
982            .fold(FloatType::INFINITY, FloatType::min);
983        for (index, (start, end)) in spans.iter().enumerate() {
984            if offset < end - OFFSET_TOLERANCE {
985                if force_position_match && (offset - start).abs() >= OFFSET_TOLERANCE {
986                    return Ok(smallest * 0.5);
987                }
988                return Ok(terminals[index].weight());
989            }
990        }
991        Ok(terminals[terminals.len() - 1].weight())
992    }
993
994    /// Weighs the accent partitions of a level, looping the weights given
995    /// over them: music21's `setAccentWeight`.
996    pub fn set_accent_weight(&mut self, weights: &[FloatType], level: usize) -> Result<()> {
997        self.accent_sequence.set_weights_at_level(level, weights)
998    }
999
1000    /// [`Self::accent_weight`] with music21's two options. With
1001    /// `force_position_match` an offset that does not start a partition
1002    /// answers half the smallest weight rather than its partition's; with
1003    /// `permit_meter_modulus` an offset beyond the bar is read within it.
1004    pub fn accent_weight_with(
1005        &self,
1006        offset: FloatType,
1007        force_position_match: bool,
1008        permit_meter_modulus: bool,
1009    ) -> Result<FloatType> {
1010        let bar = self.bar_quarter_length();
1011        let offset = if permit_meter_modulus {
1012            offset.rem_euclid(bar)
1013        } else {
1014            offset
1015        };
1016        if offset.is_nan() || offset < 0.0 || offset >= bar {
1017            return Err(Error::Meter(format!(
1018                "cannot access from qLenPos {} where total duration is {}",
1019                offset_repr(offset),
1020                offset_repr(bar)
1021            )));
1022        }
1023        let weights = self.accent_weights();
1024        let partition = self.accent_partition_quarter_length();
1025        let index = ((offset + OFFSET_TOLERANCE) / partition).floor() as usize;
1026        let index = index.min(weights.len() - 1);
1027        if force_position_match
1028            && (offset - index as FloatType * partition).abs() >= OFFSET_TOLERANCE
1029        {
1030            let smallest = weights
1031                .iter()
1032                .copied()
1033                .fold(FloatType::INFINITY, FloatType::min);
1034            return Ok(smallest * 0.5);
1035        }
1036        Ok(weights[index])
1037    }
1038
1039    /// The mean accent weight of what a stream holds: music21's
1040    /// `averageBeatStrength`, each element weighed where it falls in the
1041    /// bar, with an element off the accent grid counting half the smallest
1042    /// weight. `notes_only` weighs the notes, chords and rests alone; an empty
1043    /// stream weighs nothing.
1044    pub fn average_beat_strength(&self, stream: &crate::Stream, notes_only: bool) -> FloatType {
1045        let bar = self.bar_quarter_length();
1046        let offsets: Vec<FloatType> = if notes_only {
1047            stream
1048                .notes()
1049                .into_iter()
1050                .map(|(offset, _)| offset)
1051                .collect()
1052        } else {
1053            stream
1054                .recurse()
1055                .into_iter()
1056                .filter(|(_, element)| element.as_stream().is_none())
1057                .map(|(offset, _)| offset)
1058                .collect()
1059        };
1060        if offsets.is_empty() {
1061            return 0.0;
1062        }
1063        let total: FloatType = offsets
1064            .iter()
1065            .map(|offset| {
1066                self.accent_weight_with(offset.rem_euclid(bar), true, false)
1067                    .unwrap_or(0.0)
1068            })
1069            .sum();
1070        total / offsets.len() as FloatType
1071    }
1072
1073    /// How many levels of the beat hierarchy start at an offset: music21's
1074    /// `getBeatDepth`, which quantizes the offset to the beat's division and
1075    /// then counts the beat level and the division level. A meter of one beat
1076    /// has one level and answers one everywhere in the bar; an offset outside
1077    /// the bar is an error.
1078    pub fn beat_depth(&self, offset: FloatType) -> Result<u8> {
1079        let bar = self.bar_quarter_length();
1080        if offset.is_nan() || offset < 0.0 || offset >= bar {
1081            return Err(Error::Meter(format!(
1082                "cannot access from qLenPos {}",
1083                offset_repr(offset)
1084            )));
1085        }
1086        let depth = self
1087            .beat_sequence
1088            .offset_to_depth(offset, crate::meter::OffsetAlign::Quantize)?;
1089        Ok(depth as u8)
1090    }
1091}
1092
1093/// A fractional beat read as the fraction it suggests: music21's
1094/// `addFloatPrecision`, which snaps a third, two thirds, a sixth or five
1095/// sixths written as decimals onto the fraction itself.
1096///
1097/// It is what makes the offset of beat 2.33 the same as the offset of beat
1098/// two and a third.
1099#[must_use]
1100pub fn snapped_fraction(value: FloatType) -> FloatType {
1101    const GRAIN: FloatType = 1e-2;
1102    let thirds = [1.0 / 3.0, 2.0 / 3.0, 1.0 / 6.0, 5.0 / 6.0];
1103    for candidate in thirds {
1104        if (value - candidate).abs() <= GRAIN {
1105            return candidate;
1106        }
1107    }
1108    value
1109}
1110
1111/// One of a run of notes to be beamed: where it sits in the bar, how long it
1112/// lasts, what it is written as, and whether it sounds.
1113///
1114/// A rest is beamed alongside the notes — music21 works its beams out and
1115/// then declines to write them on it — so what matters here is the written
1116/// value and whether the thing sounds, not what kind of object it is.
1117#[derive(Clone, Copy, Debug, PartialEq)]
1118pub struct BeamedNote {
1119    /// Where it starts, in quarter lengths from the start of its measure.
1120    pub offset: FloatType,
1121    /// How long it lasts, in quarter lengths.
1122    pub quarter_length: FloatType,
1123    /// The value it is written as, which says how many beams it can carry.
1124    pub duration_type: DurationType,
1125    /// Whether it sounds. A rest carries no beam however it is written.
1126    pub sounds: bool,
1127}
1128
1129impl TimeSignature {
1130    /// Beams a run of notes: music21's `getBeams`.
1131    ///
1132    /// The notes are taken as adjoining, which is how music21 takes them —
1133    /// their offsets are read only against `measure_start_offset`, for a run
1134    /// that begins part way through a bar. `measure_padding` is the measure's
1135    /// `paddingRight` where the run came from one, and `None` where it did
1136    /// not; a run that ends an incomplete measure does not get its last beam
1137    /// stopped.
1138    ///
1139    /// A run of one is beamed as nothing at all, as music21 does.
1140    pub fn beams_for(
1141        &self,
1142        notes: &[BeamedNote],
1143        measure_start_offset: FloatType,
1144        measure_padding: Option<FloatType>,
1145    ) -> Result<Vec<Option<Beams>>> {
1146        if notes.len() <= 1 {
1147            return Ok(notes.iter().map(|_| None).collect());
1148        }
1149
1150        // music21's `naiveBeams`: the fullest set of beams each written value
1151        // can carry, with what each one does left undecided.
1152        let mut beamed: Vec<Option<Beams>> = Vec::with_capacity(notes.len());
1153        for note in notes {
1154            let levels = Beams::levels_for(note.duration_type).filter(|_| note.sounds);
1155            beamed.push(match levels {
1156                Some(levels) => {
1157                    let mut made = Beams::new();
1158                    made.fill_levels(levels, None)?;
1159                    Some(made)
1160                }
1161                None => None,
1162            });
1163        }
1164        crate::notation::remove_sandwiched_unbeamables(&mut beamed);
1165
1166        for depth in 0..Beams::LEVELS {
1167            for index in 0..notes.len() {
1168                self.fix_one_beam(
1169                    &mut beamed,
1170                    notes,
1171                    index,
1172                    depth,
1173                    measure_start_offset,
1174                    measure_padding,
1175                )?;
1176            }
1177        }
1178
1179        crate::notation::sanitize_partial_beams(&mut beamed);
1180        crate::notation::merge_connecting_partial_beams(&mut beamed);
1181        Ok(beamed)
1182    }
1183
1184    /// What one note's beam does at one depth: music21's
1185    /// `fixBeamsOneElementDepth`.
1186    fn fix_one_beam(
1187        &self,
1188        beamed: &mut [Option<Beams>],
1189        notes: &[BeamedNote],
1190        index: usize,
1191        depth: usize,
1192        measure_start_offset: FloatType,
1193        measure_padding: Option<FloatType>,
1194    ) -> Result<()> {
1195        let beam_number = depth as u32 + 1;
1196        let carries = |beams: Option<&Beams>| {
1197            beams.is_some_and(|beams| {
1198                beams
1199                    .numbers()
1200                    .into_iter()
1201                    .flatten()
1202                    .any(|n| n == beam_number)
1203            })
1204        };
1205        if !carries(beamed[index].as_ref()) {
1206            return Ok(());
1207        }
1208
1209        let note = notes[index];
1210        let start = note.offset + measure_start_offset;
1211        let end = start + note.quarter_length;
1212        let start_next = end;
1213
1214        let is_first = index == 0;
1215        let is_last = index + 1 == notes.len();
1216
1217        // Everything the neighbours are asked, read before this one is
1218        // written to: music21 reads them off a list it is editing.
1219        let previous_is_none = is_first || beamed[index - 1].is_none();
1220        let next_is_none = is_last || beamed[index + 1].is_none();
1221        let previous_carries = !is_first && carries(beamed[index - 1].as_ref());
1222        let next_carries = !is_last && carries(beamed[index + 1].as_ref());
1223        let previous_broke = !is_first
1224            && beamed[index - 1].as_ref().is_some_and(|beams| {
1225                beams.by_number(beam_number).is_some_and(|beam| {
1226                    matches!(beam.beam_type(), Some(BeamType::Stop))
1227                        || (matches!(beam.beam_type(), Some(BeamType::PartialBeam))
1228                            && beam.direction() == Some(crate::notation::BeamDirection::Left))
1229                })
1230            });
1231
1232        let archetype = self.beam_sequence.level(depth, true)?;
1233        let (span_start, span_end) = archetype.offset_to_span(start, false)?;
1234        let span_next_start = if next_is_none {
1235            0.0
1236        } else {
1237            archetype.offset_to_span(start_next, false)?.0
1238        };
1239
1240        let same = |a: FloatType, b: FloatType| (a - b).abs() < OFFSET_TOLERANCE;
1241
1242        // A note that fills its span exactly is not beamed at that level.
1243        if same(end, span_end)
1244            && (same(start, span_start) || (previous_is_none && beam_number == 1))
1245        {
1246            beamed[index] = None;
1247            return Ok(());
1248        }
1249
1250        let ends_the_measure = is_last && measure_padding.is_none_or(|padding| padding == 0.0);
1251
1252        let (beam_type, direction) = if is_first && measure_start_offset == 0.0 {
1253            if next_is_none || !next_carries {
1254                (
1255                    BeamType::PartialBeam,
1256                    Some(crate::notation::BeamDirection::Right),
1257                )
1258            } else {
1259                (BeamType::Start, None)
1260            }
1261        } else if ends_the_measure {
1262            if previous_is_none || !previous_carries {
1263                (
1264                    BeamType::PartialBeam,
1265                    Some(crate::notation::BeamDirection::Left),
1266                )
1267            } else {
1268                (BeamType::Stop, None)
1269            }
1270        } else if previous_is_none || !previous_carries {
1271            // Neither the first nor the last, and nothing to beam back to.
1272            if beam_number == 1 && next_is_none {
1273                beamed[index] = None;
1274                return Ok(());
1275            } else if (next_is_none && beam_number > 1) || start_next >= span_end - OFFSET_TOLERANCE
1276            {
1277                // music21 writes these as two branches. They answer alike,
1278                // and the second subsumes the first: with nothing after it,
1279                // a note runs to the end of its span or past it.
1280                (
1281                    BeamType::PartialBeam,
1282                    Some(crate::notation::BeamDirection::Left),
1283                )
1284            } else if next_is_none || !next_carries {
1285                (
1286                    BeamType::PartialBeam,
1287                    Some(crate::notation::BeamDirection::Right),
1288                )
1289            } else {
1290                (BeamType::Start, None)
1291            }
1292        } else if previous_broke {
1293            if next_is_none {
1294                (
1295                    BeamType::PartialBeam,
1296                    Some(crate::notation::BeamDirection::Left),
1297                )
1298            } else if next_carries {
1299                (BeamType::Start, None)
1300            } else {
1301                (
1302                    BeamType::PartialBeam,
1303                    Some(crate::notation::BeamDirection::Right),
1304                )
1305            }
1306        } else if next_is_none || !next_carries {
1307            (BeamType::Stop, None)
1308        } else if start_next < span_end - OFFSET_TOLERANCE {
1309            (BeamType::Continue, None)
1310        } else if start_next >= span_next_start - OFFSET_TOLERANCE {
1311            (BeamType::Stop, None)
1312        } else {
1313            return Err(Error::Meter("cannot match beamType".to_string()));
1314        };
1315
1316        if let Some(beams) = beamed[index].as_mut() {
1317            beams.set_by_number(beam_number, beam_type, direction)?;
1318        }
1319        Ok(())
1320    }
1321}
1322
1323/// A whole bar as a sequence of one part, which is what music21 starts each
1324/// of a meter's four sequences as: `6/8` undivided is `{6/8}`, not a bare
1325/// span, so a sequence nobody has divided still has one part.
1326fn whole_bar(
1327    numerator: UnsignedIntegerType,
1328    denominator: UnsignedIntegerType,
1329) -> Result<MeterTerminal> {
1330    let mut bar = MeterTerminal::new(numerator, denominator)?;
1331    let whole = format!("{numerator}/{denominator}");
1332    bar.partition_by_parts(&[whole.as_str()])?;
1333    Ok(bar)
1334}
1335
1336/// The word music21 allows before a ratio, saying how the bar is counted:
1337/// `"slow 6/8"` is counted in six and `"fast 6/8"` in two.
1338fn division_word(ratio: &str) -> Option<&str> {
1339    let first = ratio.trim().split('+').next()?.trim();
1340    let word = first.split_whitespace().next()?;
1341    matches!(word, "slow" | "fast").then_some(word)
1342}
1343
1344/// Whether a bar is felt in compound beats: music21's `favorCompound`.
1345///
1346/// The word written before the ratio decides it where there is one. Where
1347/// there is not, a bare three written in quarters or longer is felt slow,
1348/// which is what counts `3/4` in three while `3/8` is counted in one.
1349fn favor_compound(
1350    numerator: UnsignedIntegerType,
1351    denominator: UnsignedIntegerType,
1352    word: Option<&str>,
1353) -> bool {
1354    match word {
1355        Some("slow") => false,
1356        Some("fast") => true,
1357        _ => !(numerator == 3 && denominator < 8),
1358    }
1359}
1360
1361/// The time signature that fits what a measure holds: music21's
1362/// `bestTimeSignature`.
1363///
1364/// The shortest note value in the measure that is not a tuplet is the
1365/// denominator, halved until it divides the measure's length evenly, and the
1366/// count of it in the measure is the numerator, reduced to lowest terms with
1367/// the whole and half taken up to quarters. A measure that reads as `3/4` is
1368/// `6/8` instead when that weighs its notes at least as strongly, and one
1369/// that reads as `6/4` is whichever of `6/4`, `12/8` and `3/2` weighs them
1370/// most. A measure whose length is no binary fraction — tuplets — takes the
1371/// fraction itself.
1372pub fn best_time_signature(measure: &crate::Stream) -> Result<TimeSignature> {
1373    let elements = measure.recurse();
1374    let sum = elements
1375        .iter()
1376        .filter(|(_, element)| element.as_stream().is_none())
1377        .map(|(offset, element)| offset + element.quarter_length())
1378        .fold(0.0, FloatType::max);
1379
1380    // The shortest sounding value that is not a tuplet, with its dots.
1381    let mut min_dur = 4.0;
1382    let mut min_dots = 0;
1383    for (_, element) in &elements {
1384        let sounds =
1385            element.is_note_or_chord() || matches!(element, crate::stream::StreamElement::Rest(_));
1386        let quarter_length = element.quarter_length();
1387        if sounds && quarter_length != 0.0 && quarter_length < min_dur && is_binary(quarter_length)
1388        {
1389            min_dur = quarter_length;
1390            min_dots = element.duration().map_or(0, Duration::dots);
1391        }
1392    }
1393
1394    let (numerator, denominator) = if is_binary(sum) {
1395        binary_signature(sum, min_dur, min_dots)?
1396    } else {
1397        let (numerator, denominator) = crate::duration::limited_fraction(sum, 65535)
1398            .ok_or_else(|| Error::Meter("Cannot find a good match for this measure".to_string()))?;
1399        (
1400            numerator as UnsignedIntegerType,
1401            denominator as UnsignedIntegerType,
1402        )
1403    };
1404    let (numerator, denominator) = simplified_signature(numerator, denominator);
1405
1406    let strength =
1407        |ratio: (UnsignedIntegerType, UnsignedIntegerType)| -> Result<(TimeSignature, FloatType)> {
1408            let signature = TimeSignature::new(ratio.0, ratio.1)?;
1409            let strength = signature.average_beat_strength(measure, true);
1410            Ok((signature, strength))
1411        };
1412    match (numerator, denominator) {
1413        // Three-four or six-eight, whichever weighs the notes more strongly.
1414        (3, 4) => {
1415            let (three_four, simple) = strength((3, 4))?;
1416            let (six_eight, compound) = strength((6, 8))?;
1417            Ok(if simple <= compound {
1418                six_eight
1419            } else {
1420                three_four
1421            })
1422        }
1423        // Six-four, twelve-eight or three-two, the same way.
1424        (6, 4) => {
1425            let (six_four, first) = strength((6, 4))?;
1426            let (twelve_eight, second) = strength((12, 8))?;
1427            let (three_two, third) = strength((3, 2))?;
1428            let most = first.max(second).max(third);
1429            Ok(if most == first {
1430                six_four
1431            } else if most == third {
1432                three_two
1433            } else {
1434                twelve_eight
1435            })
1436        }
1437        _ => TimeSignature::new(numerator, denominator),
1438    }
1439}
1440
1441/// The numerator and denominator of a measure whose length is a binary
1442/// fraction: the shortest value halved, dot and all, until it divides the
1443/// length evenly, named as a note value for the denominator and counted for
1444/// the numerator, in lowest terms.
1445fn binary_signature(
1446    sum: FloatType,
1447    min_dur: FloatType,
1448    min_dots: u32,
1449) -> Result<(UnsignedIntegerType, UnsignedIntegerType)> {
1450    let no_match = || Error::Meter("Cannot find a good match for this measure".to_string());
1451    let smallest_type = crate::duration::DurationType::from_music21_name("128th")
1452        .expect("music21 names the 128th note");
1453    let limit = smallest_type.quarter_length();
1454    let dot_multiplier =
1455        FloatType::from(2u32.pow(min_dots + 1) - 1) / FloatType::from(2u32.pow(min_dots));
1456
1457    let mut min_test = min_dur;
1458    let mut remaining = 10;
1459    while remaining > 0 {
1460        let parts = sum / min_test;
1461        if parts.floor() == parts || min_test <= limit {
1462            break;
1463        }
1464        min_test /= 2.0 * dot_multiplier;
1465        remaining -= 1;
1466    }
1467    let mut remaining = 10;
1468    while remaining > 0 {
1469        if min_test < limit {
1470            min_test = limit;
1471            break;
1472        }
1473        let (duration_type, matched) =
1474            crate::duration::quarter_length_to_closest_type(min_test).map_err(|_| no_match())?;
1475        if matched || duration_type == smallest_type {
1476            break;
1477        }
1478        min_test /= 2.0 * dot_multiplier;
1479        remaining -= 1;
1480    }
1481    let (duration_type, matched) =
1482        crate::duration::quarter_length_to_closest_type(min_test).map_err(|_| no_match())?;
1483    if !matched {
1484        return Err(Error::Meter(format!(
1485            "cannot find a type for denominator {min_test}"
1486        )));
1487    }
1488    let mut float_denominator = duration_type.type_number().unwrap_or(1.0);
1489    let mut multiplier = 1.0;
1490    let mut numerator_float = 0.0;
1491    while remaining > 0 {
1492        numerator_float = multiplier * sum / min_test;
1493        if numerator_float == numerator_float.floor() {
1494            break;
1495        }
1496        multiplier *= 2.0;
1497        remaining -= 1;
1498    }
1499    float_denominator *= multiplier;
1500    let numerator = numerator_float as UnsignedIntegerType;
1501    let denominator = float_denominator as UnsignedIntegerType;
1502    let divisor = num::integer::gcd(numerator, denominator).max(1);
1503    Ok((numerator / divisor, denominator / divisor))
1504}
1505
1506/// The rare signatures written the usual way: sixteen-sixteen and one-one
1507/// are four-four, and a whole or half denominator is written in quarters.
1508fn simplified_signature(
1509    numerator: UnsignedIntegerType,
1510    denominator: UnsignedIntegerType,
1511) -> (UnsignedIntegerType, UnsignedIntegerType) {
1512    if numerator == denominator && !matches!(numerator, 2 | 4) {
1513        (4, 4)
1514    } else if numerator != denominator && denominator == 1 {
1515        (numerator * 4, 4)
1516    } else if numerator != denominator && denominator == 2 {
1517        (numerator * 2, 4)
1518    } else {
1519        (numerator, denominator)
1520    }
1521}
1522
1523/// Whether a quarter length is a binary fraction music21 keeps as a float
1524/// rather than turning into a `Fraction`: one whose exact denominator is a
1525/// power of two no larger than its `DENOM_LIMIT`.
1526fn is_binary(quarter_length: FloatType) -> bool {
1527    (quarter_length * 32768.0).fract() == 0.0
1528}
1529
1530/// A span of `count` units of `1/unit`, divided into `parts` equal spans, as
1531/// a count of a unit: `6/8` in two is `3/8`, and `1/4` in two is `1/8`.
1532fn split_span(
1533    count: UnsignedIntegerType,
1534    unit: UnsignedIntegerType,
1535    parts: UnsignedIntegerType,
1536) -> (UnsignedIntegerType, UnsignedIntegerType) {
1537    if count.is_multiple_of(parts) {
1538        (count / parts, unit)
1539    } else {
1540        (count, unit * parts)
1541    }
1542}
1543
1544/// How music21 divides a span by default when nothing says otherwise: its
1545/// `MeterSequence.subdivide` with no count given, which takes the first of
1546/// the span's division options.
1547fn default_division(count: UnsignedIntegerType, unit: UnsignedIntegerType) -> UnsignedIntegerType {
1548    let _ = unit;
1549    if count > 3 && count.is_multiple_of(3) {
1550        count / 3
1551    } else if count == 1 || count.is_multiple_of(2) {
1552        2
1553    } else {
1554        count
1555    }
1556}
1557
1558/// An offset written the way music21 writes one in a message: a whole number
1559/// as `3.0`, anything else as it is.
1560fn offset_repr(offset: FloatType) -> String {
1561    if offset.fract() == 0.0 {
1562        format!("{offset:.1}")
1563    } else {
1564        offset.to_string()
1565    }
1566}
1567
1568#[cfg(test)]
1569mod tests {
1570    /// music21 writes a word before the ratio to say how the beat is felt,
1571    /// and writes an additive meter as parts that add up. Both were read as
1572    /// nonsense here, which is ten of music21's own meter examples.
1573    #[test]
1574    fn a_meter_is_read_as_music21_writes_it() {
1575        use crate::meter::TimeSignature;
1576
1577        // The word before the ratio is not decoration: music21 counts a
1578        // `slow 6/8` in six and a plain one in two, so the two are different
1579        // meters that happen to be written over the same numbers.
1580        let plain = TimeSignature::from_ratio_string("6/8").unwrap();
1581        let slow = TimeSignature::from_ratio_string("slow 6/8").unwrap();
1582        assert_eq!(TimeSignature::from_ratio_string("fast 6/8").unwrap(), plain);
1583        assert_ne!(slow, plain);
1584        assert_eq!(
1585            (slow.numerator(), slow.denominator()),
1586            (plain.numerator(), plain.denominator())
1587        );
1588        assert_eq!(plain.beat_sequence().len(), 2);
1589        assert_eq!(slow.beat_sequence().len(), 6);
1590
1591        let additive = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
1592        assert_eq!((additive.numerator(), additive.denominator()), (5, 8));
1593        assert_eq!(
1594            TimeSignature::parts("3/8+2/8").unwrap(),
1595            vec![(3, 8), (2, 8)]
1596        );
1597        // A numerator with no denominator of its own takes the next one.
1598        assert_eq!(TimeSignature::parts("3+2/8").unwrap(), vec![(3, 8), (2, 8)]);
1599        assert_eq!(
1600            TimeSignature::parts("3+2+5/8+3/4").unwrap(),
1601            vec![(3, 8), (2, 8), (5, 8), (3, 4)]
1602        );
1603        // Parts in different notes come to what they add up to.
1604        let mixed = TimeSignature::from_ratio_string("3/8+1/4").unwrap();
1605        assert_eq!((mixed.numerator(), mixed.denominator()), (5, 8));
1606
1607        assert!(TimeSignature::parts("3+2+5").is_err());
1608        assert!(TimeSignature::from_ratio_string("").is_err());
1609        assert!(TimeSignature::from_ratio_string("3.0/4.0").is_err());
1610    }
1611
1612    #[test]
1613    fn the_sequences_are_partitioned_as_music21_partitions_them() {
1614        use crate::meter::TimeSignature;
1615
1616        // Read off music21 11.0.0b9 directly: `str(TimeSignature(r).beatSequence)`
1617        // and `.beamSequence` for each meter below.
1618        let expected = [
1619            ("1/4", "{1/4}", "{1/4}"),
1620            ("2/4", "{{1/8+1/8}+{1/8+1/8}}", "{{1/8+1/8}+{1/8+1/8}}"),
1621            (
1622                "3/4",
1623                "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1624                "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1625            ),
1626            (
1627                "4/4",
1628                "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1629                "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1630            ),
1631            (
1632                "5/4",
1633                "{{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}",
1634                "{{1/4+1/4}+{1/4+1/4+1/4}}",
1635            ),
1636            ("6/4", "{{1/4+1/4+1/4}+{1/4+1/4+1/4}}", "{3/4+3/4}"),
1637            ("2/2", "{{1/4+1/4}+{1/4+1/4}}", "{1/2+1/2}"),
1638            ("3/2", "{{1/4+1/4}+{1/4+1/4}+{1/4+1/4}}", "{1/2+1/2+1/2}"),
1639            ("3/8", "{3/8}", "{3/8}"),
1640            (
1641                "5/8",
1642                "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
1643                "{2/8+3/8}",
1644            ),
1645            ("6/8", "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}", "{3/8+3/8}"),
1646            (
1647                "9/8",
1648                "{{1/8+1/8+1/8}+{1/8+1/8+1/8}+{1/8+1/8+1/8}}",
1649                "{3/8+3/8+3/8}",
1650            ),
1651            (
1652                "12/8",
1653                "{{1/8+1/8+1/8}+{1/8+1/8+1/8}+{1/8+1/8+1/8}+{1/8+1/8+1/8}}",
1654                "{3/8+3/8+3/8+3/8}",
1655            ),
1656            (
1657                "5/16",
1658                "{{1/32+1/32}+{1/32+1/32}+{1/32+1/32}+{1/32+1/32}+{1/32+1/32}}",
1659                "{5/16}",
1660            ),
1661            (
1662                "slow 6/8",
1663                "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
1664                "{3/8+3/8}",
1665            ),
1666            ("fast 6/8", "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}", "{3/8+3/8}"),
1667            ("3/8+2/8", "{{1/8+1/8+1/8}+{1/8+1/8}}", "{2/8+3/8}"),
1668            ("2/4+3/8", "{{1/4+1/4}+{1/8+1/8+1/8}}", "{2/8+2/8+3/8}"),
1669        ];
1670        for (ratio, beats, beams) in expected {
1671            let signature = TimeSignature::from_ratio_string(ratio).unwrap();
1672            assert_eq!(
1673                signature.beat_sequence().to_string(),
1674                beats,
1675                "beats of {ratio}"
1676            );
1677            assert_eq!(
1678                signature.beam_sequence().to_string(),
1679                beams,
1680                "beams of {ratio}"
1681            );
1682        }
1683    }
1684
1685    /// Read off music21's default `accentSequence` and `getBeatDepth`.
1686    #[test]
1687    fn accent_weights_and_beat_depths_follow_the_default_hierarchy() {
1688        let weights = |ratio: &str| {
1689            TimeSignature::from_ratio_string(ratio)
1690                .unwrap()
1691                .accent_weights()
1692        };
1693        assert_eq!(
1694            weights("4/4"),
1695            [1.0, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125]
1696        );
1697        assert_eq!(
1698            weights("6/8"),
1699            [
1700                1.0, 0.125, 0.25, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125, 0.25, 0.125
1701            ]
1702        );
1703        assert_eq!(
1704            weights("12/8"),
1705            [
1706                1.0, 0.125, 0.125, 0.25, 0.125, 0.125, 0.5, 0.125, 0.125, 0.25, 0.125, 0.125
1707            ]
1708        );
1709        assert_eq!(weights("3/4").len(), 12);
1710        assert_eq!(weights("24/8").len(), 24);
1711        assert_eq!(
1712            TimeSignature::new(1, 4)
1713                .unwrap()
1714                .accent_partition_quarter_length(),
1715            0.125
1716        );
1717
1718        let three_four = TimeSignature::new(3, 4).unwrap();
1719        let read: Vec<FloatType> = (0..3)
1720            .map(|beat| three_four.accent_weight(FloatType::from(beat)).unwrap())
1721            .collect();
1722        assert_eq!(read, [1.0, 0.5, 0.5]);
1723        let beyond = three_four.accent_weight(3.0).unwrap_err().to_string();
1724        assert!(beyond.ends_with("cannot access from qLenPos 3.0 where total duration is 3.0"));
1725        // A bar whose parts would be longer than a whole note, or shorter
1726        // than a 128th, is one partition.
1727        assert_eq!(weights("16/1"), [1.0]);
1728        assert_eq!(weights("1/32"), [1.0]);
1729        assert_eq!(
1730            TimeSignature::new(24, 4)
1731                .unwrap()
1732                .accent_partition_quarter_length(),
1733            1.0
1734        );
1735        assert_eq!(
1736            three_four.accent_weight_with(4.0, false, true).unwrap(),
1737            0.5
1738        );
1739        assert_eq!(
1740            three_four.accent_weight_with(0.1, true, false).unwrap(),
1741            0.0625
1742        );
1743        assert_eq!(
1744            three_four.accent_weight_with(0.1, false, false).unwrap(),
1745            1.0
1746        );
1747        assert!(three_four.accent(2.0));
1748        assert!(!three_four.accent(0.1));
1749        assert!(!three_four.accent(3.0));
1750
1751        assert_eq!(three_four.beat_depth(0.0).unwrap(), 2);
1752        assert_eq!(three_four.beat_depth(0.25).unwrap(), 2);
1753        assert_eq!(three_four.beat_depth(0.5).unwrap(), 1);
1754        assert_eq!(three_four.beat_depth(1.0).unwrap(), 2);
1755        assert!(three_four.beat_depth(3.0).is_err());
1756        assert_eq!(
1757            TimeSignature::new(3, 8).unwrap().beat_depth(0.5).unwrap(),
1758            1
1759        );
1760        assert_eq!(
1761            TimeSignature::new(6, 8).unwrap().beat_depth(1.0).unwrap(),
1762            1
1763        );
1764        assert_eq!(
1765            TimeSignature::new(6, 8).unwrap().beat_depth(1.5).unwrap(),
1766            2
1767        );
1768    }
1769
1770    /// music21's own `bestTimeSignature` examples.
1771    #[test]
1772    fn the_best_time_signature_fits_what_the_measure_holds() {
1773        use crate::{Note, Pitch, Stream};
1774
1775        let measure = |lengths: &[FloatType]| {
1776            let mut stream = Stream::new();
1777            for length in lengths {
1778                let mut note = Note::from_pitch(Pitch::from_name("C4").unwrap());
1779                note.set_duration(Duration::new(*length).unwrap());
1780                stream.push(note);
1781            }
1782            stream
1783        };
1784        let best = |lengths: &[FloatType]| {
1785            best_time_signature(&measure(lengths))
1786                .unwrap()
1787                .ratio_string()
1788        };
1789        assert_eq!(best(&[1.0, 1.0, 0.5, 0.5]), "3/4");
1790        assert_eq!(best(&[0.75, 0.25, 0.5, 0.75, 0.25, 0.5]), "6/8");
1791        assert_eq!(best(&[2.0, 2.0, 2.0]), "3/2");
1792        assert_eq!(best(&[0.75, 0.25, 0.5, 0.75, 0.25, 0.5, 1.5, 1.5]), "12/8");
1793        assert_eq!(best(&[1.0, 2.0, 1.0, 2.0]), "6/4");
1794        assert_eq!(best(&[1.0, 0.375]), "11/32");
1795        assert_eq!(best(&[3.5, 5.5]), "9/4");
1796        assert_eq!(best(&[1.0, 1.0, 1.0, 1.0]), "4/4");
1797        assert_eq!(best(&[4.0]), "4/4");
1798        // Tuplets are passed over when the shortest value is looked for.
1799        assert_eq!(best(&[1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0, 1.0]), "2/4");
1800    }
1801
1802    /// music21's own `averageBeatStrength` example: `C4 D4 E8 F8` under
1803    /// six-eight and three-four.
1804    #[test]
1805    fn beat_strength_is_averaged_over_a_stream() {
1806        use crate::{Note, Pitch, Stream};
1807
1808        let mut stream = Stream::new();
1809        for (name, length) in [("C4", 1.0), ("D4", 1.0), ("E4", 0.5), ("F4", 0.5)] {
1810            let mut note = Note::from_pitch(Pitch::from_name(name).unwrap());
1811            note.set_duration(Duration::new(length).unwrap());
1812            stream.push(note);
1813        }
1814        assert_eq!(
1815            TimeSignature::new(6, 8)
1816                .unwrap()
1817                .average_beat_strength(&stream, true),
1818            0.4375
1819        );
1820        assert_eq!(
1821            TimeSignature::new(3, 4)
1822                .unwrap()
1823                .average_beat_strength(&stream, true),
1824            0.5625
1825        );
1826        stream.insert(0.0, TimeSignature::new(3, 4).unwrap());
1827        stream.insert(0.0, TimeSignature::new(6, 8).unwrap());
1828        assert_eq!(
1829            TimeSignature::new(6, 8)
1830                .unwrap()
1831                .average_beat_strength(&stream, false),
1832            0.625
1833        );
1834        assert_eq!(
1835            TimeSignature::new(4, 4)
1836                .unwrap()
1837                .average_beat_strength(&Stream::new(), true),
1838            0.0
1839        );
1840    }
1841
1842    #[test]
1843    fn a_signature_reports_its_two_numbers_and_its_bar() {
1844        let six_eight = TimeSignature::new(6, 8).unwrap();
1845        assert_eq!((six_eight.numerator(), six_eight.denominator()), (6, 8));
1846        assert_eq!(six_eight.bar_duration().quarter_length(), 3.0);
1847    }
1848
1849    #[test]
1850    fn division_helpers_match_music21() {
1851        let quarter_lengths = |durations: Vec<Duration>| -> Vec<FloatType> {
1852            durations.into_iter().map(|d| d.quarter_length()).collect()
1853        };
1854        let cases: [(&str, FloatType, &str, &[FloatType], usize); 7] = [
1855            ("4/4", 1.0, "Simple", &[0.5, 0.5], 4),
1856            ("6/8", 0.5, "Compound", &[0.5, 0.5, 0.5], 6),
1857            ("2/2", 2.0, "Simple", &[1.0, 1.0], 4),
1858            ("3/8", 0.5, "Other", &[1.5], 2),
1859            ("7/8", 0.5, "Simple", &[0.25, 0.25], 4),
1860            ("12/16", 0.25, "Compound", &[0.25, 0.25, 0.25], 6),
1861            ("1/4", 1.0, "Other", &[1.0], 2),
1862        ];
1863        for (ratio, beat_to_quarter, division_name, divisions, sub_divisions) in cases {
1864            let ts = ts(ratio);
1865            assert_eq!(
1866                ts.beat_length_to_quarter_length_ratio(),
1867                beat_to_quarter,
1868                "{ratio}"
1869            );
1870            assert_eq!(
1871                ts.quarter_length_to_beat_length_ratio(),
1872                1.0 / beat_to_quarter,
1873                "{ratio}"
1874            );
1875            assert_eq!(ts.beat_division_count_name(), division_name, "{ratio}");
1876            assert_eq!(
1877                quarter_lengths(ts.beat_division_durations().unwrap()),
1878                divisions,
1879                "{ratio}"
1880            );
1881            let subs = quarter_lengths(ts.beat_sub_division_durations().unwrap());
1882            assert_eq!(subs.len(), sub_divisions, "{ratio}");
1883            assert!(subs.iter().all(|ql| *ql == divisions[0] / 2.0), "{ratio}");
1884        }
1885        assert!(ts("4/4").ratio_equal(&ts("4/4")));
1886        assert!(!ts("4/4").ratio_equal(&ts("2/2")));
1887    }
1888
1889    #[test]
1890    fn beat_positions_match_music21() {
1891        let common = ts("4/4");
1892        let cases: [(FloatType, UnsignedIntegerType, FloatType, FloatType, &str); 7] = [
1893            (0.0, 1, 0.0, 1.0, "1"),
1894            (0.5, 1, 0.5, 1.5, "1 1/2"),
1895            (1.25, 2, 0.25, 2.25, "2 1/4"),
1896            (2.75, 3, 0.75, 3.75, "3 3/4"),
1897            (3.0, 4, 0.0, 4.0, "4"),
1898            (3.75, 4, 0.75, 4.75, "4 3/4"),
1899            (3.9, 4, 0.9, 4.9, "4 9/10"),
1900        ];
1901        for (offset, beat, progress, proportion, text) in cases {
1902            let (actual_beat, actual_progress) = common.beat_progress(offset).unwrap();
1903            assert_eq!(actual_beat, beat, "{offset}");
1904            assert!((actual_progress - progress).abs() < 1e-9, "{offset}");
1905            assert!(
1906                (common.beat_proportion(offset).unwrap() - proportion).abs() < 1e-9,
1907                "{offset}"
1908            );
1909            assert_eq!(
1910                common.beat_proportion_string(offset).unwrap(),
1911                text,
1912                "{offset}"
1913            );
1914        }
1915
1916        let compound = ts("6/8");
1917        assert_eq!(compound.beat_proportion_string(0.5).unwrap(), "1 1/3");
1918        assert_eq!(compound.beat_proportion_string(1.0).unwrap(), "1 2/3");
1919        assert_eq!(compound.beat_proportion_string(2.5).unwrap(), "2 2/3");
1920        assert!((compound.beat_proportion(2.0).unwrap() - 7.0 / 3.0).abs() < 1e-9);
1921        assert_eq!(ts("3/8").beat_proportion_string(1.0).unwrap(), "1 2/3");
1922        assert!(common.beat_progress(4.0).is_err());
1923    }
1924
1925    #[test]
1926    fn offset_from_beat_matches_music21() {
1927        let common = ts("4/4");
1928        for (beat, offset) in [
1929            (1.0, 0.0),
1930            (1.5, 0.5),
1931            (2.5, 1.5),
1932            (3.25, 2.25),
1933            (4.75, 3.75),
1934        ] {
1935            assert_eq!(common.offset_from_beat(beat).unwrap(), offset, "{beat}");
1936        }
1937        assert!(common.offset_from_beat(5.0).is_err());
1938        assert!(common.offset_from_beat(0.5).is_err());
1939        let compound = ts("6/8");
1940        assert_eq!(compound.offset_from_beat(1.5).unwrap(), 0.75);
1941        assert_eq!(compound.offset_from_beat(2.5).unwrap(), 2.25);
1942        assert!((compound.offset_from_beat(2.999).unwrap() - 2.9985).abs() < 1e-9);
1943        assert_eq!(ts("3/8").offset_from_beat(1.5).unwrap(), 0.75);
1944    }
1945
1946    #[test]
1947    fn closest_fraction_limits_the_denominator_like_python() {
1948        assert_eq!(closest_fraction(0.5, 16), (1, 2));
1949        assert_eq!(closest_fraction(1.0 / 3.0, 16), (1, 3));
1950        assert_eq!(closest_fraction(0.9, 16), (9, 10));
1951        assert_eq!(closest_fraction(0.75, 16), (3, 4));
1952        assert_eq!(closest_fraction(0.1234, 16), (1, 8));
1953    }
1954    use super::*;
1955
1956    fn ts(ratio: &str) -> TimeSignature {
1957        TimeSignature::from_ratio_string(ratio).expect("valid time signature")
1958    }
1959
1960    #[test]
1961    fn common_and_cut_time_match_their_ratios() {
1962        assert_eq!(TimeSignature::common().ratio_string(), "4/4");
1963        assert_eq!(TimeSignature::cut().ratio_string(), "2/2");
1964        assert_eq!(TimeSignature::default(), TimeSignature::common());
1965    }
1966
1967    #[test]
1968    fn bar_and_beat_lengths_follow_the_ratio() {
1969        assert_eq!(ts("4/4").bar_quarter_length(), 4.0);
1970        assert_eq!(ts("5/16").bar_quarter_length(), 1.25);
1971        assert_eq!(ts("3/8").bar_quarter_length(), 1.5);
1972        assert_eq!(ts("6/8").beat_quarter_length().unwrap(), 1.5);
1973        assert_eq!(ts("4/4").beat_quarter_length().unwrap(), 1.0);
1974        assert_eq!(ts("2/2").beat_duration().unwrap().quarter_length(), 2.0);
1975    }
1976
1977    #[test]
1978    fn compound_meters_beat_in_threes() {
1979        for (ratio, beats, division) in [
1980            ("6/8", 2, BeatDivision::Compound),
1981            ("9/8", 3, BeatDivision::Compound),
1982            ("12/8", 4, BeatDivision::Compound),
1983            ("15/8", 5, BeatDivision::Compound),
1984            ("18/8", 6, BeatDivision::Compound),
1985            ("24/8", 8, BeatDivision::Compound),
1986        ] {
1987            assert_eq!(ts(ratio).beat_count(), beats, "{ratio}");
1988            assert_eq!(ts(ratio).beat_division(), division, "{ratio}");
1989            assert!(ts(ratio).is_compound(), "{ratio}");
1990        }
1991    }
1992
1993    #[test]
1994    fn three_is_the_one_denominator_sensitive_numerator() {
1995        // 3/2 and 3/4 read as three beats; 3/8 and shorter read as one.
1996        assert_eq!(ts("3/2").beat_count(), 3);
1997        assert_eq!(ts("3/4").beat_count(), 3);
1998        assert_eq!(ts("3/8").beat_count(), 1);
1999        assert_eq!(ts("3/16").beat_count(), 1);
2000        assert_eq!(ts("3/32").beat_count(), 1);
2001        // The cut is at an eighth rather than at a power of two: music21
2002        // counts 3/3 and 3/6 in three, and 3/12 in one.
2003        assert_eq!(ts("3/3").beat_count(), 3);
2004        assert_eq!(ts("3/6").beat_count(), 3);
2005        assert_eq!(ts("3/12").beat_count(), 1);
2006        // Every other numerator ignores the denominator entirely.
2007        for denominator in [2, 4, 8, 16] {
2008            assert_eq!(TimeSignature::new(6, denominator).unwrap().beat_count(), 2);
2009            assert_eq!(TimeSignature::new(5, denominator).unwrap().beat_count(), 5);
2010        }
2011    }
2012
2013    #[test]
2014    fn a_bar_written_in_unequal_parts_is_counted_along_its_own_beats() {
2015        use crate::meter::TimeSignature;
2016
2017        // Read off music21 11.0.0b9. `2/4+3/8` is two beats, of two quarters
2018        // and of three eighths, so nothing here is the bar divided evenly.
2019        let mixed = TimeSignature::from_ratio_string("2/4+3/8").unwrap();
2020        assert_eq!(mixed.bar_quarter_length(), 3.5);
2021        assert_eq!(mixed.beat_count(), 2);
2022        assert_eq!(mixed.beat_offsets(), vec![0.0, 2.0]);
2023        assert_eq!(mixed.beat_at_offset(2.5).unwrap(), 2);
2024        assert_eq!(mixed.beat_duration_at(0.0).unwrap().quarter_length(), 2.0);
2025        assert_eq!(mixed.beat_duration_at(2.5).unwrap().quarter_length(), 1.5);
2026        assert_eq!(mixed.offset_from_beat(2.0).unwrap(), 2.0);
2027        // Half way through the first beat is half of *that* beat, not half of
2028        // an averaged one.
2029        assert_eq!(mixed.offset_from_beat(1.5).unwrap(), 1.0);
2030        assert_eq!(mixed.beat_proportion_string(2.5).unwrap(), "2 1/3");
2031        assert_eq!(mixed.beat_depth(0.0).unwrap(), 2);
2032
2033        let other = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2034        assert_eq!(other.beat_offsets(), vec![0.0, 1.5]);
2035        assert_eq!(other.offset_from_beat(2.0).unwrap(), 1.5);
2036        assert_eq!(other.offset_from_beat(1.5).unwrap(), 0.75);
2037        // The bar is 2.5 long, so 2.5 is past the end of it.
2038        assert!(other.beat_proportion_string(2.5).is_err());
2039
2040        // An evenly divided bar answers exactly as it did.
2041        let common = TimeSignature::common();
2042        assert_eq!(common.beat_offsets(), vec![0.0, 1.0, 2.0, 3.0]);
2043        assert_eq!(common.offset_from_beat(2.0).unwrap(), 1.0);
2044        assert_eq!(common.offset_from_beat(1.5).unwrap(), 0.5);
2045        assert_eq!(common.beat_proportion_string(2.5).unwrap(), "3 1/2");
2046    }
2047
2048    #[test]
2049    fn a_bar_whose_beats_differ_has_no_one_beat_length() {
2050        use crate::meter::TimeSignature;
2051
2052        // music21 11.0.0b9 raises on beatDuration here and reports the
2053        // division as Other, counting one.
2054        let mixed = TimeSignature::from_ratio_string("2/4+3/8").unwrap();
2055        assert!(mixed.beat_quarter_length().is_err());
2056        assert!(mixed.beat_duration().is_err());
2057        assert!(mixed.beat_division_durations().is_err());
2058        assert_eq!(mixed.beat_division_count(), 1);
2059        assert_eq!(mixed.beat_division_count_name(), "Other");
2060        assert_eq!(mixed.classification(), "Other Duple");
2061
2062        let other = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2063        assert!(other.beat_quarter_length().is_err());
2064        assert_eq!(other.beat_division_count(), 1);
2065        assert_eq!(other.classification(), "Other Duple");
2066
2067        // An evenly divided bar still answers, and answers as it did.
2068        assert_eq!(TimeSignature::common().beat_quarter_length().unwrap(), 1.0);
2069        assert_eq!(
2070            TimeSignature::from_ratio_string("6/8")
2071                .unwrap()
2072                .beat_quarter_length()
2073                .unwrap(),
2074            1.5
2075        );
2076    }
2077
2078    #[test]
2079    fn weights_a_caller_sets_are_the_weights_read_back() {
2080        use crate::meter::TimeSignature;
2081
2082        let mut common = TimeSignature::common();
2083        let default = common.accent_weights();
2084        assert_eq!(default[0], 1.0);
2085
2086        // music21 loops a shorter list over the partitions.
2087        common.set_accent_weight(&[0.8, 0.2], 0).unwrap();
2088        let set = common.accent_weights();
2089        assert_eq!(set.len(), default.len());
2090        assert!((set[0] - 0.8).abs() < 1e-9);
2091        assert!((set[1] - 0.2).abs() < 1e-9);
2092        assert!((set[2] - 0.8).abs() < 1e-9);
2093
2094        // And the reading at an offset comes off the same sequence.
2095        assert!((common.accent_weight(0.0).unwrap() - 0.8).abs() < 1e-9);
2096
2097        // A fresh meter is untouched by that.
2098        assert_eq!(TimeSignature::common().accent_weights()[0], 1.0);
2099    }
2100
2101    #[test]
2102    fn a_bar_can_be_counted_in_a_different_number_of_beats() {
2103        use crate::meter::TimeSignature;
2104
2105        // Read off music21 11.0.0b9 by assigning to `beatCount`.
2106        for (ratio, count, partitioned) in [
2107            (
2108                "6/8",
2109                6,
2110                "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
2111            ),
2112            ("6/8", 2, "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}"),
2113            ("4/4", 2, "{{1/4+1/4}+{1/4+1/4}}"),
2114            ("3/4", 1, "{3/4}"),
2115            (
2116                "2/4",
2117                4,
2118                "{{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}",
2119            ),
2120        ] {
2121            let mut signature = TimeSignature::from_ratio_string(ratio).unwrap();
2122            signature.set_beat_count(count).unwrap();
2123            assert_eq!(
2124                signature.beat_sequence().to_string(),
2125                partitioned,
2126                "{ratio} in {count}"
2127            );
2128            assert_eq!(signature.beat_count(), count, "{ratio} in {count}");
2129        }
2130    }
2131
2132    #[test]
2133    fn a_bar_can_be_written_a_different_way_than_it_is_counted() {
2134        use crate::meter::TimeSignature;
2135
2136        // music21's own `setDisplay` docstring: a 3/4 written in three
2137        // groups of two eighths.
2138        let mut signature = TimeSignature::from_ratio_string("3/4").unwrap();
2139        let counted = signature.beat_sequence().to_string();
2140        signature.set_display("2/8+2/8+2/8").unwrap();
2141        assert_eq!(signature.display_sequence().to_string(), "{2/8+2/8+2/8}");
2142        assert_eq!(
2143            signature.display_sequence().partition_display(),
2144            "2/8+2/8+2/8"
2145        );
2146        // What it counts is untouched; only how it is written changed.
2147        assert_eq!(signature.beat_sequence().to_string(), counted);
2148        assert_eq!(signature.beat_count(), 3);
2149        // music21 reads `ratioString` off the display sequence, so there it
2150        // becomes "2/8+2/8+2/8". The crate still writes it from the
2151        // numerator and the denominator; see issues.md.
2152
2153        // A bar cannot be written as a length it is not.
2154        assert!(signature.set_display("2/4").is_err());
2155
2156        // The sequences can be partitioned through.
2157        let mut common = TimeSignature::common();
2158        common
2159            .beat_sequence_mut()
2160            .partition_by_count(2, true)
2161            .unwrap();
2162        assert_eq!(common.beat_count(), 2);
2163    }
2164
2165    #[test]
2166    fn the_divisions_a_meter_is_built_with_partition_its_beats() {
2167        use crate::meter::TimeSignature;
2168
2169        // Read off music21 11.0.0b9: TimeSignature(ratio, divisions).
2170        for (ratio, divisions, partitioned) in [
2171            ("3/4", 1, "{3/4}"),
2172            ("6/8", 2, "{3/8+3/8}"),
2173            ("4/4", 2, "{1/2+1/2}"),
2174            ("3/4", 3, "{1/4+1/4+1/4}"),
2175        ] {
2176            let mut signature = TimeSignature::from_ratio_string(ratio).unwrap();
2177            signature.divide_beats(divisions).unwrap();
2178            assert_eq!(
2179                signature.beat_sequence().to_string(),
2180                partitioned,
2181                "{ratio} in {divisions}"
2182            );
2183        }
2184
2185        // Unlike counting the bar, which divides each beat again.
2186        let mut counted = TimeSignature::from_ratio_string("6/8").unwrap();
2187        counted.set_beat_count(2).unwrap();
2188        assert_eq!(
2189            counted.beat_sequence().to_string(),
2190            "{{1/8+1/8+1/8}+{1/8+1/8+1/8}}"
2191        );
2192    }
2193
2194    #[test]
2195    fn a_fractional_beat_is_read_as_the_fraction_it_suggests() {
2196        use crate::meter::TimeSignature;
2197
2198        // Read off music21 11.0.0b9: getOffsetFromBeat snaps through
2199        // addFloatPrecision, so a third written as .33 is a third.
2200        // A 6/8 beat lasts 1.5, so beat 2.33 starts a third of the way
2201        // into the second beat: 1.5 + 1.5/3. music21 answers 2.0 and 2.5.
2202        let compound = TimeSignature::from_ratio_string("6/8").unwrap();
2203        assert!((compound.offset_from_beat(2.33).unwrap() - 2.0).abs() < 1e-9);
2204        assert!((compound.offset_from_beat(2.66).unwrap() - 2.5).abs() < 1e-9);
2205        assert!((compound.offset_from_beat(1.66).unwrap() - 1.0).abs() < 1e-9);
2206
2207        // A simple beat lasts one quarter, so the same beats land on thirds
2208        // of it: music21 answers 4/3, 5/3 and 2/3 for a 4/4 bar.
2209        let simple = TimeSignature::common();
2210        let third = 1.0 / 3.0;
2211        assert!((simple.offset_from_beat(2.33).unwrap() - (1.0 + third)).abs() < 1e-9);
2212        assert!((simple.offset_from_beat(2.66).unwrap() - (1.0 + 2.0 * third)).abs() < 1e-9);
2213        assert!((simple.offset_from_beat(1.66).unwrap() - (2.0 * third)).abs() < 1e-9);
2214
2215        // The divisions reach the accents and the beams, not the beats alone.
2216        let mut divided = TimeSignature::common();
2217        divided.divide_beats(4).unwrap();
2218        assert_eq!(divided.beat_sequence().to_string(), "{1/4+1/4+1/4+1/4}");
2219        assert_eq!(divided.accent_sequence().to_string(), "{1/4+1/4+1/4+1/4}");
2220        assert_eq!(divided.beam_sequence().to_string(), "{1/4+1/4+1/4+1/4}");
2221        assert_eq!(divided.display_sequence().to_string(), "{4/4}");
2222
2223        // Weighing them then loops over four parts, not eight.
2224        divided.set_accent_weight(&[0.8, 0.2], 0).unwrap();
2225        let weights = divided.accent_weights();
2226        assert_eq!(weights.len(), 4);
2227        assert!((weights[0] - 0.8).abs() < 1e-9);
2228        assert!((weights[1] - 0.2).abs() < 1e-9);
2229    }
2230
2231    #[test]
2232    fn classification_joins_division_and_count() {
2233        assert_eq!(ts("4/4").classification(), "Simple Quadruple");
2234        assert_eq!(ts("6/8").classification(), "Compound Duple");
2235        assert_eq!(ts("3/8").classification(), "Other Single");
2236        assert_eq!(ts("5/4").classification(), "Simple Quintuple");
2237        assert_eq!(ts("13/8").classification(), "Simple 13-uple");
2238        assert_eq!(ts("21/16").classification(), "Compound Septuple");
2239    }
2240
2241    #[test]
2242    fn beat_offsets_partition_the_bar() {
2243        assert_eq!(ts("4/4").beat_offsets(), [0.0, 1.0, 2.0, 3.0]);
2244        assert_eq!(ts("6/8").beat_offsets(), [0.0, 1.5]);
2245        assert_eq!(ts("5/8").beat_offsets(), [0.0, 0.5, 1.0, 1.5, 2.0]);
2246    }
2247
2248    #[test]
2249    fn beat_at_offset_is_one_based_and_bounded() {
2250        assert_eq!(ts("4/4").beat_at_offset(1.5).unwrap(), 2);
2251        assert_eq!(ts("6/8").beat_at_offset(1.5).unwrap(), 2);
2252        assert_eq!(ts("5/8").beat_at_offset(1.5).unwrap(), 4);
2253        assert_eq!(ts("4/4").beat_at_offset(0.0).unwrap(), 1);
2254        assert!(ts("4/4").beat_at_offset(4.0).is_err());
2255        assert!(ts("4/4").beat_at_offset(-0.5).is_err());
2256        assert!(ts("4/4").beat_at_offset(FloatType::NAN).is_err());
2257    }
2258
2259    #[test]
2260    fn irrational_denominators_are_accepted_as_music21_accepts_them() {
2261        let four_three = ts("4/3");
2262        assert!((four_three.bar_quarter_length() - 16.0 / 3.0).abs() < 1e-12);
2263        assert_eq!(four_three.beat_count(), 4);
2264    }
2265
2266    #[test]
2267    fn malformed_ratios_error_instead_of_panicking() {
2268        for ratio in [
2269            "", "4", "4/", "/4", "4/4/4", "x/4", "4/x", "0/4", "4/0", "-1/4",
2270        ] {
2271            assert!(
2272                TimeSignature::from_ratio_string(ratio).is_err(),
2273                "{ratio:?} should not parse"
2274            );
2275        }
2276    }
2277
2278    #[test]
2279    fn display_is_the_ratio_string() {
2280        assert_eq!(ts("7/8").to_string(), "7/8");
2281    }
2282
2283    #[test]
2284    fn a_meter_says_how_it_was_written() {
2285        use crate::meter::TimeSignature;
2286
2287        // music21 reads `ratioString` off the display sequence.
2288        let written = TimeSignature::from_ratio_string("2/4+3/8").unwrap();
2289        assert_eq!(written.ratio_string(), "2/4+3/8");
2290        assert_eq!(written.to_string(), "2/4+3/8");
2291        // What it comes to is unchanged.
2292        assert_eq!((written.numerator(), written.denominator()), (7, 8));
2293
2294        // The order it was written in is part of what it is, which is what
2295        // stops these being two spellings of one meter.
2296        let two_three = TimeSignature::from_ratio_string("2/8+3/8").unwrap();
2297        let three_two = TimeSignature::from_ratio_string("3/8+2/8").unwrap();
2298        assert_eq!(two_three.ratio_string(), "2/8+3/8");
2299        assert_eq!(three_two.ratio_string(), "3/8+2/8");
2300        assert_ne!(two_three, three_two);
2301        // Though they do come to the same ratio, which is music21's
2302        // `ratioEqual`.
2303        assert!(two_three.ratio_equal(&three_two));
2304
2305        // The string re-reads as the meter it came from.
2306        let again = TimeSignature::from_ratio_string(&written.ratio_string()).unwrap();
2307        assert_eq!(again.ratio_string(), written.ratio_string());
2308
2309        // The word before the ratio is not part of the string, though it
2310        // does still make a different meter.
2311        let slow = TimeSignature::from_ratio_string("slow 6/8").unwrap();
2312        let fast = TimeSignature::from_ratio_string("6/8").unwrap();
2313        assert_eq!(slow.ratio_string(), "6/8");
2314        assert_eq!(fast.ratio_string(), "6/8");
2315        assert_ne!(slow, fast);
2316
2317        // A bar written as one ratio is unmoved.
2318        assert_eq!(TimeSignature::common().ratio_string(), "4/4");
2319    }
2320}