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