Compare commits

..

No commits in common. "main" and "0.5.0" have entirely different histories.
main ... 0.5.0

5 changed files with 43 additions and 199 deletions

View File

@ -1,6 +1,6 @@
# Package # Package
version = "0.5.4" version = "0.5.0"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Chord chart formatter compatible with Planning Center Online" description = "Chord chart formatter compatible with Planning Center Online"
license = "MIT" license = "MIT"
@ -12,9 +12,3 @@ bin = @["pco_chords"]
# Dependencies # Dependencies
requires "nim >= 1.6.6", "docopt", "zero_functional" requires "nim >= 1.6.6", "docopt", "zero_functional"
# Dependencies from git.jdbernard.com/jdb/nim-package
requires "update_nim_package_version"
task updateVersion, "Update the version of this tool.":
exec "update_nim_package_version pco_chords 'src/pco_chordspkg/cliconstants.nim'"

View File

@ -38,12 +38,7 @@ when isMainModule:
if args["--transpose"]: parseInt($args["--transpose"]) if args["--transpose"]: parseInt($args["--transpose"])
else: 0 else: 0
let outputHtml = let outputHtml = ast.toHtml(transpose, args["--number-chart"])
if args["--large-print"]:
ast.toHtml(transpose, args["--number-chart"],
stylesheets = @[LARGE_PRINT_STYLESHEET])
else:
ast.toHtml(transpose, args["--number-chart"])
if args["--output"]: writeFile($args["--output"], outputHtml) if args["--output"]: writeFile($args["--output"], outputHtml)
else: stdout.write(outputHtml) else: stdout.write(outputHtml)

View File

@ -23,7 +23,7 @@ type
ccnkRedefineKey, ccnkRedefineKey,
ccnkNone ccnkNone
ChordChartPitch* = enum Af, A, Bf, B, C, Df, D, Ef, E, F, Gf, G ChordChartPitch* = enum Af, A, Bf, B, C, Df, D, Ef, E, F, Fs, G
ChordChartChord* = object ChordChartChord* = object
original*: Option[string] original*: Option[string]
@ -77,60 +77,20 @@ iterator pairs*(ccmd: ChordChartMetadata): tuple[key, value: string] =
func kind*(ccn: ChordChartNode): ChordChartNodeKind = ccn.kind func kind*(ccn: ChordChartNode): ChordChartNodeKind = ccn.kind
func `$`(pitch: ChordChartPitch): string = func `$`*(pitch: ChordChartPitch): string =
["A♭", "A", "B♭", "B", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"][ord(pitch)] case pitch
of Af: "Ab"
func renderPitchInKey*( of A: "A"
pitch: ChordChartPitch, of Bf: "Bb"
key: ChordChartPitch, of B: "B"
useSharps: Option[bool] = none[bool]()): string = of C: "C"
of Df: "Db"
var scaleNames: array[(ord(high(ChordChartPitch)) + 1), string] of D: "D"
if useSharps.isNone: of Ef: "Eb"
scaleNames = case key of E: "E"
of A, B, D, E, G: of F: "F"
["G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G"] of Fs: "F#"
of Af, Bf, C, Df, Ef, F: of G: "G"
["A♭", "A", "B♭", "B", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"]
of Gf:
["A♭", "A", "B♭", "C♭", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"]
elif useSharps.isSome and useSharps.get:
scaleNames = case key
of A, B, C, D, E, G:
["G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G"]
of Af:
["G#", "A", "A#", "B", "B#", "C#", "D", "D#", "E", "E#", "F#", "F𝄪"]
of Bf:
["G#", "G𝄪", "A#", "B", "B#", "C#", "C𝄪", "D#", "E", "E#", "F#", "F𝄪"]
of Df:
["G#", "A", "A#", "B", "B#", "C#", "D", "D#", "E", "E#", "F#", "G"]
of Ef:
["G#", "A", "A#", "B", "B#", "C#", "C𝄪", "D#", "E", "E#", "F#", "F𝄪"]
of Gf:
["G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "E#", "F#", "G"]
of F:
["G#", "G𝄪", "A#", "B", "B#", "C#", "C𝄪", "D#", "D𝄪", "E#", "F#", "F𝄪"]
else: # useSharps.isSome and not useSharps.get
scaleNames = case key
of C, Af, Bf, Df, Ef, F:
["A♭", "A", "B♭", "B", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"]
of A:
["A♭", "B𝄫", "B♭", "C♭", "C", "D♭", "E𝄫", "E♭", "F♭", "F", "G♭", "G"]
of B:
["A♭", "A", "B♭", "C♭", "C", "D♭", "D", "E♭", "F♭", "F", "G♭", "G"]
of D:
["A♭", "B𝄫", "B♭", "C♭", "C", "D♭", "E𝄫", "E♭", "F♭", "F", "G♭", "A𝄫"]
of E:
["A♭", "B𝄫", "B♭", "C♭", "C", "D♭", "D", "E♭", "F♭", "F", "G♭", "G"]
of G:
["A♭", "B𝄫", "B♭", "C♭", "D𝄫", "D♭", "E𝄫", "E♭", "F♭", "F", "G♭", "A𝄫"]
of Gf:
["A♭", "A", "B♭", "C♭", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"]
return scaleNames[ord(pitch)]
func `+`*(pitch: ChordChartPitch, steps: int): ChordChartPitch = func `+`*(pitch: ChordChartPitch, steps: int): ChordChartPitch =
cast[ChordChartPitch]((ord(pitch) + steps) mod 12) cast[ChordChartPitch]((ord(pitch) + steps) mod 12)
@ -233,34 +193,29 @@ proc parsePitch*(ctx: ParserContext, keyValue: string): ChordChartPitch =
of "gs", "gis", "g#", "ab", "a♭", "af", "aes": return ChordChartPitch.Af of "gs", "gis", "g#", "ab", "a♭", "af", "aes": return ChordChartPitch.Af
of "g𝄪", "a", "a♮", "b𝄫": return ChordChartPitch.A of "g𝄪", "a", "a♮", "b𝄫": return ChordChartPitch.A
of "as", "ais", "a#", "bf", "bb", "b♭", "bes", "c𝄫": return ChordChartPitch.Bf of "as", "ais", "a#", "bf", "bb", "b♭", "bes", "c𝄫": return ChordChartPitch.Bf
of "a𝄪", "b", "c", "cb", "ces", "cf": return ChordChartPitch.B of "a𝄪", "b", "cb", "ces", "cf": return ChordChartPitch.B
of "bs", "bis", "b#", "c", "d𝄫": return ChordChartPitch.C of "bs", "bis", "b#", "c", "d𝄫": return ChordChartPitch.C
of "b𝄪", "cs", "cis", "c#", "d", "df", "db", "des": return ChordChartPitch.Df of "b𝄪", "cs", "cis", "c#", "df", "db", "des": return ChordChartPitch.Df
of "c𝄪", "d", "e𝄫": return ChordChartPitch.D of "c𝄪", "d", "e𝄫": return ChordChartPitch.D
of "ds", "dis", "d#", "ef", "e", "eb", "ees", "f𝄫": return ChordChartPitch.Ef of "ds", "dis", "d#", "ef", "eb", "ees", "f𝄫": return ChordChartPitch.Ef
of "d𝄪", "e", "f", "fes", "ff": return ChordChartPitch.E of "d𝄪", "e", "fes", "ff": return ChordChartPitch.E
of "es", "eis", "e#", "f", "g𝄫": return ChordChartPitch.F of "es", "eis", "e#", "f", "g𝄫": return ChordChartPitch.F
of "e𝄪", "fs", "fis", "f#", "g", "gf", "gb", "ges": return ChordChartPitch.Gf of "e𝄪", "fs", "fis", "f#", "gf", "gb", "ges": return ChordChartPitch.Fs
of "f𝄪", "g", "a𝄫": return ChordChartPitch.G of "f𝄪", "g", "a𝄫": return ChordChartPitch.G
of "#7", "1", "𝄫2": return ctx.curKeyCenter of "1": return ctx.curKeyCenter
of "𝄪7", "#1", "b2", "♭2": return ctx.curKeyCenter + 1 of "2": return ctx.curKeyCenter + 2
of "𝄪1", "2", "𝄫3": return ctx.curKeyCenter + 2 of "3": return ctx.curKeyCenter + 4
of "#2", "b3", "♭3", "𝄫4": return ctx.curKeyCenter + 3 of "4": return ctx.curKeyCenter + 5
of "𝄪2", "3", "b4", "♭4": return ctx.curKeyCenter + 4 of "5": return ctx.curKeyCenter + 7
of "#3", "4", "𝄫5": return ctx.curKeyCenter + 5 of "6": return ctx.curKeyCenter + 9
of "𝄪3", "#4", "b5", "♭5": return ctx.curKeyCenter + 6 of "7": return ctx.curKeyCenter + 11
of "𝄪4", "5", "𝄫6": return ctx.curKeyCenter + 7
of "#5", "b6", "♭6": return ctx.curKeyCenter + 8
of "𝄪5", "6", "𝄫7": return ctx.curKeyCenter + 9
of "#6", "b7", "♭7", "𝄫1": return ctx.curKeyCenter + 10
of "7", "b1", "♭1": return ctx.curKeyCenter + 11
else: raise ctx.makeError(keyValue.strip & " is not a recognized key.") else: raise ctx.makeError(keyValue.strip & " is not a recognized key.")
# see regexr.com/70nv1 # see regexr.com/70nv1
let CHORD_REGEX = let CHORD_REGEX =
"([b#♭♮𝄫𝄪]?[A-G1-7][b#♭♮𝄫𝄪]?)" & # chord root "([A-G1-7][b#♭♮𝄫𝄪]?)" & # chord root
"((min|maj|aug|dim|sus|6\\/9|[mM1-9#b♭♮𝄫𝄪Δ+oøoø][0-9]?|\\([1-9#b♭]+\\))*)" & # chord flavor/type "(([mM1-9#b♭♮𝄫𝄪Δ+oøoø][0-9]?|min|maj|aug|dim|sus|6\\/9|\\([1-9#b♭]+\\))*)" & # chord flavor/type
"(\\/([b#♭♮𝄫𝄪]?[A-G1-7][b#♭♮𝄫𝄪]?))?" # optional bass "(\\/([A-G1-7][b#♭♮𝄫𝄪]?))?" # optional bass
let CHORD_PAT = re(CHORD_REGEX) let CHORD_PAT = re(CHORD_REGEX)
proc parseChord*( proc parseChord*(
@ -458,7 +413,7 @@ proc parseBody(ctx: var ParserContext): seq[ChordChartNode] =
m = line.match(REDEFINE_KEY_PAT) m = line.match(REDEFINE_KEY_PAT)
if m.isSome: if m.isSome:
let newKeyInt = ( let newKeyInt = (
ord(ctx.curKeyCenter) + cast[int](ctx.curKeyCenter) +
parseInt(m.get.captures[0]) parseInt(m.get.captures[0])
) mod 12 ) mod 12

View File

@ -1,4 +1,4 @@
const PCO_CHORDS_VERSION* = "0.5.4" const PCO_CHORDS_VERSION* = "0.5.0"
const USAGE* = """Usage: const USAGE* = """Usage:
pco_chords [options] pco_chords [options]
@ -23,7 +23,6 @@ Options:
--help Print this usage information --help Print this usage information
--debug Enable debug logging. --debug Enable debug logging.
--echo-args Echo the input parameters. --echo-args Echo the input parameters.
--large-print Use the large type styling.
""" """
const ONLINE_HELP* = """ const ONLINE_HELP* = """

View File

@ -52,7 +52,7 @@ h3 .section-text {
.line { .line {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: end; align-items: flex-end;
margin-left: 0.5em; margin-left: 0.5em;
margin-bottom: 0.5em; margin-bottom: 0.5em;
@ -65,104 +65,10 @@ h3 .section-text {
} }
.word.space-after { margin-right: 0.5em; } .word.space-after { margin-right: 0.5em; }
.word.no-chord { align-self: flex-end; }
.chord { .chord {
font-weight: 600; font-weight: 600;
margin-right: 0.5em; margin-right: 0.5em;
white-space: nowrap;
}
.chord .flavor {
font-variant-position: super;
}
.song-order h3 {
font-style: italic;
font-weight: normal;
}
.song-order li {
list-style: none;
margin-left: 1em;
}
@media screen {
body { margin: 1em; }
}
@media print {
.page-contents { column-count: 2; }
}
</style>
"""
const LARGE_PRINT_STYLESHEET* = """
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: sans-serif;
font-size: 1.5em;
}
.page-contents {
column-width: 336px;
column-width: 3.5in;
}
.column-break { margin-bottom: auto; }
h2 { font-size: 1.25em; }
section {
break-inside: avoid;
padding-top: 1em;
}
h3 {
font-size: 1.125rem;
margin-bottom: 0.5em;
text-decoration: underline;
}
h3 .section-text {
font-style: italic;
font-size: 1em;
font-weight: normal;
margin: 0 0.5em;
}
.line {
display: flex;
flex-direction: row;
align-items: baseline;
margin-left: 0.5em;
margin-bottom: 0.5em;
}
.word {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.word.space-after { margin-right: 0.5em; }
.word.no-chord { align-self: flex-end; }
.lyric {
font-weight: 600;
}
.chord {
font-style: italic;
font-weight: 500;
margin-right: 0.5em;
} }
.chord .flavor { .chord .flavor {
@ -207,9 +113,9 @@ func scaleDegree(root: ChordChartPitch, p: ChordChartPitch): string =
else: raise newException(Exception, "Impossible") else: raise newException(Exception, "Impossible")
func format(ctx: FormatContext, chord: ChordChartChord, useNumber = false): string = func format(ctx: FormatContext, chord: ChordChartChord, useNumber = false): string =
##if not useNumber and chord.original.isSome: return chord.original.get if not useNumber and chord.original.isSome: return chord.original.get
if useNumber: elif useNumber:
result = "<span class=root>" & result = "<span class=root>" &
ctx.currentKey.scaleDegree(chord.rootPitch) & "</span>" ctx.currentKey.scaleDegree(chord.rootPitch) & "</span>"
@ -221,15 +127,11 @@ func format(ctx: FormatContext, chord: ChordChartChord, useNumber = false): stri
ctx.currentKey.scaleDegree(chord.bassPitch.get) & "</span>" ctx.currentKey.scaleDegree(chord.bassPitch.get) & "</span>"
else: else:
result = "<span class=root>" & result = $chord.rootPitch
renderPitchInKey(chord.rootPitch, ctx.currentKey) & "</span>" if chord.flavor.isSome: result &= chord.flavor.get
if chord.bassPitch.isSome: result &= "/" & $chord.bassPitch.get
if chord.flavor.isSome: #debug "transposed " & $ctx.sourceKey & " -> " & $ctx.currentKey &
result &= "<span class=flavor>" & chord.flavor.get & "</span>" # " (distance " & $distance & ")\p\tchord: " & $chord & " to " & result
if chord.bassPitch.isSome:
result &= "<span class=bass>/" &
renderPitchInKey(chord.bassPitch.get, ctx.currentKey) & "</span>"
proc transpose(ctx: FormatContext, chord: ChordChartChord): ChordChartChord = proc transpose(ctx: FormatContext, chord: ChordChartChord): ChordChartChord =
result = chord result = chord
@ -285,7 +187,6 @@ proc toHtml(ctx: var FormatContext, node: ChordChartNode, indent: string): strin
result &= "<span class='word" result &= "<span class='word"
#if node.spaceBefore: result&=" space-before" #if node.spaceBefore: result&=" space-before"
if node.spaceAfter: result&=" space-after" if node.spaceAfter: result&=" space-after"
if node.chord.isNone: result&=" no-chord"
result &= "'>" result &= "'>"
if node.chord.isSome: if node.chord.isSome:
@ -349,7 +250,7 @@ proc toHtml*(
result = """<!doctype html> result = """<!doctype html>
<html> <html>
<head> <head>
<title>""" & cc.metadata.title & "" & $ctx.currentKey & "</title>\p" <title>""" & cc.metadata.title & "</title>\p"
for ss in stylesheets: for ss in stylesheets:
if ss.startsWith("<style>"): result &= ss if ss.startsWith("<style>"): result &= ss