MetricalDuration
MetricalDuration(
numerator: int,
denominator: int,
dots: int = 0,
ratio: TemporalRatio | None = None,
)The duration of notes, rests, or other temporal musical items as understood and notated in Western Standard Music Notation.
Duration values are stored as nominal values, so a dotted quarter note is stored as numerator 1, denominator 4, dots 1. Actual temporal values (3/8) are calculated when queried.
Notes longer than a whole note (breve, longa, maxima) are stored with a power-of-two numerator and a denominator of 1, so a breve is (2, 1) and a dotted longa is (4, 1, dots=1).
Tuplet values are handled by storing a TemporalRatio, which is (n, TemporalElements) against (m, TemporalElements).
A quarter note inside a standard quarter-note triplet would then be:
>>> from openmusickit.values.time.duration import TemporalRatio, TemporalUnit
>>> quarter = MetricalDuration(1, 4)
>>> triplet = TemporalRatio(TemporalUnit(3, quarter), TemporalUnit(2, quarter))
>>> MetricalDuration(1, 4, ratio=triplet).rational_length
Fraction(1, 6)MetricalDurations compare, sort, and hash by their real length, so MetricalDuration(3, 8) == MetricalDuration(1, 4, dots=1).
A negative numerator is a negative duration: the same notated value in the opposite direction, produced by -duration or by subtraction. It is a signed quantity for arithmetic (an edge displacement, for instance), never the length of an event.
>>> quarter - MetricalDuration(1, 2)
MetricalDuration(-1, 4)Attributes
| Name | Description |
|---|---|
__slots__ |
|
| numerator | |
| denominator | |
| dots | |
| ratio | |
| temporal_system | |
| nominal_length | The notated value, including dots but ignoring any tuplet ratio. |
| rational_length | The real length: the notated value times the tuplet ratio, if any. |
Methods
| Name | Description |
|---|---|
__eq__ |
|
__lt__ |
Shorter than other, by rational length, within compatible systems. |
__hash__ |
|
__sub__ |
self + (-other): the difference of two durations, in whatever notation it takes. |
| from_fraction | Create a MetricalDuration from its full nominal value (e.g. 3/8 -> dotted quarter). |
| from_length | Resolve any positive rational length into notation: |
| scale | Returns this duration augmented or diminished by scalar. |
__neg__ |
The same notated value in the opposite direction. |
__add__ |
Add two durations. |
__radd__ |
Lets sum(durations) work from its default start value of 0. |
__repr__ |
__eq__
__eq__(other)__lt__
__lt__(other)Shorter than other, by rational length, within compatible systems.
>>> from openmusickit.systems.wsmn.temporal.symbols import quarter, dotted_quarter
>>> quarter < dotted_quarter, dotted_quarter < quarter
(True, False)__hash__
__hash__()__sub__
__sub__(other)self + (-other): the difference of two durations, in whatever notation it takes.
>>> from openmusickit.systems.wsmn.temporal.symbols import half, quarter
>>> half - quarter, quarter - half
(MetricalDuration(1, 4), MetricalDuration(-1, 4))from_fraction
from_fraction(value: Rational, *, ratio: TemporalRatio | None = None)Create a MetricalDuration from its full nominal value (e.g. 3/8 -> dotted quarter).
>>> MetricalDuration.from_fraction(F(3, 8))
MetricalDuration(1, 4, dots=1)
>>> MetricalDuration.from_fraction(F(5, 8))
Traceback (most recent call last):
...
ValueError: 5/8 is not a single notatable duration. ...Raises
| Name | Type | Description |
|---|---|---|
| ValueError | if the value is not a single notatable symbol (use from_length for any length). |
from_length
from_length(length: Rational, *, ratio: TemporalRatio | None = None)Resolve any positive rational length into notation: a single MetricalDuration where one exists, a tuplet member where the length needs one, and a TiedDuration where it needs a tie.
This is the front door for lengths that arrive as numbers rather than as notation: quantized MIDI or audio, dividing a bar into n equal parts, scaling by 3, and so on. It always succeeds for a positive rational, and it always picks the canonical spelling described below, so the result is predictable but not necessarily the one a composer would choose in a particular metrical context (see “When not to use”).
Examples
>>> from fractions import Fraction as F
>>> from openmusickit.systems.wsmn.temporal.symbols import *A notatable length comes back as one symbol (equivalent to from_fraction):
>>> MetricalDuration.from_length(F(3, 8))
MetricalDuration(1, 4, dots=1)
>>> MetricalDuration.from_length(2)
MetricalDuration(2, 1)An odd factor in the denominator means a tuplet. 1/3 is a half note in a half-note triplet (3 halves in the time of 2):
>>> third = MetricalDuration.from_length(F(1, 3))
>>> third
MetricalDuration(1, 2, ratio=TemporalRatio(TemporalUnit(3, MetricalDuration(1, 2)), TemporalUnit(2, MetricalDuration(1, 2))))
>>> third.rational_length
Fraction(1, 3)
>>> third == half_in_triplet
True1/6 is a quarter in a quarter-note triplet, 1/5 a quarter in a 5:4 quintuplet, 1/7 a quarter in a 7:4 septuplet, 1/9 an eighth in a 9:8 tuplet:
>>> MetricalDuration.from_length(F(1, 6)) == quarter_in_triplet
True
>>> [MetricalDuration.from_length(F(1, k)).ratio.multiplier for k in (5, 7, 9)]
[Fraction(4, 5), Fraction(4, 7), Fraction(8, 9)]Lengths that need a tie come back as a TiedDuration, split greedily largest-first (dotted values included):
>>> MetricalDuration.from_length(F(5, 8))
TiedDuration([MetricalDuration(1, 2), MetricalDuration(1, 8)])
>>> MetricalDuration.from_length(F(13, 16))
TiedDuration([MetricalDuration(1, 2, dots=1), MetricalDuration(1, 16)])
>>> MetricalDuration.from_length(F(5, 8)).rational_length
Fraction(5, 8)Ties and tuplets combine: 5/24 is a tied quarter + sixteenth inside a triplet.
>>> tied = MetricalDuration.from_length(F(5, 24))
>>> [m.nominal_length for m in tied], tied[0].ratio.multiplier, tied.rational_length
([Fraction(1, 4), Fraction(1, 16)], Fraction(2, 3), Fraction(5, 24))With ratio, length is the notated value inside an existing tuplet (as in the constructor), so no new ratio is synthesized unless that notated value itself needs one (which produces a nested tuplet):
>>> MetricalDuration.from_length(F(1, 4), ratio=triplet(quarter)) == quarter_in_triplet
True
>>> nested = MetricalDuration.from_length(F(1, 12), ratio=triplet(quarter))
>>> nested.rational_length
Fraction(1, 18)Dividing a bar of 4/4 into five equal parts:
>>> part = MetricalDuration.from_length(four_four.rational_length / 5)
>>> part.rational_length
Fraction(1, 5)
>>> part.ratio.multiplier
Fraction(4, 5)
>>> TemporalUnit(5, part) == four_four
TrueCanonical spelling
- Reduce the fraction. If it is a single symbol (
n = 2^kover 1, orn = 2^(dots+1) - 1over a power of two), return it. - If the denominator has an odd factor
k, the length is a tuplet member:kin the time ofc, wherecis the largest power of two belowk(3:2, 5:4, 7:4, 9:8, 11:8, …). The ratio’s unit is the largest plain note value that fits the notated length, and the notated length is then resolved by rule 1 or 3. - Otherwise split largest-first into single symbols and tie them.
When not to use
You already know the notation.
MetricalDuration(1, 4, dots=1)says exactly what is written;from_length(F(3, 8))merely happens to agree. Prefer the constructor (orfrom_fraction, which raises if the value is not one symbol) whenever the spelling is known.The tuplet already exists. Members of a tuplet should share the group’s
TemporalRatio; pass it asratiorather than letting this method invent a ratio that nothing else references.Metrical context matters. The greedy tie split (5/8 = half + eighth) and the simple-meter tuplet convention (7:4 rather than 7:6) ignore the time signature and beat position, and a single symbol always wins over a tuplet: a bar of 6/8 divided by four gives dotted eighths, where an engraver would write a quadruplet. Notation-quality spelling is a job for a meter-aware layer built on top of this.
>>> MetricalDuration.from_length(six_eight.rational_length / 4) MetricalDuration(1, 8, dots=1)Floats. Pass
Fractionorint. A float is converted exactly, and binary floats have power-of-two denominators, sofrom_length(1/3)is not a triplet but a chain of 27 tied notes ending in a 1/18014398509481984th. Quantize first, e.g.Fraction(x).limit_denominator(64).
Raises
| Name | Type | Description |
|---|---|---|
| ValueError | If length is not positive (use ZeroDuration for zero). |
scale
scale(scalar: int | F)Returns this duration augmented or diminished by scalar.
A power of two keeps the same spelling (a dotted eighth doubled is a dotted quarter). Any other positive scalar is resolved with from_length on the notated value, so the result may be a different single symbol (quarter * 3 = dotted half), a tuplet member (quarter * 2/3 = triplet quarter), or a TiedDuration (quarter * 5 = whole tied to quarter). An existing tuplet ratio is kept.
>>> from openmusickit.systems.wsmn.temporal.symbols import quarter, dotted_eighth, quarter_in_triplet
>>> quarter.scale(2), dotted_eighth.scale(2)
(MetricalDuration(1, 2), MetricalDuration(1, 4, dots=1))
>>> quarter.scale(3)
MetricalDuration(1, 2, dots=1)
>>> quarter.scale(F(2, 3)) == quarter_in_triplet
True
>>> quarter.scale(5)
TiedDuration([MetricalDuration(1, 1), MetricalDuration(1, 4)])Raises
| Name | Type | Description |
|---|---|---|
| ScalingError | if scalar is not a positive rational. |
__neg__
__neg__()The same notated value in the opposite direction.
>>> from openmusickit.systems.wsmn.temporal.symbols import quarter
>>> -quarter, (-quarter).rational_length
(MetricalDuration(-1, 4), Fraction(-1, 4))__add__
__add__(other)Add two durations.
Returns a single MetricalDuration when the sum is notatable as one symbol (quarter + eighth = dotted quarter), otherwise a TiedDuration. Durations in different tuplets always produce a TiedDuration. Durations of opposite sign are resolved by length, so the result is the canonical spelling of the difference (from_length), or ZeroDuration when they cancel.
>>> from openmusickit.systems.wsmn.temporal.symbols import quarter, eighth, half, quarter_in_triplet
>>> quarter + eighth
MetricalDuration(1, 4, dots=1)
>>> half + eighth
TiedDuration([MetricalDuration(1, 2), MetricalDuration(1, 8)])
>>> len(quarter_in_triplet + quarter) # different tuplets never merge
2
>>> half + (-eighth), quarter + (-quarter)
(MetricalDuration(1, 4, dots=1), ZeroDuration())A tie on the right is folded in member by member, so the sum is the same whichever side the tie is on:
>>> quarter + (half + eighth), (half + eighth) + quarter
(MetricalDuration(1, 2, dots=2), MetricalDuration(1, 2, dots=2))Raises
| Name | Type | Description |
|---|---|---|
| TemporalCompatibilityError | if other is a Duration of another temporal system (clock time). |
|
| >>> from openmusickit.values.time.clock_time import ClockDuration | ||
| >>> quarter + ClockDuration(5) | ||
| Traceback (most recent call last): | ||
| … | ||
| openmusickit.errors.TemporalCompatibilityError: Cannot add a ClockDuration to a MetricalDuration: Clock time is not Western Standard Music Notation. |
__radd__
__radd__(other)Lets sum(durations) work from its default start value of 0.
>>> from openmusickit.systems.wsmn.temporal.symbols import quarter, eighth
>>> sum([quarter, eighth, eighth])
MetricalDuration(1, 2)__repr__
__repr__()