chord_pro
A Dart parser for the ChordPro 6 song format.
Built for music apps and digital songbooks — parses .cho / .crd / .chopro files into typed chords, lyrics, metadata, comments, images, layout hints, and chord diagrams.
Installation
dart pub add chord_pro
Usage
Parse a document
import 'package:chord_pro/chord_pro.dart';
final result = ChordPro.parse(source);
final song = result.songs.first;
print(song.metadata.titles.firstOrNull);
print(song.metadata.key);
ChordPro.parse returns a ParseResult with every song in the document (split on {new_song} / {ns}) plus any diagnostics. Use ChordPro.parseSong if you only want the first song.
What a Song contains
metadata— typedMetadata: titles, sort titles, subtitles, artists, sort artist, composers, lyricists, arrangers, copyright, album, year, key, time, tempo, duration, capo, transpose (plustransposeQualifier), columns, tags, plus anothermap for anything custom.sections— orderedSections for verse, chorus, bridge, tab, grid, abc, ly, svg, textblock, custom environments, and loose lines. Each section exposeslabel,attributes, plus typedgridAttributesandtextblockAttributeswhere applicable.chordDefinitions— parsed{define}/{chord}bodies includingdisplay,format,keys,copy,copyall,diagram, and the transposable bracketed[Name]form.formatting— typed font / size / colour overrides for chord, text, title, chorus, label, and friends.directives— the raw directive stream in source order.customExtensions—x_*namespaced directives.tocSuppressed—truewhen{ns toc=no}requested the song be omitted from the table of contents.titlesAlignment— typed{titles}alignment hint.diagrams— typed{diagrams}(or{g}) setting (enabledflag plus position enum).transposed(semitones)— returns a copy with every chord shifted.
Walk sections and lines
for (final section in song.sections) {
print('${section.kind} ${section.label ?? ''}');
for (final line in section.lines) {
switch (line.kind) {
case LineKind.structured:
for (final token in line.tokens) {
// TextToken / ChordToken / AnnotationToken / InlineDirectiveToken
}
case LineKind.verbatim:
print(line.verbatim);
case LineKind.comment:
print('${line.commentStyle}: ${line.comment}');
case LineKind.image:
print('image: ${line.image?.src}');
case LineKind.layoutBreak:
print('break: ${line.layoutBreak}');
}
}
}
Transpose and conditional selectors
Shift every chord by N semitones:
final upTwo = ChordPro.parseSong(source).transposed(2);
Activate conditional directives like {title-guitar: …} / {title-guitar!: …} by passing a selector set:
final guitar = ChordPro.parseSong(source, selectors: {'guitar'});
The selector set gates metadata, formatting, sections, comments, images, layout breaks, chord recalls, and {define} / {chord} definitions. Matching is case-insensitive.
Supported features
All facts per the ChordPro chord reference and directive reference.
Chords
- Letter roots
A–G, Nashville1–7, RomanI–VII. - Sharps and flats:
#,b. - Slash bass.
- Minor variants:
m,mi,min,-. - Major qualifier
majand the spec alternate^. - Diminished
dim/0, half-diminishedh. - Augmented
augand the spec alternate+. sus,sus2,sus4,add.- Emergency-bracket recovery (ChordPro 6.020/6.080):
[ ]+and[|]parse as annotations; empty[]is a zero-width placeholder.
Directives
- Metadata —
title/t,sorttitle,subtitle/st,artist,sortartist,composer,lyricist,arranger,copyright,album,year,key,time,tempo,duration,capo,transpose(with optionals/f/k/#/b/♯/♭qualifier),columns/col,tag, plus{meta: key value}desugaring. Auto-generated names (_key,key.print,today, …) are reserved. - Comments —
{comment},{ci},{cb},{highlight}emit as in-flow comment lines. - Images —
{image: …}parsed into a typedImageDirectivewith full attribute coverage:src,width,height,scale,align,border,bordertrbl,title,label,href,id,chord,type,x,y,spread,center,persist,omit, plus a validatedanchorEnum(paper/page/allpages/column/float/line). - Layout breaks —
{new_page},{new_physical_page},{column_break}emit as in-flow layout breaks. - Output / song boundary —
{ns toc=no}(ortoc=false/toc=0) setsSong.tocSuppressed.{titles: left|center|right}and{diagrams: on|off|top|bottom|right|below}(with{g}alias) become typed song-level settings. - Formatting —
chordfont,textsize,titlecolour, … reduce intoFormattingSettings. Bothcolourandcoloraccepted. - Custom —
x_*extensions preserved onSong.customExtensions.
Sections
- Built-in environments:
verse/sov,chorus/soc,bridge/sob,tab/sot,grid/sog. - Delegated
abc,ly,svg,textblockcaptured verbatim. - Custom
start_of_<name>/end_of_<name>sections preserved with their custom kind. label="…"attribute parsed for every{start_of_*}(alongside the legacy bare-value form).{start_of_grid}exposes typedshape(left+measures × beats+right),cc, andlabelviaSection.gridAttributes.{start_of_textblock}exposes the full ChordPro 6.050 attribute set (textblock-specific plus image-inherited) viaSection.textblockAttributes.{chorus}recall accepts all four spec forms —{chorus},{chorus: Final},{chorus: label="Final"},{chorus label="Final"}.
Conditional selectors
- Positive form
{title-guitar: …}and spec-form negation{title-guitar!: …}. - Gates metadata, formatting, sections, comments, images, layout breaks, chord recalls, and
{define}/{chord}definitions.
Source features
- ChordPro 6.01 scanner: trailing
\line continuation and\uXXXXUnicode escapes anywhere in input text. - ChordPro 6.060 brace-form
\u{X+}Unicode escape (1+ hex digits, supports surrogate-pair recombination). - File-level
#comments dropped. - Diagnostics with 1-based source spans for every problem.
Non-spec extensions
The parser is more lenient than the published spec in a few places. Each extension is opt-in by simply being present in your input — files parse either way, but the named feature is not required by ChordPro itself.
| Extension | Spec equivalent | Notes |
|---|---|---|
H letter root |
B |
German notation for B natural. |
♯ (U+266F), ♭ (U+266D) |
#, b |
Unicode accidentals. |
ø, ° |
h, 0 / dim |
Half-diminished and diminished glyphs. |
NC, N.C., N.C |
— | No-chord markers; spec is silent. |
| Backslash escapes inside lyrics | — | Escape [, ], {, }, \ inside lyric lines. |
Mid-lyric {…} directives |
— | Spec requires directives to occupy a whole line. |
{name-!sel}, {name+sel} |
{name-sel!} |
Legacy negative-selector forms; new files use the spec form. |
Known limitations
- Pango-style markup (
<b>,<i>,<span …>,<sym …/>,<img …/>,<strut …/>) inside lyrics and comments is preserved verbatim — no inline parsing or rendering. {define format="…"}is captured as a typedStringbut the%{…}substitutions inside it are not interpreted (rendering concern).- The directive parser closes on the first unescaped
}, so attribute values cannot themselves contain a literal}. {pagetype}lands inSong.directivesonly — no typed access.
Example songs
Runnable demo: example/chord_pro_example.dart.
ChordPro source files:
Libraries
- chord_pro
- Parser for the ChordPro 6 song format.