Compare commits
7 Commits
9741c55cee
...
0.6.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 18bc7d25ac | |||
| 7ff6dde168 | |||
| 8d71f86034 | |||
| 4abc299ae9 | |||
| 2f561e5047 | |||
| dee33ac299 | |||
| 97ef8a7491 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
pco_chords
|
pco_chords
|
||||||
|
src/pco_chordspkg/notation
|
||||||
.*.sw?
|
.*.sw?
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "0.6.0"
|
version = "0.6.2"
|
||||||
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"
|
||||||
|
|||||||
@@ -186,12 +186,12 @@ proc parseChord*(
|
|||||||
|
|
||||||
let bass =
|
let bass =
|
||||||
if m.get.captures.contains(4) and m.get.captures[4].len > 0:
|
if m.get.captures.contains(4) and m.get.captures[4].len > 0:
|
||||||
some(toScaleDegree(ctx.curKey, parseSpelledPitch(m.get.captures[4])))
|
some(parseSpelledPitchOrScaleDegree(ctx.curKey, m.get.captures[4]))
|
||||||
else: none[ScaleDegree]()
|
else: none[ScaleDegree]()
|
||||||
|
|
||||||
return some(ChordChartChord(
|
return some(ChordChartChord(
|
||||||
original: some(chordValue),
|
original: some(chordValue),
|
||||||
root: toScaleDegree(ctx.curKey, parseSpelledPitch(m.get.captures[0])),
|
root: parseSpelledPitchOrScaleDegree(ctx.curKey, m.get.captures[0]),
|
||||||
flavor: flavor,
|
flavor: flavor,
|
||||||
bass: bass))
|
bass: bass))
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const PCO_CHORDS_VERSION* = "0.6.0"
|
const PCO_CHORDS_VERSION* = "0.6.2"
|
||||||
|
|
||||||
const USAGE* = """Usage:
|
const USAGE* = """Usage:
|
||||||
pco_chords [options]
|
pco_chords [options]
|
||||||
|
|||||||
@@ -112,9 +112,11 @@ const LARGE_PRINT_STYLESHEET* = """
|
|||||||
|
|
||||||
html {
|
html {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
font-size: 1.5em;
|
font-size: 1.3em;
|
||||||
|
font-variant: small-caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-contents { line-height: 1.1; }
|
||||||
.one-column .page-contents { column-count: 1; }
|
.one-column .page-contents { column-count: 1; }
|
||||||
.two-column .page-contents { column-count: 2; }
|
.two-column .page-contents { column-count: 2; }
|
||||||
|
|
||||||
@@ -157,7 +159,7 @@ h3 .section-text {
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.word.space-after { margin-right: 0.5em; }
|
.word.space-after { margin-right: 0.3em; }
|
||||||
.word.no-chord { align-self: flex-end; }
|
.word.no-chord { align-self: flex-end; }
|
||||||
|
|
||||||
.lyric {
|
.lyric {
|
||||||
@@ -165,8 +167,9 @@ h3 .section-text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chord {
|
.chord {
|
||||||
|
font-size: 125%;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 500;
|
font-weight: 700;
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,8 +312,8 @@ proc toHtml*(
|
|||||||
numberChart: numberChart,
|
numberChart: numberChart,
|
||||||
transposeSteps: transpose)
|
transposeSteps: transpose)
|
||||||
|
|
||||||
debug "Formatting:\p\tsource key: " & $ctx.sourceKey & "\p\ttransposing: " &
|
debug "Formatting:\p\tsource key: $#\p\ttransposing: $#\p\ttarget key: $#" %
|
||||||
$transpose & "\p\ttarget key: " & $ctx.currentKey
|
[$ctx.sourceKey, $transpose, $ctx.currentKey]
|
||||||
|
|
||||||
result = """<!doctype html>
|
result = """<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
|
|||||||
Binary file not shown.
@@ -207,7 +207,7 @@ func toNote*(p: Pitch): Note =
|
|||||||
of Df, D: Note.D
|
of Df, D: Note.D
|
||||||
of Ef, E: Note.E
|
of Ef, E: Note.E
|
||||||
of F: Note.F
|
of F: Note.F
|
||||||
of Gf, G: Note.D
|
of Gf, G: Note.G
|
||||||
|
|
||||||
|
|
||||||
func toSpelledPitch*(p: Pitch): SpelledPitch =
|
func toSpelledPitch*(p: Pitch): SpelledPitch =
|
||||||
@@ -270,26 +270,66 @@ func ionianPitch*(key: Key, degreeNumber: int): Pitch =
|
|||||||
cast[Pitch]((ord(key.tonic.toPitch) + MajorIntervals[degreeNumber - 1]) mod 12)
|
cast[Pitch]((ord(key.tonic.toPitch) + MajorIntervals[degreeNumber - 1]) mod 12)
|
||||||
|
|
||||||
|
|
||||||
func spellPitch*(key: Key, sd: ScaleDegree): SpelledPitch =
|
func spellPitch*(k: Key, sd: ScaleDegree): SpelledPitch =
|
||||||
## Given a key and scale degree, spell it correctly in that key. For example,
|
## Given a key and scale degree, spell it correctly in that key. For example,
|
||||||
## the ♭7 in C major is spelled B♭, the ♭7 in key of Db Locrian is spelled
|
## the ♭7 in C major is spelled B♭, the ♭7 in key of Db Locrian is spelled
|
||||||
## B𝄫, and the ♭7 in F# major is E.
|
## B𝄫, and the ♭7 in F# major is E.
|
||||||
result.note = key.tonic.note + (sd.number - 1)
|
result.note = k.tonic.note + (sd.number - 1)
|
||||||
let resultingPitch = ord(ionianPitch(key, sd.number)) + ord(sd.alteration)
|
let resultingPitch = ord(ionianPitch(k, sd.number)) + ord(sd.alteration)
|
||||||
result.alteration = cast[NoteAlteration](
|
result.alteration = cast[NoteAlteration](
|
||||||
resultingPitch - ord(result.note.toPitch))
|
resultingPitch - ord(result.note.toPitch))
|
||||||
|
#[
|
||||||
|
debugEcho "Spelling " & $sd & " in the key of " & $k & ":\n" &
|
||||||
|
"\tsd.alteration: " & $ord(sd.alteration) &
|
||||||
|
"\tkey.tonic.note: " & $k.tonic.note &
|
||||||
|
"\tsd.number - 1: " & $(sd.number - 1) &
|
||||||
|
"\tionianPitch: " & $ionianPitch(k, sd.number) &
|
||||||
|
"\tord(ionianPitch): " & $ord(ionianPitch(k, sd.number)) &
|
||||||
|
"\talteration: " & $ord(sd.alteration) &
|
||||||
|
"\tresultingPitch: " & $resultingPitch &
|
||||||
|
"\tresult.note: " & $(result.note) &
|
||||||
|
"\tresult.alteration: " & $ord(result.alteration)
|
||||||
|
]#
|
||||||
|
|
||||||
|
|
||||||
func toScaleDegree*(key: Key, sp: SpelledPitch): ScaleDegree =
|
func toScaleDegree*(k: Key, sp: SpelledPitch): ScaleDegree =
|
||||||
## Determine the ScaleDegree of a pitch according to how it is spelled and
|
## Determine the ScaleDegree of a pitch according to how it is spelled and
|
||||||
## the key it is in. For example, Pitch.B will be the ♮2 in the key of A
|
## the key it is in. For example, Pitch.B will be the ♮2 in the key of A
|
||||||
## major, the ♭2 in the key of A# major, or the #1 in the key of B♭ major.
|
## major, the ♭2 in the key of A# major, or the #1 in the key of B♭ major.
|
||||||
result.number = sp.note - key.tonic.note + 1
|
result.number = sp.note - k.tonic.note + 1
|
||||||
result.alteration = cast[NoteAlteration](
|
var distance = ord(sp.toPitch) - ord(ionianPitch(k, result.number))
|
||||||
ord(sp.toPitch) - ord(ionianPitch(key, result.number)))
|
if distance < -2: distance += 12
|
||||||
|
elif distance > 2: distance -= 12
|
||||||
|
result.alteration = cast[NoteAlteration](distance)
|
||||||
|
#[
|
||||||
|
debugEcho "toScaleDegree: " & $sp & " in the key of " & $k & ":\n" &
|
||||||
|
"\tsp.note: " & $sp.note &
|
||||||
|
"\tkey.tonic.note: " & $k.tonic.note &
|
||||||
|
"\tresult.number: " & $result.number &
|
||||||
|
"\tsp.toPitch: " & $sp.toPitch &
|
||||||
|
"\tionianPitch: " & $ionianPitch(k, result.number) &
|
||||||
|
"\tord(sp.toPitch): " & $ord(sp.toPitch) &
|
||||||
|
"\tord(ionianPitch): " & $ord(ionianPitch(k, result.number)) &
|
||||||
|
"\tresult.alteration: " & $ord(result.alteration)
|
||||||
|
]#
|
||||||
|
|
||||||
|
|
||||||
|
func parseSpelledPitchOrScaleDegree*(key: Key, str: string): ScaleDegree =
|
||||||
|
try:
|
||||||
|
let pitch =parseSpelledPitch(str)
|
||||||
|
return toScaleDegree(key, pitch)
|
||||||
|
except:
|
||||||
|
try: return parseScaleDegree(str)
|
||||||
|
except:
|
||||||
|
raise newException(ValueError,
|
||||||
|
str & " is not a recognized pitch or scale degree")
|
||||||
|
|
||||||
|
|
||||||
func transpose*(k: Key, steps: int): Key =
|
func transpose*(k: Key, steps: int): Key =
|
||||||
|
#[
|
||||||
|
debugEcho "transpose: $# by $# steps, resulting in $# ($#)" %
|
||||||
|
[$k, $steps, $(k.tonic.toPitch + steps), $toSpelledPitch(k.tonic.toPitch + steps)]
|
||||||
|
]#
|
||||||
Key(
|
Key(
|
||||||
tonic: toSpelledPitch(k.tonic.toPitch + steps),
|
tonic: toSpelledPitch(k.tonic.toPitch + steps),
|
||||||
mode: k.mode)
|
mode: k.mode)
|
||||||
@@ -347,3 +387,63 @@ when isMainModule:
|
|||||||
assert "#5" == $toScaleDegree(
|
assert "#5" == $toScaleDegree(
|
||||||
Key(tonic: parseSpelledPitch("Eb"), mode: Ionian),
|
Key(tonic: parseSpelledPitch("Eb"), mode: Ionian),
|
||||||
parseSpelledPitch("B"))
|
parseSpelledPitch("B"))
|
||||||
|
|
||||||
|
assert toSpelledPitch(Pitch.F + 2) ==
|
||||||
|
SpelledPitch(note: Note.G, alteration: naNatural)
|
||||||
|
|
||||||
|
let pitchCases = [
|
||||||
|
(Pitch.Af, SpelledPitch(note: Note.A, alteration: naFlat)),
|
||||||
|
(Pitch.A, SpelledPitch(note: Note.A, alteration: naNatural)),
|
||||||
|
(Pitch.Bf, SpelledPitch(note: Note.B, alteration: naFlat)),
|
||||||
|
(Pitch.B, SpelledPitch(note: Note.B, alteration: naNatural)),
|
||||||
|
(Pitch.C, SpelledPitch(note: Note.C, alteration: naNatural)),
|
||||||
|
(Pitch.Df, SpelledPitch(note: Note.D, alteration: naFlat)),
|
||||||
|
(Pitch.D, SpelledPitch(note: Note.D, alteration: naNatural)),
|
||||||
|
(Pitch.Ef, SpelledPitch(note: Note.E, alteration: naFlat)),
|
||||||
|
(Pitch.E, SpelledPitch(note: Note.E, alteration: naNatural)),
|
||||||
|
(Pitch.F, SpelledPitch(note: Note.F, alteration: naNatural)),
|
||||||
|
(Pitch.Gf, SpelledPitch(note: Note.G, alteration: naFlat)),
|
||||||
|
(Pitch.G, SpelledPitch(note: Note.G, alteration: naNatural)),
|
||||||
|
]
|
||||||
|
for (pitch, expected) in pitchCases:
|
||||||
|
assert toSpelledPitch(pitch) == expected
|
||||||
|
|
||||||
|
let spelledPitchCases = [
|
||||||
|
("Ab", Pitch.Af),
|
||||||
|
("G#", Pitch.Af),
|
||||||
|
("A", Pitch.A),
|
||||||
|
("G##", Pitch.A),
|
||||||
|
("Bbb", Pitch.A),
|
||||||
|
("Bb", Pitch.Bf),
|
||||||
|
("A#", Pitch.Bf),
|
||||||
|
("Cbb", Pitch.Bf),
|
||||||
|
("B", Pitch.B),
|
||||||
|
("Cb", Pitch.B),
|
||||||
|
("A##", Pitch.B),
|
||||||
|
("C", Pitch.C),
|
||||||
|
("B#", Pitch.C),
|
||||||
|
("Dbb", Pitch.C),
|
||||||
|
("Db", Pitch.Df),
|
||||||
|
("C#", Pitch.Df),
|
||||||
|
("B##", Pitch.Df),
|
||||||
|
("D", Pitch.D),
|
||||||
|
("C##", Pitch.D),
|
||||||
|
("Ebb", Pitch.D),
|
||||||
|
("Eb", Pitch.Ef),
|
||||||
|
("D#", Pitch.Ef),
|
||||||
|
("Fbb", Pitch.Ef),
|
||||||
|
("E", Pitch.E),
|
||||||
|
("Fb", Pitch.E),
|
||||||
|
("D##", Pitch.E),
|
||||||
|
("F", Pitch.F),
|
||||||
|
("E#", Pitch.F),
|
||||||
|
("Gbb", Pitch.F),
|
||||||
|
("Gb", Pitch.Gf),
|
||||||
|
("F#", Pitch.Gf),
|
||||||
|
("E##", Pitch.Gf),
|
||||||
|
("G", Pitch.G),
|
||||||
|
("F##", Pitch.G),
|
||||||
|
("Abb", Pitch.G),
|
||||||
|
]
|
||||||
|
for (spelled, expected) in spelledPitchCases:
|
||||||
|
assert parseSpelledPitch(spelled).toPitch == expected
|
||||||
|
|||||||
Reference in New Issue
Block a user