The time module

Functionality to compute the time length of musical expressions.

class Result(node, time)

Bases: tuple

The result value (if not None) of the position() and duration() methods.


The topmost Music expression.


The position or duration time value.

class Time(scope=None, wait=False)[source]

Bases: object

Compute the length of musical expressions.

A Scope, if given using the scope parameter, is used to resolve include files. If no scope is given, only searches the current DOM document for variable assignments.

If a scope is given, include commands are followed and wait determines whether to wait for ongoing transformations of external DOM documents. If wait is False, and a transformation is not yet finished the include is not followed.

An example:

>>> import parce, quickly.time
>>> d = parce.Document(quickly.find('lilypond'), r'''
... music = { c4 d e f }
... { c2 \music g a b8 g f d }
... ''', transformer=True)
>>> m = d.get_transform(True)
>>> m.dump()
<lily.Document (2 children)>
 ├╴<lily.Assignment music (3 children)>
 │  ├╴<lily.Identifier (1 child)>
 │  │  ╰╴<lily.Symbol 'music' [1:6]>
 │  ├╴<lily.EqualSign [7:8]>
 │  ╰╴<lily.MusicList (4 children) [9:21]>
 │     ├╴<lily.Note 'c' (1 child) [11:12]>
 │     │  ╰╴<lily.Duration Fraction(1, 4) [12:13]>
 │     ├╴<lily.Note 'd' [14:15]>
 │     ├╴<lily.Note 'e' [16:17]>
 │     ╰╴<lily.Note 'f' [18:19]>
 ╰╴<lily.MusicList (8 children) [23:49]>
    ├╴<lily.Note 'c' (1 child) [25:26]>
    │  ╰╴<lily.Duration Fraction(1, 2) [26:27]>
    ├╴<lily.IdentifierRef 'music' [28:34]>
    ├╴<lily.Note 'g' [35:36]>
    ├╴<lily.Note 'a' [37:38]>
    ├╴<lily.Note 'b' (1 child) [39:40]>
    │  ╰╴<lily.Duration Fraction(1, 8) [40:41]>
    ├╴<lily.Note 'g' [42:43]>
    ├╴<lily.Note 'f' [44:45]>
    ╰╴<lily.Note 'd' [46:47]>
>>> t=quickly.time.Time()
>>> t.position(m[1][0])         # first note in second expression
Result(node=<lily.MusicList (8 children) [23:49]>, time=0)
>>> t.position(m[1][1])         # \music identifier ref
Result(node=<lily.MusicList (8 children) [23:49]>, time=Fraction(1, 2))
>>> t.position(m[1][2])         # the g after the \music ref
Result(node=<lily.MusicList (8 children) [23:49]>, time=Fraction(3, 2))
>>> t.length(m[0][2])           # the \music expression
Fraction(1, 1)
>>> t.length(m[1])              # total length of second expression
Fraction(3, 1)                  # referenced \music correctly counted in :)
>>> t.duration(m[1][0], m[1][2])# length of "c2 \music g" part (g has duration 2)
Result(node=<lily.MusicList (8 children) [23:49]>, time=Fraction(2, 1))

There are convenient methods to get the musical position of a parce Cursor:

>>> c = parce.Cursor(d, 44, 47)
>>> c.text()                    # two notes
'f d'
>>> t.cursor_position(c)        # length or music before the cursor
Result(node=<lily.MusicList (8 children) [23:49]>, time=Fraction(11, 4))
>>> t.cursor_duration(c)        # duration of the selected music
Result(node=<lily.MusicList (8 children) [23:49]>, time=Fraction(1, 4))

LilyPond music functions that alter durations are recognized, and are abstracted in simple transformations that alter log, dotcount and/or scaling. An example:

>>> from quickly.dom import read
>>> m = read.lily(r"\tuplet 3/2 { c8 d e }")
>>> t.length(m)
Fraction(1, 4)
>>> m[1][1]                     # a single note in the tuplet
<lily.Note 'd'>
>>> t.length(m[1][1])
Fraction(1, 12)
>>> m = read.lily(r"\shiftDurations #1 #1 { c4 d e f }")
>>> t.length(m)         # note value halved and dot added, so should be 3/4
Fraction(3, 4)
>>> m[2][2]
<lily.Note 'e'>
>>> t.length(m[2][2])   # autodiscovers the current duration transform
Fraction(3, 16)


As a Time instance uses some caching for the duration of individual notes, don’t rely on its computations while also modifying durations of music notes, rests etc.


Our Scope.


If True, parce transformations are waited for.


Return the musical length of this node.

position(node, include=False)[source]

Return a Result two-tuple(node, time).

The node argument must be (a child of) a Music instance.

The returned node is the topmost Music element, and the time is the computed length of all preceding music nodes. If include is True, the node’s length itself is also added.

duration(start_node, end_node)[source]

Return a Result two-tuple(node, time) or None.

The returned node is the topmost Music element both nodes must be a descendant of. Both nodes must be (children of) Music nodes. If they don’t share the same ancestor, None is returned. The returned time value can be negative when the end node precedes the start node.


Return a Result two-tuple(node, time) or None.

The node is the music expression the cursor is in, and the time is the time offset from the start of that expression to the cursor’s position.

Returns None if the cursor is not in music.


Return a Result two-tuple(node, time) or None.

The node is the music expression the cursor is in, and the time is the length of the selected music fragment.

Returns None if there’s no selection or the selection’s start and/or end are not in music, or in different music expressions.

class TimeContext(time, transform=None, properties=None)[source]

Bases: object

Encapsulates the transform and properties during time calculations.

The transform (Transform) determines the actual length of Durable objects, and the properties (Properties) are forwarded to child contexts, where inside the time_length() of Music nodes values can be read and also modified.


The Time object we originate from.


The current Transform.


The current Properties.

enter(node, time=None)[source]

Return a new TimeContext.

The returned TimeContext uses the new Time (if given, otherwise the same as ours) and adds the Transform and the Properties of the specified node to the current ones.

length(node, end=None)[source]

Return the length of any node.

Follows variable references using the scope (if given to the Time instance) and calls time_length() for Music nodes. Returns 0 for any other, non-musical, node.


Return the length of a Durable node.


Return the length of the value of an IdentifierRef node.

Returns 0 if the node can’t be found or is no music.