chordsong
Version:
ChordSong is a simple text format for the notation of lyrics with guitar chords, and an application that renders them to portable HTML pages.
107 lines (91 loc) • 3.42 kB
text/typescript
import { Diagram } from './Diagram'
import { readFileSync } from 'fs'
import { join } from 'path'
type StringType = 'open' | 'muted' | 'played'
export interface SvgDiagramOptions {
fretWidth?: number
fretHeight?: number
stringLineColor?: string
stringLineWidth?: number
fretLineColor?: string
fretLineWidth?: number
fingerOrBarreWidth?: number
fingerOrBarreLineWidth?: number
fingerOrBarreLineColor?: string
openStringSymbolColor?: string
openStringSymbolLineColor?: string
}
export class SvgDiagram {
static svgDefs: string = readFileSync(join(__dirname, 'chord-defs.svg'), { encoding: 'utf8' })
diagram: Diagram
constructor (diagram: Diagram, options?: SvgDiagramOptions) {
this.diagram = diagram
}
get openStrings (): number[] {
return this.diagram.fretStrings['0'] ?? []
}
get mutedStrings (): number[] {
return this.diagram.fretStrings['-1'] ?? []
}
get playedStrings (): number[] {
const playedStrings: number[] = []
this.diagram.stringFrets.forEach((fret, string) => {
if (fret > 0) { playedStrings.push(string) }
})
return playedStrings
}
svg (showVariant?: boolean): string {
let minFret: number = this.diagram.minFret
// make sure there are at most five frets
if (this.diagram.maxFret - minFret > 4) minFret = this.diagram.maxFret - 4
// don't capo if a chord is within the first five frets
if (this.diagram.maxFret <= 5) minFret = 1
return `
<svg width="64" height="64">
${this.svgTitle(showVariant)}
<g class="chord-chord" transform="translate(8.5,13.5)">
<use href="#chord-diapason" x="0" y="0" />
${this.svgNut(minFret)}
${this.svgMinFretText(minFret)}
${this.svgFingersOrBarres(minFret)}
${this.svgStrings('muted')}
${this.svgStrings('open')}
${this.svgStrings('played')}
</g>
</svg>`
}
private svgTitle (showVariant?: boolean): string {
let title = ''
if (this.diagram.chordName !== undefined) {
title = this.diagram.chordName
if (showVariant === true && this.diagram.chordVariant !== undefined) {
title = `${title}:${this.diagram.chordVariant}`
}
}
return (title !== '') ? `<text class="chord-name" x="26" y="0" dominant-baseline="hanging" text-anchor="middle">${title}</text>` : ''
}
private svgNut (minFret: number): string {
return (minFret > 1) ? '' : '<use href="#chord-nut" x="0" y="0" />'
}
private svgMinFretText (minFret: number): string {
return (minFret > 1)
? `<text class="chord-min-fret" x="38" y="6.5" dominant-baseline="middle">${String(minFret)}</text>`
: ''
}
private svgFingersOrBarres (minFret: number): string {
let ret = ''
this.diagram.fingersAndBarrels.forEach(barre => {
const barreLength = barre.stringTo - barre.stringFrom + 1
ret += `<use href="#chord-barre${barreLength}" x="${barre.stringFrom * 7}" y="${(barre.fret - minFret) * 8}" />`
})
return `<g class="chord-fingers-and-barres" transform="translate(-2,4)">${ret}</g>`
}
private svgStrings (stringType: StringType): string {
let ret = ''
const shape = (stringType === 'played') ? 'barre1' : stringType
this[`${(stringType)}Strings`].forEach((chordString: number) => {
ret += `<use href="#chord-${shape}" x="${chordString * 7}" y="0" />`
})
return `<g class="chord-${stringType}-strings" transform="translate(-2,46)">${ret}</g>`
}
}