// abc2svg - roman.js - convert the chord symbols to the RNN
// (Roman Numeral Notation)
//
// Copyright (C) 2021-2023 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg. If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded by %%roman.
//
// Parameters
// %%roman int
// <int>
// = '1': use uppercase letters with 'm' for minor chords
// = '2': user lowercase letters for minor chords
if (typeof abc2svg == "undefined")
var abc2svg = {}
abc2svg.roman = {
note_nm: "CDEFGAB",
// chord names
nm_M: ["I", "♯I", "II", "♭III", "III", "IV", "♯IV",
"V", "♯V", "VI", "♭VII", "VII"],
nm_m: ["I", "♭II", "II", "III", "♯III", "IV", "♭V",
"V", "VI", "♯VI", "VII", "♯VII"],
// inversions: 1st: (upper)6, 2nd: (upper)6 (lower) 4
gch_build: function(of, s) {
var gch, ix, t,
ty = this.cfmt().roman
// transpose the chord back to "C"
function set_nm(p) {
var i, o, o2, a, n,
csa = []
i = p.indexOf('/') // get the bass if any
while (i > 0) {
if (p[i - 1] != '<') // if not </tag
break
i = p.indexOf('/', i + 1)
}
if (i < 0) {
csa.push(p)
} else {
csa.push(p.slice(0, i))
csa.push(p.slice(i + 1))
}
for (i = 0; i < csa.length; i++) { // main and optional bass
p = csa[i]
o = p.search(/[A-G]/) // get the base chord
if (o < 0)
continue // not a chord!
a = 0
o2 = o + 1
if (p[o2] == '#' || p[o2] == '♯') {
a++
o2++
} else if (p[o2] == 'b' || p[o2] == '♭') {
a--
o2++
}
n = [0, 2, 4, 5, 7, 9, 11]
[abc2svg.roman.note_nm.indexOf(p[o])]
+ a
+ abc2svg.roman.tr
n = abc2svg.roman.nm[n % 12]
if (ty == 2 && p[o2] == 'm') {
n = n.toLowerCase()
o2++
}
csa[i] = p.slice(0, o)
+ n
+ p.slice(o2)
}
return csa.join('/')
} // set_nm()
if (ty && s.a_gch) {
for (ix = 0; ix < s.a_gch.length; ix++) {
gch = s.a_gch[ix]
t = gch.text
if (gch.type == 'g' && t && t[0] != 'N') {
t = set_nm(t)
if (t)
gch.text = t
}
}
}
of(s)
}, // gch_build()
// hook before the generation
o_mus: function(of) {
var tsfirst = this.get_tsfirst()
if (this.cfmt().roman) {
abc2svg.roman.tr = (tsfirst.p_v.key.k_sf + 12) * 5
// transposition to "C"
if (tsfirst.p_v.key.k_mode) {
abc2svg.roman.tr += 3 // minor
abc2svg.roman.nm = abc2svg.roman.nm_m
} else {
abc2svg.roman.nm = abc2svg.roman.nm_M
}
}
of()
}, // o_mus()
set_fmt: function(of, cmd, parm) {
var ty,
cfmt = this.cfmt()
if (cmd == "roman") {
if (!parm)
parm = "1"
ty = Number(parm)
if (isNaN(ty))
this.syntax(1, this.errs.bad_val, "%%roman")
else
cfmt.roman = ty
return
}
of(cmd, parm)
}, // set_fmt()
set_hooks: function(abc) {
abc.gch_build = abc2svg.roman.gch_build.bind(abc, abc.gch_build)
abc.output_music = abc2svg.roman.o_mus.bind(abc, abc.output_music)
abc.set_format = abc2svg.roman.set_fmt.bind(abc, abc.set_format)
} // set_hooks()
} // roman
if (!abc2svg.mhooks)
abc2svg.mhooks = {}
abc2svg.mhooks.roman = abc2svg.roman.set_hooks