1# -*- coding: utf-8 -*-
2# -----------------------------------------------------------------------------
3# Name:         meter.py
4# Purpose:      Classes for meters
5#
6# Authors:      Christopher Ariza
7#               Michael Scott Cuthbert
8#
9# Copyright:    Copyright © 2009-2012, 2015, 2021 Michael Scott Cuthbert
10#               and the music21 Project
11# License:      BSD, see license.txt
12# -----------------------------------------------------------------------------
13'''
14This module defines the :class:`~music21.meter.TimeSignature` object,
15as well as component objects for defining nested metrical structures,
16:class:`~music21.meter.MeterTerminal` and :class:`~music21.meter.MeterSequence` objects.
17'''
18import copy
19import fractions
20from typing import Optional
21
22from music21 import base
23from music21 import beam
24from music21 import common
25from music21 import defaults
26from music21 import duration
27from music21 import environment
28from music21 import style
29from music21.exceptions21 import MeterException, TimeSignatureException
30
31from music21.common.enums import MeterDivision
32from music21.common.numberTools import opFrac
33from music21.meter.tools import slashToTuple, proportionToFraction
34from music21.meter.core import MeterSequence
35
36environLocal = environment.Environment('meter')
37
38
39# -----------------------------------------------------------------------------
40
41# also [pow(2,x) for x in range(8)]
42MIN_DENOMINATOR_TYPE = '128th'
43
44# store a module-level dictionary of partitioned meter sequences used
45# for setting default accent weights; store as needed
46_meterSequenceAccentArchetypes = {}
47
48
49def bestTimeSignature(meas: 'music21.stream.Stream') -> 'music21.meter.TimeSignature':
50    # noinspection PyShadowingNames
51    '''
52    Given a Measure (or any Stream) with elements in it, get a TimeSignature that contains all
53    elements.
54
55    Note: this does not yet accommodate triplets.
56
57    >>> s = converter.parse('tinynotation: C4 D4 E8 F8').flatten().notes
58    >>> m = stream.Measure()
59    >>> for el in s:
60    ...     m.insert(el.offset, el)
61    >>> ts = meter.bestTimeSignature(m)
62    >>> ts
63    <music21.meter.TimeSignature 3/4>
64
65    >>> s2 = converter.parse('tinynotation: C8. D16 E8 F8. G16 A8').flatten().notes
66    >>> m2 = stream.Measure()
67    >>> for el in s2:
68    ...     m2.insert(el.offset, el)
69    >>> ts2 = meter.bestTimeSignature(m2)
70    >>> ts2
71    <music21.meter.TimeSignature 6/8>
72
73    >>> s3 = converter.parse('C2 D2 E2', format='tinyNotation').flatten().notes
74    >>> m3 = stream.Measure()
75    >>> for el in s3:
76    ...     m3.insert(el.offset, el)
77    >>> ts3 = meter.bestTimeSignature(m3)
78    >>> ts3
79    <music21.meter.TimeSignature 3/2>
80
81    >>> s4 = converter.parse('C8. D16 E8 F8. G16 A8 C4. D4.', format='tinyNotation').flatten().notes
82    >>> m4 = stream.Measure()
83    >>> for el in s4:
84    ...     m4.insert(el.offset, el)
85    >>> ts4 = meter.bestTimeSignature(m4)
86    >>> ts4
87    <music21.meter.TimeSignature 12/8>
88
89    >>> s5 = converter.parse('C4 D2 E4 F2', format='tinyNotation').flatten().notes
90    >>> m5 = stream.Measure()
91    >>> for el in s5:
92    ...     m5.insert(el.offset, el)
93    >>> ts5 = meter.bestTimeSignature(m5)
94    >>> ts5
95    <music21.meter.TimeSignature 6/4>
96
97    >>> s6 = converter.parse('C4 D16.', format='tinyNotation').flatten().notes
98    >>> m6 = stream.Measure()
99    >>> for el in s6:
100    ...     m6.insert(el.offset, el)
101    >>> ts6 = meter.bestTimeSignature(m6)
102    >>> ts6
103    <music21.meter.TimeSignature 11/32>
104
105
106    Complex durations (arose in han2.abc, number 445)
107
108    >>> m7 = stream.Measure()
109    >>> m7.append(note.Note('D', quarterLength=3.5))
110    >>> m7.append(note.Note('E', quarterLength=5.5))
111    >>> ts7 = meter.bestTimeSignature(m7)
112    >>> ts7
113    <music21.meter.TimeSignature 9/4>
114    '''
115    minDurQL = 4  # smallest denominator; start with a whole note
116    # find sum of all durations in quarter length
117    # find if there are any dotted durations
118    minDurDots = 0
119    sumDurQL = opFrac(meas.duration.quarterLength)
120    # beatStrAvg = 0
121    # beatStrAvg += e.beatStrength
122    numerator = 0
123    denominator = 1
124
125    for e in meas.recurse().notesAndRests:
126        if e.quarterLength == 0.0:
127            continue  # case of grace durations
128        if (e.quarterLength < minDurQL
129                and not isinstance(opFrac(e.quarterLength), fractions.Fraction)):
130            # no non-power2 signatures
131            minDurQL = e.quarterLength
132            minDurDots = e.duration.dots
133
134    # first, we need to evenly divide min dur into total
135    minDurTest = minDurQL
136    if isinstance(sumDurQL, fractions.Fraction):
137        # not a power of two -- some tuplets, etc.
138        numerator = sumDurQL.numerator
139        denominator = sumDurQL.denominator
140    else:
141        i = 10
142        while i > 0:
143            partsFloor = int(sumDurQL / minDurTest)
144            partsReal = opFrac(sumDurQL / minDurTest)
145            if (partsFloor == partsReal
146                    or minDurTest <= duration.typeToDuration[MIN_DENOMINATOR_TYPE]):
147                break
148            # need to break down minDur until we can get a match
149            else:
150                minDurTest = minDurTest / (2 * common.dotMultiplier(minDurDots))
151            i -= 1
152
153        # see if we can get a type for the denominator
154        # if we do not have a match; we need to break down this value
155        match = False
156        durationMinLimit = duration.typeToDuration[MIN_DENOMINATOR_TYPE]
157        i = 10
158        while i > 0:
159            if minDurTest < durationMinLimit:
160                minDurTest = durationMinLimit
161                break
162            try:
163                dType, match = duration.quarterLengthToClosestType(minDurTest)
164            except ZeroDivisionError:
165                raise MeterException('Cannot find a good match for this measure')
166
167            if match or dType == MIN_DENOMINATOR_TYPE:
168                break
169            minDurTest = minDurTest / (2 * common.dotMultiplier(minDurDots))
170            i -= 1
171
172        minDurQL = minDurTest
173        dType, match = duration.quarterLengthToClosestType(minDurQL)
174        if not match:  # cant find a type for a denominator
175            raise MeterException(f'cannot find a type for denominator {minDurQL}')
176
177        # denominator is the numerical representation of the min type
178        # e.g., quarter is 4, whole is 1
179        for num, typeName in duration.typeFromNumDict.items():
180            if typeName == dType:
181                if num >= 1:
182                    num = int(num)
183                denominator = num
184                break
185        # numerator is the count of min parts in the sum
186        multiplier = 1
187        while i > 0:
188            numerator = multiplier * sumDurQL / minDurQL
189            if numerator == int(numerator):
190                break
191            multiplier *= 2
192            i -= 1
193
194        numerator = int(numerator)
195        denominator *= multiplier
196        # simplifies to "simplest terms," with 4 in denominator, before testing beat strengths
197        gcdValue = common.euclidGCD(numerator, denominator)
198        numerator = numerator // gcdValue
199        denominator = denominator // gcdValue
200
201    # simplifies rare time signatures like 16/16 and 1/1 to 4/4
202    if numerator == denominator and numerator not in [2, 4]:
203        numerator = 4
204        denominator = 4
205    elif numerator != denominator and denominator == 1:
206        numerator *= 4
207        denominator *= 4
208    elif numerator != denominator and denominator == 2:
209        numerator *= 2
210        denominator *= 2
211
212    # a fairly accurate test of whether 3/4 or 6/8 is more appropriate (see doctests)
213    if numerator == 3 and denominator == 4:
214        ts1 = TimeSignature('3/4')
215        ts2 = TimeSignature('6/8')
216        str1 = ts1.averageBeatStrength(meas)
217        str2 = ts2.averageBeatStrength(meas)
218
219        if str1 <= str2:
220            return ts2
221        else:
222            return ts1
223
224    # tries three time signatures if "simplest" time signature is 6/4 or 3/2
225    elif numerator == 6 and denominator == 4:
226        ts1 = TimeSignature('6/4')
227        ts2 = TimeSignature('12/8')
228        ts3 = TimeSignature('3/2')
229        str1 = ts1.averageBeatStrength(meas)
230        str2 = ts2.averageBeatStrength(meas)
231        str3 = ts3.averageBeatStrength(meas)
232        m = max(str1, str2, str3)
233        if m == str1:
234            return ts1
235        elif m == str3:
236            return ts3
237        else:
238            return ts2
239
240    # environLocal.printDebug(['n/d', numerator, denominator])
241    else:
242        ts = TimeSignature()
243        ts.loadRatio(numerator, denominator)
244        return ts
245
246
247# -----------------------------------------------------------------------------
248
249
250class TimeSignature(base.Music21Object):
251    r'''
252    The `TimeSignature` object represents time signatures in musical scores
253    (4/4, 3/8, 2/4+5/16, Cut, etc.).
254
255    `TimeSignatures` should be present in the first `Measure` of each `Part`
256    that they apply to.  Alternatively you can put the time signature at the
257    front of a `Part` or at the beginning of a `Score` and they will work
258    within music21 but they won't necessarily display properly in musicxml,
259    lilypond, etc.  So best is to create structures like this:
260
261    >>> s = stream.Score()
262    >>> p = stream.Part()
263    >>> m1 = stream.Measure()
264    >>> ts = meter.TimeSignature('3/4')
265    >>> m1.insert(0, ts)
266    >>> m1.insert(0, note.Note('C#3', type='half'))
267    >>> n = note.Note('D3', type='quarter')  # we will need this later
268    >>> m1.insert(1.0, n)
269    >>> m1.number = 1
270    >>> p.insert(0, m1)
271    >>> s.insert(0, p)
272    >>> s.show('t')
273    {0.0} <music21.stream.Part ...>
274        {0.0} <music21.stream.Measure 1 offset=0.0>
275            {0.0} <music21.meter.TimeSignature 3/4>
276            {0.0} <music21.note.Note C#>
277            {1.0} <music21.note.Note D>
278
279    Basic operations on a TimeSignature object are designed to be very simple.
280
281    >>> ts.ratioString
282    '3/4'
283
284    >>> ts.numerator
285    3
286
287    >>> ts.beatCount
288    3
289
290    >>> ts.beatCountName
291    'Triple'
292
293    >>> ts.beatDuration.quarterLength
294    1.0
295
296    As an alternative to putting a `TimeSignature` in a Stream at a specific
297    position (offset), it can be assigned to a special property in Measure that
298    positions the TimeSignature at the start of a Measure.  Notice that when we
299    `show()` the Measure (or if we iterate through it), the TimeSignature
300    appears as if it's in the measure itself:
301
302    >>> m2 = stream.Measure()
303    >>> m2.number = 2
304    >>> ts2 = meter.TimeSignature('2/4')
305    >>> m2.timeSignature = ts2
306    >>> m2.append(note.Note('E3', type='half'))
307    >>> p.append(m2)
308    >>> s.show('text')
309    {0.0} <music21.stream.Part ...>
310        {0.0} <music21.stream.Measure 1 offset=0.0>
311            {0.0} <music21.meter.TimeSignature 3/4>
312            {0.0} <music21.note.Note C#>
313            {1.0} <music21.note.Note D>
314        {2.0} <music21.stream.Measure 2 offset=2.0>
315            {0.0} <music21.meter.TimeSignature 2/4>
316            {0.0} <music21.note.Note E>
317
318    Once a Note has a local TimeSignature, a Note can get its beat position and
319    other meter-specific parameters.  Remember `n`, our quarter note at offset
320    2.0 of `m1`, a 3/4 measure? Let's get its beat:
321
322    >>> n.beat
323    2.0
324
325    This feature is more useful if there are more beats:
326
327    >>> m3 = stream.Measure()
328    >>> m3.timeSignature = meter.TimeSignature('3/4')
329    >>> eighth = note.Note(type='eighth')
330    >>> m3.repeatAppend(eighth, 6)
331    >>> [thisNote.beatStr for thisNote in m3.notes]
332    ['1', '1 1/2', '2', '2 1/2', '3', '3 1/2']
333
334    Now lets change its measure's TimeSignature and see what happens:
335
336    >>> sixEight = meter.TimeSignature('6/8')
337    >>> m3.timeSignature = sixEight
338    >>> [thisNote.beatStr for thisNote in m3.notes]
339    ['1', '1 1/3', '1 2/3', '2', '2 1/3', '2 2/3']
340
341    TimeSignature('6/8') defaults to fast 6/8:
342
343    >>> sixEight.beatCount
344    2
345
346    >>> sixEight.beatDuration.quarterLength
347    1.5
348
349    >>> sixEight.beatDivisionCountName
350    'Compound'
351
352    Let's make it slow 6/8 instead:
353
354    >>> sixEight.beatCount = 6
355    >>> sixEight.beatDuration.quarterLength
356    0.5
357
358    >>> sixEight.beatDivisionCountName
359    'Simple'
360
361    Now let's look at the `beatStr` for each of the notes in `m3`:
362
363    >>> [thisNote.beatStr for thisNote in m3.notes]
364    ['1', '2', '3', '4', '5', '6']
365
366    As of v7., 3/8 also defaults to fast 3/8, that is, one beat:
367
368    >>> meter.TimeSignature('3/8').beatCount
369    1
370
371    `TimeSignatures` can also use symbols instead of numbers
372
373    >>> tsCommon = meter.TimeSignature('c')  # or common
374    >>> tsCommon.beatCount
375    4
376    >>> tsCommon.denominator
377    4
378
379    >>> tsCommon.symbol
380    'common'
381
382    >>> tsCut = meter.TimeSignature('cut')
383    >>> tsCut.beatCount
384    2
385    >>> tsCut.denominator
386    2
387
388    >>> tsCut.symbol
389    'cut'
390
391    For other time signatures, the symbol is '' (not set) or 'normal'
392
393    >>> sixEight.symbol
394    ''
395
396
397    For complete details on using this object, see
398    :ref:`User's Guide Chapter 14: Time Signatures <usersGuide_14_timeSignatures>` and
399    :ref:`User's Guide Chapter 55: Advanced Meter <usersGuide_55_advancedMeter>` and
400
401
402    That's it for the simple aspects of `TimeSignature` objects.  You know
403    enough to get started now!
404
405    Under the hood, they're extremely powerful.  For musicians, TimeSignatures
406    do (at least) three different things:
407
408    * They define where the beats in the measure are and how many there are.
409
410    * They indicate how the notes should be beamed
411
412    * They give a sense of how much accent or weight each note gets, which
413      also defines which are important notes and which might be ornaments.
414
415    These three aspects of `TimeSignatures` are controlled by the
416    :attr:`~music21.meter.TimeSignature.beatSequence`,
417    :attr:`~music21.meter.TimeSignature.beamSequence`, and
418    :attr:`~music21.meter.TimeSignature.accentSequence` properties of the
419    `TimeSignature`.  Each of them is an independent
420    :class:`~music21.meter.MeterSequence` element which might have nested
421    properties (e.g., a 11/16 meter might be beamed as {1/4+1/4+{1/8+1/16}}),
422    so if you want to change how beats are calculated or beams are generated
423    you'll want to learn more about `meter.MeterSequence` objects.
424
425    There's a fourth `MeterSequence` object inside a TimeSignature, and that is
426    the :attr:`~music21.meter.TimeSignature.displaySequence`. That determines
427    how the `TimeSignature` should actually look on paper.  Normally this
428    `MeterSequence` is pretty simple.  In '4/4' it's usually just '4/4'.  But
429    if you have the '11/16' time above, you may want to have it displayed as
430    '2/4+3/16' or '11/16 (2/4+3/16)'.  Or you might want the written
431    TimeSignature to contradict what the notes imply.  All this can be done
432    with .displaySequence.
433    '''
434    _styleClass = style.TextStyle
435    classSortOrder = 4
436
437    _DOC_ATTR = {
438        'beatSequence': 'A :class:`~music21.meter.MeterSequence` governing beat partitioning.',
439        'beamSequence': 'A :class:`~music21.meter.MeterSequence` governing automatic beaming.',
440        'accentSequence': 'A :class:`~music21.meter.MeterSequence` governing accent partitioning.',
441        'displaySequence': '''
442            A :class:`~music21.meter.MeterSequence` governing the display of the TimeSignature.''',
443        'symbol': '''
444            A string representation of how to display the TimeSignature.
445            can be "common", "cut", "single-number" (i.e.,
446            no denominator), or "normal" or "".''',
447        'symbolizeDenominator': '''
448            If set to `True` (default is `False`) then the denominator
449            will be displayed as a symbol rather than
450            a number.  Hindemith uses this in his scores.
451            Finale and other MusicXML readers do not support this
452            so do not expect proper output yet.''',
453    }
454
455    def __init__(self, value: str = '4/4', divisions=None):
456        super().__init__()
457
458        if value is None:
459            value = f'{defaults.meterNumerator}/{defaults.meterDenominatorBeatType}'
460
461        self._overriddenBarDuration = None
462        self.symbol = ''
463        self.displaySequence: Optional[MeterSequence] = None
464        self.beatSequence: Optional[MeterSequence] = None
465        self.accentSequence: Optional[MeterSequence] = None
466        self.beamSequence: Optional[MeterSequence] = None
467        self.symbolizeDenominator = False
468
469        self.resetValues(value, divisions)
470
471    def _reprInternal(self):
472        return self.ratioString
473
474    def resetValues(self, value: str = '4/4', divisions=None):
475        '''
476        reset all values according to a new value and optionally, the number of
477        divisions.
478        '''
479        self.symbol = ''  # common, cut, single-number, normal
480
481        # a parameter to determine if the denominator is represented
482        # as either a symbol (a note) or as a number
483        self.symbolizeDenominator = False
484
485        self._overriddenBarDuration = None
486
487        # creates MeterSequence data representations
488        # creates .displaySequence, .beamSequence, .beatSequence, .accentSequence
489        self.load(value, divisions)
490
491    def load(self, value: str, divisions=None):
492        '''
493        Load up a TimeSignature with a string value.
494
495        >>> ts = meter.TimeSignature()
496        >>> ts.load('4/4')
497        >>> ts
498        <music21.meter.TimeSignature 4/4>
499
500        >>> ts.load('c')
501        >>> ts.symbol
502        'common'
503
504
505        >>> ts.load('2/4+3/8')
506        >>> ts
507        <music21.meter.TimeSignature 2/4+3/8>
508
509
510        >>> ts.load('fast 6/8')
511        >>> ts.beatCount
512        2
513        >>> ts.load('slow 6/8')
514        >>> ts.beatCount
515        6
516
517        Loading destroys all preexisting internal representations
518        '''
519        # create parallel MeterSequence objects to provide all data
520        # these all refer to the same .numerator/.denominator
521        # relationship
522
523        # used for drawing the time signature symbol
524        # this is the only one that can be  unlinked
525        if value.lower() in ('common', 'c'):
526            value = '4/4'
527            self.symbol = 'common'
528        elif value.lower() in ('cut', 'allabreve'):
529            # allaBreve is the capella version
530            value = '2/2'
531            self.symbol = 'cut'
532
533        self.displaySequence = MeterSequence(value)
534
535        # get simple representation; presently, only slashToTuple
536        # supports the fast/slow indication
537        numerator, denominator, division = slashToTuple(value)
538        if division == MeterDivision.NONE:
539            if numerator % 3 == 0 and denominator >= 8:
540                division = MeterDivision.FAST
541            elif numerator == 3:
542                division = MeterDivision.SLOW
543        favorCompound = (division != MeterDivision.SLOW)
544
545        # used for beaming
546        self.beamSequence = MeterSequence(value, divisions)
547        # used for getting beat divisions
548        self.beatSequence = MeterSequence(value, divisions)
549
550        # accentSequence is used for setting one level of accents
551        self.accentSequence = MeterSequence(value, divisions)
552
553        if divisions is None:  # set default beam partitions
554            # beam is not adjust by tempo indication
555            self._setDefaultBeamPartitions()
556            self._setDefaultBeatPartitions(favorCompound=favorCompound)
557
558            # for some summed meters default accent weights are difficult
559            # to obtain
560            try:
561                self._setDefaultAccentWeights(3)  # set partitions based on beat
562            except MeterException:
563                environLocal.printDebug(['cannot set default accents for:', self])
564
565    def loadRatio(self, numerator, denominator, divisions=None):
566        '''
567        Change the numerator and denominator, like ratioString, but with
568        optional divisions and without resetting other parameters.
569
570        DEPRECATED in v7. -- call .ratioString or .load with
571        value = f'{numerator}/{denominator}'
572        '''
573        value = f'{numerator}/{denominator}'
574        self.load(value, divisions)
575
576    @property
577    def ratioString(self):
578        '''
579        Returns or sets a simple string representing the time signature ratio.
580
581        >>> threeFour = meter.TimeSignature('3/4')
582        >>> threeFour.ratioString
583        '3/4'
584
585        It can also be set to load a new one, but '.load()' is better...
586
587        >>> threeFour.ratioString = '5/8'  # now this variable name is dumb!
588        >>> threeFour.numerator
589        5
590        >>> threeFour.denominator
591        8
592
593        >>> complexTime = meter.TimeSignature('2/4+3/8')
594        >>> complexTime.ratioString
595        '2/4+3/8'
596
597        For advanced users, getting the ratioString is the equivalent of
598        :attr:`~music21.meter.core.MeterSequence.partitionDisplay` on the displaySequence:
599
600        >>> complexTime.displaySequence.partitionDisplay
601        '2/4+3/8'
602        '''
603        return self.displaySequence.partitionDisplay
604
605    @ratioString.setter
606    def ratioString(self, newRatioString):
607        self.resetValues(newRatioString)
608
609    def ratioEqual(self, other):
610        '''
611        A basic form of comparison; does not determine if any internal structures are equal; o
612        only outermost ratio.
613        '''
614        if other is None:
615            return False
616        if (other.numerator == self.numerator
617                and other.denominator == self.denominator):
618            return True
619        else:
620            return False
621
622    # --------------------------------------------------------------------------
623    # properties
624
625    @property
626    def numerator(self):
627        '''
628        Return the numerator of the TimeSignature as a number.
629
630        Can set the numerator for a simple TimeSignature.
631        To set the numerator of a complex TimeSignature, change beatCount.
632
633        (for complex TimeSignatures, note that this comes from the .beamSequence
634        of the TimeSignature)
635
636
637        >>> ts = meter.TimeSignature('3/4')
638        >>> ts.numerator
639        3
640        >>> ts.numerator = 5
641        >>> ts
642        <music21.meter.TimeSignature 5/4>
643
644
645        In this case, the TimeSignature is silently being converted to 9/8
646        to get a single digit numerator:
647
648        >>> ts = meter.TimeSignature('2/4+5/8')
649        >>> ts.numerator
650        9
651
652        Setting a summed time signature's numerator will change to a
653        simple time signature
654
655        >>> ts.numerator = 11
656        >>> ts
657        <music21.meter.TimeSignature 11/8>
658        '''
659        return self.beamSequence.numerator
660
661    @numerator.setter
662    def numerator(self, value):
663        denominator = self.denominator
664        newRatioString = str(value) + '/' + str(denominator)
665        self.resetValues(newRatioString)
666
667
668    @property
669    def denominator(self):
670        '''
671        Return the denominator of the TimeSignature as a number or set it.
672
673        (for complex TimeSignatures, note that this comes from the .beamSequence
674        of the TimeSignature)
675
676        >>> ts = meter.TimeSignature('3/4')
677        >>> ts.denominator
678        4
679        >>> ts.denominator = 8
680        >>> ts.ratioString
681        '3/8'
682
683
684        In this following case, the TimeSignature is silently being converted to 9/8
685        to get a single digit denominator:
686
687        >>> ts = meter.TimeSignature('2/4+5/8')
688        >>> ts.denominator
689        8
690        '''
691        return self.beamSequence.denominator
692
693    @denominator.setter
694    def denominator(self, value):
695        numeratorValue = self.numerator
696        newRatioString = str(numeratorValue) + '/' + str(value)
697        self.resetValues(newRatioString)
698
699    @property
700    def barDuration(self) -> duration.Duration:
701        '''
702        Return a :class:`~music21.duration.Duration` object equal to the
703        total length of this TimeSignature.
704
705        >>> ts = meter.TimeSignature('5/16')
706        >>> ts.barDuration
707        <music21.duration.Duration 1.25>
708
709        >>> ts2 = meter.TimeSignature('3/8')
710        >>> d = ts2.barDuration
711        >>> d.type
712        'quarter'
713        >>> d.dots
714        1
715        >>> d.quarterLength
716        1.5
717
718        This can be overridden to create different representations
719        or to contradict the meter.
720
721        >>> d2 = duration.Duration(1.75)
722        >>> ts2.barDuration = d2
723        >>> ts2.barDuration
724        <music21.duration.Duration 1.75>
725        '''
726
727        if self._overriddenBarDuration:
728            return self._overriddenBarDuration
729        else:
730            # could come from self.beamSequence, self.accentSequence,
731            #   self.displaySequence, self.accentSequence
732            return self.beamSequence.duration
733
734    @barDuration.setter
735    def barDuration(self, value: duration.Duration):
736        self._overriddenBarDuration = value
737
738    @property
739    def beatLengthToQuarterLengthRatio(self):
740        '''
741        Returns 4.0 / denominator... seems a bit silly...
742
743        >>> a = meter.TimeSignature('3/2')
744        >>> a.beatLengthToQuarterLengthRatio
745        2.0
746        '''
747        return 4 / self.denominator
748
749    @property
750    def quarterLengthToBeatLengthRatio(self):
751        '''
752        Returns denominator/4.0... seems a bit silly...
753        '''
754        return self.denominator / 4.0
755
756    # --------------------------------------------------------------------------
757    # meter classifications used for classifying meters such as
758    # duple triple, etc.
759
760    @property
761    def beatCount(self):
762        '''
763        Return or set the count of beat units, or the number of beats in this TimeSignature.
764
765        When setting beat units, one level of sub-partitions is automatically defined.
766        Users can specify beat count values as integers or as lists of durations.
767        For more precise configuration of the beat MeterSequence,
768        manipulate the .beatSequence attribute directly.
769
770        >>> ts = meter.TimeSignature('3/4')
771        >>> ts.beatCount
772        3
773        >>> ts.beatDuration.quarterLength
774        1.0
775        >>> ts.beatCount = [1, 1, 1, 1, 1, 1]
776        >>> ts.beatCount
777        6
778        >>> ts.beatDuration.quarterLength
779        0.5
780
781        Setting a beat-count directly is a simple, high-level way to configure the beatSequence.
782        Note that his may not configure lower level partitions correctly,
783        and will raise an error if the provided beat count is not supported by the
784        overall duration of the .beatSequence MeterSequence.
785
786        >>> ts = meter.TimeSignature('6/8')
787        >>> ts.beatCount  # default is 2 beats
788        2
789        >>> ts.beatSequence
790        <music21.meter.core.MeterSequence {{1/8+1/8+1/8}+{1/8+1/8+1/8}}>
791        >>> ts.beatDivisionCountName
792        'Compound'
793        >>> ts.beatCount = 6
794        >>> ts.beatSequence
795        <music21.meter.core.MeterSequence
796            {{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}+{1/16+1/16}}>
797        >>> ts.beatDivisionCountName
798        'Simple'
799        >>> ts.beatCount = 123
800        Traceback (most recent call last):
801        music21.exceptions21.TimeSignatureException: cannot partition beat with provided value: 123
802
803        >>> ts = meter.TimeSignature('3/4')
804        >>> ts.beatCount = 6
805        >>> ts.beatDuration.quarterLength
806        0.5
807        '''
808        # the default is for the beat to be defined by the first, not zero,
809        # level partition.
810        return len(self.beatSequence)
811
812    @beatCount.setter
813    def beatCount(self, value):
814        try:
815            self.beatSequence.partition(value)
816        except MeterException:
817            raise TimeSignatureException(f'cannot partition beat with provided value: {value}')
818        # create subdivisions using default parameters
819        if len(self.beatSequence) > 1:  # if partitioned
820            self.beatSequence.subdividePartitionsEqual()
821
822    @property
823    def beatCountName(self):
824        '''
825        Return the beat count name, or the name given for the number of beat units.
826        For example, 2/4 is duple; 9/4 is triple.
827
828        >>> ts = meter.TimeSignature('3/4')
829        >>> ts.beatCountName
830        'Triple'
831
832        >>> ts = meter.TimeSignature('6/8')
833        >>> ts.beatCountName
834        'Duple'
835        '''
836        return self.beatSequence.partitionStr
837
838    @property
839    def beatDuration(self) -> duration.Duration:
840        '''
841        Return a :class:`~music21.duration.Duration` object equal to the beat unit
842        of this Time Signature, if and only if this TimeSignature has a uniform beat unit.
843
844        Otherwise raises an exception in v7.1 but will change to returning NaN
845        soon fasterwards.
846
847        >>> ts = meter.TimeSignature('3/4')
848        >>> ts.beatDuration
849        <music21.duration.Duration 1.0>
850        >>> ts = meter.TimeSignature('6/8')
851        >>> ts.beatDuration
852        <music21.duration.Duration 1.5>
853
854        >>> ts = meter.TimeSignature('7/8')
855        >>> ts.beatDuration
856        <music21.duration.Duration 0.5>
857
858        >>> ts = meter.TimeSignature('3/8')
859        >>> ts.beatDuration
860        <music21.duration.Duration 1.5>
861        >>> ts.beatCount = 3
862        >>> ts.beatDuration
863        <music21.duration.Duration 0.5>
864
865        Cannot do this because of asymmetry
866
867        >>> ts = meter.TimeSignature('2/4+3/16')
868        >>> ts.beatDuration
869        Traceback (most recent call last):
870        music21.exceptions21.TimeSignatureException: non-uniform beat unit: [2.0, 0.75]
871
872        Changed in v7. -- return NaN rather than raising Exception in property.
873        '''
874        post = []
875        for ms in self.beatSequence:
876            post.append(ms.duration.quarterLength)
877        if len(set(post)) == 1:
878            return self.beatSequence[0].duration  # all are the same
879        else:
880            raise TimeSignatureException(f'non-uniform beat unit: {post}')
881
882    @property
883    def beatDivisionCount(self):
884        '''
885        Return the count of background beat units found within one beat,
886        or the number of subdivisions in the beat unit in this TimeSignature.
887
888        >>> ts = meter.TimeSignature('3/4')
889        >>> ts.beatDivisionCount
890        2
891
892        >>> ts = meter.TimeSignature('6/8')
893        >>> ts.beatDivisionCount
894        3
895
896        >>> ts = meter.TimeSignature('15/8')
897        >>> ts.beatDivisionCount
898        3
899
900        >>> ts = meter.TimeSignature('3/8')
901        >>> ts.beatDivisionCount
902        1
903
904        >>> ts = meter.TimeSignature('13/8', 13)
905        >>> ts.beatDivisionCount
906        1
907
908        Changed in v7. -- return 1 instead of a TimeSignatureException.
909        '''
910        # first, find if there is more than one beat and if all beats are uniformly partitioned
911        post = []
912        if len(self.beatSequence) == 1:
913            return 1
914
915        # need to see if first-level subdivisions are partitioned
916        if not isinstance(self.beatSequence[0], MeterSequence):
917            return 1
918
919        # getting length here gives number of subdivisions
920        for ms in self.beatSequence:
921            post.append(len(ms))
922
923        # convert this to a set; if length is 1, then all beats are uniform
924        if len(set(post)) == 1:
925            return len(self.beatSequence[0])  # all are the same
926        else:
927            return 1
928
929    @property
930    def beatDivisionCountName(self):
931        '''
932        Return the beat count name, or the name given for the number of beat units.
933        For example, 2/4 is duple; 9/4 is triple.
934
935        >>> ts = meter.TimeSignature('3/4')
936        >>> ts.beatDivisionCountName
937        'Simple'
938
939        >>> ts = meter.TimeSignature('6/8')
940        >>> ts.beatDivisionCountName
941        'Compound'
942
943        Rare cases of 5-beat divisions return 'Other', like this 10/8 divided into
944        5/8 + 5/8 with no further subdivisions:
945
946        >>> ts = meter.TimeSignature('10/8')
947        >>> ts.beatSequence.partition(2)
948        >>> ts.beatSequence
949        <music21.meter.core.MeterSequence {5/8+5/8}>
950        >>> for i, mt in enumerate(ts.beatSequence):
951        ...     ts.beatSequence[i] = mt.subdivideByCount(5)
952        >>> ts.beatSequence
953        <music21.meter.core.MeterSequence {{1/8+1/8+1/8+1/8+1/8}+{1/8+1/8+1/8+1/8+1/8}}>
954        >>> ts.beatDivisionCountName
955        'Other'
956        '''
957        beatDivision = self.beatDivisionCount
958        if beatDivision == 2:
959            return 'Simple'
960        elif beatDivision == 3:
961            return 'Compound'
962        else:
963            return 'Other'
964
965    @property
966    def beatDivisionDurations(self):
967        '''
968        Return the beat division, or the durations that make up one beat,
969        as a list of :class:`~music21.duration.Duration` objects, if and only if
970        the TimeSignature has a uniform beat division for all beats.
971
972        >>> ts = meter.TimeSignature('3/4')
973        >>> ts.beatDivisionDurations
974        [<music21.duration.Duration 0.5>,
975         <music21.duration.Duration 0.5>]
976
977        >>> ts = meter.TimeSignature('6/8')
978        >>> ts.beatDivisionDurations
979        [<music21.duration.Duration 0.5>,
980         <music21.duration.Duration 0.5>,
981         <music21.duration.Duration 0.5>]
982
983        Value returned of non-uniform beat divisions will change at any time
984        after v7.1 to avoid raising an exception.
985        '''
986        post = []
987        if len(self.beatSequence) == 1:
988            raise TimeSignatureException(
989                'cannot determine beat division for a non-partitioned beat')
990        for mt in self.beatSequence:
991            for subMt in mt:
992                post.append(subMt.duration.quarterLength)
993        if len(set(post)) == 1:  # all the same
994            out = []
995            for subMt in self.beatSequence[0]:
996                out.append(subMt.duration)
997            return out
998        else:
999            raise TimeSignatureException(f'non uniform beat division: {post}')
1000
1001    @property
1002    def beatSubDivisionDurations(self):
1003        '''
1004        Return a subdivision of the beat division, or a list
1005        of :class:`~music21.duration.Duration` objects representing each beat division
1006        divided by two.
1007
1008        >>> ts = meter.TimeSignature('3/4')
1009        >>> ts.beatSubDivisionDurations
1010        [<music21.duration.Duration 0.25>, <music21.duration.Duration 0.25>,
1011         <music21.duration.Duration 0.25>, <music21.duration.Duration 0.25>]
1012
1013        >>> ts = meter.TimeSignature('6/8')
1014        >>> ts.beatSubDivisionDurations
1015        [<music21.duration.Duration 0.25>, <music21.duration.Duration 0.25>,
1016         <music21.duration.Duration 0.25>, <music21.duration.Duration 0.25>,
1017         <music21.duration.Duration 0.25>, <music21.duration.Duration 0.25>]
1018        '''
1019        post = []
1020        src = self.beatDivisionDurations
1021        for d in src:
1022            # this is too slow... TODO: fix, but make sure all durations are unique.
1023            post.append(d.augmentOrDiminish(0.5))
1024            post.append(d.augmentOrDiminish(0.5))
1025        return post
1026
1027    @property
1028    def classification(self):
1029        '''
1030        Return the classification of this TimeSignature,
1031        such as Simple Triple or Compound Quadruple.
1032
1033        >>> ts = meter.TimeSignature('3/4')
1034        >>> ts.classification
1035        'Simple Triple'
1036        >>> ts = meter.TimeSignature('6/8')
1037        >>> ts.classification
1038        'Compound Duple'
1039        >>> ts = meter.TimeSignature('4/32')
1040        >>> ts.classification
1041        'Simple Quadruple'
1042        '''
1043        return f'{self.beatDivisionCountName} {self.beatCountName}'
1044
1045    @property
1046    def summedNumerator(self) -> bool:
1047        if self.displaySequence is None:
1048            return False
1049        return self.displaySequence.summedNumerator
1050
1051    @summedNumerator.setter
1052    def summedNumerator(self, value: bool):
1053        if self.displaySequence is not None:
1054            self.displaySequence.summedNumerator = value
1055
1056    # --------------------------------------------------------------------------
1057    # private methods -- most to be put into the various sequences.
1058
1059    def _setDefaultBeatPartitions(self, *, favorCompound=True):
1060        '''Set default beat partitions based on numerator and denominator.
1061
1062        >>> ts = meter.TimeSignature('3/4')
1063        >>> len(ts.beatSequence)  # first, not zeroth, level stores beat
1064        3
1065        >>> ts = meter.TimeSignature('6/8')
1066        >>> len(ts.beatSequence)
1067        2
1068        >>> ts = meter.TimeSignature('slow 6/8')
1069        >>> len(ts.beatSequence)
1070        6
1071
1072        Changed in v7 -- favorCompound is keyword only
1073        '''
1074        # if a non-compound meter has been given, as in
1075        # not 3+1/4; just 5/4
1076        if len(self.displaySequence) == 1:
1077            # create toplevel partitions
1078            if self.numerator == 2:  # duple meters
1079                self.beatSequence.partition(2)
1080            elif self.numerator == 6 and favorCompound:  # duple meters
1081                self.beatSequence.partition(2)
1082            elif self.numerator == 3 and favorCompound:  # 3/8, 3/16, but not 3/4
1083                self.beatSequence.partition(1)
1084            elif self.numerator == 3:  # triple meters
1085                self.beatSequence.partition([1, 1, 1])
1086            elif self.numerator == 9 and favorCompound:  # triple meters
1087                self.beatSequence.partition([3, 3, 3])
1088            elif self.numerator == 4:  # quadruple meters
1089                self.beatSequence.partition(4)
1090            elif self.numerator == 12 and favorCompound:
1091                self.beatSequence.partition(4)
1092            elif self.numerator >= 15 and self.numerator % 3 == 0 and favorCompound:
1093                # quintuple meters and above.
1094                num_triples = self.numerator // 3
1095                self.beatSequence.partition([3] * num_triples)
1096            # skip 6 numerators; covered above
1097            else:  # case of odd meters: 11, 13
1098                self.beatSequence.partition(self.numerator)
1099
1100        # if a complex meter has been given
1101        else:  # partition by display
1102            # TODO: remove partitionByMeterSequence usage.
1103            self.beatSequence.partition(self.displaySequence)
1104
1105        # create subdivisions, and thus define compound/simple distinction
1106        if len(self.beatSequence) > 1:  # if partitioned
1107            try:
1108                self.beatSequence.subdividePartitionsEqual()
1109            except MeterException:
1110                if self.denominator >= 128:
1111                    pass  # do not raise an exception for unable to subdivide smaller than 128
1112
1113    def _setDefaultBeamPartitions(self):
1114        '''
1115        This sets default beam partitions when partitionRequest is None.
1116        '''
1117        # beam short measures of 8ths, 16ths, or 32nds all together
1118        if self.beamSequence.summedNumerator:
1119            pass  # do not mess with a numerator such as (3+2)/8
1120        elif self.denominator == 8 and self.numerator in (1, 2, 3):
1121            pass  # doing nothing will beam all together
1122        elif self.denominator == 16 and self.numerator in range(1, 6):
1123            # 1 - 5 -- beam all together
1124            pass
1125        elif self.denominator == 32 and self.numerator in range(1, 12):
1126            # 1 - 11 -- beam all together.
1127            pass
1128
1129        # more general, based only on numerator
1130        elif self.numerator in (2, 3, 4):
1131            self.beamSequence.partition(self.numerator)
1132            # if denominator is 4, subdivide each partition
1133            if self.denominator == 4:
1134                for i in range(len(self.beamSequence)):  # subdivide  each beat in 2
1135                    self.beamSequence[i] = self.beamSequence[i].subdivide(2)
1136        elif self.numerator == 5:
1137            default = [2, 3]
1138            self.beamSequence.partition(default)
1139            # if denominator is 4, subdivide each partition
1140            if self.denominator == 4:
1141                for i in range(len(self.beamSequence)):  # subdivide  each beat in 2
1142                    self.beamSequence[i] = self.beamSequence[i].subdivide(default[i])
1143
1144        elif self.numerator == 7:
1145            self.beamSequence.partition(3)  # divide into three groups
1146
1147        elif self.numerator in [6, 9, 12, 15, 18, 21]:
1148            self.beamSequence.partition([3] * int(self.numerator / 3))
1149        else:
1150            pass  # doing nothing will beam all together
1151        # environLocal.printDebug('default beam partitions set to: %s' % self.beamSequence)
1152
1153    def _setDefaultAccentWeights(self, depth=3):
1154        '''
1155        This sets default accent weights based on common hierarchical notions for meters;
1156        each beat is given a weight, as defined by the top level count of self.beatSequence
1157
1158        >>> ts1 = meter.TimeSignature('4/4')
1159        >>> ts1._setDefaultAccentWeights(4)
1160        >>> [mt.weight for mt in ts1.accentSequence]
1161        [1.0, 0.0625, 0.125, 0.0625, 0.25, 0.0625, 0.125, 0.0625,
1162         0.5, 0.0625, 0.125, 0.0625, 0.25, 0.0625, 0.125, 0.0625]
1163
1164        >>> ts2 = meter.TimeSignature('3/4')
1165        >>> ts2._setDefaultAccentWeights(4)
1166        >>> [mt.weight for mt in ts2.accentSequence]
1167        [1.0, 0.0625, 0.125, 0.0625, 0.25, 0.0625, 0.125, 0.0625,
1168         0.5, 0.0625, 0.125, 0.0625, 0.25, 0.0625, 0.125, 0.0625,
1169         0.5, 0.0625, 0.125, 0.0625, 0.25, 0.0625, 0.125, 0.0625]
1170
1171        >>> ts2._setDefaultAccentWeights(3)  # lower depth
1172        >>> [mt.weight for mt in ts2.accentSequence]
1173        [1.0, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125, 0.5, 0.125, 0.25, 0.125]
1174
1175        '''
1176        # NOTE: this is a performance critical method
1177
1178        # create a scratch MeterSequence for structure
1179        tsStr = f'{self.numerator}/{self.denominator}'
1180        if self.beatSequence.isUniformPartition():
1181            firstPartitionForm = len(self.beatSequence)
1182            cacheKey = (tsStr, firstPartitionForm, depth)
1183        else:  # derive from meter sequence
1184            firstPartitionForm = self.beatSequence
1185            cacheKey = None  # cannot cache based on beat form
1186
1187        # environLocal.printDebug(['_setDefaultAccentWeights(): firstPartitionForm set to',
1188        #    firstPartitionForm, 'self.beatSequence: ', self.beatSequence, tsStr])
1189        # using cacheKey speeds up TS creation from 2300 microseconds to 500microseconds
1190        try:
1191            self.accentSequence = copy.deepcopy(
1192                _meterSequenceAccentArchetypes[cacheKey]
1193            )
1194            # environLocal.printDebug(['using stored accent archetype:'])
1195        except KeyError:
1196            # environLocal.printDebug(['creating a new accent archetype'])
1197            ms = MeterSequence(tsStr)
1198            # key operation here
1199            # div count needs to be the number of top-level beat divisions
1200            ms.subdivideNestedHierarchy(depth,
1201                                        firstPartitionForm=firstPartitionForm)
1202
1203            # provide a partition for each flat division
1204            accentCount = len(ms.flat)
1205            # environLocal.printDebug(['got accentCount', accentCount, 'ms: ', ms])
1206            divStep = self.barDuration.quarterLength / accentCount
1207            weightInts = [0] * accentCount  # weights as integer/depth counts
1208            for i in range(accentCount):
1209                ql = i * divStep
1210                weightInts[i] = ms.offsetToDepth(ql, align='quantize', index=i)
1211
1212            maxInt = max(weightInts)
1213            weightValues = {}  # reference dictionary
1214            # minimum value, something like 1/16., to be multiplied by powers of 2
1215            weightValueMin = 1 / pow(2, maxInt - 1)
1216            for x in range(maxInt):
1217                # multiply base value (0.125) by 1, 2, 4
1218                # there is never a 0 integer weight, so add 1 to dictionary
1219                weightValues[x + 1] = weightValueMin * pow(2, x)
1220
1221            # set weights on accent partitions
1222            self.accentSequence.partition([1] * accentCount)
1223            for i in range(accentCount):
1224                # get values from weightValues dictionary
1225                self.accentSequence[i].weight = weightValues[weightInts[i]]
1226
1227            if cacheKey is not None:
1228                _meterSequenceAccentArchetypes[cacheKey] = copy.deepcopy(self.accentSequence)
1229
1230    # --------------------------------------------------------------------------
1231    # access data for other processing
1232    def getBeams(self, srcList, measureStartOffset=0.0):
1233        '''
1234        Given a qLen position and an iterable of Music21Objects, return a list of Beams objects.
1235
1236        The iterable can be a list (of elements) or a Stream (preferably flat)
1237        or a :class:`~music21.stream.iterator.StreamIterator` from which Durations
1238        and information about note vs. rest will be
1239        extracted.
1240
1241        Objects are assumed to be adjoining; offsets are not used, except for
1242        measureStartOffset()
1243
1244        Must process a list/Stream at time, because we cannot tell when a beam ends
1245        unless we see the context of adjoining durations.
1246
1247
1248        >>> a = meter.TimeSignature('2/4', 2)
1249        >>> a.beamSequence[0] = a.beamSequence[0].subdivide(2)
1250        >>> a.beamSequence[1] = a.beamSequence[1].subdivide(2)
1251        >>> a.beamSequence
1252        <music21.meter.core.MeterSequence {{1/8+1/8}+{1/8+1/8}}>
1253        >>> b = [note.Note(type='16th') for _ in range(8)]
1254        >>> c = a.getBeams(b)
1255        >>> len(c) == len(b)
1256        True
1257        >>> print(c)
1258        [<music21.beam.Beams <music21.beam.Beam 1/start>/<music21.beam.Beam 2/start>>,
1259         <music21.beam.Beams <music21.beam.Beam 1/continue>/<music21.beam.Beam 2/stop>>,
1260         <music21.beam.Beams <music21.beam.Beam 1/continue>/<music21.beam.Beam 2/start>>,
1261         <music21.beam.Beams <music21.beam.Beam 1/stop>/<music21.beam.Beam 2/stop>>,
1262         <music21.beam.Beams <music21.beam.Beam 1/start>/<music21.beam.Beam 2/start>>,
1263         <music21.beam.Beams <music21.beam.Beam 1/continue>/<music21.beam.Beam 2/stop>>,
1264         <music21.beam.Beams <music21.beam.Beam 1/continue>/<music21.beam.Beam 2/start>>,
1265         <music21.beam.Beams <music21.beam.Beam 1/stop>/<music21.beam.Beam 2/stop>>]
1266
1267        >>> a = meter.TimeSignature('6/8')
1268        >>> b = [note.Note(type='eighth') for _ in range(6)]
1269        >>> c = a.getBeams(b)
1270        >>> print(c)
1271        [<music21.beam.Beams <music21.beam.Beam 1/start>>,
1272         <music21.beam.Beams <music21.beam.Beam 1/continue>>,
1273         <music21.beam.Beams <music21.beam.Beam 1/stop>>,
1274         <music21.beam.Beams <music21.beam.Beam 1/start>>,
1275         <music21.beam.Beams <music21.beam.Beam 1/continue>>,
1276         <music21.beam.Beams <music21.beam.Beam 1/stop>>]
1277
1278        >>> fourFour = meter.TimeSignature('4/4')
1279        >>> nList = [note.Note(type=d) for d in ('eighth', 'quarter', 'eighth',
1280        ...                                      'eighth', 'quarter', 'eighth')]
1281        >>> beamList = fourFour.getBeams(nList)
1282        >>> print(beamList)
1283        [None, None, None, None, None, None]
1284
1285        Pickup measure support included by taking in an additional measureStartOffset argument.
1286
1287        >>> twoTwo = meter.TimeSignature('2/2')
1288        >>> nList = [note.Note(type='eighth') for _ in range(5)]
1289        >>> beamList = twoTwo.getBeams(nList, measureStartOffset=1.5)
1290        >>> print(beamList)
1291        [None,
1292         <music21.beam.Beams <music21.beam.Beam 1/start>>,
1293         <music21.beam.Beams <music21.beam.Beam 1/continue>>,
1294         <music21.beam.Beams <music21.beam.Beam 1/continue>>,
1295         <music21.beam.Beams <music21.beam.Beam 1/stop>>]
1296
1297        Fixed in v.7 -- incomplete final measures in 6/8:
1298
1299        >>> sixEight = meter.TimeSignature('6/8')
1300        >>> nList = [note.Note(type='quarter'), note.Note(type='eighth'), note.Note(type='eighth')]
1301        >>> beamList = sixEight.getBeams(nList)
1302        >>> print(beamList)
1303        [None, None, None]
1304
1305        And Measure objects with :attr:`~music21.stream.Measure.paddingRight` set:
1306
1307        >>> twoFour = meter.TimeSignature('2/4')
1308        >>> m = stream.Measure([note.Note(type='eighth') for _ in range(3)])
1309        >>> m.paddingRight = 0.5
1310        >>> twoFour.getBeams(m)
1311        [<music21.beam.Beams <music21.beam.Beam 1/start>>,
1312         <music21.beam.Beams <music21.beam.Beam 1/stop>>,
1313         None]
1314        '''
1315        from music21 import stream
1316        if isinstance(srcList, stream.Stream):
1317            srcStream = srcList
1318            srcList = list(srcList)  # do not change to [srcList]
1319        elif srcList and isinstance(srcList[0], base.Music21Object):
1320            # make into a stream to get proper offsets:
1321            # for eventually removing measureStartOffset
1322            srcStream = stream.Measure()
1323            srcStream.append(srcList)
1324        else:
1325            return []
1326
1327        if len(srcList) <= 1:
1328            return [None for _ in srcList]
1329
1330        beamsList = beam.Beams.naiveBeams(srcList)  # hold maximum Beams objects, all with type None
1331        beamsList = beam.Beams.removeSandwichedUnbeamables(beamsList)
1332
1333        def fixBeamsOneElementDepth(i, el, depth):
1334            beams = beamsList[i]
1335            if beams is None:
1336                return
1337
1338            beamNumber = depth + 1
1339            # see if there is a component defined for this beam number
1340            # if not, continue
1341            if beamNumber not in beams.getNumbers():
1342                return
1343
1344            dur = el.duration
1345            pos = el.offset + measureStartOffset
1346
1347            start = opFrac(pos)
1348            end = opFrac(pos + dur.quarterLength)
1349            startNext = end
1350
1351            isLast = (i == len(srcList) - 1)
1352            isFirst = (i == 0)
1353
1354            beamNext = beamsList[i + 1] if not isLast else None
1355            beamPrevious = beamsList[i - 1] if not isFirst else None
1356
1357            # get an archetype of the MeterSequence for this level
1358            # level is depth, starting at zero
1359            archetype = self.beamSequence.getLevel(depth)
1360            # span is the quarter note duration points for each partition
1361            # at this level
1362            archetypeSpanStart, archetypeSpanEnd = archetype.offsetToSpan(start)
1363            # environLocal.printDebug(['at level, got archetype span', depth,
1364            #                         archetypeSpan])
1365
1366            if beamNext is None:  # last note or before a non-beamable note (half, whole, etc.)
1367                archetypeSpanNextStart = 0.0
1368            else:
1369                archetypeSpanNextStart = archetype.offsetToSpan(startNext)[0]
1370
1371            # watch for a special case where a duration completely fills
1372            # the archetype; this generally should not be beamed
1373            # same if beamPrevious is None and beamNumber == 1 (quarter-eighth in 6/8)
1374            if end == archetypeSpanEnd and (
1375                start == archetypeSpanStart
1376                or (beamPrevious is None and beamNumber == 1)
1377            ):
1378                # increment position and continue loop
1379                beamsList[i] = None  # replace with None!
1380                return
1381
1382            # determine beamType
1383            # if first w/o pickup, always start
1384            if isFirst and measureStartOffset == 0:
1385                beamType = 'start'
1386                # get a partial beam if we cannot continue this
1387                if (beamNext is None
1388                        or beamNumber not in beamNext.getNumbers()):
1389                    beamType = 'partial-right'
1390
1391            # if last in complete measure or not in a measure, always stop
1392            elif isLast and (not srcStream.isMeasure or srcStream.paddingRight == 0.0):
1393                beamType = 'stop'
1394                # get a partial beam if we cannot form a beam
1395                if (beamPrevious is None
1396                        or beamNumber not in beamPrevious.getNumbers()):
1397                    # environLocal.warn(['triggering partial left where a stop normally falls'])
1398                    beamType = 'partial-left'
1399
1400            # here on we know that it is neither the first nor last
1401
1402            # if last beam was not defined, we need to either
1403            # start or have a partial left beam
1404            # or, if beam number was not active in last beams
1405            elif beamPrevious is None or beamNumber not in beamPrevious.getNumbers():
1406                if beamNumber == 1 and beamNext is None:
1407                    beamsList[i] = None
1408                    return
1409                elif beamNext is None and beamNumber > 1:
1410                    beamType = 'partial-left'
1411
1412                elif startNext >= archetypeSpanEnd:
1413                    # case of where we need a partial left:
1414                    # if the next start value is outside of this span (or at the
1415                    # the greater boundary of this span), and we did not have a
1416                    # beam or beam number in the previous beam
1417
1418                    # first note in pickup measures might also get 'partial-left'
1419                    # here, but this gets fixed in sanitize partial beams
1420
1421                    # may need to check: beamNext is not None and
1422                    #   beamNumber in beamNext.getNumbers()
1423                    # note: it is critical that we check archetypeSpan here
1424                    # not archetypeSpanNext
1425                    # environLocal.printDebug(['matching partial left'])
1426                    beamType = 'partial-left'
1427                elif beamNext is None or beamNumber not in beamNext.getNumbers():
1428                    beamType = 'partial-right'
1429                else:
1430                    beamType = 'start'
1431
1432            # last beams was active, last beamNumber was active,
1433            # and it was stopped or was a partial-left
1434            elif (beamPrevious is not None
1435                    and beamNumber in beamPrevious.getNumbers()
1436                    and beamPrevious.getTypeByNumber(beamNumber) in ['stop', 'partial-left']):
1437                if beamNext is not None:
1438                    beamType = 'start' if beamNumber in beamNext.getNumbers() else 'partial-right'
1439
1440                # last note had beams but stopped, next note cannot be beamed to
1441                # was active, last beamNumber was active,
1442                # and it was stopped or was a partial-left
1443                else:
1444                    beamType = 'partial-left'  # will be deleted later in the script
1445
1446            # if no beam is defined next (we know this already)
1447            # then must stop
1448            elif (beamNext is None
1449                  or beamNumber not in beamNext.getNumbers()):
1450                beamType = 'stop'
1451
1452            # the last cases are when to stop, or when to continue
1453            # when we know we have a beam next
1454
1455            # we continue if the next beam is in the same beaming archetype
1456            # as this one.
1457            # if endNext is outside of the archetype span,
1458            # not sure what to do
1459            elif startNext < archetypeSpanEnd:
1460                # environLocal.printDebug(['continue match: dur.type, startNext, archetypeSpan',
1461                #   dur.type, startNext, archetypeSpan])
1462                beamType = 'continue'
1463
1464            # we stop if the next beam is not in the same beaming archetype
1465            # and (as shown above) a valid beam number is not previous
1466            elif startNext >= archetypeSpanNextStart:
1467                beamType = 'stop'
1468
1469            else:
1470                raise TimeSignatureException('cannot match beamType')
1471
1472            # debugging information displays:
1473            # if beamPrevious is not None:
1474            #     environLocal.printDebug(['beamPrevious', beamPrevious,
1475            #     'beamPrevious.getNumbers()', beamPrevious.getNumbers(),
1476            #        'beamPrevious.getByNumber(beamNumber).type'])
1477            #
1478            #     if beamNumber in beamPrevious.getNumbers():
1479            #         environLocal.printDebug(['beamPrevious type',
1480            #            beamPrevious.getByNumber(beamNumber).type])
1481
1482            # environLocal.printDebug(['beamNumber, start, archetypeSpan, beamType',
1483            # beamNumber, start, dur.type, archetypeSpan, beamType])
1484
1485            beams.setByNumber(beamNumber, beamType)
1486
1487        # environLocal.printDebug(['beamsList', beamsList])
1488        # iter over each beams line, from top to bottom (1 through 5)
1489        for outer_depth in range(len(beam.beamableDurationTypes)):
1490            # increment to count from 1 not 0
1491            # assume we are always starting at offset w/n this meter (Jose)
1492            for outer_i, outer_el in enumerate(srcList):
1493                fixBeamsOneElementDepth(outer_i, outer_el, outer_depth)
1494
1495        beamsList = beam.Beams.sanitizePartialBeams(beamsList)
1496        beamsList = beam.Beams.mergeConnectingPartialBeams(beamsList)
1497
1498        return beamsList
1499
1500    def setDisplay(self, value, partitionRequest=None):
1501        '''
1502        Set an independent display value for a meter.
1503
1504        >>> a = meter.TimeSignature()
1505        >>> a.load('3/4')
1506        >>> a.setDisplay('2/8+2/8+2/8')
1507        >>> a.displaySequence
1508        <music21.meter.core.MeterSequence {2/8+2/8+2/8}>
1509        >>> a.beamSequence
1510        <music21.meter.core.MeterSequence {{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}>
1511        >>> a.beatSequence  # a single top-level partition is default for beat
1512        <music21.meter.core.MeterSequence {{1/8+1/8}+{1/8+1/8}+{1/8+1/8}}>
1513        >>> a.setDisplay('3/4')
1514        >>> a.displaySequence
1515        <music21.meter.core.MeterSequence {3/4}>
1516        '''
1517        if isinstance(value, MeterSequence):  # can set to an existing MeterSequence
1518            # must make a copy
1519            self.displaySequence = copy.deepcopy(value)
1520        else:
1521            # create a new object; it will not be linked
1522            self.displaySequence = MeterSequence(value, partitionRequest)
1523
1524    def getAccent(self, qLenPos: float) -> bool:
1525        '''
1526        Return True or False if the qLenPos is at the start of an accent
1527        division.
1528
1529        >>> a = meter.TimeSignature('3/4', 3)
1530        >>> a.accentSequence.partition([2, 1])
1531        >>> a.accentSequence
1532        <music21.meter.core.MeterSequence {2/4+1/4}>
1533        >>> a.getAccent(0.0)
1534        True
1535        >>> a.getAccent(1.0)
1536        False
1537        >>> a.getAccent(2.0)
1538        True
1539        '''
1540        pos = 0
1541        qLenPos = opFrac(qLenPos)
1542        for i in range(len(self.accentSequence)):
1543            if pos == qLenPos:
1544                return True
1545            pos += self.accentSequence[i].duration.quarterLength
1546        return False
1547
1548    def setAccentWeight(self, weightList, level=0):
1549        '''Set accent weight, or floating point scalars, for the accent MeterSequence.
1550        Provide a list of values; if this list is shorter than the length of the MeterSequence,
1551        it will be looped; if this list is longer, only the first relevant value will be used.
1552
1553        If the accent MeterSequence is subdivided, the level of depth to set is given by the
1554        optional level argument.
1555
1556
1557        >>> a = meter.TimeSignature('4/4', 4)
1558        >>> len(a.accentSequence)
1559        4
1560        >>> a.setAccentWeight([0.8, 0.2])
1561        >>> a.getAccentWeight(0)
1562        0.8...
1563        >>> a.getAccentWeight(0.5)
1564        0.8...
1565        >>> a.getAccentWeight(1)
1566        0.2...
1567        >>> a.getAccentWeight(2.5)
1568        0.8...
1569        >>> a.getAccentWeight(3.5)
1570        0.2...
1571        '''
1572        if not common.isListLike(weightList):
1573            weightList = [weightList]
1574
1575        msLevel = self.accentSequence.getLevel(level)
1576        for i in range(len(msLevel)):
1577            msLevel[i].weight = weightList[i % len(weightList)]
1578
1579    def averageBeatStrength(self, streamIn, notesOnly=True):
1580        '''
1581        returns a float of the average beat strength of all objects (or if notesOnly is True
1582        [default] only the notes) in the `Stream` specified as streamIn.
1583
1584
1585        >>> s = converter.parse('C4 D4 E8 F8', format='tinyNotation').flatten().notes.stream()
1586        >>> sixEight = meter.TimeSignature('6/8')
1587        >>> sixEight.averageBeatStrength(s)
1588        0.4375
1589        >>> threeFour = meter.TimeSignature('3/4')
1590        >>> threeFour.averageBeatStrength(s)
1591        0.5625
1592
1593        If `notesOnly` is `False` then test objects will give added
1594        weight to the beginning of the measure:
1595
1596        >>> sixEight.averageBeatStrength(s, notesOnly=False)
1597        0.4375
1598        >>> s.insert(0.0, clef.TrebleClef())
1599        >>> s.insert(0.0, clef.BassClef())
1600        >>> sixEight.averageBeatStrength(s, notesOnly=False)
1601        0.625
1602        '''
1603        if notesOnly is True:
1604            streamIn = streamIn.notes
1605
1606        totalWeight = 0.0
1607        totalObjects = len(streamIn)
1608        if totalObjects == 0:
1609            return 0.0  # or raise exception?  add doc test
1610        for el in streamIn:
1611            elWeight = self.getAccentWeight(
1612                self.getMeasureOffsetOrMeterModulusOffset(el),
1613                forcePositionMatch=True, permitMeterModulus=False)
1614            totalWeight += elWeight
1615        return totalWeight / totalObjects
1616
1617    def getMeasureOffsetOrMeterModulusOffset(self, el):
1618        '''
1619        Return the measure offset based on a Measure, if it exists,
1620        otherwise based on meter modulus of the TimeSignature.
1621
1622        >>> m = stream.Measure()
1623        >>> ts1 = meter.TimeSignature('3/4')
1624        >>> m.insert(0, ts1)
1625        >>> n1 = note.Note()
1626        >>> m.insert(2, n1)
1627        >>> ts1.getMeasureOffsetOrMeterModulusOffset(n1)
1628        2.0
1629
1630        Exceeding the range of the Measure gets a modulus
1631
1632        >>> n2 = note.Note()
1633        >>> m.insert(4.0, n2)
1634        >>> ts1.getMeasureOffsetOrMeterModulusOffset(n2)
1635        1.0
1636
1637        Can be applied to Notes in a Stream with a TimeSignature.
1638
1639        >>> ts2 = meter.TimeSignature('5/4')
1640        >>> s2 = stream.Stream()
1641        >>> s2.insert(0, ts2)
1642        >>> n3 = note.Note()
1643        >>> s2.insert(3, n3)
1644        >>> ts2.getMeasureOffsetOrMeterModulusOffset(n3)
1645        3.0
1646
1647        >>> n4 = note.Note()
1648        >>> s2.insert(5, n4)
1649        >>> ts2.getMeasureOffsetOrMeterModulusOffset(n4)
1650        0.0
1651        '''
1652        mOffset = el._getMeasureOffset()  # TODO(msc): expose this method and remove private
1653        tsMeasureOffset = self._getMeasureOffset(includeMeasurePadding=False)
1654        if (mOffset + tsMeasureOffset) < self.barDuration.quarterLength:
1655            return mOffset
1656        else:
1657            # must get offset relative to not just start of Stream, but the last
1658            # time signature
1659            post = ((mOffset - tsMeasureOffset) % self.barDuration.quarterLength)
1660            # environLocal.printDebug(['result', post])
1661            return post
1662
1663    def getAccentWeight(self, qLenPos, level=0, forcePositionMatch=False,
1664                        permitMeterModulus=False):
1665        '''Given a qLenPos,  return an accent level. In general, accents are assumed to
1666        define only a first-level weight.
1667
1668        If `forcePositionMatch` is True, an accent will only be returned if the
1669        provided qLenPos is a near exact match to the provided quarter length. Otherwise,
1670        half of the minimum quarter length will be provided.
1671
1672        If `permitMeterModulus` is True, quarter length positions greater than
1673        the duration of the Meter will be accepted as the modulus of the total meter duration.
1674
1675
1676        >>> ts1 = meter.TimeSignature('3/4')
1677        >>> [ts1.getAccentWeight(x) for x in range(3)]
1678        [1.0, 0.5, 0.5]
1679
1680
1681        Returns an error...
1682
1683        >>> [ts1.getAccentWeight(x) for x in range(6)]
1684        Traceback (most recent call last):
1685        music21.exceptions21.MeterException: cannot access from qLenPos 3.0
1686            where total duration is 3.0
1687
1688        ...unless permitMeterModulus is employed
1689
1690        >>> [ts1.getAccentWeight(x, permitMeterModulus=True) for x in range(6)]
1691        [1.0, 0.5, 0.5, 1.0, 0.5, 0.5]
1692
1693        '''
1694        qLenPos = opFrac(qLenPos)
1695        # might store this weight every time it is set, rather than
1696        # getting it here
1697        minWeight = min(
1698            [mt.weight for mt in self.accentSequence]) * 0.5
1699        msLevel = self.accentSequence.getLevel(level)
1700
1701        if permitMeterModulus:
1702            environLocal.printDebug(
1703                [' self.duration.quarterLength', self.duration.quarterLength,
1704                 'self.barDuration.quarterLength', self.barDuration.quarterLength])
1705            qLenPos = qLenPos % self.barDuration.quarterLength
1706
1707        if forcePositionMatch:
1708            # only return values for qLen positions that are at the start
1709            # of a span; for those that are not, we need to return a minWeight
1710            localSpan = msLevel.offsetToSpan(qLenPos,
1711                                             permitMeterModulus=permitMeterModulus)
1712
1713            if qLenPos != localSpan[0]:
1714                return minWeight
1715        return msLevel[msLevel.offsetToIndex(qLenPos)].weight
1716
1717    def getBeat(self, offset):
1718        '''
1719        Given an offset (quarterLength position), get the beat, where beats count from 1
1720
1721        If you want a fractional number for the beat, see `getBeatProportion`.
1722
1723        TODO: In v7 -- getBeat will probably do what getBeatProportion does now...
1724        but just with 1 added to it.
1725
1726        >>> a = meter.TimeSignature('3/4', 3)
1727        >>> a.getBeat(0)
1728        1
1729        >>> a.getBeat(2.5)
1730        3
1731        >>> a.beatSequence.partition(['3/8', '3/8'])
1732        >>> a.getBeat(2.5)
1733        2
1734        '''
1735        return self.beatSequence.offsetToIndex(offset) + 1
1736
1737    def getBeatOffsets(self):
1738        '''Return offset positions in a list for the start of each beat,
1739        assuming this object is found at offset zero.
1740
1741        >>> a = meter.TimeSignature('3/4')
1742        >>> a.getBeatOffsets()
1743        [0.0, 1.0, 2.0]
1744        >>> a = meter.TimeSignature('6/8')
1745        >>> a.getBeatOffsets()
1746        [0.0, 1.5]
1747        '''
1748        post = []
1749        post.append(0.0)
1750        if len(self.beatSequence) == 1:
1751            return post
1752        else:
1753            endOffset = self.barDuration.quarterLength
1754            o = 0.0
1755            for ms in self.beatSequence:
1756                o = opFrac(o + ms.duration.quarterLength)
1757                if o >= endOffset:
1758                    return post  # do not add offset for end of bar
1759                post.append(o)
1760
1761    def getBeatDuration(self, qLenPos):
1762        '''
1763        Returns a :class:`~music21.duration.Duration`
1764        object representing the length of the beat
1765        found at qLenPos.  For most standard
1766        meters, you can give qLenPos = 0
1767        and get the length of any beat in
1768        the TimeSignature; but the simpler
1769        :attr:`music21.meter.TimeSignature.beatDuration` parameter,
1770        will do that for you just as well.
1771
1772        The advantage of this method is that
1773        it will work for asymmetrical meters, as the second
1774        example shows.
1775
1776
1777        Ex. 1: beat duration for 3/4 is always 1.0
1778        no matter where in the meter you query.
1779
1780
1781        >>> ts1 = meter.TimeSignature('3/4')
1782        >>> ts1.getBeatDuration(0.5)
1783        <music21.duration.Duration 1.0>
1784        >>> ts1.getBeatDuration(2.5)
1785        <music21.duration.Duration 1.0>
1786
1787
1788        Ex. 2: same for 6/8:
1789
1790
1791        >>> ts2 = meter.TimeSignature('6/8')
1792        >>> ts2.getBeatDuration(2.5)
1793        <music21.duration.Duration 1.5>
1794
1795
1796        Ex. 3: but for a compound meter of 3/8 + 2/8,
1797        where you ask for the beat duration
1798        will determine the length of the beat:
1799
1800
1801        >>> ts3 = meter.TimeSignature('3/8+2/8')  # will partition as 2 beat
1802        >>> ts3.getBeatDuration(0.5)
1803        <music21.duration.Duration 1.5>
1804        >>> ts3.getBeatDuration(1.5)
1805        <music21.duration.Duration 1.0>
1806        '''
1807        return self.beatSequence[self.beatSequence.offsetToIndex(qLenPos)].duration
1808
1809    def getOffsetFromBeat(self, beat):
1810        '''
1811        Given a beat value, convert into an offset position.
1812
1813
1814        >>> ts1 = meter.TimeSignature('3/4')
1815        >>> ts1.getOffsetFromBeat(1)
1816        0.0
1817        >>> ts1.getOffsetFromBeat(2)
1818        1.0
1819        >>> ts1.getOffsetFromBeat(3)
1820        2.0
1821        >>> ts1.getOffsetFromBeat(3.5)
1822        2.5
1823        >>> ts1.getOffsetFromBeat(3.25)
1824        2.25
1825
1826        >>> from fractions import Fraction
1827        >>> ts1.getOffsetFromBeat(Fraction(8, 3))  # 2.66666
1828        Fraction(5, 3)
1829
1830
1831        >>> ts1 = meter.TimeSignature('6/8')
1832        >>> ts1.getOffsetFromBeat(1)
1833        0.0
1834        >>> ts1.getOffsetFromBeat(2)
1835        1.5
1836        >>> ts1.getOffsetFromBeat(2.33)
1837        2.0
1838        >>> ts1.getOffsetFromBeat(2.5)  # will be + 0.5 * 1.5
1839        2.25
1840        >>> ts1.getOffsetFromBeat(2.66)
1841        2.5
1842
1843
1844        Works for asymmetrical meters as well:
1845
1846
1847        >>> ts3 = meter.TimeSignature('3/8+2/8')  # will partition as 2 beat
1848        >>> ts3.getOffsetFromBeat(1)
1849        0.0
1850        >>> ts3.getOffsetFromBeat(2)
1851        1.5
1852        >>> ts3.getOffsetFromBeat(1.66)
1853        1.0
1854        >>> ts3.getOffsetFromBeat(2.5)
1855        2.0
1856
1857
1858        Let's try this on a real piece, a 4/4 chorale with a one beat pickup.  Here we get the
1859        normal offset from the active TimeSignature but we subtract out the pickup length which
1860        is in a `Measure`'s :attr:`~music21.stream.Measure.paddingLeft` property.
1861
1862        >>> c = corpus.parse('bwv1.6')
1863        >>> for m in c.parts.first().getElementsByClass('Measure'):
1864        ...     ts = m.timeSignature or m.getContextByClass('TimeSignature')
1865        ...     print('%s %s' % (m.number, ts.getOffsetFromBeat(4.5) - m.paddingLeft))
1866        0 0.5
1867        1 3.5
1868        2 3.5
1869        ...
1870        '''
1871        # divide into integer and floating point components
1872        beatInt, beatFraction = divmod(beat, 1)
1873        beatInt = int(beatInt)  # convert to integer
1874
1875        # resolve 0.33 to 0.3333333 (actually Fraction(1, 3). )
1876        beatFraction = common.addFloatPrecision(beatFraction)
1877
1878        if beatInt - 1 > len(self.beatSequence) - 1:
1879            raise TimeSignatureException(
1880                'requested beat value (%s) not found in beat partitions (%s) of ts %s' % (
1881                    beatInt, self.beatSequence, self))
1882        # get a duration object for the beat; will translate into quarterLength
1883        # beat int counts from 1; subtract 1 to get index
1884        beatDur = self.beatSequence[beatInt - 1].duration
1885        oStart, unused_oEnd = self.beatSequence.getLevelSpan()[beatInt - 1]
1886        post = opFrac(oStart + (beatDur.quarterLength * beatFraction))
1887        return post
1888
1889    def getBeatProgress(self, qLenPos):
1890        '''
1891        Given a quarterLength position, get the beat,
1892        where beats count from 1, and return the the
1893        amount of qLen into this beat the supplied qLenPos
1894        is.
1895
1896        >>> a = meter.TimeSignature('3/4', 3)
1897        >>> a.getBeatProgress(0)
1898        (1, 0)
1899        >>> a.getBeatProgress(0.75)
1900        (1, 0.75)
1901        >>> a.getBeatProgress(1.0)
1902        (2, 0.0)
1903        >>> a.getBeatProgress(2.5)
1904        (3, 0.5)
1905
1906
1907        Works for specifically partitioned meters too:
1908
1909        >>> a.beatSequence.partition(['3/8', '3/8'])
1910        >>> a.getBeatProgress(2.5)
1911        (2, 1.0)
1912        '''
1913        beatIndex = self.beatSequence.offsetToIndex(qLenPos)
1914        start, unused_end = self.beatSequence.offsetToSpan(qLenPos)
1915        return beatIndex + 1, qLenPos - start
1916
1917    def getBeatProportion(self, qLenPos):
1918        '''
1919        Given a quarter length position into the meter, return a numerical progress
1920        through the beat (where beats count from one) with a floating-point or fractional value
1921        between 0 and 1 appended to this value that gives the proportional progress into the beat.
1922
1923        For faster, integer values, use simply `.getBeat()`
1924
1925        >>> ts1 = meter.TimeSignature('3/4')
1926        >>> ts1.getBeatProportion(0.0)
1927        1.0
1928        >>> ts1.getBeatProportion(0.5)
1929        1.5
1930        >>> ts1.getBeatProportion(1.0)
1931        2.0
1932
1933        >>> ts3 = meter.TimeSignature('3/8+2/8')  # will partition as 2 beat
1934        >>> ts3.getBeatProportion(0.75)
1935        1.5
1936        >>> ts3.getBeatProportion(2.0)
1937        2.5
1938        '''
1939        beatIndex = self.beatSequence.offsetToIndex(qLenPos)
1940        start, end = self.beatSequence.offsetToSpan(qLenPos)
1941        totalRange = end - start
1942        progress = qLenPos - start  # how far in QL
1943        return opFrac(beatIndex + 1 + (progress / totalRange))
1944
1945    def getBeatProportionStr(self, qLenPos):
1946        '''Return a string presentation of the beat.
1947
1948        >>> ts1 = meter.TimeSignature('3/4')
1949        >>> ts1.getBeatProportionStr(0.0)
1950        '1'
1951        >>> ts1.getBeatProportionStr(0.5)
1952        '1 1/2'
1953        >>> ts1.getBeatProportionStr(1.0)
1954        '2'
1955        >>> ts3 = meter.TimeSignature('3/8+2/8')  # will partition as 2 beat
1956        >>> ts3.getBeatProportionStr(0.75)
1957        '1 1/2'
1958        >>> ts3.getBeatProportionStr(2)
1959        '2 1/2'
1960
1961        >>> ts4 = meter.TimeSignature('6/8')  # will partition as 2 beat
1962        '''
1963        beatIndex = int(self.beatSequence.offsetToIndex(qLenPos))
1964        start, end = self.beatSequence.offsetToSpan(qLenPos)
1965        totalRange = end - start
1966        progress = qLenPos - start  # how far in QL
1967
1968        if (progress / totalRange) == 0.0:
1969            post = f'{beatIndex + 1}'  # just show beat
1970        else:
1971            a, b = proportionToFraction(progress / totalRange)
1972            post = f'{beatIndex + 1} {a}/{b}'  # just show beat
1973        return post
1974
1975    def getBeatDepth(self, qLenPos, align='quantize'):
1976        '''Return the number of levels of beat partitioning given a QL into the TimeSignature.
1977        Note that by default beat partitioning always has a single, top-level partition.
1978
1979        The `align` parameter is passed to the :meth:`~music21.meter.MeterSequence.offsetToDepth`
1980        method, and can be used to find depths based on start position overlaps.
1981
1982        >>> a = meter.TimeSignature('3/4', 3)
1983        >>> a.getBeatDepth(0)
1984        1
1985        >>> a.getBeatDepth(1)
1986        1
1987        >>> a.getBeatDepth(2)
1988        1
1989
1990        >>> b = meter.TimeSignature('3/4', 1)
1991        >>> b.beatSequence[0] = b.beatSequence[0].subdivide(3)
1992        >>> b.beatSequence[0][0] = b.beatSequence[0][0].subdivide(2)
1993        >>> b.beatSequence[0][1] = b.beatSequence[0][1].subdivide(2)
1994        >>> b.beatSequence[0][2] = b.beatSequence[0][2].subdivide(2)
1995        >>> b.getBeatDepth(0)
1996        3
1997        >>> b.getBeatDepth(0.5)
1998        1
1999        >>> b.getBeatDepth(1)
2000        2
2001        '''
2002        return self.beatSequence.offsetToDepth(qLenPos, align)
2003
2004
2005# -----------------------------------------------------------------------------
2006class SenzaMisuraTimeSignature(base.Music21Object):
2007    '''
2008    A SenzaMisuraTimeSignature represents the absence of a TimeSignature
2009
2010    It is NOT a TimeSignature subclass, only because it has none of the attributes
2011    of a TimeSignature.
2012
2013    >>> smts = meter.SenzaMisuraTimeSignature('0')
2014    >>> smts.text
2015    '0'
2016    >>> smts
2017    <music21.meter.SenzaMisuraTimeSignature 0>
2018    '''
2019
2020    def __init__(self, text=None):
2021        super().__init__()
2022        self.text = text
2023
2024    def _reprInternal(self):
2025        if self.text is None:
2026            return ''
2027        else:
2028            return str(self.text)
2029
2030
2031# TODO: Implement or delete...
2032
2033# class NonPowerOfTwoTimeSignature(TimeSignature):
2034#     pass
2035# class AutoAdjustTimeSignature(TimeSignature):
2036#     automatically adjusts to fit its measure context.
2037
2038
2039# -----------------------------------------------------------------------------
2040# define presented order in documentation
2041_DOC_ORDER = [TimeSignature]
2042
2043
2044if __name__ == '__main__':
2045    import music21
2046    music21.mainTest()
2047