Features of quickly

Supported document types

quickly supports LilyPond, Html, LaTeX and Scheme documents. DocBook and TexInfo are in the works.

Html, LaTeX, DocBook and TexInfo are used to edit documents for lilypond-book, a script provided by the LilyPond project that can extract fragments of LilyPond music and run other document processors to produce output of the texts with the musical fragments properly inserted. quickly is able to recognize LilyPond music inside these document formats and allows the user to manipulate the music.

Importing quickly adds those language definitions to the parce registry. Most language definitions included in quickly.lang inherit from the language defitions with the same name from parce.

So, after importing quickly, parce.find() will find the language definitions of quickly:

>>> import parce
>>> parce.find('lilypond').language
parce.lang.lilypond.LilyPond
>>> import quickly
>>> parce.find('lilypond').language
quickly.lang.lilypond.LilyPond

Music manipulations

Most of quickly’s features manipulate music on the DOM document level, writing back the modifications to the originating text, without interfering with other parts of the source text.

There are four “levels” at which documents can be manipulated: a DOM node tree, a Range of a DOM tree, a parce Document, or a selection of a parce document, specified by a parce Cursor.

All manipulation functions can handle all four levels of operation, because they inherit Edit. Typically only the edit_range() method needs to be implemented for the others to work equally well. Most modules have convenience functions that can be called with all four types.

Transpose

Transposing music is done using the transpose module. A Transposer is created that can actually transpose pitches according to the user’s wish, and optionally a PitchProcessor that reads and writes LilyPond pitch names in all languages.

An example; create a document:

>>> import parce, quickly
>>> doc = parce.Document(parce.find("lilypond"), r"music = { c d e f g }", transformer=True)

Create a transposer:

>>> from quickly.pitch import Pitch
>>> from quickly.transpose import transpose, Transposer
>>> p1 = Pitch(-1, 0, 0)     # -> c
>>> p2 = Pitch(-1, 3, 0)     # -> f
>>> t = Transposer(p1, p2)

Now transpose the music from c to f and view the result:

>>> transpose(doc, t)
>>> doc.text()
"music = { f g a bes c' }"

Using the cursor, we can also operate on a fragment of the document:

>>> cur = parce.Cursor(doc, 12, 15)     # only the second and third note
>>> transpose(cur, t)
>>> doc.text()
"music = { f c' d' bes c' }"

Only the second and third note are transposed. The function transpose() is a convenience function that creates a Transpose object and calls its edit() method.

Convert pitches to and from relative notation

The relative module contains functions to convert music to and from relative notation. These functions also use the PitchProcessor to read and write pitch names in all languages, and automatically adapt to the pitch language used in a document.

To convert all music from relative to absolute notation:

>>> import parce
>>> from quickly import find
>>> doc = parce.Document(find("lilypond"), r"music = \relative c' { c d e f g }", transformer=True)
>>> from quickly.relative import rel2abs
>>> rel2abs(doc)
>>> doc.text()
"music = { c' d' e' f' g' }"

And convert back to relative:

>>> from quickly.relative import abs2rel
>>> abs2rel(doc)
>>> doc.text()
"music = \\relative c' { c d e f g }"

The function abs2rel() and rel2abs() are convenience functions that create respectively a Abs2rel or Rel2abs object and call their edit() method.

Time and rhythm

The rhythm module provides easy-to-use functions and classes to modify the durations of music. Nodes that have a duration (such as notes, rests, spacers, skips, drum notes but also lyric words) always inherit Durable, and can have a Duration child node that writes the duration. Durable nodes also have convenient attributes duration and scaling to manipulate their Duration and DurationScaling child nodes.

That makes it not too complicated to build nice functions editing these nodes, that are used to refactor or modify existing music:

remove()

remove all durations from music

remove_scaling()

remove scaling e.g. (*2 or *1/3) from all durations

remove_fraction_scaling()

remove only scaling that contains a fraction from the durations

explicit()

write the duration after all notes, rests etc

implicit()

only write the duration if different from the previous

transform()

modify duration log, number of dots and/or scaling

copy()

extract durations to a list of (duration, scaling) tuples

paste()

overwrite durations in music from a list of (duration, scaling) tuples

There is also the time module, which provides functions to compute the length of musical fragments, or to compute the musical position a text cursor is at. Low level duration logic is in the duration module.