view release on metacpan or search on metacpan
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
bmlFNEUxB3VuaUU0RTIHdW5pRTRFMwd1bmlFNEU0B3VuaUU0RTUHdW5pRTRFNgd1bmlFNEU3B3Vu\
aUU0RTgHdW5pRTRFOQd1bmlFNEVBB3VuaUU1MDAHdW5pRTUwMQd1bmlFNTIwB3VuaUU1MjEHdW5p\
RTUyMgd1bmlFNTIzB3VuaUU1MjQHdW5pRTUyNQd1bmlFNTI5B3VuaUU1MkEHdW5pRTUyQgd1bmlF\
NTJDB3VuaUU1MkQHdW5pRTUyRgd1bmlFNTMwB3VuaUU1MzEHdW5pRTUzOQd1bmlFNTY2B3VuaUU1\
NjcHdW5pRTU2OQd1bmlFNTZDB3VuaUU1NkQHdW5pRTU4Mgd1bmlFNUQwB3VuaUU1RTIHdW5pRTYx\
MAd1bmlFNjEyB3VuaUU2MTQHdW5pRTYxOAd1bmlFNjI0B3VuaUU2MzAHdW5pRTY1MAd1bmlFNjU1\
B3VuaUU5MTAHdW5pRTkxMQd1bmlFOTEyB3VuaUU5MTQHdW5pRTkxNQd1bmlFOTE4B3VuaUU5NUQH\
dW5pRUEwMgd1bmlFQUE0B3VuaUVDQTIHdW5pRUNBMwd1bmlFQ0E1B3VuaUVDQTcHdW5pRUNBOQd1\
bmlFQ0I3AAAAAAH//wACAAEAAAAAAAAADAAUAAQAAAACAAAAAQAAAAEAAAAAAAEAAAAA44To7gAA\
AADRlyIXAAAAAOPSSjM=\
") format("truetype")'
// abc2svg - format.js - formatting functions
//
// Copyright (C) 2014-2025 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core 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-core 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-core. If not, see <http://www.gnu.org/licenses/>.
var font_scale_tb = {
serif: 1,
serifBold: 1,
'sans-serif': 1,
'sans-serifBold': 1,
Palatino: 1.1,
monospace: 1
},
txt_ff = "text,serif", // text font-family (serif for compatibility)
ff = {}, // font-face's from %%beginsvg
fmt_lock = {}
var cfmt = {
"abc-version": "1", // default: old version
annotationfont: {name: "text,sans-serif", size: 12},
aligncomposer: 1,
beamslope: .4, // max slope of a beam
// botmargin: .7 * IN, // != 1.8 * CM,
bardef: {
"[": "", // invisible
"[]": "",
"|:": "[|:",
"|::": "[|::",
"|:::": "[|:::",
":|": ":|]",
"::|": "::|]",
":::|": ":::|]",
"::": ":][:"
},
breaklimit: .7,
breakoneoln: true,
cancelkey: true,
composerfont: { name: txt_ff, style: "italic", size: 14 },
composerspace: 6,
// contbarnb: false,
decoerr: true,
dynalign: true,
footerfont: { name: txt_ff, size: 16 },
fullsvg: '',
gchordfont: { name: "text,sans-serif", size: 12 },
gracespace: new Float32Array([6, 8, 11]), // left, inside, right
graceslurs: true,
headerfont: { name: txt_ff, size: 16 },
historyfont: { name: txt_ff, size: 16 },
hyphencont: true,
indent: 0,
infofont: {name: txt_ff, style: "italic", size: 14 },
infoname: 'R "Rhythm: "\n\
B "Book: "\n\
S "Source: "\n\
D "Discography: "\n\
N "Notes: "\n\
Z "Transcription: "\n\
H "History: "',
infospace: 0,
keywarn: true,
leftmargin: 1.4 * CM,
lineskipfac: 1.1,
linewarn: true,
maxshrink: .65, // nice scores
maxstaffsep: 2000,
maxsysstaffsep: 2000,
measrepnb: 1,
measurefont: {name: txt_ff, style: "italic", size: 10},
measurenb: -1,
musicfont: {name: "music", src: musicfont, size: 24},
musicspace: 6,
// notespacingfactor: "1.3, 38",
partsfont: {name: txt_ff, size: 15},
parskipfac: .4,
partsspace: 8,
// pageheight: 29.7 * CM,
pagewidth: 21 * CM,
"propagate-accidentals": "o", // octave
printmargin: 0,
rightmargin: 1.4 * CM,
rbmax: 4,
rbmin: 2,
repeatfont: {name: txt_ff, size: 9},
scale: 1,
slurheight: 1.0,
spatab: // spacing table (see "notespacingfactor" and set_space())
new Float32Array([ // default = "1.3, 38"
10.2, 13.3, 17.3, 22.48, 29.2,
38,
49.4, 64.2, 83.5, 108.5]),
staffsep: 46,
stemheight: 21, // one octave
stretchlast: .25,
stretchstaff: true,
subtitlefont: {name: txt_ff, size: 16},
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
case "systnames":
case "systvoices":
v = parseInt(param)
if (isNaN(v)) {
syntax(1, "Bad integer value");
break
}
if (cmd == "systnames") { // compatibility
switch (v) {
case -1: v = 3; break
case 1: v = 2; break
case 2: v = 1; break
}
cmd = "systvoices"
}
cfmt[cmd] = v
break
case "abc-version":
case "bgcolor":
case "fgcolor":
case "propagate-accidentals":
case "writeout-accidentals":
cfmt[cmd] = param
break
case "beamslope":
case "breaklimit": // float values
case "lineskipfac":
case "maxshrink":
case "pagescale":
case "parskipfac":
case "scale":
case "slurheight":
case "stemheight":
case "tieheight":
f = +param
if (isNaN(f) || !param || f < 0) {
syntax(1, errs.bad_val, '%%' + cmd)
break
}
switch (cmd) {
case "scale": // old scale
f /= .75
case "pagescale":
if (f < .1)
f = .1 // smallest scale
cmd = "scale";
img.chg = true
break
}
cfmt[cmd] = f
break
case "annotationbox":
case "gchordbox":
case "measurebox":
case "partsbox":
param_set_font(cmd.replace("box", "font"), // font
"* * " + (get_bool(param) ? "box" : "nobox"))
break
case "altchord":
case "bstemdown":
case "breakoneoln":
case "cancelkey":
case "checkbars":
case "contbarnb":
case "custos":
case "decoerr":
case "flatbeams":
case "graceslurs":
case "graceword":
case "hyphencont":
case "keywarn":
case "linewarn":
case "squarebreve":
case "splittune":
case "straightflags":
case "stretchstaff":
case "timewarn":
case "titlecaps":
case "titleleft":
case "trimsvg":
cfmt[cmd] = get_bool(param)
break
case "dblrepbar":
param = ":: " + param
// fall thru
case "bardef": // %%bardef oldbar newbar
v = param.split(/\s+/)
if (v.length != 2) {
syntax(1, errs.bad_val, "%%bardef")
} else {
if (parse.ufmt)
cfmt.bardef = Object.create(cfmt.bardef) // new object
cfmt.bardef[v[0]] = v[1]
}
break
case "chordalias":
v = param.split(/\s+/)
if (!v.length)
syntax(1, errs.bad_val, "%%chordalias")
else
abc2svg.ch_alias[v[0]] = v[1] || ""
break
case "composerspace":
case "indent":
case "infospace":
case "maxstaffsep":
case "maxsysstaffsep":
case "musicspace":
case "partsspace":
case "staffsep":
case "subtitlespace":
case "sysstaffsep":
case "textspace":
case "titlespace":
case "topspace":
case "vocalspace":
case "wordsspace":
f = get_unit(param) // normally, unit in points - 72 DPI accepted
if (isNaN(f) || f < 0)
syntax(1, errs.bad_val, '%%' + cmd)
else
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
continue
case 'v':
dst += c2 + "\u030c" // caron
j = i + 2
continue
// case ',':
// dst += c2 + "\u0326" // comma below
// j = i + 2
// continue
case 'c':
dst += c2 + "\u0327" // cedilla
j = i + 2
continue
case ';':
dst += c2 + "\u0328" // ogonek
j = i + 2
continue
}
break
}
if (flag == 'w') // if lyrics line (w:)
dst += '\\' // keep the backslash
dst += c
j = i + 1
}
return dst + src.slice(j)
}
// ABC include
var include = 0
function do_include(fn) {
var file, parse_sav
if (!user.read_file) {
syntax(1, "No read_file support")
return
}
if (include > 2) {
syntax(1, "Too many include levels")
return
}
file = user.read_file(fn)
if (!file) {
syntax(1, "Cannot read file '$1'", fn)
return
}
include++;
parse_sav = clone(parse);
tosvg(fn, file);
parse_sav.state = parse.state;
parse_sav.ckey = parse.ckey
parse = parse_sav;
include--
}
// parse ABC code
function tosvg(in_fname, // file name
file, // file content
bol, eof) { // beginning/end of file
var i, c, eol, end,
select,
line0, line1,
last_info, opt, text, a, b, s,
pscom,
txt_add = '\n' // for "+:"
// check if a tune is selected
function tune_selected() {
var re, res,
i = file.indexOf('K:', bol)
if (i < 0) {
// syntax(1, "No K: in tune")
return false
}
i = file.indexOf('\n', i)
if (parse.select.test(file.slice(parse.bol, i)))
return true
re = /\n\w*\n/;
re.lastIndex = i;
res = re.exec(file)
if (res)
eol = re.lastIndex
else
eol = eof
return false
} // tune_selected()
// remove the comment at end of text
// if flag, handle the escape sequences
// if flag is 'w' (lyrics), keep the '\'s
function uncomment(src, flag) {
if (!src)
return src
var i = src.indexOf('%')
if (i == 0)
return ''
if (i > 0)
src = src.replace(/([^\\])%.*/, '$1')
.replace(/\\%/g, '%');
src = src.replace(/\s+$/, '')
if (flag && src.indexOf('\\') >= 0)
return cnv_escape(src, flag)
return src
} // uncomment()
// set the sequence showing the source and save it in sav.src
function set_src(stag, se) {
var r, t,
etag = ""
if (!se)
se = file.indexOf('\n\n', bol) // end of tune
if (se < 0)
se = eof
if (typeof stag != "object") { // set the tag after source
if (stag[0] != 'b' && stag[0] != 'a' && stag[0] != '+'
&& stag[0] != '*')
stag = 'b' + stag // default: source before
if (stag[1] != '<') // (if bool)
stag = stag[0] + "<pre>"
r = stag.match(/<\/?[^>]*>/g)
while (1) {
t = r.pop()
if (!t)
break
if (t[1] == '/' || t.slice(-2) == '/>')
r.pop() // skip this stop/start tag
else
etag += '</' + t.slice(1)
}
cfmt.show_source = stag = [stag, etag]
}
t = stag[0].slice(1)
+ clean_txt(file.slice(bol, se))
+ stag[1]
if (stag[0][0] == '+' && sav.src)
sav.src += t
else
sav.src = t
} // set_src()
function end_tune() {
parse.bol = bol // (for multi V:)
generate()
cfmt = sav.cfmt;
info = sav.info;
char_tb = sav.char_tb;
glovar = sav.glovar;
maps = sav.maps;
mac = sav.mac;
maci = sav.maci;
parse.tune_v_opts = null;
parse.scores = null;
parse.ufmt = false
delete parse.pq
init_tune()
img.chg = true;
set_page();
if (cfmt.show_source) {
user.img_out("</div>")
if (cfmt.show_source[0][0] == 'a')
user.img_out(sav.src)
}
} // end_tune()
// get %%voice
function do_voice(select, in_tune) {
var opt, bol
if (select == "end")
return // end of previous %%voice
// get the options
if (in_tune) {
if (!parse.tune_v_opts)
parse.tune_v_opts = {};
opt = parse.tune_v_opts
} else {
if (!parse.voice_opts)
parse.voice_opts = {};
opt = parse.voice_opts
}
opt[select] = []
while (1) {
bol = ++eol
if (file[bol] != '%')
break
eol = file.indexOf('\n', eol);
if (file[bol + 1] != line1)
continue
bol += 2
if (eol < 0)
text = file.slice(bol)
else
text = file.slice(bol, eol);
a = text.match(/\S+/)
switch (a[0]) {
default:
opt[select].push(uncomment(text, true))
continue
case "score":
case "staves":
case "tune":
case "voice":
bol -= 2
break
}
break
}
eol = parse.eol = bol - 1
} // do_voice()
// apply the options to the current tune
function tune_filter() {
var o, opts, j, pc, h,
i = file.indexOf('K:', bol)
i = file.indexOf('\n', i);
h = file.slice(parse.bol, i) // tune header
for (i in parse.tune_opts) {
if (!parse.tune_opts.hasOwnProperty(i))
continue
if (!(new RegExp(i)).test(h))
continue
opts = parse.tune_opts[i]
for (j = 0; j < opts.t_opts.length; j++) {
pc = opts.t_opts[j]
switch (pc.match(/\S+/)[0]) {
case "score":
case "staves":
if (!parse.scores)
parse.scores = [];
parse.scores.push(pc)
break
default:
self.do_pscom(pc)
break
}
}
opts = opts.v_opts
if (!opts)
continue
for (j in opts) {
if (!opts.hasOwnProperty(j))
continue
if (!parse.tune_v_opts)
parse.tune_v_opts = {};
if (!parse.tune_v_opts[j])
parse.tune_v_opts[j] = opts[j]
else
parse.tune_v_opts[j] =
parse.tune_v_opts[j].
concat(opts[j])
}
}
} // tune_filter()
// export functions and/or set module hooks
if (abc2svg.mhooks) {
for (i in abc2svg.mhooks) {
if (!modone[i]) {
modone[i] = 1 //true
abc2svg.mhooks[i](self)
}
}
}
// initialize
parse.file = file; // used for errors
parse.fname = in_fname
// scan the file
if (bol == undefined)
bol = 0
if (!eof)
eof = file.length
if (file.slice(bol, bol + 5) == "%abc-")
cfmt["abc-version"] = /[1-9.]+/.exec(file.slice(bol + 5, bol + 10))
for ( ; bol < eof; bol = parse.eol + 1) {
eol = file.indexOf('\n', bol) // get a line
if (eol < 0 || eol > eof)
eol = eof;
parse.eol = eol
// remove the ending white spaces
while (1) {
eol--
switch (file[eol]) {
case ' ':
case '\t':
continue
}
break
}
eol++
if (eol == bol) { // empty line
if (parse.state == 1) {
parse.istart = bol;
syntax(1, "Empty line in tune header - ignored")
} else if (parse.state >= 2) {
end_tune()
parse.state = 0
if (parse.select) { // skip to next tune
eol = file.indexOf('\nX:', parse.eol)
if (eol < 0)
eol = eof
parse.eol = eol
}
}
continue
}
parse.istart = parse.bol = bol;
parse.iend = eol;
parse.line.index = 0;
// check if the line is a pseudo-comment or I:
line0 = file[bol];
line1 = file[bol + 1]
if ((line0 == 'I' && line1 == ':')
|| line0 == '%') {
if (line0 == '%' && parse.prefix.indexOf(line1) < 0)
continue // comment
// change "%%abc xxxx" to "xxxx"
if (file[bol + 2] == 'a'
&& file[bol + 3] == 'b'
&& file[bol + 4] == 'c'
&& file[bol + 5] == ' ') {
bol += 6;
line0 = file[bol];
line1 = file[bol + 1]
} else {
pscom = true
}
}
// pseudo-comments
if (pscom) {
pscom = false;
bol += 2 // skip %%/I:
text = file.slice(bol, eol)
a = text.match(/([^\s]+)\s*(.*)/)
if (!a || a[1][0] == '%')
continue
switch (a[1]) {
case "abcm2ps":
case "ss-pref":
parse.prefix = a[2] // may contain a '%'
continue
case "abc-include":
do_include(uncomment(a[2]))
continue
}
// beginxxx/endxxx
if (a[1].slice(0, 5) == 'begin') {
b = a[1].substr(5);
end = '\n' + line0 + line1 + "end" + b;
i = file.indexOf(end, eol)
if (i < 0) {
syntax(1, "No $1 after %%$2",
end.slice(1), a[1]);
parse.eol = eof
continue
}
self.do_begin_end(b, uncomment(a[2]),
file.slice(eol + 1, i)
.replace(/\n%[^%].*$/gm,'')
.replace(/^%%/gm,''))
parse.eol = file.indexOf('\n', i + 6)
if (parse.eol < 0)
parse.eol = eof
continue
}
switch (a[1]) {
case "show_source":
b = uncomment(a[2])
switch (b[0]) {
case '*':
i = file.indexOf('\n' + line0 + line1
+ "show_source", eol)
bol -= 2 // keep %%show_.. in the source
set_src(b, i)
user.img_out(sav.src)
// fall thru
case '0':
b = ""
// fall thru
default:
cfmt[a[1]] = b
// fall thru
}
continue
case "select":
if (parse.state != 0) {
syntax(1, errs.not_in_tune, "%%select")
continue
}
select = uncomment(a[2])
if (select[0] == '"')
select = select.slice(1, -1);
if (!select) {
delete parse.select
continue
}
select = select.replace(/\(/g, '\\(');
select = select.replace(/\)/g, '\\)');
// select = select.replace(/\|/g, '\\|');
parse.select = new RegExp(select, 'm')
continue
case "tune":
if (parse.state != 0) {
syntax(1, errs.not_in_tune, "%%tune")
continue
}
select = uncomment(a[2])
// if void %%tune, free all tune options
if (!select) {
parse.tune_opts = {}
continue
}
if (select == "end")
continue // end of previous %%tune
if (!parse.tune_opts)
parse.tune_opts = {};
parse.tune_opts[select] = opt = {
t_opts: []
// v_opts: {}
};
while (1) {
bol = eol
if (file[bol + 1] != '%')
break
eol = file.indexOf('\n', eol + 1)
if (file[bol + 2] != line1)
continue
text = file.slice(bol + 3,
eol < 0 ? undefined : eol)
a = text.match(/([^\s]+)\s*(.*)/)
switch (a[1]) {
case "tune":
break
case "voice":
do_voice(uncomment(a[2],
true), true)
continue
default:
opt.t_opts.push(
uncomment(text, true))
continue
}
break
}
if (parse.tune_v_opts) {
opt.v_opts = parse.tune_v_opts;
parse.tune_v_opts = null
}
parse.eol = bol
continue
case "voice":
if (parse.state != 0) {
syntax(1, errs.not_in_tune, "%%voice")
continue
}
select = uncomment(a[2])
/* if void %%voice, free all voice options */
if (!select) {
parse.voice_opts = null
continue
}
do_voice(select)
continue
}
self.do_pscom(uncomment(text, true))
continue
}
// music line (or free text)
if (line1 != ':' || !/[A-Za-z+]/.test(line0)) {
last_info = undefined;
if (parse.state < 2)
continue
parse.line.buffer = uncomment(file.slice(bol, eol))
if (parse.line.buffer)
parse_music_line()
continue
}
// information fields
bol += 2
while (1) {
switch (file[bol]) {
case ' ':
case '\t':
bol++
continue
}
break
}
if (line0 == '+') {
if (!last_info) {
syntax(1, "+: without previous info field")
continue
}
txt_add = ' '; // concatenate
line0 = last_info
}
text = uncomment(file.slice(bol, eol), line0)
switch (line0) {
case 'X': // start of tune
if (parse.state != 0) {
syntax(1, errs.ignored, line0)
continue
}
if (parse.select
&& !tune_selected()) { // skip to the next tune
eol = file.indexOf('\nX:', parse.eol)
if (eol < 0)
eol = eof;
parse.eol = eol
continue
}
sav.cfmt = clone(cfmt);
sav.info = clone(info, 2) // (level 2 for info.V[])
sav.char_tb = clone(char_tb);
sav.glovar = clone(glovar);
sav.maps = clone(maps, 1);
sav.mac = clone(mac);
sav.maci = clone(maci);
if (cfmt.show_source) {
bol -= 2
set_src(cfmt.show_source)
if (cfmt.show_source[0][0] == 'b')
user.img_out(sav.src)
user.img_out('<div class="source">')
}
info.X = text;
parse.state = 1 // tune header
if (parse.tune_opts)
tune_filter()
continue
case 'T':
switch (parse.state) {
case 0:
continue
case 1:
case 2:
text = trim_title(text, info.T)
if (info.T == undefined) // (keep empty T:)
info.T = text
else
info.T += "\n" + text
continue
}
s = new_block("title");
s.text = text
continue
case 'K':
switch (parse.state) {
case 0:
continue
case 1: // tune header
info.K = text
break
}
do_info(line0, text)
continue
case 'W':
if (parse.state == 0
|| cfmt.writefields.indexOf(line0) < 0)
break
if (info.W == undefined)
info.W = text
else
info.W += txt_add + text
break
case 'm':
if (parse.state >= 2) {
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
// if (!p_voice.last_sym)
// p_voice.sym = null;
p_voice.time = s.time;
new_s = sym_add(p_voice, C.CUSTOS);
new_s.next = s;
s.prev = new_s;
new_s.wl = 0 // (needed here for lktsym)
new_s.wr = 4
lktsym(new_s, s);
new_s.shrink = s.shrink
if (new_s.shrink < 8 + 4)
new_s.shrink = 8 + 4;
new_s.space = s2.space;
new_s.head = C.FULL
new_s.stem = s2.stem
new_s.nhd = s2.nhd;
new_s.notes = []
for (i = 0; i < s2.notes.length; i++) {
new_s.notes[i] = {
pit: s2.notes[i].pit,
shhd: 0,
dur: C.BLEN / 4
}
}
new_s.stemless = true
}
/* -- define the beginning of a new music line -- */
function set_nl(s) { // s = start of line
var p_voice, done, tim, ptyp
// divide the left repeat (|:) or variant bars (|1)
// the new bars go in the next line
function bardiv(so) { // start of next line
var s, s1, s2, t1, t2, i
function new_type(s) {
var t = s.bar_type.match(/(:*)([^:]*)(:*)/)
// [1] = starting ':'s, [2] = middle, [3] = ending ':'s
if (!t[3]) { // if start of variant
// |1 -> | [1
// :|]1 -> :|] [1
t1 = t[1] + t[2]
t2 = '['
} else if (!t[1]) { // if left repeat only
// x|: -> || [|:
t1 = '||'
t2 = '[|' + t[3]
} else {
// :][: -> :|] [|:
i = (t[2].length / 2) | 0
t1 = t[1] + '|' + t[2].slice(0, i)
t2 = t[2].slice(i) +'|' + t[3]
}
} // new_typ()
// change or add a bar for the voice in the previous line
function eol_bar(s, // bar |:
so, // start of new line
sst) { // first bar (for seqst)
var s1, s2, s3
// check if a bar in the previous line
for (s1 = so.ts_prev ; s1.time == s.time; s1 = s1.ts_prev) {
if (s1.v != s.v)
continue
if (s1.bar_type) {
if (s1.bar_type != '|')
return // don't change
s2 = s1 // last symbol in previous line
break
}
if (!s3)
s3 = s1.next // possible anchor for the new bar
}
if (!s2) { // if no symbol in previous line
s2 = clone(s)
if (!s3)
s3 = s
s2.next = s3
s2.prev = s3.prev
if (s2.prev)
s2.prev.next = s2
s3.prev = s2
s2.ts_prev = so.ts_prev // time linkage
s2.ts_prev.ts_next = s2
s2.ts_next = so
so.ts_prev = s2
if (s == sst) // if first inserted bar
s2.seqst = 1 //true
if (s2.seqst) {
for (s = s2.ts_next; !s.seqst; s = s.ts_next)
;
s2.shrink = s.shrink
s.shrink = s2.wr + s.wl
s2.space = s.space
s.space = 0
}
delete s2.part
}
s2.bar_type = "||"
} // eol_bar()
// check if there is a left repeat bar at start of the new line
s = so // start of new music line
while (s && s.time == so.time) {
if (s.bar_type && s.bar_type.slice(-1) == ':') {
s2 = s
break
}
s = s.ts_next
}
if (s2) {
s = s2
while (1) { // loop on all voices
eol_bar(s2, so, s)
s2 = s2.ts_next
if (!s2 || s2.seqst)
break
}
return so
}
s = so
while (s.ts_prev
&& s.ts_prev.time == so.time) {
s = s.ts_prev
if (s.bar_type)
s1 = s // first previous bar
else if (!s1 && s.type == C.GRACE && s.seqst)
so = s // if grace note after a bar
// move the start of line
}
if (!s1
|| !s1.bar_type
|| (s1.bar_type.slice(-1) != ':'
&& !s1.text))
return so
// search the new start of the next line
for (so = s1; so.time == s1.time; so = so.ts_prev) {
switch (so.ts_prev.type) {
case C.KEY:
case C.METER:
// case C.PART:
case C.TEMPO:
case C.STAVES:
case C.STBRK:
continue
}
break
}
// put the new bar before the end of music line
s = s1 // keep first bar
while (1) {
new_type(s1)
s2 = clone(s1)
s2.bar_type = t1
s1.bar_type = t2
s2.ts_prev = so.ts_prev
s2.ts_prev.ts_next = s2
s2.ts_next = so
so.ts_prev = s2
if (s1 == s)
s2.seqst = 1 //true
s2.next = s1
if (s2.prev)
s2.prev.next = s2
s1.prev = s2
if (s1.rbstop)
s2.rbstop = s1.rbstop
if (s1.text) {
s1.invis = 1 //true
delete s1.xsh
delete s2.text
delete s2.rbstart
}
delete s2.part
delete s1.a_dd
delete s1.a_gch
do {
s1 = s1.ts_next
} while (!s1.seqst && !s1.bar_type)
if (s1.seqst)
break
}
return so
} // bardiv()
// set the start of line marker
function set_eol(s) {
if (cfmt.custos && voice_tb.length == 1)
custos_add(s)
s.nl = true
s = s.ts_prev
if (s.type != C.BAR)
add_end_bar(s)
} // set_eol()
// put the warning symbols
// the new symbols go in the previous line
function do_warn(s) { // start of next line
var s1, s2, s3, s4, w
// advance in the next line
for (s2 = s; s2 && s2.time == s.time; s2 = s2.ts_next) {
switch (s2.type) {
case C.KEY:
if (!s2.fmt.keywarn
|| s2.invis)
continue
for (s1 = s.ts_prev; s1 ;s1 = s1.ts_prev) {
if (s1.type != C.METER)
break
}
// fall thru
case C.METER:
if (s2.type == C.METER) {
if (!s.fmt.timewarn)
continue
s1 = s.ts_prev
}
// fall thru
case C.CLEF:
if (!s2.prev) // start of voice
continue
if (s2.type == C.CLEF) {
if (s2.clef_none) // if 'K: clef=none' after bar
break
for (s1 = s.ts_prev; s1; s1 = s1.ts_prev) {
switch (s1.type) {
case C.BAR:
if (s1.bar_type[0] == ':')
break
// fall thru
case C.KEY:
case C.METER:
continue
}
break
}
}
// put the warning symbol at end of line
s3 = clone(s2) // duplicate the K:/M:/clef
lktsym(s3, s1.ts_next) // time link
s1 = s3
while (1) {
s1 = s1.ts_next
if (s1.v == s2.v)
break
}
lkvsym(s3, s1) // voice link
// care with spacing
if (s3.seqst) {
self.set_width(s3)
s3.shrink = s3.wl
s4 = s3.ts_prev
w = 0
while (1) {
if (s4.wr > w)
w = s4.wr
if (s4.seqst)
break
s4 = s4.ts_prev
}
s3.shrink += w
s3.space = 0
s4 = s3
while (1) {
if (s4.ts_next.seqst)
break
s4 = s4.ts_next
}
w = 0
while (1) {
if (s4.wl > w)
w = s4.wl
s4 = s4.ts_next
if (s4.seqst)
break
}
s4.shrink = s3.wr + w
}
delete s3.part
continue
}
if (w_tb[s2.type])
break // symbol with a width
}
} // do_warn()
// divide the left repeat and variant bars
s = bardiv(s)
// add the warning symbols at the end of the previous line
do_warn(s)
/* if normal symbol, cut here */
if (s.ts_prev.type != C.STAVES) {
set_eol(s)
return s
}
/* go back to handle the staff breaks at end of line */
for (s = s.ts_prev; s; s = s.ts_prev) {
if (s.seqst && s.type != C.CLEF)
break
}
done = 0
ptyp = s.type
for ( ; ; s = s.ts_next) {
if (!s)
return s
if (s.type == ptyp)
continue
ptyp = s.type
if (done < 0)
break
switch (s.type) {
case C.STAVES:
if (!s.ts_prev)
return // null // no music yet
if (s.ts_prev.type == C.BAR)
break
while (s.ts_next) {
if (w_tb[s.ts_next.type]
&& s.ts_next.type != C.CLEF)
break
s = s.ts_next
}
if (!s.ts_next || s.ts_next.type != C.BAR)
continue
s = s.ts_next
// fall thru
case C.BAR:
if (done)
break
done = 1;
continue
case C.STBRK:
if (!s.stbrk_forced)
unlksym(s) /* remove */
else
done = -1 // keep the next symbols on the next line
continue
case C.CLEF:
if (done)
break
continue
default:
if (!done || (s.prev && s.prev.type == C.GRACE))
continue
break
}
break
}
set_eol(s)
return s
}
/* get the width of the starting clef and key signature */
// return
// r[0] = width of clef and key signature
// r[1] = width of the meter
function get_ck_width() {
var r0, r1,
p_voice = voice_tb[0]
self.set_width(p_voice.clef);
self.set_width(p_voice.ckey);
self.set_width(p_voice.meter)
return [p_voice.clef.wl + p_voice.clef.wr +
p_voice.ckey.wl + p_voice.ckey.wr,
p_voice.meter.wl + p_voice.meter.wr]
}
// get the width of the symbols up to the next soln or eof
// also, set a x (nice spacing) to all symbols
// two returned values: width of nice spacing, width with max shrinking
function get_width(s, next) {
var shrink, space,
w = 0,
wmx = 0,
sp_fac = (1 - s.fmt.maxshrink)
while (s != next) {
if (s.seqst) {
shrink = s.shrink
wmx += shrink
if ((space = s.space) < shrink)
w += shrink
else
w += shrink * s.fmt.maxshrink
+ space * sp_fac
s.x = w
}
s = s.ts_next
}
if (next)
wmx += next.wr // big key signatures may be wide enough
return [w, wmx]
}
/* -- search where to cut the lines according to the staff width -- */
function set_lines( s, /* first symbol */
next, /* symbol of the next line / null */
lwidth, /* w - (clef & key sig) */
indent) { /* for start of tune */
var first, s2, s3, s4, s5, x, xmin, xmid, xmax, wwidth, shrink, space,
nlines,
last = next ? next.ts_prev : null,
ws = get_width(s, next) // 2 widths: nice and shrunk
// split a big lyric word on two music lines
function ly_split(s, wmax) {
var i, wh,
s2 = clone(s),
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
return
}
if (c == '"') {
line.index++
break
}
s.text += c
}
}
// ']' as the first character indicates a repeat bar stop
if (bar_type[0] == ']') {
s.rbstop = 2 // with end
if (bar_type.length != 1)
bar_type = bar_type.slice(1)
else
s.invis = true
}
s.iend = parse.bol + line.index
if (s.text
&& bar_type.slice(-1) == '['
&& bar_type != '[')
bar_type = bar_type.slice(0, -1)
// there cannot be variants on a left repeat bar
if (bar_type.slice(-1) == ':') { // left repeat
s.rbstop = 1 // end the bracket
if (s.text) {
syntax(1, "Variant ending on a left repeat bar")
delete s.text
}
curvoice.tie_s_rep = null // no tie anymore on new variant
}
// handle the accidentals (ties and repeat)
if (s.text) {
s.rbstart = s.rbstop = 2
if (s.text[0] == '1') {
curvoice.tie_s_rep = curvoice.tie_s
if (curvoice.acc_tie)
curvoice.acc_tie_rep = curvoice.acc_tie.slice()
else if (curvoice.acc_tie_rep)
curvoice.acc_tie_rep = null
} else {
curvoice.tie_s = curvoice.tie_s_rep
if (curvoice.acc_tie_rep)
curvoice.acc_tie = curvoice.acc_tie_rep.slice()
}
if (curvoice.norepbra
&& !curvoice.second)
s.norepbra = 1 //true
}
if (curvoice.ulen < 0) // L:auto
adjust_dur(s);
// merge ":| |:" into "::" and other cases
if ((bar_type == "[" || bar_type == "|:")
&& !curvoice.eoln
&& !s.a_gch && !s.invis) { // no annotation nor invisible
s2 = curvoice.last_sym
// if the previous symbol is also a bar
if (s2 && s2.type == C.BAR) {
// && !s2.a_gch && !s2.a_dd
// && !s.a_gch && !s.a_dd) {
// remove the invisible variant bars
// when no shift is needed
if ((bar_type == "["
&& !s2.text)
|| s.norepbra) {
if (s.text) {
s2.text = s.text
if (curvoice.st && !s.norepbra
&& !(par_sy.staves[curvoice.st - 1]
.flags & STOP_BAR))
s2.xsh = 4 // volta shift
}
// if (s.a_gch)
// s2.a_gch = s.a_gch
if (s.norepbra)
s2.norepbra = 1 //true
if (s.rbstart)
s2.rbstart = s.rbstart
if (s.rbstop)
s2.rbstop = s.rbstop
//--fixme: pb when on next line and empty staff above
return
}
// merge back-to-back repeat bars
if (bar_type == "|:") {
switch (s2.bar_type) {
case ":|": // :| + |: => ::
s2.bar_type = "::";
s2.rbstop = 2
return
}
}
}
}
/* set some flags */
switch (bar_type) {
case "[":
case "[]":
case "[|]":
s.invis = true;
bar_type = s.rbstart ? "[" : "[]"
break
case ":|:":
case ":||:":
bar_type = "::"
break
case "||":
if (cfmt["abc-version"] >= "2.2")
break
// fall thru - play repeat on double bar when old ABC version
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
var c,
line = parse.line,
i = line.index, // in case no deco end
dcn = ""
while (1) {
c = line.next_char()
if (!c) {
line.index = i
syntax(1, "No end of decoration")
return
}
if (c == '!')
break
dcn += c
}
a_dcn.push(dcn)
} // get_deco()
// characters in the music line (ASCII only)
var nil = "0",
char_tb = [
nil, nil, nil, nil, /* 00 - .. */
nil, nil, nil, nil,
nil, " ", "\n", nil, /* . \t \n . */
nil, nil, nil, nil,
nil, nil, nil, nil,
nil, nil, nil, nil,
nil, nil, nil, nil,
nil, nil, nil, nil, /* .. - 1f */
" ", "!", '"', "i", /* (sp) ! " # */
"\n", nil, "&", nil, /* $ % & ' */
"(", ")", "i", nil, /* ( ) * + */
nil, "-", "!dot!", nil, /* , - . / */
nil, nil, nil, nil, /* 0 1 2 3 */
nil, nil, nil, nil, /* 4 5 6 7 */
nil, nil, "|", "i", /* 8 9 : ; */
"<", "n", "<", "i", /* < = > ? */
"i", "n", "n", "n", /* @ A B C */
"n", "n", "n", "n", /* D E F G */
"!fermata!", "d", "d", "d", /* H I J K */
"!emphasis!", "!lowermordent!",
"d", "!coda!", /* L M N O */
"!uppermordent!", "d",
"d", "!segno!", /* P Q R S */
"!trill!", "d", "d", "d", /* T U V W */
"n", "d", "n", "[", /* X Y Z [ */
"\\","|", "n", "n", /* \ ] ^ _ */
"i", "n", "n", "n", /* ` a b c */
"n", "n", "n", "n", /* d e f g */
"d", "d", "d", "d", /* h i j k */
"d", "d", "d", "d", /* l m n o */
"d", "d", "d", "d", /* p q r s */
"d", "!upbow!",
"!downbow!", "d", /* t u v w */
"n", "n", "n", "{", /* x y z { */
"|", "}", "!gmark!", nil, /* | } ~ (del) */
] // char_tb[]
function parse_music_line() {
var grace, last_note_sav, a_dcn_sav, no_eol, s, tps,
tp = [],
tpn = -1,
sls = [],
line = parse.line
// check if a transposing macro matches a source sequence
// if yes return the base note
function check_mac(m) {
var i, j, b
for (i = 1, j = line.index + 1; i < m.length; i++, j++) {
if (m[i] == line.buffer[j])
continue
if (m[i] != 'n') // search the base note
return //undefined
b = ntb.indexOf(line.buffer[j])
if (b < 0)
return //undefined
while (line.buffer[j + 1] == "'") {
b += 7;
j++
}
while (line.buffer[j + 1] == ',') {
b -= 7;
j++
}
}
line.index = j
return b
} // check_mac()
// convert a note as a number into a note as a ABC string
function n2n(n) {
var c = ''
while (n < 0) {
n += 7;
c += ','
}
while (n >= 14) {
n -= 7;
c += "'"
}
return ntb[n] + c
} // n2n()
// expand a transposing macro
function expand(m, b) {
if (b == undefined) // if static macro
return m
var c, i,
r = "", // result
n = m.length
for (i = 0; i < n; i++) {
c = m[i]
if (c >= 'h' && c <= 'z') {
r += n2n(b + c.charCodeAt(0) - 'n'.charCodeAt(0))
} else {
r += c
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
}
parse.line = line = line_sav
parse.istart = istart_sav
} // parse_mac()
// parse a music sequence
function parse_seq(in_mac) {
var c, idx, type, k, s, dcn, i, n, text, note
while (1) {
c = line.char()
if (!c)
break
// check if start of a macro
if (!in_mac && maci[c]) {
n = undefined
for (k in mac) {
if (!mac.hasOwnProperty(k)
|| k[0] != c)
continue
if (k.indexOf('n') < 0) {
if (line.buffer.indexOf(k, line.index)
!= line.index)
continue
line.index += k.length
} else {
n = check_mac(k)
if (n == undefined)
continue
}
parse_mac(k, mac[k], n)
n = 1
break
}
if (n)
continue
}
idx = c.charCodeAt(0)
if (idx >= 128) {
syntax(1, errs.not_ascii)
line.index++
break
}
type = char_tb[idx]
switch (type[0]) {
case ' ': // beam break
s = curvoice.last_note
if (s) {
s.beam_end = true
if (grace)
grace.gr_shift = true
}
break
case '\n': // line break
if (cfmt.barsperstaff)
break
curvoice.eoln = true
break
case '&': // voice overlay
if (grace) {
syntax(1, errs.bad_grace)
break
}
c = line.next_char()
if (c == ')') {
get_vover(c) // full overlay stop
break
}
get_vover('&')
continue
case '(': // slur start - tuplet - vover
c = line.next_char()
if (c > '0' && c <= '9') { // tuplet
if (grace) {
syntax(1, errs.bad_grace)
break
}
var pplet = line.get_int(),
qplet = qplet_tb[pplet],
rplet = pplet
c = line.char()
if (c == ':') {
c = line.next_char()
if (c > '0' && c <= '9') {
qplet = line.get_int();
c = line.char()
}
if (c == ':') {
c = line.next_char()
if (c > '0' && c <= '9') {
rplet = line.get_int();
c = line.char()
} else {
syntax(1, "Invalid 'r' in tuplet")
continue
}
}
}
if (qplet == 0 || qplet == undefined)
qplet = (curvoice.wmeasure % 9) == 0 ?
3 : 2;
if (tpn < 0)
tpn = tp.length // new tuplet
tp.push({
p: pplet,
q: qplet,
r: rplet,
ro: rplet,
f: curvoice.tup || cfmt.tuplets
})
continue
}
if (c == '&') { // voice overlay start
if (grace) {
syntax(1, errs.bad_grace)
break
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
curvoice.last_note = null;
a_dcn_sav = a_dcn;
a_dcn = []
grace = {
type: C.GRACE,
fname: parse.fname,
istart: parse.bol + line.index,
dur: 0,
multi: 0
}
if (curvoice.color)
grace.color = curvoice.color
switch (curvoice.pos.gst & 0x07) {
case C.SL_ABOVE: grace.stem = 1; break
case C.SL_BELOW: grace.stem = -1; break
case C.SL_HIDDEN: grace.stem = 2; break /* opposite */
}
sym_link(grace);
c = line.next_char()
if (c == '/') {
grace.sappo = true // acciaccatura
break
}
continue
case '|':
if (grace) {
syntax(1, errs.bar_grace)
break
}
new_bar()
continue
case '}':
if (curvoice.ignore) {
grace = null
break
}
s = curvoice.last_note
if (!grace || !s) {
syntax(1, errs.bad_char, c)
break
}
if (a_dcn.length)
syntax(1, "Decoration ignored");
grace.extra = grace.next;
grace.extra.prev = null;
grace.next = null;
curvoice.last_sym = grace;
grace = null
if (!s.prev // if one grace note
&& !curvoice.ckey.k_bagpipe) {
for (i = 0; i <= s.nhd; i++)
s.notes[i].dur *= 2;
s.dur *= 2;
s.dur_orig *= 2
}
curvoice.last_note = last_note_sav;
a_dcn = a_dcn_sav
break
case "\\":
if (!line.buffer[line.index + 1]) {
no_eol = true
break
}
// fall thru
default:
syntax(1, errs.bad_char, c)
break
}
line.index++
}
} // parse_seq()
if (parse.state != 3) // if not in tune body
return
if (parse.tp) {
tp = parse.tp
tpn = parse.tpn
tps = parse.tps
parse.tp = null
}
parse_seq()
if (tp.length) {
parse.tp = tp
parse.tps = tps
parse.tpn = tpn
}
if (sls.length)
syntax(1, "Start of slur without note")
if (grace) {
syntax(1, "No end of grace note sequence");
curvoice.last_sym = grace.prev;
curvoice.last_note = last_note_sav
if (grace.prev)
grace.prev.next = null
}
if (!no_eol && !cfmt.barsperstaff && !vover
&& char_tb['\n'.charCodeAt(0)] == '\n')
curvoice.eoln = true
if (curvoice.eoln && cfmt.breakoneoln && curvoice.last_note)
curvoice.last_note.beam_end = true
}
// abc2svg - subs.js - text output
//
// Copyright (C) 2014-2025 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core 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-core 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-core. If not, see <http://www.gnu.org/licenses/>.
// add font styles
var sheet
var add_fstyle = abc2svg.el
? function(s) {
var e
font_style += "\n" + s
if (!abc2svg.styles) {
e = document.createElement('style')
document.head.appendChild(e)
abc2svg.styles = e
}
sheet = abc2svg.styles.sheet
s = s.match(/[^{]+{[^}]+}/g) // insert each style
while (1) {
e = s.shift()
if (!e)
break
sheet.insertRule(e, sheet.cssRules.length)
}
} // add_fstyle()
: function(s) { font_style += "\n" + s }
// width of characters according to the font type
// these tables were created from the font 'Liberation'
// serif
var
sw_tb = new Float32Array([
.000,.000,.000,.000,.000,.000,.000,.000, // 00
.000,.000,.000,.000,.000,.000,.000,.000,
.000,.000,.000,.000,.000,.000,.000,.000, // 10
.000,.000,.000,.000,.000,.000,.000,.000,
.250,.333,.408,.500,.500,.833,.778,.333, // 20
.333,.333,.500,.564,.250,.564,.250,.278,
.500,.500,.500,.500,.500,.500,.500,.500, // 30
.500,.500,.278,.278,.564,.564,.564,.444,
.921,.722,.667,.667,.722,.611,.556,.722, // 40
.722,.333,.389,.722,.611,.889,.722,.722,
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
img.wx = x + wx
}
output += '<text class="' + font_class(gene.deffont)
if (action != 'j' && str.length > 5
&& gene.deffont.wadj)
output += '" lengthAdjust="' + gene.deffont.wadj +
'" textLength="' + wh[0].toFixed(1);
output += '" x="';
out_sxsy(x, '" y="', y)
switch (action) {
case 'c':
output += '" text-anchor="middle">'
break
case 'j':
output += '" textLength="' + w.toFixed(1) + '">'
break
case 'r':
output += '" text-anchor="end">'
break
default:
output += '">'
break
}
out_str(str);
output += "</text>\n"
}
// move last capitalized word to front when after a comma
function trim_title(title, is_subtitle) {
var i
if (cfmt.titletrim) {
i = title.lastIndexOf(", ")
if (i < 0 || title[i + 2] < 'A' || title[i + 2] > 'Z') {
i = 0
} else if (cfmt.titletrim == 1) { // (true) compatibility
if (i < title.length - 7
|| title.indexOf(' ', i + 3) >= 0)
i = 0
} else {
if (i < title.length - cfmt.titletrim - 2)
i = 0
}
if (i)
title = title.slice(i + 2).trim() + ' ' + title.slice(0, i)
}
if (!is_subtitle
&& cfmt.writefields.indexOf('X') >= 0)
title = info.X + '. ' + title
if (cfmt.titlecaps)
return title.toUpperCase()
return title
}
// return the width of the music line
function get_lwidth() {
if (img.chg)
set_page()
return (img.width - img.lm - img.rm
- 2) // for bar thickness at eol
/ cfmt.scale
}
// header generation functions
function write_title(title, is_subtitle) {
var h, wh
if (!title)
return
set_page();
if (is_subtitle) {
set_font("subtitle");
h = cfmt.subtitlespace
} else {
set_font("title");
h = cfmt.titlespace
}
wh = strwh(title)
wh[1] += gene.curfont.pad * 2
vskip(wh[1] + h + gene.curfont.pad)
h = gene.curfont.pad + wh[1] * .22 // + descent
if (cfmt.titleleft)
xy_str(0, h, title, null, null, wh)
else
xy_str(get_lwidth() / 2, h, title, "c", null, wh)
}
/* -- output a header format '111 (222)' -- */
function put_inf2r(x, y, str1, str2, action) {
if (!str1) {
if (!str2)
return
str1 = str2;
str2 = null
}
if (!str2)
xy_str(x, y, str1, action)
else
xy_str(x, y, str1 + ' (' + str2 + ')', action)
}
/* -- write a text block (%%begintext / %%text / %%center) -- */
function write_text(text, action) {
if (action == 's')
return // skip
set_page();
var wh, font, o,
strlw = get_lwidth(),
sz = gene.curfont.size,
lineskip = sz * cfmt.lineskipfac,
parskip = sz * cfmt.parskipfac,
i, j, x, words, w, k, ww, str;
switch (action) {
default:
// case 'c':
// case 'r':
font = gene.curfont
switch (action) {
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
function voice_filter() {
var opt
function vfilt(opts, opt) {
var i,
sel = new RegExp(opt)
if (sel.test(curvoice.id)
|| sel.test(curvoice.nm)) {
for (i = 0; i < opts.length; i++)
self.do_pscom(opts[i])
}
}
// global
if (parse.voice_opts)
for (opt in parse.voice_opts) {
if (parse.voice_opts.hasOwnProperty(opt))
vfilt(parse.voice_opts[opt], opt)
}
// tune
if (parse.tune_v_opts)
for (opt in parse.tune_v_opts) {
if (parse.tune_v_opts.hasOwnProperty(opt))
vfilt(parse.tune_v_opts[opt], opt)
}
}
/* -- link a ABC symbol into the current voice -- */
// if a voice is ignored (not in %%staves) don't link the symbol
// but update the time for P: and Q:
function sym_link(s) {
var tim = curvoice.time
if (!s.fname)
set_ref(s)
if (!curvoice.ignore) {
s.prev = curvoice.last_sym
if (curvoice.last_sym)
curvoice.last_sym.next = s
else
curvoice.sym = s
} else if (s.bar_type) {
curvoice.last_bar = s
}
curvoice.last_sym = s
s.v = curvoice.v;
s.p_v = curvoice;
s.st = curvoice.cst;
s.time = tim
if (s.dur && !s.grace)
curvoice.time += s.dur;
parse.ufmt = true
s.fmt = cfmt // global parameters
s.pos = curvoice.pos
if (curvoice.second)
s.second = true
if (curvoice.floating)
s.floating = true
if (curvoice.eoln) {
s.soln = true
curvoice.eoln = false
}
}
/* -- add a new symbol in a voice -- */
function sym_add(p_voice, type) {
var s = {
type:type,
dur:0
},
s2,
p_voice2 = curvoice;
curvoice = p_voice;
sym_link(s);
curvoice = p_voice2;
s2 = s.prev
if (!s2)
s2 = s.next
if (s2) {
s.fname = s2.fname;
s.istart = s2.istart;
s.iend = s2.iend
}
return s
}
/* -- sort all symbols by time and vertical sequence -- */
// weight of the symbols !! depends on the symbol type !!
var w_tb = new Uint8Array([
6, // bar
2, // clef
8, // custos
6, // sm (sequence marker, after bar)
7, // grace
3, // key
4, // meter
9, // mrest
9, // note
0, // part
9, // rest
5, // space (before bar)
0, // staves
1, // stbrk
0, // tempo
0, // (free)
0, // block
0 // remark
])
function sort_all() {
var s, s2, time, w, wmin, ir, fmt, v, p_voice, prev,
fl, new_sy,
nv = voice_tb.length,
vtb = [],
vn = [], // voice indexed by range
sy = cur_sy // first staff system
// check if different bars at the same time
function b_chk() {
var bt, s, s2, v, t,
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
if (s2.time >= s.time
&& s2.dur) {
s.next = s2
s.prev = s2.prev
s.prev.next =
s2.prev = s
s.v = s2.v
s.p_v = p_v
s.st = s2.st
break
}
}
}
} // ins_pq()
// set the duration of the notes under a feathered beam
function set_feathered_beam(s1) {
var s, s2, t, d, b, i, a,
d = s1.dur,
n = 1
/* search the end of the beam */
for (s = s1; s; s = s.next) {
if (s.beam_end || !s.next)
break
n++
}
if (n <= 1) {
delete s1.feathered_beam
return
}
s2 = s;
b = d / 2; /* smallest note duration */
a = d / (n - 1); /* delta duration */
t = s1.time
if (s1.feathered_beam > 0) { /* !beam-accel! */
for (s = s1, i = n - 1;
s != s2;
s = s.next, i--) {
d = ((a * i) | 0) + b;
s.dur = d;
s.time = t;
t += d
}
} else { /* !beam-rall! */
for (s = s1, i = 0;
s != s2;
s = s.next, i++) {
d = ((a * i) | 0) + b;
s.dur = d;
s.time = t;
t += d
}
}
s.dur = s.time + s.dur - t;
s.time = t
} // end set_feathered_beam()
// terminate voice cloning
if (curvoice && curvoice.clone) {
parse.istart = parse.eol
do_cloning()
}
// if only one voice and a time skip,
// fill the voice with the sequence "Z |" (multi-rest and bar)
if (par_sy.one_v) // if one voice
fill_mr_ba(voice_tb[par_sy.top_voice])
if (parse.pq_d)
ins_pq() // insert delayed P: and Q:
for (v = 0; v < voice_tb.length; v++) {
p_voice = voice_tb[v]
if (!sys_chg) { // if not %%score
delete p_voice.eoln
while (1) { // set the end of slurs
sl = p_voice.sls.shift()
if (!sl)
break
s = sl.ss
// error(1, s, "Lack of ending slur(s)")
if (!s.sls)
s.sls = []
sl.loc = 'o' // no slur end
s.sls.push(sl)
}
} // not %%score
for (s = p_voice.sym; s; s = s.next) {
if (s.time >= staves_found)
break
}
for ( ; s; s = s.next) {
// if the symbol has a sequence weight smaller than the bar one
// and if there a time skip,
// add an invisible bar before it
if (w_tb[s.type] < 5
&& s.type != C.STAVES
&& s.type != C.CLEF
&& s.time // not at start of tune
&& (!s.prev || s.time > s.prev.time + s.prev.dur)) {
s2 = {
type: C.BAR,
bar_type: "[]",
v: s.v,
p_v: s.p_v,
st: s.st,
time: s.time,
dur:0,
next: s,
prev: s.prev,
fmt: s.fmt,
invis: 1
}
if (s.prev)
s.prev.next = s2
else
voice_tb[s.v].sym = s2
s.prev = s2
}
switch (s.type) {
case C.GRACE:
if (!cfmt.graceword)
continue
for (s2 = s.next; s2; s2 = s2.next) {
switch (s2.type) {
case C.SPACE:
continue
case C.NOTE:
if (!s2.a_ly)
break
s.a_ly = s2.a_ly;
s2.a_ly = null
break
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
}
param = text.replace(cmd, '').trim()
if (param.slice(-5) == ' lock') {
fmt_lock[cmd] = true;
param = param.slice(0, -5).trim()
} else if (fmt_lock[cmd]) {
return
}
switch (cmd) {
case "clef":
if (parse.state >= 2) {
s = new_clef(param)
if (s)
get_clef(s)
}
return
case "deco":
deco_add(param)
return
case "linebreak":
set_linebreak(param)
return
case "map":
get_map(param)
return
case "maxsysstaffsep":
case "sysstaffsep":
if (parse.state == 3) {
val = get_unit(param)
if (isNaN(val)) {
syntax(1, errs.bad_val, "%%" + cmd)
return
}
par_sy.voices[curvoice.v][cmd[0] == 'm' ? "maxsep" : "sep"] =
val
return
}
break
case "multicol":
switch (param) {
case "start":
case "new":
case "end":
break
default:
syntax(1, "Unknown keyword '$1' in %%multicol", param)
return
}
s = {
type: C.BLOCK,
subtype: "mc_" + param,
dur: 0
}
if (parse.state >= 2) {
if (curvoice.clone)
do_cloning()
curvoice = voice_tb[0]
curvoice.eoln = 1 //true
sym_link(s)
return
}
set_ref(s)
self.block_gen(s)
return
case "ottava":
if (parse.state != 3)
return
n = parseInt(param)
if (isNaN(n) || n < -2 || n > 2
|| (!n && !curvoice.ottava)) {
syntax(1, errs.bad_val, "%%ottava")
return
}
k = n
if (n) {
curvoice.ottava = n
} else {
n = curvoice.ottava
curvoice.ottava = 0
}
a_dcn.push(["15mb", "8vb", "", "8va", "15ma"][n + 2]
+ (k ? '(' : ')'))
return
case "repbra":
if (curvoice)
curvoice.norepbra = !get_bool(param)
return
case "repeat":
if (parse.state != 3)
return
if (!curvoice.last_sym) {
syntax(1, "%%repeat cannot start a tune")
return
}
if (!param.length) {
n = 1;
k = 1
} else {
b = param.split(/\s+/);
n = parseInt(b[0]);
k = parseInt(b[1])
if (isNaN(n) || n < 1
|| (curvoice.last_sym.type == C.BAR
&& n > 2)) {
syntax(1, "Incorrect 1st value in %%repeat")
return
}
if (isNaN(k)) {
k = 1
} else {
if (k < 1) {
syntax(1, "Incorrect 2nd value in %%repeat")
return
}
}
}
parse.repeat_n = curvoice.last_sym.type == C.BAR ? n : -n;
parse.repeat_k = k
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
acc: abc2svg.b40a(b40) || 3
}
}
s.k_a_acc = a_acc
}
// fill a voice with a multi-rest and a bar
function fill_mr_ba(p_v) {
var v, p_v2,
mxt = 0
for (v = 0; v < voice_tb.length; v++) {
if (voice_tb[v].time > mxt) {
p_v2 = voice_tb[v]
mxt = p_v2.time
}
}
if (p_v.time >= mxt)
return
var p_v_sav = curvoice,
dur = mxt - p_v.time,
s = {
type: C.MREST,
stem: 0,
multi: 0,
nhd: 0,
xmx: 0,
frm: 1, //true // full measure rest
dur: dur,
dur_orig: dur,
nmes: dur / p_v.wmeasure,
notes: [{
pit: 18,
dur: dur
}],
tacet: p_v.tacet
},
s2 = {
type: C.BAR,
bar_type: '|',
dur: 0,
multi: 0
}
if (p_v2.last_sym.bar_type)
s2.bar_type = p_v2.last_sym.bar_type
// s2.soln = p_v2.last_sym.soln
glovar.mrest_p = 1 //true
curvoice = p_v
sym_link(s)
sym_link(s2)
curvoice = p_v_sav
} // fill_mr_ba()
/* -- get staves definition (%%staves / %%score) -- */
function get_staves(cmd, parm) {
var s, p_voice, p_voice2, i, flags, v, vid, a_vf, eoln,
st, range,
nv = voice_tb.length,
maxtime = 0
// if sequence with many voices, load the other voices
if (curvoice && curvoice.clone) {
// i = parse.eol
// parse.eol = parse.bol // remove the %%staves line
do_cloning()
// parse.eol = i
}
if (parm) {
a_vf = parse_staves(parm) // => array of [vid, flags]
if (!a_vf)
return
} else if (staves_found < 0) {
syntax(1, errs.bad_val, '%%' + cmd)
return
}
/* create a new staff system */
for (v = 0; v < nv; v++) {
p_voice = voice_tb[v]
if (p_voice.eoln) {
eoln = 1
delete p_voice.eoln
}
if (p_voice.time > maxtime)
maxtime = p_voice.time
}
if (!maxtime) { // if first %%staves
par_sy.staves = []
par_sy.voices = []
} else {
// if (nv) // if many voices
self.voice_adj(1)
// synchronize the voices
for (v = 0; v < nv; v++) {
p_voice = voice_tb[v]
//fixme: does not work if measure bar and %%staves delta time < measure duration
if (maxtime - p_voice.time >= p_voice.meter.wmeasure)
p_voice.acc = [] // no accidental anymore
p_voice.time = maxtime
p_voice.lyric_restart = p_voice.last_sym
p_voice.sym_restart = p_voice.last_sym
}
/*
* create a new staff system and
* link the 'staves' symbol in a voice which is seen from
* the previous system - see sort_all
*/
if (!par_sy.voices[curvoice.v])
for (v = 0; v < par_sy.voices.length; v++) {
if (par_sy.voices[v]) {
curvoice = voice_tb[v]
break
}
}
curvoice.eoln = eoln
s = {
type: C.STAVES,
dur: 0
}
sym_link(s); // link the staves in this voice
par_sy.nstaff = nstaff;
// if no parameter, duplicate the current staff system
if (!parm) {
s.sy = clone(par_sy, 2) // clone the staves and voices
par_sy.next = s.sy
par_sy = s.sy
staves_found = maxtime
curvoice = voice_tb[par_sy.top_voice]
return
}
new_syst();
s.sy = par_sy
}
staves_found = maxtime
/* initialize the (old) voices */
for (v = 0; v < nv; v++) {
p_voice = voice_tb[v]
delete p_voice.second
delete p_voice.floating
if (p_voice.ignore) {
p_voice.ignore = 0 //false
s = p_voice.sym
if (s) {
while (s.next)
s = s.next
}
p_voice.last_sym = s // set back the last symbol
}
}
range = 0
for (i = 0; i < a_vf.length; i++) {
vid = a_vf[i][0];
p_voice = new_voice(vid);
v = p_voice.v
a_vf[i][0] = p_voice;
// set the range and add the overlay voices
while (1) {
par_sy.voices[v] = {
range: range++
}
p_voice = p_voice.voice_down
if (!p_voice)
break
v = p_voice.v
}
}
par_sy.top_voice = a_vf[0][0].v
if (a_vf.length == 1)
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
if (!curvoice.last_sym)
return true
for (s = curvoice.last_sym; s; s = s.prev)
if (w_tb[s.type])
return false
return true
}
// treat a clef found in the tune body
function get_clef(s) {
var s2, s3
// special case for percussion
if (s.clef_type == 'p') { // if percussion clef
s2 = curvoice.ckey
s2.k_drum = 1 //true
s2.k_sf = 0
s2.k_b40 = 2
s2.k_map = abc2svg.keys[7]
if (!curvoice.key)
curvoice.key = s2 // new root key
}
if (!curvoice.time // (force a clef when new voice)
&& is_voice_sig()) {
curvoice.clef = s
s.fmt = cfmt
return
}
// if not clef=none,
// move the clef before a key and/or a (not right repeat) bar
if (s.clef_none)
s2 = null
else
for (s2 = curvoice.last_sym;
s2 && s2.time == curvoice.time;
s2 = s2.prev) {
if (w_tb[s2.type])
break
}
if (s2
&& s2.time == curvoice.time // if no time skip
&& s2.k_sf != undefined) {
s3 = s2 // move before a key signature
s2 = s2.prev
}
if (s2
&& s2.time == curvoice.time
&& s2.bar_type && s2.bar_type[0] != ':')
s3 = s2 // move before a measure bar
if (s3) {
s2 = curvoice.last_sym
curvoice.last_sym = s3.prev
sym_link(s)
s.next = s3
s3.prev = s
curvoice.last_sym = s2
if (s.soln) {
delete s.soln
curvoice.eoln = true
}
} else {
sym_link(s)
}
}
// treat K: (kp = key signature + parameters)
function get_key(parm) {
var v, p_voice,
// [s_key, a] = new_key(parm) // KO with nodejs
a = new_key(parm),
s_key = a[0],
s = s_key,
empty = s.k_sf == undefined && !s.k_a_acc
a = a[1]
if (empty)
s.invis = 1 //true // don't display empty K:
else
s.orig = s // new transposition base
if (parse.state == 1) { // in tune header (first K:)
parse.ckey = s // root key
if (empty) {
s_key.k_sf = 0;
s_key.k_none = true
s_key.k_map = abc2svg.keys[7]
}
for (v = 0; v < voice_tb.length; v++) {
p_voice = voice_tb[v];
p_voice.ckey = clone(s_key)
}
if (a.length) {
memo_kv_parm('*', a)
a = []
}
if (!glovar.ulen)
glovar.ulen = C.BLEN / 8;
goto_tune()
} else if (!empty) {
if (curvoice.tr_sco)
curvoice.tr_sco = undefined
s.k_old_sf = curvoice.ckey.k_sf // memorize the previous key
curvoice.ckey = s
sym_link(s)
}
// set the voice parameters
if (!curvoice) { // if first K:
if (!voice_tb.length) {
curvoice = new_voice("1")
var def = 1 // true
} else {
curvoice = voice_tb[staves_found < 0 ? 0 : par_sy.top_voice]
}
}
p_voice = curvoice.clone
if (p_voice)
lib/ChordPro/res/abc/abc2svg/abc2svg-1.js view on Meta::CPAN
scale: 1,
// st: 0,
// cst: 0,
ulen: glovar.ulen,
dur_fact: 1,
// key: clone(parse.ckey), // key at start of tune (parse / gene)
// ckey: clone(parse.ckey), // current key (parse / gene)
meter: clone(glovar.meter),
wmeasure: glovar.meter.wmeasure,
staffnonote: 1,
clef: {
type: C.CLEF,
clef_auto: true,
clef_type: "a", // auto
time: 0
},
acc: [], // accidentals of the measure (parse)
sls: [], // slurs - used in parsing and in generation
hy_st: 0
}
voice_tb.push(p_voice);
if (parse.state == 3) {
// p_voice.key = parse.ckey // (done later in music.js)
p_voice.ckey = clone(parse.ckey)
if (p_voice.ckey.k_bagpipe
&& !p_voice.pos.stm) {
p_voice.pos = clone(p_voice.pos)
p_voice.pos.stm &= ~0x07
p_voice.pos.stm |= C.SL_BELOW
}
}
// par_sy.voices[v] = {
// range: -1
// }
return p_voice
}
// this function is called at program start and on end of tune
function init_tune() {
nstaff = -1;
voice_tb = [];
curvoice = null;
new_syst(true);
staves_found = -1;
gene = {}
a_de = [] // remove old decorations
cross = {} // new cross voice decorations
}
// treat V: with many voices
function do_cloning() {
var i,
clone = curvoice.clone,
vs = clone.vs,
a = clone.a,
bol = clone.bol,
eol = parse.bol,
parse_sav = parse,
file = parse.file
delete curvoice.clone
if (file[eol - 1] == '[') // if stop on [V:xx]
eol--
// insert the music sequence in each voice
include++;
for (i = 0; i < vs.length; i++) {
parse = Object.create(parse_sav) // create a new parse context
parse.line = Object.create(parse_sav.line)
get_voice(vs[i] + ' ' + a.join(' '))
tosvg(parse.fname, file, bol, eol)
}
include--
parse = parse_sav // restore the parse context
}
// treat a 'V:' info
function get_voice(parm) {
var v, vs,
a = info_split(parm),
vid = a.shift()
if (!vid)
return // empty V:
// if end of sequence with many voices, load the other voices
if (curvoice && curvoice.clone)
do_cloning()
if (vid.indexOf(',') > 0) // if many voices
vs = vid.split(',')
else
vs = [vid]
if (parse.state < 2) { // memorize the voice parameters
while (1) {
vid = vs.shift()
if (!vid)
break
if (a.length)
memo_kv_parm(vid, a)
if (vid != '*' && parse.state == 1)
curvoice = new_voice(vid)
}
return
}
if (vid == '*') {
syntax(1, "Cannot have V:* in tune body")
return
}
curvoice = new_voice(vs[0])
// if many voices, memorize the start of sequence
if (vs.length > 1) {
vs.shift()
curvoice.clone = {
vs: vs,
a: a.slice(0), // copy the parameters
bol: parse.iend
}
if (parse.file[curvoice.clone.bol - 1] != ']')
curvoice.clone.bol++ // start of new line
}
set_kv_parm(a)
key_trans()
v = curvoice.v