songleaf¶
songleaf: dense, readable one-page song sheets from searchable sources.
Find a song, keep it as lyrics plus anchored annotations (sections, chords, provenance, score links), and render it onto one A4 page with the lyrics as large as will fit.
>>> import songleaf
>>> song = songleaf.parse_chords_over_lyrics("C G\nHello there my friend")
>>> [a.body["symbol"] for a in song.of_kind("chord")]
['C', 'G']
From a query to a PDF, on the Kaggle chords-and-lyrics corpus:
songleaf.sheet("wonderwall oasis") # -> {'path': '.../sheets/oasis-wonderwall.pdf', ...}
or python -m songleaf sheet "wonderwall oasis".
Functions
|
A chord landing on the syllable that starts at offset |
|
The largest lyric font size at which |
|
The song named by |
|
The layout of a spec: options of |
|
The one spelling of a layout spec, for names: |
|
A |
|
Parse chords-over-lyrics text into a |
|
Render |
|
Render |
|
A link from |
|
Search every source and merge the hits, best first. |
|
A section (verse, chorus, ...) spanning |
|
Make a one-page song sheet (PDF) for the best match of |
|
Songs as JSON files in |
|
The keys of the stored songs. |
Classes
|
One annotation over |
|
Typography of the dense layouts. |
|
One search result: a song a source can |
|
The Kaggle chords-and-lyrics corpus (~135K songs, chords over lyrics). |
|
One way to lay a song out on a page. |
|
Lyrics text plus annotations, metadata and provenance. |
- class songleaf.Annotation(kind, start, end, body=dict())[source]¶
Bases:
objectOne annotation over
Song.text[start:end]; a point whenstart == end.
- class songleaf.DenseStyle(lyric_font='DejaVuSans', chord_font='DejaVuSans-Bold', label_font='DejaVuSans-Bold', title_font='DejaVuSans-Bold', lyric_color='#000000', chord_color='#3b73c4', label_color='#8c8c8c', separator_color='#a6a6a6', title_color='#595959', chord_scale=0.62, label_scale=0.5, title_scale=0.6, chord_rise=0.56, chord_halo='', chord_halo_width=0.16, ascent=0.72, descent=0.21, row_gap=0.06, paragraph_gap=0.3, separator=' / ', chord_gap=0.3, bare_chord_gap=0.9, label_gap=0.4, inline_chord_scale=0.7, inline_chord_rise=0.2, inline_chord_pad=0.15, before_marker='‹', column_gap=0.8, column_rule_color='#d9d9d9', chorus_shade='#f3efe4', bridge_shade='#e8eef6', band_pad=0.12, repeat_color='#4d4d4d', tonic_chord_color='#1c4a91', outside_chord_color='#c0602a', staff_space=0.4, snippet_color='#333333')[source]¶
Bases:
objectTypography of the dense layouts. Lengths are fractions of the lyric font size.
- class songleaf.Hit(source, id, title, artist='', score=0.0, meta=dict())[source]¶
Bases:
objectOne search result: a song a source can
get().- property key: str¶
"<source>:<id>", the song’s name forget_song()and the store.
- class songleaf.KaggleChordsSource(*, loader=None, min_score=70)[source]¶
Bases:
objectThe Kaggle chords-and-lyrics corpus (~135K songs, chords over lyrics).
Read through
sungfrom a local copy of the zip, with no Kaggle credentials needed when that copy exists. The corpus was scraped from a chords site: personal use only (licence taggray).Title and artist are matched fuzzily (typos, punctuation and word order do not matter); lyrics must contain every word of the
lyricsquery as a whole word. Lyrics matches are not ranked beyond popularity.- Parameters:
loader –
() -> pandas.DataFramewithcolumns. Defaults to sung’s loader of the local corpus zip.min_score (
float) – Fuzzy-match cutoff, 0-100.
- search(query='', *, title='', artist='', lyrics='', limit=10)[source]¶
Songs matching every given constraint, best first.
- search_tiers(query, *, limit=10)[source]¶
The songs whose title, artist or lyrics match
query, by where they match.{"title": [...], "artist": [...], "lyrics": [...]}, at mostlimitin each, best first. A song is in the first kind it matches, even when it ranks below the limit there, so a song is never shown as an artist match because more titles matched.Title and artist match word by word (
WordIndex): the query is inside the field ("wonder"finds Wonderwall) or the field is inside the query ("wonderwall oasis"finds the title Wonderwall and the artist Oasis), a word of four letters or more forgiving a typo; or every word of the query is in the title or in the artist ("paper boats invented band"). A song whose title holds a word of the query that its artist does not is a title match ("wonderwall oasis": Wonderwall (ver. 2) by Oasis), otherwise an artist match. Within each kind, the songs whose title and artist together hold the most words of the query come first (so Wonderwall by Oasis leads), then the closest by fuzzy score (of the title, or of title and artist, whichever is closer; of the artist), then the most popular. Lyrics hold every word of the query, rank by popularity, and carrymeta["snippet"]: the line that matched, with its spans.
- class songleaf.SheetLayout(chords='over', packing='greedy', columns=1, shading=False, diagrams=None, style=DenseStyle(), score=False)[source]¶
Bases:
objectOne way to lay a song out on a page.
- Parameters:
chords (
str|ChordPlacement) – Where chords go:"over"(overlapping the lyric row) or"inline"(in the line, before their syllable), or anyChordPlacement.packing (
str|Callable) – Which lines share a row:"greedy"or"structured", or any packer with the signature ofsongleaf.packing.pack_greedy().columns (
int) – Columns per page.shading (
bool) – Shade sections, repeated lines and chord functions.diagrams (
str|None) –"piano"or"guitar"draws the song’s chords as a strip of diagrams at the foot of the first page (songleaf.diagrams); the lyrics shrink to make room.None: no diagrams.score (
bool) – Draw the song’s score snippet, if its provider has one, above the line it belongs to (songleaf.snippets); the lyrics shrink to make room.style (
DenseStyle) – Typography and colours.
- property placement: ChordPlacement¶
The chord placement strategy.
- class songleaf.Song(text, annotations=list(), meta=dict(), provenance=dict())[source]¶
Bases:
objectLyrics text plus annotations, metadata and provenance.
- Parameters:
text (
str) – The lyrics, lines separated by"\n".annotations (
list[Annotation]) – Standoff annotations overtext(kept sorted by start).meta (
dict) – Descriptive fields:title,artist,capo,key, …provenance (
dict) – Where the song came from:source,id,url,license.
- songleaf.chord(at, symbol, *, timing='at')[source]¶
A chord landing on the syllable that starts at offset
at.- Return type:
- songleaf.fit_font_size(song, *, page_size='A4', margin=14.0, style=None, layout=None, min_font_size=6.0, max_font_size=72.0, precision=0.05, snippets=None)[source]¶
The largest lyric font size at which
songfits one page inlayout(default: v1’s).style, if given, replaces the layout’s style. Returnsmin_font_sizeif even that does not fit (the song then needs more pages).snippetsis the score-snippet provider of ascorelayout (defaultsongleaf.snippets.default_provider()’s).- Return type:
- songleaf.get_song(key, *, sources=None)[source]¶
The song named by
key("<source>:<id>"), from the source of that name.- Return type:
- songleaf.layout_named(spec, *, style=None)[source]¶
The layout of a spec: options of
LAYOUT_OPTIONSjoined by+("inline+two-column").Options apply in order, over the v1 layout and
style(defaultDenseStyle).- Return type:
- songleaf.layout_spec(spec)[source]¶
The one spelling of a layout spec, for names:
"Dense"->"dense".>>> layout_spec("2col + INLINE"), layout_spec("dense+"), layout_spec("inline+2col+inline") ('two-column+inline', 'dense', 'two-column+inline')
- Return type:
- songleaf.make_renderer(layout='dense', **render_options)[source]¶
A
(song, output) -> dictrenderer forlayout, the shapesheet’srenderer=takes.render_optionsare passed torender_sheet()(page_size,margin, …). A bad spec fails here, not at render time.>>> make_renderer("packed+shaded").keywords["layout"].packing 'structured'
- Return type:
- songleaf.parse_chords_over_lyrics(raw, *, meta=None, provenance=None)[source]¶
Parse chords-over-lyrics text into a
Song.
- songleaf.render_dense_a4(song, output, *, page_size='A4', margin=14.0, style=None, min_font_size=6.0, max_font_size=72.0)[source]¶
Render
songin the v1 layout as a PDF tooutput(a path or a binary file object).The same as
render_sheet()withlayout="dense". Returns{"path", "font_size", "pages", "rows"};pathisNonewhenoutputis a file object.- Return type:
- songleaf.render_sheet(song, output, *, layout='dense', page_size='A4', margin=14.0, min_font_size=6.0, max_font_size=72.0, scale=1.0, snippets=None)[source]¶
Render
songinlayout(aSheetLayoutor a spec) as a PDF tooutput.The lyric font size is the largest that fits the song on one page, times
scale:1fills the page,0.8leaves room,1.2is larger type that runs onto a second page. It never goes pastmax_font_size.snippetsprovides ascorelayout’s snippet:song -> ScoreSnippet | None(defaultsongleaf.snippets.default_provider()’s).outputis a path or a binary file object. Returns{"path", "font_size", "pages", "rows"};pathisNonewhenoutputis a file object.- Return type:
- songleaf.score_link(start, end, *, source, id, url='', format='', notation='', rule='')[source]¶
A link from
[start, end)to a score snippet held by a source.notationholds a short snippet inline (formatsays in what, such as"abc");rulenames the rule that chose the stretch ("chosen"when picked by hand). Both are left out of the body when empty.- Return type:
- songleaf.search(query='', *, title='', artist='', lyrics='', limit=10, sources=None)[source]¶
Search every source and merge the hits, best first.
>>> search("paper boats", sources=[]) []
- songleaf.section(start, end, label)[source]¶
A section (verse, chorus, …) spanning
[start, end).- Return type:
- songleaf.sheet(query, *, output='', pick=1, refresh=False, layout='dense', transpose=0, scale=1.0, sources=None, store=None, renderer=None)[source]¶
Make a one-page song sheet (PDF) for the best match of
query.querymay also be a song key, assearchandsongsshow them (kaggle_chords:1234): a stored song renders without searching, and the key of a known source is fetched from it directly.pickchooses the n-th best match instead of the best. The song is saved to the store;refreshfetches it from its source again, replacing the stored copy. The PDF goes tooutput, by default thesheetsdata directory.layoutisdense(the default), or options joined with+:overlap(chords over the words themselves),inline(chords in the line, before their syllable),packed(rows break where the lyrics do),two-column,shaded(sections, repeated lines and chord functions); for exampleinline+packed+two-column. Arendererdecides the layout itself, so it does not go withlayoutorscale.transposemoves every chord by that many semitones (spelled for the new key); the stored song keeps its chords.scalemultiplies the font size that fills the page:0.8for smaller type,1.2for larger type over more pages.- Return type:
- songleaf.song_store(rootdir=None)[source]¶
Songs as JSON files in
rootdir(default: thesongsdata dir).- Return type:
Modules
Parse ChordPro text into a |
|
Where the |
|
Chord diagrams for the sheet: piano keys or guitar shapes (songleaf#13). |
|
Score snippets engraved by Verovio: the |
|
Export a song as ChordPro or as plain text (chords over lyrics), transposition applied. |
|
What a chord does in its song's key, so that sheets can shade chords by function. |
|
Turn pasted text into a |
|
A search index of the Kaggle corpus: the same songs and keys, without the corpus in memory. |
|
The library's lists: which stored songs are in which lists, shared by everyone using it. |
|
Where a query matched: the words it shares with a field, as spans to highlight, and the lyric line. |
|
The linked-artifact model: a song is lyrics text plus standoff annotations. |
|
Line packing: which consecutive lines of a paragraph share a row. |
|
Parse chords-over-lyrics text into a |
|
Render a song onto one dense page, with the lyrics as large as will fit. |
|
Score snippets on the sheet (songleaf#12): which stretch of a song, and a small staff for it. |
|
A songbook: several songs in one PDF, with a table of contents, page numbers and bookmarks. |
|
Where songs are found: sources, and a search that composes them. |
|
Where songs persist: a |
|
Play-along: a song's media links and line timings (songleaf#30). |
|
The single source of truth for every surface: plain functions, flat arguments, JSON-ready results. |
|
Ultimate Guitar: community chord charts the reader opened themselves (songleaf#28). |
|
Each viewer's Viewed list: the songs they opened, the most recent first. |