songleaf.model

The linked-artifact model: a song is lyrics text plus standoff annotations.

A Song keeps its lyrics as one plain string and everything else as Annotation records anchored on character offsets into that string. The text is never marked up, so any number of annotation layers can coexist and be added later without touching the lyrics (the standoff design of lacing, over characters instead of seconds).

Kinds used so far:

  • "section": spans the section’s lines, body={"label": "Chorus"}. A label-only section (a bare [Chorus] marker meaning “repeat the chorus”) is empty (start == end), sits on an empty line of its own, and has "marker": True in its body.

  • "chord": a point (start == end) on the first character of the syllable the chord lands on, body={"symbol": "G/B", "timing": "at"}. timing is "at" (the chord sounds with that syllable, the default) or "before" (it sounds ahead of it). Chords of a line with no lyrics (an intro, an instrumental bar) sit, in order, on an empty line of the text.

  • "score": a link to a score snippet for [start, end), body={"source": ..., "id": ..., "url": ..., "format": ...}.

>>> song = Song("Hello there", [chord(0, "C"), chord(6, "G")], meta={"title": "Hi"})
>>> [(a.start, a.body["symbol"]) for a in song.of_kind("chord")]
[(0, 'C'), (6, 'G')]
>>> Song.from_dict(song.to_dict()) == song
True

Module Attributes

CHORD_TIMINGS

When a chord sounds, relative to the syllable it is anchored on.

Functions

chord(at, symbol, *[, timing])

A chord landing on the syllable that starts at offset at.

score_link(start, end, *, source, id[, url, ...])

A link from [start, end) to a score snippet held by a source.

section(start, end, label)

A section (verse, chorus, ...) spanning [start, end).

Classes

Annotation(kind, start, end[, body])

One annotation over Song.text[start:end]; a point when start == end.

Line(start, end, text)

One line of a song's text: text == song.text[start:end].

Song(text[, annotations, meta, provenance])

Lyrics text plus annotations, metadata and provenance.

class songleaf.model.Annotation(kind, start, end, body=<factory>)[source]

Bases: object

One annotation over Song.text[start:end]; a point when start == end.

to_dict()[source]

The JSON-ready form.

Return type:

dict

songleaf.model.CHORD_TIMINGS = ('at', 'before')

When a chord sounds, relative to the syllable it is anchored on.

class songleaf.model.Line(start, end, text)[source]

Bases: object

One line of a song’s text: text == song.text[start:end].

class songleaf.model.Song(text, annotations=<factory>, meta=<factory>, provenance=<factory>)[source]

Bases: object

Lyrics text plus annotations, metadata and provenance.

Parameters:
  • text (str) – The lyrics, lines separated by "\n".

  • annotations (list[Annotation]) – Standoff annotations over text (kept sorted by start).

  • meta (dict) – Descriptive fields: title, artist, capo, key, …

  • provenance (dict) – Where the song came from: source, id, url, license.

classmethod from_dict(data)[source]

The inverse of to_dict().

Return type:

Song

lines()[source]

The lines of the text, with their offsets.

Return type:

Iterator[Line]

of_kind(kind)[source]

The annotations of one kind, in text order.

Return type:

list[Annotation]

to_dict()[source]

The JSON-ready form (what the store persists).

Return type:

dict

songleaf.model.chord(at, symbol, *, timing='at')[source]

A chord landing on the syllable that starts at offset at.

Return type:

Annotation

A link from [start, end) to a score snippet held by a source.

Return type:

Annotation

songleaf.model.section(start, end, label)[source]

A section (verse, chorus, …) spanning [start, end).

Return type:

Annotation