Better support for transposition and Nashville numbers.
- Non-diatonic pitches are supported using Nashville numbers (#5). - When using Nashville numbers chord variants with non-diatonic roots are now recognized (e.g. b7) - Pitch rendering is now aware of key centers. For example, F# is F# rendered as F# when in the key of G but Gb when in the key of Ab.
This commit is contained in:
parent
5b1238f038
commit
8271129b90
@ -1,6 +1,6 @@
|
||||
# Package
|
||||
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
author = "Jonathan Bernard"
|
||||
description = "Chord chart formatter compatible with Planning Center Online"
|
||||
license = "MIT"
|
||||
|
@ -23,7 +23,7 @@ type
|
||||
ccnkRedefineKey,
|
||||
ccnkNone
|
||||
|
||||
ChordChartPitch* = enum Af, A, Bf, B, C, Df, D, Ef, E, F, Fs, G
|
||||
ChordChartPitch* = enum Af, A, Bf, B, C, Df, D, Ef, E, F, Gf, G
|
||||
|
||||
ChordChartChord* = object
|
||||
original*: Option[string]
|
||||
@ -77,20 +77,60 @@ iterator pairs*(ccmd: ChordChartMetadata): tuple[key, value: string] =
|
||||
|
||||
func kind*(ccn: ChordChartNode): ChordChartNodeKind = ccn.kind
|
||||
|
||||
func `$`*(pitch: ChordChartPitch): string =
|
||||
case pitch
|
||||
of Af: "Ab"
|
||||
of A: "A"
|
||||
of Bf: "Bb"
|
||||
of B: "B"
|
||||
of C: "C"
|
||||
of Df: "Db"
|
||||
of D: "D"
|
||||
of Ef: "Eb"
|
||||
of E: "E"
|
||||
of F: "F"
|
||||
of Fs: "F#"
|
||||
of G: "G"
|
||||
func `$`(pitch: ChordChartPitch): string =
|
||||
["A♭", "A", "B♭", "B", "C", "D♭", "D", "E♭", "E", "F", "G♭", "G"][ord(pitch)]
|
||||
|
||||
func renderPitchInKey*(
|
||||
pitch: ChordChartPitch,
|
||||
key: ChordChartPitch,
|
||||
useSharps: Option[bool] = none[bool]()): string =
|
||||
|
||||
var scaleNames: array[(ord(high(ChordChartPitch)) + 1), string]
|
||||
if useSharps.isNone:
|
||||
scaleNames = case key
|
||||
of A, B, C, D, E, G:
|
||||
["G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G"]
|
||||
of Af, Bf, Df, Ef, F:
|
||||
["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 =
|
||||
cast[ChordChartPitch]((ord(pitch) + steps) mod 12)
|
||||
@ -193,29 +233,34 @@ proc parsePitch*(ctx: ParserContext, keyValue: string): ChordChartPitch =
|
||||
of "gs", "gis", "g#", "ab", "a♭", "af", "aes": return ChordChartPitch.Af
|
||||
of "g𝄪", "a", "a♮", "b𝄫": return ChordChartPitch.A
|
||||
of "as", "ais", "a#", "bf", "bb", "b♭", "bes", "c𝄫": return ChordChartPitch.Bf
|
||||
of "a𝄪", "b", "cb", "ces", "cf": return ChordChartPitch.B
|
||||
of "a𝄪", "b", "c♭", "cb", "ces", "cf": return ChordChartPitch.B
|
||||
of "bs", "bis", "b#", "c", "d𝄫": return ChordChartPitch.C
|
||||
of "b𝄪", "cs", "cis", "c#", "df", "db", "des": return ChordChartPitch.Df
|
||||
of "b𝄪", "cs", "cis", "c#", "d♭", "df", "db", "des": return ChordChartPitch.Df
|
||||
of "c𝄪", "d", "e𝄫": return ChordChartPitch.D
|
||||
of "ds", "dis", "d#", "ef", "eb", "ees", "f𝄫": return ChordChartPitch.Ef
|
||||
of "d𝄪", "e", "fes", "ff": return ChordChartPitch.E
|
||||
of "ds", "dis", "d#", "ef", "e♭", "eb", "ees", "f𝄫": return ChordChartPitch.Ef
|
||||
of "d𝄪", "e", "f♭", "fes", "ff": return ChordChartPitch.E
|
||||
of "es", "eis", "e#", "f", "g𝄫": return ChordChartPitch.F
|
||||
of "e𝄪", "fs", "fis", "f#", "gf", "gb", "ges": return ChordChartPitch.Fs
|
||||
of "e𝄪", "fs", "fis", "f#", "g♭", "gf", "gb", "ges": return ChordChartPitch.Gf
|
||||
of "f𝄪", "g", "a𝄫": return ChordChartPitch.G
|
||||
of "1": return ctx.curKeyCenter
|
||||
of "2": return ctx.curKeyCenter + 2
|
||||
of "3": return ctx.curKeyCenter + 4
|
||||
of "4": return ctx.curKeyCenter + 5
|
||||
of "5": return ctx.curKeyCenter + 7
|
||||
of "6": return ctx.curKeyCenter + 9
|
||||
of "7": return ctx.curKeyCenter + 11
|
||||
of "#7", "1", "𝄫2": return ctx.curKeyCenter
|
||||
of "𝄪7", "#1", "b2", "♭2": return ctx.curKeyCenter + 1
|
||||
of "𝄪1", "2", "𝄫3": return ctx.curKeyCenter + 2
|
||||
of "#2", "b3", "♭3", "𝄫4": return ctx.curKeyCenter + 3
|
||||
of "𝄪2", "3", "b4", "♭4": return ctx.curKeyCenter + 4
|
||||
of "#3", "4", "𝄫5": return ctx.curKeyCenter + 5
|
||||
of "𝄪3", "#4", "b5", "♭5": return ctx.curKeyCenter + 6
|
||||
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.")
|
||||
|
||||
# see regexr.com/70nv1
|
||||
let CHORD_REGEX =
|
||||
"([A-G1-7][b#♭♮𝄫𝄪]?)" & # chord root
|
||||
"([b#♭♮𝄫𝄪]?[A-G1-7][b#♭♮𝄫𝄪]?)" & # chord root
|
||||
"(([mM1-9#b♭♮𝄫𝄪Δ+oøoø][0-9]?|min|maj|aug|dim|sus|6\\/9|\\([1-9#b♭]+\\))*)" & # chord flavor/type
|
||||
"(\\/([A-G1-7][b#♭♮𝄫𝄪]?))?" # optional bass
|
||||
"(\\/([b#♭♮𝄫𝄪]?[A-G1-7][b#♭♮𝄫𝄪]?))?" # optional bass
|
||||
let CHORD_PAT = re(CHORD_REGEX)
|
||||
|
||||
proc parseChord*(
|
||||
@ -413,7 +458,7 @@ proc parseBody(ctx: var ParserContext): seq[ChordChartNode] =
|
||||
m = line.match(REDEFINE_KEY_PAT)
|
||||
if m.isSome:
|
||||
let newKeyInt = (
|
||||
cast[int](ctx.curKeyCenter) +
|
||||
ord(ctx.curKeyCenter) +
|
||||
parseInt(m.get.captures[0])
|
||||
) mod 12
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
const PCO_CHORDS_VERSION* = "0.5.0"
|
||||
const PCO_CHORDS_VERSION* = "0.5.1"
|
||||
|
||||
const USAGE* = """Usage:
|
||||
pco_chords [options]
|
||||
|
@ -113,9 +113,9 @@ func scaleDegree(root: ChordChartPitch, p: ChordChartPitch): string =
|
||||
else: raise newException(Exception, "Impossible")
|
||||
|
||||
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
|
||||
|
||||
elif useNumber:
|
||||
if useNumber:
|
||||
result = "<span class=root>" &
|
||||
ctx.currentKey.scaleDegree(chord.rootPitch) & "</span>"
|
||||
|
||||
@ -127,11 +127,15 @@ func format(ctx: FormatContext, chord: ChordChartChord, useNumber = false): stri
|
||||
ctx.currentKey.scaleDegree(chord.bassPitch.get) & "</span>"
|
||||
|
||||
else:
|
||||
result = $chord.rootPitch
|
||||
if chord.flavor.isSome: result &= chord.flavor.get
|
||||
if chord.bassPitch.isSome: result &= "/" & $chord.bassPitch.get
|
||||
#debug "transposed " & $ctx.sourceKey & " -> " & $ctx.currentKey &
|
||||
# " (distance " & $distance & ")\p\tchord: " & $chord & " to " & result
|
||||
result = "<span class=root>" &
|
||||
renderPitchInKey(chord.rootPitch, ctx.currentKey) & "</span>"
|
||||
|
||||
if chord.flavor.isSome:
|
||||
result &= "<span class=flavor>" & chord.flavor.get & "</span>"
|
||||
|
||||
if chord.bassPitch.isSome:
|
||||
result &= "<span class=bass>/" &
|
||||
renderPitchInKey(chord.bassPitch.get, ctx.currentKey) & "</span>"
|
||||
|
||||
proc transpose(ctx: FormatContext, chord: ChordChartChord): ChordChartChord =
|
||||
result = chord
|
||||
|
Loading…
x
Reference in New Issue
Block a user