SuperAPI AI Directory

chordpro

Writing & research

View repository

Updated 10-16 08:49

About this skill

chordpro

Produce and work with ChordPro chord sheets — the plain-text format where chords live inline in the lyrics as [C]like [G]this. This skill is a router that degrades gracefully: it picks the most reliable workflow for whatever the user actually has (a .cho file, a chords-over-lyrics paste, bare lyrics, an audio file, or just a song title) and every path ends by validating the output. It is honest about the one hard part — extracting chords from raw audio is an ~80%-accurate draft, never an oracle — and falls back to interactive, fill-in-the-gaps assistance instead of pretending otherwise.

When to use

  • User names ChordPro / .cho / .crd / .chopro / .pro, inline [C]lyric chords, or {title:} / {start_of_chorus} / {soc} directives.
  • Convert an existing sheet: chords-over-lyrics text (Ultimate Guitar, OnSong, a lyric with chord letters on the line above) → ChordPro.
  • Render / transpose / validate a .cho with the chordpro or a2crd CLI.
  • "Make a chord sheet for " starting from lyrics only, or from a YouTube / Bilibili / SoundCloud link or an mp3 / wav file.
  • Ask about lyrics sources (Mojim/Magic Mirror, KKBOX, Musixmatch, LRCLIB, Genius, NetEase, QQ Music) — which give time-synced .lrc vs plain text.

When NOT to use

  • Engraving staff notation (notes on a staff, MusicXML / MIDI / LilyPond / MuseScore output) — ChordPro is lyrics+chords, not a score. Hand off to a notation tool. (ChordPro can embed ABC/LilyPond fragments — see the format reference — but it is not an engraver.)
  • OMR from a scanned score image (photo/PDF of printed sheet music → notes) is out of scope; there is no reliable open path. Offer manual transcription into ChordPro instead, and say so plainly.

Authoritative sources

The router — pick the workflow by what the user has

User has… Do this Reliability
A .cho/.crd/.pro file Render / transpose / validate via chordpro CLI High
Chords-over-lyrics text (UG/OnSong) a2crd → light manual cleanup → validate High
A known song (title / lyrics / a link) and no chords yet Search existing chord charts online → chart-to-cho.py (preserves the chart's own alignment) → sanity-check → validate. See references/chord-tab-sources.md Med–High (best first move)
Lyrics only, no chart found Fetch/confirm lyrics → interactively fill chords (propose from key, ask the user to confirm/correct) Medium (human-in-loop)
An audio file or link, want a machine draft scripts/audio-to-chords.py → draft with AUTO-GENERATED header → human correction ~80% (last resort)

Prefer the highest row that fits. Two rules that matter: don't jump to the audio pipeline when the user already pasted chords (a2crd is faster and correct); and for a named/popular song, search for an existing human-made chart first — it's usually more accurate than audio ACR and sidesteps the download-ToS problem entirely.

Before charting a named song, confirm it exists as described — user-supplied artist/album is sometimes wrong, and searching a bad artist can grab a mislabeled chart; a quick Wikipedia/LRCLIB match prevents shipping a misattribution. Non-interactive/batch runs can't use the "ask the user" rows — instead produce a best-effort chart from the published chord vocabulary, caption every uncertainty in a {comment:}, and omit unknown metadata rather than fabricate it.

ChordPro cheat-sheet + output template

  • Extensions: .cho (recommended), also .crd .chopro .chord .pro. Plain UTF-8. # starts a comment line; blank lines separate blocks.
  • Inline chords: [C] immediately before the syllable it sits over — Swing [G]low, sweet [C]chari[G]ot. Annotations (non-chords): [*softly].
  • Metadata: {title:}/{t:}, {subtitle:}/{st:}, {artist:}, {composer:}, {album:}, {year:}, {key:}, {tempo:}, {time:}, {capo:}.
  • Environments (long / short): chorus {start_of_chorus}/{soc} … {end_of_chorus}/{eoc}; verse {sov}…{eov}; bridge {sob}…{eob}; tab (monospaced, chords NOT parsed) {sot}…{eot}. Recall a chorus with {chorus}. Comments: {comment:}/{c:}.
  • Key vs capo: {key:} is the written/fingering key of the shapes shown; {capo:} is the capo used. If charts disagree, prefer the one whose chord vocabulary is internally consistent, and caption the sounding pitch.
  • Chorus recall: {chorus} reprints the most-recently-defined chorus (showing only a "Chorus" label by default). With 2+ distinct recurring sections, label them — {start_of_chorus: name} … {chorus: name} — or expand the repeat.

Emit this shape — metadata block → environments/comments → verses/choruses:

{title: Swing Low Sweet Chariot}
{artist: Traditional}
{key: G}
{tempo: 90}
{time: 4/4}

{comment: Intro}
[G] [C] [G] [D]

{start_of_chorus: Chorus}
Swing [G]low, sweet [C]chari[G]ot,
Comin' for to carry me [D]home.
{end_of_chorus}

{start_of_verse: Verse 1}
I [G]looked over Jordan, and [C]what did I [G]see,
Comin' for to carry me [D]home.
{end_of_verse}

{chorus}

Full directive set (chord {define} diagrams, {transpose}, markup, ABC/LilyPond blocks, x_ custom namespace) → read references/chordpro-format.md.

The chordpro / a2crd CLI (essentials)

The official tool is a Perl program (CPAN dist App::Music::ChordPro). Current 6.x releases install the chordpro command; the chords-over-lyrics importer is chordpro --a2crd (a standalone a2crd binary shipped in older releases and may be absent — prefer chordpro --a2crd).

# Install (macOS — there is NO official Homebrew formula; use CPAN)
brew install perl cpanminus && cpanm App::Music::ChordPro
# If `chordpro` then errors "Can't locate ChordPro.pm", activate local::lib once:
eval "$(perl -I"$HOME/perl5/lib/perl5" -Mlocal::lib)"   # add to ~/.zshrc to persist

# Render
chordpro -o song.pdf song.cho          # PDF (format inferred from extension)
chordpro --generate=HTML -o song.html song.cho

# Transpose (N semitones; suffix s/f forces sharp/flat spelling)
chordpro -x 2  -o up.pdf   song.cho
chordpro -x -3f -o down.pdf song.cho

# Convert chords-over-lyrics text -> ChordPro (the official importer)
chordpro --a2crd input.txt -o song.cho

Install-per-platform, generators, config JSON, songbook --toc, and a2crd heuristic tuning → read references/cli-and-rendering.md.

Verify loop — never hand back an unvalidated .cho

Every generated or edited file goes through the parser before you present it:

scripts/validate-cho.sh song.cho          # wraps the command below; PASS/FAIL + warnings
# equivalently:
chordpro --strict --generate=Text -o - song.cho   # exit 0 + no stderr warnings = valid

To auto-normalize to canonical form (fix spacing, round-trip directives): chordpro --generate=ChordPro -o song.cho song.cho. If chordpro isn't installed, the script prints the install one-liner — say so and offer to proceed without rendering (the format is still human-checkable).

Sanity-check the harmony (non-blocking). After building a chart — especially a fetched or auto-generated one — run scripts/analyze-progression.py song.cho. It detects the key, re-expresses every chord as a scale degree (Roman + Nashville), and flags the ones worth a second look: out-of-key chords reached by an improbable move (the strongest "likely mis-transcribed" signal), plus duplicate and floating chords. It's advisory, not a gate — unusual ≠ wrong — but many low-plausibility flags mean the chart or the detected key needs another look, and it's a good tie-breaker when two fetched charts disagree. Theory + how to read the verdict → references/music-theory.md.

Producing a PDF? Verify it renders, not just parses. validate-cho.sh only checks that the file parses — it can't see a broken PDF. chordpro's default fonts have no CJK glyphs, so a Chinese/Japanese/Korean song renders as blank tofu while still exiting 0 and passing validation. Use scripts/render-cho.sh song.cho — it auto-detects a CJK font, renders, and glyph-checks the PDF (fails loudly on tofu).

Finding existing chord charts (the usual first move)

For a named/popular song, a human-made chart almost always already exists online and beats audio extraction on accuracy. scripts/chart-to-cho.py fetches and converts it while preserving the chart's own chord-to-syllable alignment — the key to accuracy. The source already encodes which syllable each chord sits on (Chord4's 後來的生(活), UG's monospace columns); the script reproduces that. Never re-align fetched chords onto separately-fetched lyrics by eye — that lossy re-merge is what drifts chords and drops them as floaters.

# 1. search: 中文 吉他谱/和弦/弹唱谱 ; EN "<artist> <title>" chords / ultimate guitar
# 2. convert (auto-detects Chord4 vs Ultimate Guitar; keeps alignment + metadata):
uv run scripts/chart-to-cho.py --url "<chart-url>" -o song.cho
#    Cloudflare wall? save the page in a browser, then: --html saved.html --site chord4
# 3. LRCLIB is only the .lrc sidecar + a gap-filler for missing lines — NOT the
#    alignment source (the chart already aligned the chords):
uv run scripts/fetch-lyrics.py --artist "…" --track "…" -o song.lrc
# 4. sanity-check the harmony (key + degrees + likely mis-transcriptions):
uv run scripts/analyze-progression.py song.cho
# 5. validate + render (CJK-safe):
scripts/validate-cho.sh song.cho && scripts/render-cho.sh song.cho

chart-to-cho.py adds the source {comment:} caption itself. Human-made chart, not machine ACR — so no AUTO-GENERATED header. Per-site extraction internals, the site catalog (91pu/Chord4/UG — many are JS-rendered or image-only six-line tablature), simplified↔traditional (--opencc / URL variant), and legality → read references/chord-tab-sources.md. Harmony theory behind step 4 → references/music-theory.md.

Generating from audio / links — the fallback when no chart exists

This is the shakiest path — reach for it only when no existing chart is available.

Set expectations first: open automatic chord recognition tops out around 78–80% on simple major/minor pop and drops sharply on 7ths, extended/jazz chords, key changes, and quiet or dense mixes; inversions/slash chords are unreliable; and it cannot know the capo a guitarist used. Treat the result as a draft to correct, not a transcription.

# Best-effort turnkey: link or local file -> draft ChordPro
uv run scripts/audio-to-chords.py --dry-run "https://youtu.be/..."   # show the plan first
uv run scripts/audio-to-chords.py -o draft.cho "https://youtu.be/..."

The script downloads audio with yt-dlp (YouTube/Bilibili/SoundCloud) and runs chord-extractor/Chordino. Its output must carry the {comment: AUTO-GENERATED — verify chords/key/timing} header. On macOS the Chordino Vamp plugin often isn't installed; the script detects that and prints exact install guidance + manual alternatives instead of crashing — relay that to the user rather than silently failing. Tool comparison, beat/key detection, and commercial escape hatches (Moises/Songsterr have APIs; Chordify/Chord AI/Ultimate Guitar don't) → read references/audio-to-chords.md.

Legal: only process content the user owns or has the right to use, for personal use, locally; downloading copyrighted audio can violate a platform's ToS. Surface this — don't bury it.

Lyrics sources

Default to LRCLIB — free, no API key, returns time-synced .lrc:

uv run scripts/fetch-lyrics.py --artist "Adele" --track "Hello" --duration 295
uv run scripts/fetch-lyrics.py --plain --artist "..." --track "..."   # plain text
Source Access Synced .lrc?
LRCLIB free, no key yes
Musixmatch API key; full/synced is paid yes (paid)
NetEase / QQ Music unofficial community APIs yes (Chinese catalog)
Genius (lyricsgenius) API token; lyrics scraped from page no (plain)
Mojim / Magic Mirror scrape-only no (plain)
KKBOX partner-gated n/a

Full comparison, legality, and the LRCLIB API → read references/lyrics-sources.md.

Available scripts

  • scripts/chart-to-cho.py — fetch an existing chart (Chord4 primary, Ultimate Guitar fallback) and convert it to inline ChordPro, preserving the chart's own chord-to-syllable alignment. Auto-detects the site; pulls title/artist/key/capo. Flags: --url/--html, --site chord4|ug|auto, -o, --opencc s2t|t2s, --dry-run, --help.
  • scripts/analyze-progression.py — sanity-check a progression: detect key, label each chord by scale degree (Roman + Nashville + diatonic/borrowed/secondary/ out-of-key), score each move against a rock-corpus transition matrix, and flag likely mis-transcriptions + duplicate/floating chords. Zero-dep (music21 optional). Flags: <file.cho> or --key/--chords, --json, --help.
  • scripts/validate-cho.sh <file.cho> — verify a file parses cleanly (chordpro --strict); PASS/FAIL to stdout, warnings to stderr. Guides install if chordpro is missing. Flags: --help.
  • scripts/render-cho.sh <file.cho> — render to PDF with a CJK-safe font (auto-detected) and glyph-check the result, so a Chinese song can't silently render as tofu. Flags: -o/--output, --font, --keep-config, --help.
  • scripts/fetch-lyrics.py — fetch lyrics from LRCLIB (synced .lrc default, --plain for text). Auto-retries a CJK miss with a romanized/pinyin query. Use it for the .lrc sidecar / to fill missing lines, not to re-align a chart's chords. Self-contained via uv run. Flags: --artist, --track, --duration, --album, --plain, -o/--output, --help, --dry-run.
  • scripts/audio-to-chords.py — best-effort link/audio → draft ChordPro (yt-dlp + chord-extractor). Degrades gracefully when the ACR backend is unavailable. Flags: -o/--output, --lrc, --help, --dry-run.

Bundled assets

Ready-to-open examples + a template under assets/ (all songs are public domain; every .cho validates clean with chordpro --strict). Use them as copy-paste starting points and few-shot references:

  • assets/example-amazing-grace.cho — simplest real song: metadata + inline chords.
  • assets/example-when-the-saints.cho — chorus/verse structure + {chorus} recall.
  • assets/example-greensleeves.cho — feature showcase: {define} diagrams, a {sot} tab block, chorus recall, transpose note.
  • assets/template-song.cho — fill-in-the-blanks scaffold (renders as-is); the starting point for the interactive chord-fill flow.
  • assets/example-synced-lyrics.lrc — sample time-synced .lrc (what fetch-lyrics.py returns; feed it to audio-to-chords.py --lrc).
  • assets/example-a2crd-input.txt — chords-over-lyrics plain text; convert with chordpro --a2crd assets/example-a2crd-input.txt.

Reference files

  • references/chordpro-format.md — Read when hand-authoring or you need a directive beyond the cheat-sheet ({define} diagrams, {transpose}, markup, ABC/LilyPond, x_ custom namespace).
  • references/chord-tab-sources.md — Read when you need chords for a named song and have none: chart-to-cho.py usage, where to find existing charts (91pu/Chord4/Ultimate Guitar/…), the per-site markup it parses, and the legality.
  • references/music-theory.md — Read when validating a progression (key detection, Roman/Nashville degrees, diatonic-fit, the rock-corpus transition matrix, how to read analyze-progression.py's verdict).
  • references/cli-and-rendering.md — Read when installing, rendering, transposing, converting with a2crd, or validating/normalizing.
  • references/audio-to-chords.md — Read when the user wants chords from an audio file or a link (ACR tool choice, beat/key, limits, escape hatches).
  • references/lyrics-sources.md — Read when fetching lyrics (source table, synced-vs-plain, API-vs-scrape, legality, LRCLIB API).

Gotchas

  • macOS has no official Homebrew chordpro — install via CPAN (brew install perl cpanminus && cpanm App::Music::ChordPro). A signed .dmg GUI exists on GitHub Releases but the CLI is the CPAN route.
  • Can't locate ChordPro.pm in @INC after cpanm — cpanm installed into a local::lib (~/perl5) that isn't on Perl's path. Activate it: eval "$(perl -I"$HOME/perl5/lib/perl5" -Mlocal::lib)" (persist in ~/.zshrc).
  • No standalone a2crd in ChordPro 6.x — use chordpro --a2crd input.txt. Its output needs a human pass (heuristic, column-based chord placement); always validate and eyeball before rendering.
  • Angle brackets < > are text markup in ChordPro v6. Don't put <...> placeholders in lyrics/chords — the parser reads <b>/<i>-style markup and floods warnings. Use plain-text placeholders (see assets/template-song.cho).
  • No official online validator. chordpro.org hosts docs + a desktop GUI, not a "try it online" box. Validate locally with the CLI (validate-cho.sh).
  • CJK renders as tofu by default — and passes validation. chordpro's default fonts have no Han/Kana/Hangul glyphs, so a Chinese/Japanese/Korean song produces a blank PDF that still exits 0 and passes validate-cho.sh (which only checks parsing). Render CJK with scripts/render-cho.sh (auto CJK font + glyph-check), or pass --config pointing pdf.fonts.* at a CJK font.
  • a2crd is Latin-only. Its column heuristic assumes 1 char = 1 column, but full-width CJK occupies 2 display cells, so chords drift. For CJK, align chords to syllables/phrases by musical judgment — don't trust column arithmetic.
  • Preserve a fetched chart's own alignment — don't re-merge. A published chart already places each chord on a specific syllable (Chord4's (字) markup, UG's columns); chart-to-cho.py reproduces that exactly. Re-fetching "clean" lyrics from LRCLIB and re-aligning the chords by eye discards that ground truth and is what produces drifted, duplicated, and floating chords. LRCLIB is for the .lrc sidecar and for filling lines the chart omits — not for re-alignment. Note that a faithful transcription legitimately includes chords that land on a rest between syllables (instrumental beats), so not every "floating" chord is a bug — analyze-progression.py flags them for review, not deletion.
  • A strict "Unknown chord" warning ≠ a parse error. chordpro --strict warns "Unknown chord" for a valid chord it has no built-in diagram for (e.g. Em/C#, F#m7b5); it still renders. Add a {define} (see the format reference) or ignore.
  • {chorus} recall is a trap with 2+ recurring sections. It reprints only the most-recently-defined chorus, as a bare "Chorus" tag by default. Label distinct blocks ({start_of_chorus: name} / {chorus: name}) or expand repeats explicitly.
  • WebFetch strips copyrighted lyrics (returns "[lyrics omitted]"), so the chart-fetch path needs raw curl + per-site parsing; and from an image-only chart WebFetch may fabricate chord placement — trust only an explicit chord list.
  • Chord accuracy from audio is ~80% at best on simple pop, worse otherwise. Never present a machine-extracted sheet without the AUTO-GENERATED header and a "verify chords/key/timing" caveat.
  • Auto-generated chart sites are ACR in disguise. Chordu / Chordify / GuitarTuna charts are machine-derived (~80%, same caveat) — treat them as drafts, unlike human-made charts on 91pu / Chord4 / Ultimate Guitar.
  • Detected key is a suggestion. ACR outputs absolute pitches; key detection confuses relative major/minor and keys a fifth apart, and enharmonic spelling (Gb vs F#) is ambiguous. Confirm with the user before committing {key:}.
  • Chords in {sot}…{eot} tab blocks are NOT parsed — that's for ASCII tab. Use inline [..] in verses/choruses; reserve tab blocks for fret diagrams.
  • Lyrics/audio ToS. Prefer LRCLIB (open); most other lyric sources are scrape-only or paid, and downloading audio has copyright/ToS limits. Keep it personal-use and local; surface the caveat rather than hiding it.