The rhythm module

Classes and convenience functions to manipulate the rhythm of music.

For all convenience functions: the music argument may be a parce.Document, a parce Cursor (optionally only selecting a range of the document to edit), a node Range or any Element node (DOM tree).

class EditRhythm[source]

Bases: Edit

Base class for rhythm editing operations.

static durables(r)[source]

Yield all Durable instances in range.

static may_remove(node)[source]

Return True if the duration of this node may be removed.

A duration may not be removed if node.duration_required is True, or when the node’s right sibling has only the duration visible, such as is the case with an unpitched note or an empty lyric item.

In that case, the current duration may not be removed, because the next duration would then be understood as the duration of the current node when rewriting the music text.

edit_range(r)[source]

Perform our operations on all Durables in the range.

process(node, prev)[source]

Implement to perform an operation on the node.

The prev parameter is the value the previous call to this method returned, it is None on the first call.

class Remove[source]

Bases: EditRhythm

Remove duration from Durable nodes, if allowed.

process(node, prev)[source]

Remove duration from node; prev is unused.

class RemoveScaling[source]

Bases: EditRhythm

Remove scaling from Durable nodes.

process(node, prev)[source]

Remove scaling from node; prev is unused.

class RemoveFractionScaling[source]

Bases: EditRhythm

Remove scaling if it contains a fraction.

process(node, prev)[source]

Remove scaling if it contains a fraction from node; prev is unused.

class RhythmExplicit[source]

Bases: EditRhythm

Add the current duration to all nodes that don’t have one.

process(node, prev)[source]

Add duration to node if absent; prev is the previous Duration node.

class RhythmImplicit[source]

Bases: EditRhythm

Remove reoccurring durations.

process(node, prev)[source]

Remove duration from node if same as (duration, scaling) tuple in prev.

class RhythmImplicitPerLine[source]

Bases: EditRhythm

Remove reoccurring durations within the same line, but always add a duration to the first Durable on a line.

This only works when editing from a parce Document, otherwise we don’t know the newlines in the original text. If there is no parce Document, the behaviour is the same as RhythmImplicit.

process(node, prev)[source]

Remove duration from node if duration and text block are the same as [duration, scaling, block] list in prev.

class RhythmTransform(transform)[source]

Bases: EditRhythm

Transform durations using a Transform.

This can be used for all types of shift and scale operations. For example, to add a dot to all durations:

>>> from quickly.rhythm import RhythmTransform
>>> from quickly.duration import Transform
>>> from quickly.dom import read
>>> music = read.lily_document(r"{ c4 d8 e16 f g2 }")
>>> t = Transform(dotcount=1) # add a dot
>>> RhythmTransform(t).edit(music)
>>> music.write()
'{ c4. d8. e16. f g2. }'

Remove a dot:

>>> t = Transform(dotcount=-1)
>>> RhythmTransform(t).edit(music)
>>> music.write()
'{ c4 d8 e16 f g2 }'

Double durations:

>>> t = Transform(log=-1)
>>> RhythmTransform(t).edit(music)
>>> music.write()
'{ c2 d4 e8 f g1 }'

Add a scaling factor:

>>> t = Transform(scale=1/3)
>>> RhythmTransform(t).edit(music)
>>> music.write()
'{ c2*1/3 d4*1/3 e8*1/3 f g1*1/3 }'

Or modify all in one go:

>>> t = Transform(1, 1, 3)
>>> RhythmTransform(t).edit(music)
>>> music.write()
'{ c4. d8. e16. f g2. }'

This function also modifies durations in \tempo, \tuplet, \after and \partial commands.

edit_range(r)[source]

Transform all durations.

class CopyRhythm(explicit=False)[source]

Bases: EditRhythm

Extract durations from a range in the form of (duration, scaling) tuples.

The durations are returned by edit_range() and thus also all other edit methods. Durables without duration yield a None if explicit is False, otherwise the previous duration is repeated. Example:

>>> from quickly.dom import read
>>> music = read.lily_document(r"{ c4 d8 e16 f g2 }")
>>> from quickly.rhythm import CopyRhythm
>>> durations = CopyRhythm().edit(music)
>>> durations
[(Fraction(1, 4), 1), (Fraction(1, 8), 1), (Fraction(1, 16), 1), None,
(Fraction(1, 2), 1.0)]
>>> CopyRhythm(True).edit(music)
[(Fraction(1, 4), 1), (Fraction(1, 8), 1), (Fraction(1, 16), 1),
(Fraction(1, 16), 1), (Fraction(1, 2), 1)]
readonly = True

If True, does not write back changes to the parce Document

explicit

If True, yield every reoccurring duration explicit instead of None

edit_range(r)[source]

Return the list of extracted durations.

class PasteRhythm(durations, cycle=True)[source]

Bases: EditRhythm

Paste durations such as returned by CopyRhythm into music.

The durations are an iterable of either the two-tuple (duration, scaling) or None. If cycle is True, the pasted durations are endlessly repeated in the selected range. Example:

>>> from fractions import Fraction
>>> durations = [(Fraction(1, 4), 1), (Fraction(3, 16), 0.5), None]
>>> from quickly.dom import read
>>> music = read.lily_document(r"{ c4 d8 e16 f g2 }")
>>> from quickly.rhythm import PasteRhythm
>>> PasteRhythm(durations).edit(music)
>>> music.write()
'{ c4 d8.*1/2 e f4 g8.*1/2 }'
edit_range(r)[source]

Paste the durations.

remove(music)[source]

Remove all durations from music.

Does not remove the duration from \skip and Unpitched notes, and also not from durables that immediately precede Unpitched notes (or empty lyric items), because the Unpitched’s duration would then be mistakenly held for the duration of the preceding note.

remove_scaling(music)[source]

Remove all scalings from the durations in music.

remove_fraction_scaling(music)[source]

Remove all scalings that contain a fraction (like 1/3) from the durations in music.

explicit(music)[source]

Add the current duration to all notes, chords, rests etc in the music.

implicit(music, per_line=False)[source]

Remove all reoccuring durations from the music.

If per_line is True, the first duration in a text line is not removed, but rather added if absent. (This only works when editing a parce document or cursor, otherwise we can’t know the newlines in the original text.)

An example:

>>> import parce
>>> import quickly.rhythm
>>> d=parce.Document(quickly.find('lilypond'), r'''music = {
...   c4 d8 e8 f8 g8 a4
...   g f e4 d
...   c d4 e2
... }
... ''', transformer=True)
>>> quickly.rhythm.implicit(d, True)
>>> print(d.text())
music = {
  c4 d8 e f g a4
  g4 f e d
  c4 d e2
}
transform(music, log=0, dotcount=0, scale=1)[source]

Transform durations in music by modifying log, dot count and/or scaling.

Increasing the log by 1 halves the durations, decreasing the log doubles them. (See also the duration module.) An example, where the duration is halved and one dot is added:

>>> from quickly.dom import read
>>> from quickly import rhythm
>>> m = read.lily_document("{ c4 d8 e16 f g2 }")
>>> rhythm.transform(m, 1, 1)
>>> m.write()
'{ c8. d16. e32. f g4. }'

This function also modifies durations in \tempo, \tuplet, \after and \partial commands.

copy(music, explicit=False)[source]

Extract durations from music.

Every duration is a two-tuple of integers or fractions (duration, scaling), or, if explicit is False, None for Durables without duration. If explicit is True, the previous duration is repeated for Durables without duration.

paste(music, durations, cycle=True)[source]

Replace durations in the music with the specified durations.

Every duration is a two-tuple of integers or fractions (duration, scaling), or None for Durables without duration. If cycle is True, the pasted durations are endlessly repeated in the selected range. An example:

>>> from quickly.dom import read
>>> from quickly.rhythm import copy, paste
>>> durs = copy(read.lily_document("{ 8. 16 8 }"))
>>> durs
[(Fraction(3, 16), 1.0), (Fraction(1, 16), 1.0), (Fraction(1, 8), 1.0)]
>>> music = read.lily_document("{ g a g c d c a b a f g f }")
>>> paste(music, durs)
>>> music.write()
'{ g8. a16 g8 c8. d16 c8 a8. b16 a8 f8. g16 f8 }'