App-Music-ChordPro-6.050.7/0000755000400000040000000000000014567360040013010 5ustar jvjvApp-Music-ChordPro-6.050.7/lib/0000755000400000040000000000000014567360037013564 5ustar jvjvApp-Music-ChordPro-6.050.7/lib/ChordPro/0000755000400000040000000000000014567360037015304 5ustar jvjvApp-Music-ChordPro-6.050.7/lib/ChordPro/res/0000755000400000040000000000000014567360037016075 5ustar jvjvApp-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/0000755000400000040000000000000014567360037016622 5ustar jvjvApp-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/0000755000400000040000000000000014567360037020151 5ustar jvjvApp-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/roman-1.js0000644000400000040000000000720614565315274021767 0ustar jvjv// 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 . // // This module is loaded by %%roman. // // Parameters // %%roman 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 . // // Code adapted from Chris Fargen. // https://gist.github.com/chrisfargen/4324c6cf6fed2c8f9a6eae1680e53169 // // This module is loaded by %%jazzchord. // // Parameters // %%jazzchord [ string '=' replacement-string ]* if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.jazzchord = { // default replacements defrep: { "-": "–", "°": "o", "º": "o", "ᵒ": "o", "0": "ø", // "6/9": "⁶⁄₉", // "maj": "Δ", // "M": "Δ", // "min": "–", // "m": "–", "^": "∆" }, gch_build: function(of, s) { var gch, ix, r, t, fmt = s.fmt if (!fmt.jazzchord) { of(s) return } // jazzify a chord function jzch(t) { var r = '', a = t.match(/(\[?[A-G])([#♯b♭]?)([^/]*)\/?(.*)\)?/) // a[1]=note, a[2]=acc, a[3]=quality, a[4]=bass if (!a) return t if (a[2]) r = "$7" + a[2] if (a[3][0] == 'm' // if minor && a[3].slice(0, 3) != "maj") { if (!r) r += "$7" if (a[3].slice(0, 3) == "min") { r += a[3].slice(0, 3) a[3] = a[3].slice(3) } else { // assume 'm' r += 'm' a[3] = a[3].slice(1) } } if (a[3]) r += (r ? "$0" : '') + "$8" + a[3] if (a[4]) r += (r ? "$0" : '') + "$9/" + a[4] if (!r) return t return a[1] + r + "$0" } // jzch() for (ix = 0; ix < s.a_gch.length; ix++) { gch = s.a_gch[ix] t = gch.text if (gch.type != 'g' || t.indexOf('$') >= 0) // if some formatting already continue switch (t) { case "/": gch.text = "\ue101"; continue case "%": gch.text = "\ue500"; continue case "%%": gch.text = "\ue501"; continue } if (fmt.jzreg) { // if replacement list t = t.replace(fmt.jzRE, function(x) { return fmt.jzrep[x] }) } if (fmt.jazzchord == 1) { if (t[0] == '(') t = t.slice(1, -1) t = t.split('(') // possible "ch1(ch2)" r = jzch(t[0]) if (t.length > 1) r += '(' + jzch(t[1]) } else { r = t } if (gch.text[0] == '(') gch.text = '(' + r + ')' else gch.text = r } of(s) // build the chord symbols }, // gch_build() set_fmt: function(of, cmd, parm) { var i, k, s, cfmt = this.cfmt() if (cmd == "jazzchord") { cfmt.jazzchord = this.get_bool(parm) if (!cfmt.jazzchord) return if (parm[0] == '2') cfmt.jazzchord = 2 // no style if(!cfmt.jzreg) { // if new definition // cfmt.jzreg = "-|°|º|ᵒ|0|6/9|maj|M|min|m|\\^" cfmt.jzreg = "-|°|º|ᵒ|0|\\^" cfmt.jzrep = Object.create(abc2svg.jazzchord.defrep) cfmt.jzRE = new RegExp(cfmt.jzreg, 'g') } if (parm && parm.indexOf('=') > 0) { parm = parm.split(/[\s]+/) for (cmd = 0; cmd < parm.length; cmd++) { k = parm[cmd].split('=') if (k.length != 2) //fixme: error continue s = k[1] // replacement k = k[0] // key i = cfmt.jzreg.indexOf(k) if (i >= 0) { // if old key if (s) { // new value cfmt.jzrep[k] = s } else { cfmt.jzreg = cfmt.jzreg.replace(k, '') cfmt.jzreg = cfmt.jzreg.replace('||', '|') delete cfmt.jzrep[k] } } else { cfmt.jzreg += '|' + k cfmt.jzrep[k] = s } cfmt.jzRE = new RegExp(cfmt.jzreg, 'g') } } return } of(cmd, parm) }, // set_fmt() set_hooks: function(abc) { abc.gch_build = abc2svg.jazzchord.gch_build.bind(abc, abc.gch_build) abc.set_format = abc2svg.jazzchord.set_fmt.bind(abc, abc.set_format) abc.add_style("\ \n.jc7{font-size:90%}\ \n.jc8{baseline-shift:25%;font-size:75%;letter-spacing:-0.05em}\ \n.jc9{font-size:75%;letter-spacing:-0.05em}\ ") abc.param_set_font("setfont-7", "* * class=jc7") abc.param_set_font("setfont-8", "* * class=jc8") abc.param_set_font("setfont-9", "* * class=jc9") } // set_hooks() } // jazzchord if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.jazzchord = abc2svg.jazzchord.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/equalbars-1.js0000644000400000040000000000761214565315274022633 0ustar jvjv// equalbars.js - module to set equal spaced measure bars // // Copyright (C) 2018-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 . // // This module is loaded when "%%equalbars" appears in a ABC source. // // Parameters // %%equalbars bool if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.equalbars = { // new tune - clear the width of the start of the staff output_music: function(of) { this.equalbars_d = 0; of() }, // get the equalbars parameter set_fmt: function(of, cmd, parm) { if (cmd != "equalbars") { of(cmd, parm) return } var fmt = this.cfmt() fmt.equalbars = this.get_bool(parm) fmt.stretchlast = 1 }, // adjust the symbol offsets of a music line // only the bars of the first voice are treated set_sym_glue: function(of, width) { var C = abc2svg.C, s, s2, d, w, i, n, x, g, t, t0, bars = [], tsfirst = this.get_tsfirst(); of(width) // compute the x offset of the symbols if (!this.cfmt().equalbars) return // search the first note/rest/space for (s2 = tsfirst; s2; s2 = s2.next) { switch (s2.type) { default: continue case C.GRACE: case C.MREST: case C.NOTE: case C.REST: case C.SPACE: break } break } if (!s2) return // build an array of the bars t0 = t = s2.time for (s = s2; s.next; s = s.next) { if (s.type == C.BAR && s.seqst && s.time != t) { bars.push([s, s.time - t]); t = s.time } } // push the last bar or replace it in the array if (s.time != t) bars.push([s, s.time - t]) else bars[bars.length - 1][0] = s // replace the last bar t = s.time if (s.dur) t += s.dur; n = bars.length if (n <= 1) return // one or no bar // if small width, get the widest measure if (s.x < width) { w = 0 x = 0 for (i = 0; i < n; i++) { s = bars[i][0] if (s.x - x > w) w = s.x - x x = s.x } if (w * n < width) width = w * n this.set_realwidth(width) } // set the measure parameters x = s2.type == C.GRACE ? s2.extra.x : (s2.x - s2.wl) if (this.equalbars_d < x) this.equalbars_d = x // new offset of the first note/rest d = this.equalbars_d w = (width - d) / (t - t0) // width per time unit // loop on the bars for (i = 0; i < n; i++) { do { // don't shift the 1st note from the bar if (s2.type == C.GRACE) { for (g = s2.extra; g; g = g.next) g.x = d + g.x - x } else { s2.x = d + s2.x - x } s2 = s2.ts_next } while (!s2.seqst) s = bars[i][0]; // next bar f = w * bars[i][1] / (s.x - x) // and update the x offsets for ( ; s2 != s; s2 = s2.ts_next) { if (s2.type == C.GRACE) { for (g = s2.extra; g; g = g.next) g.x = d + (g.x - x) * f // } else if (s2.x) { } else { s2.x = d + (s2.x - x) * f } } d += w * bars[i][1]; x = s2.x while (1) { s2.x = d; s2 = s2.ts_next if (!s2 || s2.seqst) break } if (!s2) break } }, // set_sym_glue() set_hooks: function(abc) { abc.output_music = abc2svg.equalbars.output_music.bind(abc, abc.output_music); abc.set_format = abc2svg.equalbars.set_fmt.bind(abc, abc.set_format); abc.set_sym_glue = abc2svg.equalbars.set_sym_glue.bind(abc, abc.set_sym_glue) } } // equalbars if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.equalbars = abc2svg.equalbars.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/README.FIRST0000644000400000040000000000034714564715177021731 0ustar jvjvThis directory contains a subset of the ABC2SVG software. It is intended to be used by Chordpro and may not be useful for other purposes. For more info, https://chiselapp.com/user/moinejf/repository/abc2svg/doc/trunk/README.md. App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/mdnn-1.js0000644000400000040000000001736514565315274021616 0ustar jvjv// mdnn.js - module to output Modernised Diatonic Numerical Notation // (https://medium.com/@info_70544/the-case-for-numerical-music-notation-part-1-introduction-and-history-5f1543ca8a95) // // Copyright (C) 2020-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 . // // This module is loaded when "%%mdnn" appears in a ABC source. // // Parameters // (none) // //fixme:nsk // - Non Standard Key signature - other way to display the accidentals on key change // polyfill if (typeof Object.assign !== 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, "assign", { value: function assign(target, varArgs) { // .length of function is 2 'use strict'; if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource !== null && nextSource !== undefined) { for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true }); } if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.mdnn = { cde2fcg: new Int8Array([0, 2, 4, -1, 1, 3, 5]), cgd2cde: new Int8Array([0, -4, -1, -5, -2, -6, -3, 0, -4, -1, -5, -2, -6, -3, 0]), acc_tb: ["aff", "af", "n", "s", "ss"], glyphs: { n1: '1', n2: '2', n3: '3', n4: '4', n5: '5', n6: '6', n7: '7', nq: '\'', nc: ',', nh: '', nw: '', aff: '', af: '', nn: '', ns: '', nss: '' }, //glyphs decos: { n1: "9 n1 0 0 0", n2: "9 n2 0 0 0", n3: "9 n3 0 0 0", n4: "9 n4 0 0 0", n5: "9 n5 0 0 0", n6: "9 n6 0 0 0", n7: "9 n7 0 0 0", q: "0 nq 0 0 0", c: "0 nc 0 0 0", h: "0 nh 0 0 0", w: "0 nw 0 0 0", aff: "0 aff 0 0 0", af: "0 af 0 0 0", n: "0 nn 0 0 0", s: "0 ns 0 0 0", ss: "0 nss 0 0 0" }, // decos // change the voices into MDNN //fixme: actually, one voice only output_music: function(of) { var C = abc2svg.C, abc = this, cfmt = abc.cfmt(), cur_sy = abc.get_cur_sy(), voice_tb = abc.get_voice_tb(), sf = voice_tb[0].key.k_sf, delta = abc2svg.mdnn.cgd2cde[sf + 7] - 2 var s, s2, note, pit, nn, p, a, i, prev_oct = -10 if (!cfmt.mdnn) { of() return } delete voice_tb[0].key.k_a_acc // no accidental voice_tb[0].clef.invis = true // no (visible) clef cur_sy.staves[0].stafflines = "..." // empty staff // scan the first voice of the tune for (s = voice_tb[0].sym; s; s = s.next) { switch (s.type) { case C.CLEF: s.invis = true continue case C.KEY: //fixme:nsk // delete s.k_a_acc // don't display the accidentals sf = s.k_sf delta = abc2svg.mdnn.cgd2cde[sf + 7] - 2 nn = sf - s.k_old_sf // delta accidentals //fixme:nsk // if (!nn) { // s.k_old_sf = s.k_sf = 0 // don't display the accidentals // continue // } //fixme:comment if nsk // display normal accidentals s.k_old_sf = 0 s.k_sf = nn continue //fixme:nsk // // display '[' 0 ? //// "\u266f\u266f\u266f\u266f\u266f\u266f".slice(0, nn) : //// "\u266d\u266d\u266d\u266d\u266d\u266d".slice(0, -nn)) + //// ']' // text: '[' + (nn > 0 ? // nn + '\u266f' : (-nn) + '\u266d') + // ']' // } // s2.width = abc.strwh(s2.a_gch[0].text + ' ')[0] // s2.a_gch[0].x = -s2.width / 2 // s = s2 // continue default: continue case C.NOTE: // change the notes break } note = s.notes[0] // note head p = note.pit pit = p + delta nn = ((pit + 77) % 7) + 1 // note number abc.dh_put('n' + nn, s, note) // display the note as C5 with stem up note.pit = 23 // 'c' s.stem = 1 // octave nn = (pit / 7) | 0 if (nn > prev_oct) { if (prev_oct != -10) abc.dh_put('q', s, note) prev_oct = nn } else if (nn < prev_oct) { abc.dh_put('c', s, note) prev_oct = nn } // half and whole notes if (s.dur >= C.BLEN / 2) abc.dh_put(s.dur >= C.BLEN ? 'w' : 'h', s, note) // accidentals a = note.acc if (a) { note.acc = 0 nn = abc2svg.mdnn.cde2fcg[(p + 5 + 16 * 7) % 7] - sf if (a != 3) nn += a * 7 nn = ((((nn + 1 + 21) / 7) | 0) + 2 - 3 + 32 * 5) % 5 abc.dh_put(abc2svg.mdnn.acc_tb[nn], s, note) } // set the slurs and ties up if (s.sls) { for (i = 0; i < s.sls.length; i++) s.sls[i].ty = C.SL_ABOVE } if (note.sls) { for (i = 0; i < note.sls.length; i++) note.sls[i].ty = C.SL_ABOVE } if (note.tie_ty != undefined) note.tie_ty = C.SL_ABOVE } of() }, // output_music() set_fmt: function(of, cmd, param) { if (cmd == "mdnn") this.cfmt().mdnn = param else of(cmd, param) }, // set_fmt() // don't display the key signature at start of staff set_pitch: function(of, last_s) { of(last_s) if (!last_s // first time || !this.cfmt().mdnn) return var C = abc2svg.C, s = this.get_tsfirst() if (s && s.next && s.next.type == C.KEY) //fixme:nsk delete s.next.k_a_acc // s.next.a_gch = null }, // set_pitch() set_hooks: function(abc) { abc.output_music = abc2svg.mdnn.output_music.bind(abc, abc.output_music) abc.set_format = abc2svg.mdnn.set_fmt.bind(abc, abc.set_format) abc.set_pitch = abc2svg.mdnn.set_pitch.bind(abc, abc.set_pitch) Object.assign(abc.get_glyphs(), abc2svg.mdnn.glyphs) Object.assign(abc.get_decos(), abc2svg.mdnn.decos) abc.add_style("\n.bn {font-family:sans-serif; font-size:15px}") } } // mdnn if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.mdnn = abc2svg.mdnn.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/pedline-1.js0000644000400000040000000000544514565315274022276 0ustar jvjv// pedline.js - module to draw pedal lines instead of 'Ped .. *' // // Copyright (C) 2020-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 . // // This module is loaded when "%%pedline" appears in a ABC source. // // Parameters // %%pedline 1 if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.pedline = { draw_all_deco: function(of) { var de, i, x, dp, ds, a_de = this.a_de() if (!a_de.length) return // no decoration in this music line if (this.cfmt().pedline) { for (i = 0; i < a_de.length; i++) { de = a_de[i] if (de.dd.name != "ped)") continue ds = de.start dp = de.prev // ( .. ) ( .. ) // \ de // \ de.start // \ de.prev // \ de.prev.start // |_____/\____| if (dp && dp.dd.name == "ped)" && dp.s.v == ds.s.v) { de.defl.nost = // /\ dp.defl.noen = 2 de.x = ds.s.x - 10 de.val = de.s.x - ds.s.x - 3 dp.val = de.x - dp.x if (de.y > dp.y) de.y = dp.y dp.y = de.y } else { de.x = ds.s.x - 8 if (!de.defl.noen) de.val = de.s.x - ds.s.x - de.s.wl } } } of() }, // draw_all_deco() out_lped: function(of, x, y, val, defl) { if (!this.cfmt().pedline) { of(x, y, val, defl) return } this.xypath(x, y + 8) if (defl.nost) { if (defl.nost == 2) { // \ this.out_svg("l2.5 6") val -= 2.5 } else { this.out_svg("m0 6") } } else { this.out_svg("v6") } if (defl.noen) { if (defl.noen == 2) { // / val -= 2.5 this.out_svg("h" + val.toFixed(1) + 'l2.5 -6') } else { this.out_svg("h" + val.toFixed(1)) } } else { this.out_svg("h" + val.toFixed(1) + 'v-6') } this.out_svg('"/>\n') }, // out_lped() set_fmt: function(of, cmd, param) { if (cmd == "pedline") this.cfmt().pedline = this.get_bool(param) else of(cmd, param) }, // set_fmt() set_hooks: function(abc) { abc.draw_all_deco = abc2svg.pedline.draw_all_deco.bind(abc, abc.draw_all_deco) abc.out_lped = abc2svg.pedline.out_lped.bind(abc, abc.out_lped) abc.set_format = abc2svg.pedline.set_fmt.bind(abc, abc.set_format) } // set_hooks() } // pedline if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.pedline = abc2svg.pedline.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/perc-1.js0000644000400000040000000001311714565315274021602 0ustar jvjv// perc.js - module to handle %%percmap // // Copyright (C) 2018-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 . // // This module is loaded when "%%percmap" appears in a ABC source. // // Parameters (from W. Vree) // %%percmap ABC_note percussion [note_head] // The percussion may be a number (MIDI percussion number range 35..81), // a ABC note or a possibly abbreviated percussion name. // See https://wim.vree.org/js2/tabDrumDoc.html for more information. // Using this command creates a voicemap named "MIDIdrum". if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.perc = { // parse %%percmap do_perc: function(parm) { var pits = new Int8Array([0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6]), accs = new Int8Array([3, 1, 3, -1, 3, 3, 1, 3, -1, 3, -1, 3]) // GM drum // 35 B,,, Acoustic Bass Drum a-b-d // 36 C,, Bass Drum 1 b-d-1 // 37 ^C,, Side Stick s-s // 38 D,, Acoustic Snare a-s // 39 ^D,, Hand Clap h-c // 40 E,, Electric Snare e-s // 41 F,, Low Floor Tom l-f-t // 42 ^F,, Closed Hi Hat c-h-h // 43 G,, High Floor Tom h-f-t // 44 ^G,, Pedal Hi-Hat p-h-h // 45 A,, Low Tom l-to // 46 ^A,, Open Hi-Hat o-h-h // 47 B,, Low-Mid Tom l-m-t // 48 C, Hi Mid Tom h-m-t // 49 ^C, Crash Cymbal 1 c-c-1 // 50 D, High Tom h-to // 51 ^D, Ride Cymbal 1 r-c-1 // 52 E, Chinese Cymbal c-c // 53 F, Ride Bell r-b // 54 ^F, Tambourine t // 55 G, Splash Cymbal s-c // 56 ^G, Cowbell co // 57 A, Crash Cymbal 2 c-c-2 // 58 ^A, Vibraslap v // 59 B, Ride Cymbal 2 r-c-2 // 60 C Hi Bongo h-b // 61 ^C Low Bongo l-b // 62 D Mute Hi Conga m-h-c // 63 ^D Open Hi Conga o-h-c // 64 E Low Conga l-c // 65 F High Timbale h-ti // 66 ^F Low Timbale l-ti // 67 G High Agogo h-a // 68 ^G Low Agogo l-a // 69 A Cabasa ca // 70 ^A Maracas m // 71 B Short Whistle s-w // 72 c Long Whistle l-w // 73 ^c Short Guiro s-g // 74 d Long Guiro l-g // 75 ^d Claves cl // 76 e Hi Wood Block h-w-b // 77 f Low Wood Block l-w-b // 78 ^f Mute Cuica m-c // 79 g Open Cuica o-c // 80 ^g Mute Triangle m-t // 81 a Open Triangle o-t // percussion reduced names (alphabetic order) var prn = { "a-b-d": 35, "a-s": 38, "b-d-1": 36, "ca": 69, "cl": 75, "co": 56, "c-c": 52, "c-c-1": 49, "c-c-2": 57, "c-h-h": 42, "e-s": 40, "h-a": 67, "h-b": 60, "h-c": 39, "h-f-t": 43, "h-m-t": 48, "h-ti": 65, "h-to": 50, "h-w-b": 76, "l-a": 68, "l-b": 61, "l-c": 64, "l-f-t": 41, "l-g": 74, "l-m-t": 47, "l-ti": 66, "l-to": 45, "l-w": 72, "l-w-b": 77, "m": 70, "m-c": 78, "m-h-c": 62, "m-t": 80, "o-c": 79, "o-h-c": 63, "o-h-h": 46, "o-t": 81, "p-h-h": 44, "r-b": 53, "r-c-1": 51, "r-c-2": 59, "s-c": 55, "s-g": 73, "s-s": 37, "s-w": 71, "t": 54, "v": 58 } // convert a drum instrument to a ABC note function toabc(p) { var i, j, s, pit if (/^[_^]*[A-Ga-g][,']*$/.test(p)) // ' return p // ABC note // if not a MIDI pitch, try a drum instrument name pit = Number(p) if (isNaN(pit)) { p = p.toLowerCase(p); s = p[0]; // get the 1st letters after '-' i = 0 while (1) { j = p.indexOf('-', i) if (j < 0) break i = j + 1; s += '-' + p[i] } pit = prn[s] // solve some specific cases if (!pit) { switch (p[0]) { case 'c': switch (p[1]) { case 'a': pit = prn.ca; break case 'l': pit = prn.cl; break case 'o': pit = prn.co; break } break case 'h': case 'l': i = p.indexOf('-') if (p[i + 1] != 't') break switch (p[i + 2]) { case 'i': case 'o': pit = prn[s + p[i + 2]] break } break } if (!pit) return } } p = ["C","^C","D","_E","E","F","^F","G","^G","A","_B","B"][pit % 12] while (pit < 60) { p += ',' pit += 12 } while (pit >= 72) { p += "'" pit -= 12 } return p } // toabc() // do_perc() var a = parm.split(/\s+/), p = a[1].replace(/[=_^]/, '') this.do_pscom("map MIDIdrum " + a[1] + " play=" + toabc(a[2]) + " print=" + p + (a[3] ? (" heads=" + a[3]) : '')) this.set_v_param("perc", "MIDIdrum") }, // do_perc() // set the MIDI parameters in the current voice set_perc: function(a) { var i, item, s, curvoice = this.get_curvoice() for (i = 0; i < a.length; i++) { switch (a[i]) { case "perc=": // %%percmap if (!curvoice.map) curvoice.map = {} curvoice.map = a[i + 1]; s = this.new_block("midiprog") s.play = s.invis = 1 //true curvoice.chn = s.chn = 9 // channel 10 break } } }, // set_perc() do_pscom: function(of, text) { if (text.slice(0, 8) == "percmap ") abc2svg.perc.do_perc.call(this, text) else of(text) }, set_vp: function(of, a) { abc2svg.perc.set_perc.call(this, a); of(a) }, set_hooks: function(abc) { abc.do_pscom = abc2svg.perc.do_pscom.bind(abc, abc.do_pscom); abc.set_vp = abc2svg.perc.set_vp.bind(abc, abc.set_vp) } } // perc if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.perc = abc2svg.perc.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/combine-1.js0000644000400000040000000001520214565315274022262 0ustar jvjv// combine.js - module to add a combine chord line // // Copyright (C) 2018-2024 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 . // // This module is loaded when "%%voicecombine" appears in a ABC source. // // Parameters // %%voicecombine n 'n' is the combine level if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.combine = { // function called at start of the generation when multi-voices comb_v: function() { var C = abc2svg.C, abc = this // get the symbol of the note to combine function get_cmb(s) { var p, s2 = s.ts_next, i = s.p_v.id.indexOf('.') // '.' is the group separator if (i >= 0) { p = s.p_v.id.slice(0, i) // group while (s2 && s2.time == s.time) { if (s2.p_v.id.indexOf(p) == 0) break s2 = s2.ts_next } } return s2 } // get_cmb() // check if voice combine may occur function may_combine(s) { var nhd2, s2 = get_cmb(s) if (!s2 || (s2.type != C.NOTE && s2.type != C.REST)) return false if (s2.st != s.st || s2.time != s.time || s2.dur != s.dur) return false if (s.combine <= 0 && s2.type != s.type) return false // if (s2.a_dd) { //fixme: should check the double decorations // return false // } if (s.a_gch && s2.a_gch) return false if (s.type == C.REST) { if (s.type == s2.type) { if (s.invis && !s2.invis) return //false } else if (s.combine <= 2) { return //false } return true } if (s2.a_ly) return false if (s2.beam_st != s.beam_st || s2.beam_end != s.beam_end) return false; nhd2 = s2.nhd if (s.combine <= 1 && s.notes[0].pit <= s2.notes[nhd2].pit + 1) return false return true } // may_combine() // combine two notes function combine_notes(s, s2) { var nhd, type, m, not // put the notes of the 2nd voice into the 1st one for (m = 0; m <= s2.nhd; m++) { not = abc.clone(s2.notes[m]) not.noplay = true // and don't play it s.notes.push(not) } s.nhd = nhd = s.notes.length - 1; s.notes.sort(abc2svg.pitcmp) // sort the notes by pitch if (s.combine >= 3) { // remove unison heads //fixme: KO for playback for (m = nhd; m > 0; m--) { if (s.notes[m].pit == s.notes[m - 1].pit && s.notes[m].acc == s.notes[m - 1].acc) s.notes.splice(m, 1) } s.nhd = nhd = s.notes.length - 1 } s.ymx = 3 * (s.notes[nhd].pit - 18) + 4; s.ymn = 3 * (s.notes[0].pit - 18) - 4; // force the tie directions type = s.notes[0].tie_ty if ((type & 0x07) == C.SL_AUTO) s.notes[0].tie_ty = C.SL_BELOW | (type & C.SL_DOTTED); type = s.notes[nhd].tie_ty if ((type & 0x07) == C.SL_AUTO) s.notes[nhd].tie_ty = C.SL_ABOVE | (type & C.SL_DOTTED) } // combine_notes() // combine 2 voices // return the remaining one function do_combine(s) { var s2, s3, type, i, n, sl s2 = get_cmb(s) // there may be more voices if (!s.in_tuplet && s2.combine != undefined && s2.combine >= 0 && may_combine(s2)) s2 = do_combine(s2) if (s.type != s2.type) { // if note and rest if (s2.type != C.REST) { s2 = s; s = s2.ts_next } } else if (s.type == C.REST) { if (s.invis && !s2.invis) delete s.invis s.multi = 0 } else { combine_notes(s, s2) if (s2.ti1) s.ti1 = true if (s2.ti2) s.ti2 = true } // if some slurs start on the second symbol // move them to the combined symbol // also, set a flag in the symbols of the ending slurs if (s2.sls) { if (s.sls) Array.prototype.push.apply(s.sls, s2.sls) else s.sls = s2.sls for (i = 0; i < s2.sls.length; i++) { sl = s2.sls[i] if (sl.se) sl.se.slsr = s // reverse pointer sl.ty = C.SL_BELOW } delete s2.sls } // if a combined slur is ending on the second symbol, // update its starting symbol s3 = s2.slsr // pointer to the starting symbol if (s3) { for (i = 0; i < s3.sls.length; i++) { sl = s3.sls[i] if (sl.se == s2) sl.se = s } } if (s2.a_gch) s.a_gch = s2.a_gch if (s2.a_dd) { if (!s.a_dd) s.a_dd = s2.a_dd else Array.prototype.push.apply(s.a_dd, s2.a_dd) } s2.play = s2.invis = true // don't display, but play return s } // do_combine() // code of comb_v() var s, s2, g, i, r for (s = abc.get_tsfirst(); s; s = s.ts_next) { switch (s.type) { case C.REST: if (s.combine == undefined || s.combine < 0) continue if (may_combine(s)) s = do_combine(s) // continue // fall thru default: continue case C.NOTE: if (s.combine == undefined || s.combine <= 0) continue break } if (!s.beam_st) continue s2 = s while (1) { if (!may_combine(s2)) { s2 = null break } //fixme: may have rests in beam if (s2.beam_end) break do { s2 = s2.next } while (s2.type != C.NOTE && s2.type != C.REST) } if (!s2) continue s2 = s while (1) { s2 = do_combine(s2) //fixme: may have rests in beam if (s2.beam_end) break do { s2 = s2.next } while (s2.type != C.NOTE && s2.type != C.REST) } } }, // comb_v() do_pscom: function(of, text) { if (text.slice(0, 13) == "voicecombine ") this.set_v_param("combine", text.split(/[ \t]/)[1]) else of(text) }, new_note: function(of, gr, tp) { var curvoice = this.get_curvoice() var s = of(gr, tp) if (s && s.notes && curvoice.combine != undefined) s.combine = curvoice.combine return s }, set_stem_dir: function(of) { of(); abc2svg.combine.comb_v.call(this) }, // set the combine parameter in the current voice set_vp: function(of, a) { var i, curvoice = this.get_curvoice() for (i = 0; i < a.length; i++) { if (a[i] == "combine=") { // %%voicecombine curvoice.combine = a[i + 1] break } } of(a) }, set_hooks: function(abc) { abc.do_pscom = abc2svg.combine.do_pscom.bind(abc, abc.do_pscom); abc.new_note = abc2svg.combine.new_note.bind(abc, abc.new_note); abc.set_stem_dir = abc2svg.combine.set_stem_dir.bind(abc, abc.set_stem_dir); abc.set_vp = abc2svg.combine.set_vp.bind(abc, abc.set_vp) } } // combine if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.combine = abc2svg.combine.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/sth-1.js0000644000400000040000000000724114565315274021450 0ustar jvjv// sth.js - module to set the stem heights // // Copyright (C) 2018-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 . // // This module is loaded when "%%sth" appears in a ABC source. // // Parameters // %%sth h1 h2 h3 ... // The values h1, h2, .. are applied to the following notes which // have a stem and which are not inside a beam. // The value may be '*' for keeping the original stem length. if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.sth = { // function called after beam calculation recal_beam: function(bm, s) { var staff_tb = this.get_staff_tb(), y = staff_tb[s.st].y, s2 = bm.s2, y2 = staff_tb[s2.st].y if (s.sth != undefined) s.ys = s.sth if (s2.sth != undefined) s2.ys = s2.sth; bm.a = (s.ys + y - s2.ys - y2) / (s.xs - s2.xs); bm.b = s.ys - s.xs * bm.a + y while (1) { s.ys = bm.a * s.xs + bm.b - y if (s.stem > 0) s.ymx = s.ys + 2.5 else s.ymn = s.ys - 2.5; s = s.next if (s == s2) break } }, // function called after the stem heights have been computed set_sth: function() { var s, h, v, sth_a, p_voice, voice_tb = this.get_voice_tb() for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v] if (p_voice.sth != null) // if no stem length in this voice continue sth_a = [] for (s = p_voice.sym; s; s = s.next) { if (s.sth) { sth_a = s.sth; s.sth = null } if (sth_a.length == 0 || s.nflags <= -2 || s.stemless || !(s.beam_st || s.beam_end)) continue h = sth_a.shift() if (h == '*') continue // no change if (h == '|') { // skip to the next measure bar for (s = s.next; s; s = s.next) { if (s.bar_type) break } continue } h = Number(h) if (isNaN(h) || !h) continue // fixme: error if (s.stem >= 0) { s.ys = s.y + h; s.ymx = (s.ys + 2.5) | 0 } else { s.ys = s.y - h; s.ymn = (s.ys - 2.5) | 0 } s.sth = s.ys } } }, // set_sth() calculate_beam: function(of, bm, s1) { var done = of(bm, s1) if (done && bm.s2 && (s1.sth || bm.s2.sth)) abc2svg.sth.recal_beam.call(this, bm, s1) return done }, new_note: function(of, grace, tp_fact) { var C = abc2svg.C, s = of(grace, tp_fact), curvoice = this.get_curvoice() if (curvoice.sth && s && s.type == C.NOTE) { s.sth = curvoice.sth; curvoice.sth = null // some stem widths in this voice } return s }, set_fmt: function(of, cmd, param) { if (cmd == "sth") { var curvoice = this.get_curvoice() if (curvoice) curvoice.sth = param.split(/[ \t;-]+/) return } of(cmd, param) }, set_stems: function(of) { of(); abc2svg.sth.set_sth.call(this) }, set_hooks: function(abc) { abc.calculate_beam = abc2svg.sth.calculate_beam.bind(abc, abc.calculate_beam); abc.new_note = abc2svg.sth.new_note.bind(abc, abc.new_note); abc.set_format = abc2svg.sth.set_fmt.bind(abc, abc.set_format); abc.set_stems = abc2svg.sth.set_stems.bind(abc, abc.set_stems) } } // sth if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.sth = abc2svg.sth.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/break-1.js0000644000400000040000000000574014565315274021740 0ustar jvjv// break.js - module to handle the %%break command // // Copyright (C) 2018-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 . // // This module is loaded when "%%break" appears in a ABC source. // // Parameters // %%break measure_nb [":" num "/" den] [" " measure ...]* if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.break = { // get the %%break parameters get_break: function(parm) { var C = abc2svg.C, b, c, d, sq, a = parm.split(/[ ,]/), cfmt = this.cfmt() if (!cfmt.break) cfmt.break = [] for (n = 1; n < a.length; n++) { b = a[n]; c = b.match(/(\d+)([a-z]?)(:\d+\/\d+)?/) if (!c) { this.syntax(1, this.errs.bad_val, "%%break") continue } if (c[2]) sq = c[2].charCodeAt(0) - 0x61 if (!c[3]) { cfmt.break.push({ // on measure bar m: c[1], t: 0, sq: sq}) continue } d = c[3].match(/:(\d+)\/(\d+)/) if (!d || d[2] < 1) { this.syntax(1, "Bad denominator in %%break") continue } cfmt.break.push({ m: c[1], t: d[1] * C.BLEN / d[2], sq: sq}) } }, // get_break() // insert the EOLs of %%break do_break: function() { var i, m, t, brk, seq, voice_tb = this.get_voice_tb() v = this.get_cur_sy().top_voice, s1 = voice_tb[v].sym, cfmt = this.cfmt() for (i = 0; i < cfmt.break.length; i++) { brk = cfmt.break[i]; m = brk.m for (s = s1; s; s = s.next) { if (s.bar_num == m) break } if (!s) continue if (brk.sq) { seq = brk.sq for (s = s.ts_next; s; s = s.ts_next) { if (s.bar_num == m) { if (--seq == 0) break } } if (!s) continue } t = brk.t if (t) { t = s.time + t for ( ; s; s = s.next) { if (s.time >= t) break } } else { s = s.next } if (s) s.soln = true } }, // do_break() do_pscom: function (of, text) { if (text.slice(0, 6) == "break ") abc2svg.break.get_break.call(this, text) else of(text) }, set_bar_num: function(of) { of() if (this.cfmt().break) abc2svg.break.do_break.call(this) }, set_hooks: function(abc) { abc.do_pscom = abc2svg.break.do_pscom.bind(abc, abc.do_pscom); abc.set_bar_num = abc2svg.break.set_bar_num.bind(abc, abc.set_bar_num) } } // break if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.break = abc2svg.break.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/grid-1.js0000644000400000040000000003174014565315274021600 0ustar jvjv// abc2svg - grid.js - module to insert a chord grid before or after a tune // // Copyright (C) 2018-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 . // // This module is loaded when "%%grid" appears in a ABC source. // // Parameters // %%grid [include=] [nomusic] [norepeat] [repbrk] [parts] // = number of columns (1: auto) // > 0: above the tune, < 0: under the tune // = comma separated list of (continuous) measure numbers // 'nomusic' displays only the grid // 'norepeat' omits the ':' indications // 'repbrk' starts a new grid line on start/stop repeat // 'parts' displays the parts on the left side of the grid // %%gridfont font_name size (default: 'serif 16') if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.grid = { pl: ' 1) { abc.out_svg(abc2svg.grid.pl) // / line abc.out_sxsy(x - wmx / 2, ' ', yl) abc.out_svg('l' + wmx.toFixed(1) + ' -' + hr.toFixed(1) + '"/>\n') if (cell[1]) { abc.out_svg(abc2svg.grid.pl) // \ left line abc.out_sxsy(x - wmx / 2, ' ', yl + hr) abc.out_svg('l' + (wmx / 2).toFixed(1) + ' ' + (hr / 2).toFixed(1) + '"/>\n') abc.set_font('gs') // small font abc.xy_str(x - wmx / 3, y, cell[0]) abc.xy_str(x, y + hr / 3, cell[1]) } else { abc.set_font('gs') abc.xy_str(x - wmx * .2, y + hr / 4, cell[0]) } if (cell.length >= 3) { if (cell[3]) { abc.out_svg(abc2svg.grid.pl) // \ right line abc.out_sxsy(x, ' ', yl + hr / 2) abc.out_svg('l' + (wmx / 2).toFixed(1) + ' ' + (hr / 2).toFixed(1) + '"/>\n') abc.set_font('gs') abc.xy_str(x, y - hr / 3, cell[2]) abc.xy_str(x + wmx / 3, y, cell[3]) } else { abc.set_font('gs') abc.xy_str(x + wmx * .2, y - hr / 4, cell[2]) } } } else { abc.set_font('grid') abc.xy_str(x, y, cell[0]) } } // build_cell() // draw the horizontal lines function draw_hl() { var i, i1, j, x, y = -1 for (i = 0; i <= nr + 1; i++) { j = 0 i1 = i > 0 ? i - 1 : 0 while (1) { while (j <= nc && !d[i1][j]) j++ if (j > nc) break x = wmx * j while (j <= nc && d[i1][j]) j++ if (i && i1 < nr) { while (j <= nc && d[i1 + 1][j]) j++ } abc.out_svg('M') abc.out_sxsy(x0 + x, ' ', y) abc.out_svg('h' + (wmx * j - x).toFixed(1)+ '\n') } y -= hr } } // draw_hl() // draw the vertical lines function draw_vl() { var i, i1, j, y, x = x0 for (i = 0; i <= nc; i++) { j = 0 i1 = i > 0 ? i - 1 : 0 while (1) { while (j <= nr && !d[j][i1]) j++ if (j > nr) break y = hr * j while (j <= nr && d[j][i1]) j++ abc.out_svg('M') abc.out_sxsy(x, ' ', -y - .5) abc.out_svg('v' + (hr * j - y + 1).toFixed(1) + '\n') } x += wmx } } // draw_vl() // ------- build_grid() ------- // set some chords in each cell set_chords() // build the content of the cells if (!grid.ls) { cells = chords } else { // with list of mesure numbers bar = bars; bars = [ ] ps = parts parts = [] for (i = 0; i < grid.ls.length; i++) { l = grid.ls[i] if (l.indexOf('-') < 0) l = [l, l] else l = l.split('-') for (k = l[0] - 1; k < l[1]; k++) { if (!chords[k]) // error break cells.push(chords[k]); bars.push(bar[k]) parts.push(ps[k]) } } bars.push(bar[k]) // ending bar } // get the number of columns if (nc < 0) nc = -nc if (nc < 3) // auto nc = cells.length % 6 == 0 ? 6 : 8 if (nc > cells.length) nc = cells.length; hr = font.size * 2 if (wmx < hr * 1.5) wmx = hr * 1.5 // cell width x0 = img.width - img.lm - img.rm // staff width w = wmx * nc if (w > x0) { nc /= 2; w /= 2 } // generate the cells yl = -1 y = -1 + font.size * .6 nr = -1 x0 = (x0 / cfmt.scale - w) / 2 d = [] for (i = 0; i < cells.length; i++) { if (i == 0 || (grid.repbrk && (bars[i].slice(-1) == ':' || bars[i][0] == ':')) || parts[i] || k >= nc) { y -= hr // new row yl -= hr x = x0 + wmx / 2 k = 0 nr++ d[nr] = [] } d[nr][k] = 1 k++ build_cell(cells[i], x, y, yl, hr) x += wmx } // draw the lines abc.out_svg('\n') // show the repeat signs and the parts y = -1 + font.size * .7 x = x0 for (i = 0; i < bars.length; i++) { bar = bars[i] if (bar[0] == ':') { abc.out_svg(':\n') } if (i == 0 || (grid.repbrk && (bar.slice(-1) == ':' || bar[0] == ':')) || parts[i] || k >= nc) { y -= hr; // new row x = x0 k = 0 if (parts[i]) { w = abc.strwh(parts[i])[0] abc.out_svg('' + parts[i] + '\n') } } k++ if (bar.slice(-1) == ':') { abc.out_svg(':\n') } x += wmx } abc.vskip(hr * (nr + 1) + 6) } // build_grid() // ----- block_gen() ----- var p_voice, n, font, f2 abc.set_page() img = abc.get_img() // set the text style font = abc.get_font('grid') if (font.class) font.class += ' mid' else font.class = 'mid' cls = abc.font_class(font) // define a smaller font abc.param_set_font("gsfont", font.name + ' ' + (font.size * .7).toFixed(1)) f2 = cfmt.gsfont if (font.weight) f2.weight = font.weight if (font.style) f2.style = font.style f2.class = font.class abc.add_style("\n.mid {text-anchor:middle}") // create the grid abc.blk_flush() build_grid(s, font) abc.blk_flush() }, // block_gen() set_stems: function(of) { var C = abc2svg.C, abc = this, tsfirst = abc.get_tsfirst(), voice_tb = abc.get_voice_tb(), cfmt = abc.cfmt(), grid = cfmt.grid // extract one of the chord symbols // With chords as "[yyy];xxx" // (!sel - default) returns "yyy" and (sel) returns "xxx" function cs_filter(a_cs) { var i, cs, t for (i = 0; i < a_cs.length; i++) { cs = a_cs[i] if (cs.type != 'g') continue t = cs.text if (cfmt.altchord) { for (i++; i < a_cs.length; i++) { cs = a_cs[i] if (cs.type != 'g') continue t = cs.text break } } return t.replace(/\[|\]/g, '') } } // cs_filter() function get_beat(s) { var beat = C.BLEN / 4 if (!s.a_meter[0] || s.a_meter[0].top[0] == 'C' || !s.a_meter[0].bot) return beat beat = C.BLEN / s.a_meter[0].bot[0] |0 if (s.a_meter[0].bot[0] == 8 && s.a_meter[0].top[0] % 3 == 0) beat = C.BLEN / 8 * 3 return beat } // get_beat() // build the arrays of chords and bars function build_chords(sb) { // block 'grid' var s, i, w, bt, rep, bars = [], chords = [], parts = [], chord = [], beat = get_beat(voice_tb[0].meter), wm = voice_tb[0].meter.wmeasure, cur_beat = 0, beat_i = 0, wmx = 0, some_chord = 0 // scan the music symbols bars.push('|') for (s = tsfirst; s; s = s.ts_next) { while (s.time > cur_beat) { if (beat_i < 3) // only 2, 3 or 4 beats / measure... beat_i++ cur_beat += beat } if (s.part) parts[chords.length] = s.part.text switch (s.type) { case C.NOTE: case C.REST: case C.SPACE: if (!s.a_gch || chord[beat_i]) break bt = cs_filter(s.a_gch) if (!bt) break w = abc.strwh(bt.replace( /<[^>]+>/gm,'')) if (w[0] > wmx) wmx = w[0] bt = new String(bt) bt.wh = w chord[beat_i] = bt break case C.BAR: i = s.bar_num // check if normal measure bar bt = s.bar_type while (s.ts_next && s.ts_next.time == s.time) { if (s.ts_next.dur || s.ts_next.type == C.SPACE) break s = s.ts_next if (s.type == C.METER) { beat = get_beat(s) wm = s.wmeasure continue } if (s.type != C.BAR) continue if (s.bar_type[0] == ':' && bt[0] != ':') bt = ':' + bt if (s.bar_type.slice(-1) == ':' && bt.slice(-1) != ':') bt += ':' if (s.bar_num) i = s.bar_num if (s.part) parts[chords.length + 1] = s.part.text } if (grid.norep) bt = '|' if (s.time < wm) { // if anacrusis if (chord.length) { chords.push(chord) bars.push(bt) } else { bars[0] = bt } } else { if (!i) // if not normal measure bar break chords.push(chord) bars.push(bt) } if (chord.length) some_chord++ chord = [] cur_beat = s.time // synchronize in case of error beat_i = 0 if (bt.indexOf(':') >= 0) rep = true // some repeat break case C.METER: beat = get_beat(s) wm = s.wmeasure break } } if (chord.length) { bars.push('') chords.push(chord) } if (!some_chord) return // no chord in this tune wmx += abc.strwh(rep ? ' ' : ' ')[0] sb.chords = chords sb.bars = bars if (grid.parts && parts.length) sb.parts = parts sb.wmx = wmx } // build_chords // -------- set_stems -------- // create a specific block if (grid) { var C = abc2svg.C, tsfirst = this.get_tsfirst(), fmt = tsfirst.fmt, voice_tb = this.get_voice_tb(), p_v = voice_tb[this.get_top_v()], s = { type: C.BLOCK, subtype: 'grid', dur: 0, time: 0, p_v: p_v, v: p_v.v, st: p_v.st } if (!cfmt.gridfont) abc.param_set_font("gridfont", "serif 16") abc.set_font('grid') build_chords(s) // build the array of the chords // and insert it in the tune if (!s.chords) { // if no chord ; } else if (grid.nomusic) { // if just the grid this.set_tsfirst(s) } else if (grid.n < 0) { // below for (var s2 = tsfirst; s2.ts_next; s2 = s2.ts_next) ; s.time = s2.time s.prev = p_v.last_sym.prev // before the last symbol s.prev.next = s s.next = p_v.last_sym p_v.last_sym.prev = s s.ts_prev = s2.ts_prev s.ts_prev.ts_next = s s.ts_next = s2 s2.ts_prev = s if (s2.seqst) { s.seqst = true s2.seqst = false } } else { // above s.next = p_v.sym s.ts_next = tsfirst tsfirst.ts_prev = s this.set_tsfirst(s) p_v.sym.prev = s p_v.sym = s } s.fmt = s.prev ? s.prev.fmt : fmt } of() }, // set_stems() set_fmt: function(of, cmd, parm) { if (cmd == "grid") { if (!parm) parm = "1"; parm = parm.split(/\s+/) var grid = {n: Number(parm.shift())} if (isNaN(grid.n)) { if (parm.length) { this.syntax(1, this.errs.bad_val, "%%grid") return } grid.n = 1 } while (parm.length) { var item = parm.shift() if (item == "norepeat") grid.norep = true else if (item == "nomusic") grid.nomusic = true else if (item == "parts") grid.parts = true else if (item == "repbrk") grid.repbrk = true else if (item.slice(0, 8) == "include=") grid.ls = item.slice(8).split(',') } this.cfmt().grid = grid return } of(cmd, parm) }, set_hooks: function(abc) { abc.block_gen = abc2svg.grid.block_gen.bind(abc, abc.block_gen) abc.set_stems = abc2svg.grid.set_stems.bind(abc, abc.set_stems) abc.set_format = abc2svg.grid.set_fmt.bind(abc, abc.set_format) } } // grid if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.grid = abc2svg.grid.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/grid3-1.js0000644000400000040000000002423014565315274021657 0ustar jvjv// grid3.js - module to insert a manual chords // // Copyright (C) 2020-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 . // // This module is loaded when "%%begingrid" appears in a ABC source. // // Parameters // %%begingrid [ chord-define [ noprint ] ] // list of chords, '-' or '.', measure bars ('|') and ':' for repeat // %%endgrid // // %%gridfont font_name size (default: 'serif 16') // // When this command appears inside a tune and when 'chord-define' is present, // the chords are used to define the chord symbols that are displayed // above the staff system. // When 'noprint' is also present, the grid itself is not displayed. if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.grid3 = { // generate the grid block_gen: function(of, s) { if (s.subtype != "grid3") { of(s) return } this.set_page() this.blk_flush() var abc = this, cfmt = abc.cfmt(), img = abc.get_img(), posy = abc.get_posy(), txt = s.text, font, font_cl, cls, w, ln, i, lines = [], cl = [], bars = [], cells = [], nr = 0, // number of rows nc = 0, // number of columns wc = 0 // width of a cell // generate the grid function build_grid() { var i, k, l, line, bl, bar, w, hr, x0, x, y, yl, cl, cell, lc = '', path = '' + cell[n] + '\n' } // build_ch() function build_cell(cell, x, y, yl, hr) { var line if (cell.length > 1) { line = path + (x - wc / 2).toFixed(1) + ' ' + yl.toFixed(1) + 'l' + wc.toFixed(1) + ' -' + hr.toFixed(1) + '"/>\n' if (cell[1]) { line += path + (x - wc / 2).toFixed(1) + ' ' + (yl - hr).toFixed(1) + 'l' + (wc / 2).toFixed(1) + ' ' + (hr / 2).toFixed(1) + '"/>\n' + build_ch(cls + sf, x - wc / 3, y, 0) + build_ch(cls + sf, x, y - hr * .32, 1) } else { line += build_ch(cls + sf, x - wc * .2, y - hr / 4, 0) } if (cell.length >= 3) { if (cell[3]) { line += path + x.toFixed(1) + ' ' + (yl - hr / 2).toFixed(1) + 'l' + (wc / 2).toFixed(1) + ' ' + (hr / 2).toFixed(1) + '"/>\n' + build_ch(cls + sf, x, y + hr * .3, 2) + build_ch(cls + sf, x + wc / 3, y, 3) } else { line += build_ch(cls + sf, x + wc * .2, y + hr / 4, 2) } } } else { line = build_ch(cls, x, y, 0) } return line } // build_cell() // -- build_grid() -- // build the content of the cells hr = font.size * 2.1 if (wc < hr * 1.4) wc = hr * 1.4 // cell width w = wc * nc // grid width // generate the cells yl = posy + 3 y = posy + 3 - font.size * .7 x0 = (img.width / cfmt.scale - w) / 2 while (1) { cl = cells.shift() if (!cl) break y += hr yl += hr x = x0 + wc / 2 while (1) { cell = cl.shift() if (!cell) break lc += build_cell(cell, x, y, yl, hr) x += wc } } // draw the lines line = '\n' // insert the cells line += lc // show the repeat signs y = posy + 3 - font.size * .7 while (1) { bl = bars.shift() if (!bl) break x = x0 y += hr while (1) { bar = bl.shift() if (!bar) break if (bar[0] == ':') line += ':\n' if (bar.slice(-1) == ':') line += ':\n' x += wc } } abc.out_svg(line) abc.vskip(hr * nr + 6) } // build_grid() // ----- block_gen ---- // set the text style if (!cfmt.gridfont) abc.param_set_font("gridfont", "serif 16") font = abc.get_font('grid') font_cl = abc.font_class(font) cls = font_cl + " mid" abc.add_style("\n.mid {text-anchor:middle}") abc.set_font('grid') // (for strwh()) // scan the grid content txt = txt.split('\n') while (1) { ln = txt.shift() // line if (!ln) break // extract the bars and the chords ln = ln.match(/[|:]+|[^|:\s]+/g) bars[nr] = [] cells[nr] = [] i = -1 while (1) { cl = ln.shift() if (!cl) break if (cl.match(/[:|]+/)) { bars[nr][++i] = cl cells[nr][i] = [] } else { if (!cells[nr][i]) { // if starting '|' is missing bars[nr][++i] = '|' cells[nr][i] = [] } if (cl == '.' || cl == '-') cl = '' cells[nr][i].push(cl) } } if (cells[nr][i].length) bars[nr][++i] = '|' // missing ending bar else cells[nr][i] = null // keep just the measure bar if (i > nc) nc = i i = 0 while (1) { cl = cells[nr][i++] if (!cl) break if (cl.length == 2) { cl[2] = cl[1] // "| A B |" => "|A - B|" cl[1] = '' } w = abc.strwh(cl.join(''))[0] if (w > wc) wc = w } nr++ } wc += abc.strwh(' ')[0] // build the grid and insert it in the music build_grid() abc.blk_flush() }, // block_gen() // handle %%begingrid do_begin_end: function(of, type, opt, txt) { var vt = this.get_voice_tb() if (type != "grid") { of(type, opt, txt) return } // replace the accidentals txt = txt.replace(/#|=|b/g, function(x) { switch (x) { case '#': return "\u266f" case '=': return "\u266e" // case 'b': return "\u266d" } return "\u266d" }) if (opt.indexOf("chord-define") >= 0) this.cfmt().csdef = txt if (opt.indexOf("noprint") < 0) { type += "3" if (this.parse.state >= 2) { s = this.new_block(type) s.text = txt } else { abc2svg.grid3.block_gen.call(this, null, { subtype: type, text: txt }) } } }, // do_begin_end() output_music: function(of) { var ln, i, dt, ss, ntim, p_vc, s3, C = abc2svg.C, abc = this, s = abc.get_tsfirst(), vt = abc.get_voice_tb(), t = abc.cfmt().csdef, cs = [] // add a chord symbol function add_cs(ss, ch) { var s = { // invisible rest in the voice "grid3" type: C.REST, fname: ss.fname, istart: ss.istart, iend: ss.iend, v: p_vc.v, p_v: p_vc, time: ntim, st: 0, fmt: ss.fmt, dur: 0, dur_orig: 0, invis: true, seqst: true, nhd: 0, notes: [{ pit: 18, dur: 0 }] } if (ch != '.' && ch != '-') { abc.set_a_gch(s, [{ // define the chord symbol type: 'g', text: ch, otext: ch, istart: ss.istart, iend: ss.iend, font: abc.get_font("gchord"), pos: p_vc.pos.gch || C.SL_ABOVE }]) } // insert the rest if (!p_vc.last_sym) { p_vc.sym = s } else { s.prev = p_vc.last_sym s.prev.next = s } p_vc.last_sym = s s.ts_next = ss s.ts_prev = ss.ts_prev s.ts_prev.ts_next = s ss.ts_prev = s if (s.time == ss.time) delete ss.seqst return s } // add_cs() if (t) { // if chord-define p_vc = { id: "grid3", v: vt.length, time: 0, pos: { gst: 0 }, scale: 1, st: 0, second: true, sls: [] } vt.push(p_vc) // add the voice supporting the chord symbols t = t.split('\n') while (1) { // scan the grid content ln = t.shift() // line if (!ln) break ln = ln.trimLeft() if (ln[0] == '|') ln = ln.slice(ln[1] == ':' ? 2 : 1) if (ln[ln.length - 1] != '|') ln = ln + '|' // extract the bars and the chords ln = ln.match(/[|:]+|[^|:\s]+/g) while (1) { cl = ln.shift() if (!cl) break if (cl[0] == '|' || cl[0] == ':') { while (s && !s.dur) s = s.ts_next if (!s) break ss = s // first note/rest while (s && !s.bar_type) s = s.ts_next // end of measure if (!cs.length) cs = ['.'] ntim = ss.time dt = (s.time - ntim) / cs.length s3 = null for (i = 0; i < cs.length; i++) { if ((cs[i] != '.' && cs[i] != '-') || !s3) { while (ss.time < ntim) ss = ss.ts_next s3 = add_cs(ss, cs[i]) } s3.dur += dt s3.dur_orig = s3.notes[0].dur = s3.dur ntim += dt } while (s && s.type != C.BAR) s = s.ts_next ss = { type: C.BAR, bar_type: "|", fname: s.fname, istart: s.istart, iend: s.iend, v: p_vc.v, p_v: p_vc, st: 0, time: s.time, dur: 0, nhd: 0, notes: [{ pit: 18 }], next: s, ts_next: s, prev: s.prev, ts_prev: s.ts_prev } if (!s) break ss.fmt = s.fmt if (s.seqst) { ss.seqst = true delete s.seqst } ss.prev.next = ss.ts_prev.ts_next = s.prev = s.ts_prev = ss cs = [] } else { cs.push(cl) } } } } of() }, // output_music() set_hooks: function(abc) { abc.block_gen = abc2svg.grid3.block_gen.bind(abc, abc.block_gen) abc.do_begin_end = abc2svg.grid3.do_begin_end.bind(abc, abc.do_begin_end) abc.output_music = abc2svg.grid3.output_music.bind(abc, abc.output_music) } } // grid3 if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.grid3 = abc2svg.grid3.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/ambitus-1.js0000644000400000040000000000700214565315274022311 0ustar jvjv// ambitus.js - module to insert an ambitus at start of a voice // // Copyright (C) 2018-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 . // // This module is loaded when "%%ambitus" appears in a ABC source. // // Parameters // %%ambitus 1 if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.ambitus = { do_ambitus: function() { var C = abc2svg.C, s, v, p_v, min, max, voice_tb = this.get_voice_tb() for (v = 0; v < voice_tb.length; v++) { p_v = voice_tb[v]; if (p_v.second) continue min = 100; max = -100 // search the top and bottom pitches for (s = p_v.sym; s; s = s.next) { if (s.type != C.NOTE) continue if (s.notes[s.nhd].pit > max) max = s.notes[s.nhd].pit if (s.notes[0].pit < min) min = s.notes[0].pit } if (min == 100) continue // no note s = p_v.clef; s.stem = 1; s.head = C.FULL; s.stemless = true; s.nhd = 1; s.notes = [{ dur: C.BLEN / 4, pit: min, shhd: 0 },{ dur: C.BLEN / 4, pit: max, shhd: 0 }] } }, // do_ambitus() draw_symbols: function(of, p_voice) { var staff_tb = this.get_staff_tb(), s = p_voice.sym if (s.clef_type != undefined && s.nhd > 0) { s.x -= 26; this.set_scale(s); this.draw_note(s) if (s.notes[1].pit - s.notes[0].pit > 4) { this.xypath(s.x, 3 * (s.notes[1].pit - 18) + staff_tb[s.st].y); this.out_svg('v' + ((s.notes[1].pit - s.notes[0].pit) * 3).toFixed(1) + '" stroke-width=".6"/>\n'); } s.x += 26; s.nhd = 0 p_voice.clef.nhd = 0 } of(p_voice) }, // draw_symbols() set_pitch: function(of, last_s) { of(last_s) if (!last_s && this.cfmt().ambitus) abc2svg.ambitus.do_ambitus.call(this) }, set_fmt: function(of, cmd, param) { if (cmd == "ambitus") { this.cfmt().ambitus = param return } of(cmd, param) }, set_glue: function(of, w) { var v, s, voice_tb = this.get_voice_tb() of(w) if (this.cfmt().ambitus) { for (v = 0; v < voice_tb.length; v++) { s = voice_tb[v].sym while (!s.time && !s.clef_type) s = s.next if (s && s.nhd) { // draw the helper lines if any s.x -= 26 this.draw_hl(s) s.ymn = 3 * (s.notes[0].pit - 18) - 2 s.ymx = 3 * (s.notes[1].pit - 18) + 2 s.x += 26 } } } }, // set_glue() set_width: function(of, s) { if (s.clef_type != undefined && s.nhd > 0) { s.wl = 40; s.wr = 12 } else { of(s) } }, set_hooks: function(abc) { abc.draw_symbols = abc2svg.ambitus.draw_symbols.bind(abc, abc.draw_symbols); abc.set_pitch = abc2svg.ambitus.set_pitch.bind(abc, abc.set_pitch); abc.set_format = abc2svg.ambitus.set_fmt.bind(abc, abc.set_format); abc.set_sym_glue = abc2svg.ambitus.set_glue.bind(abc, abc.set_sym_glue) abc.set_width = abc2svg.ambitus.set_width.bind(abc, abc.set_width) } } // ambitus if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.ambitus = abc2svg.ambitus.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/grid2-1.js0000644000400000040000000000637414565315274021667 0ustar jvjv// grid2.js - module to replace a voice in the music by a chord grid // // Copyright (C) 2018-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 . // // This module is loaded when "%%grid2" appears in a ABC source. // // Parameters // %%grid2 y // This command may appear globally or in a voice. if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.grid2 = { // function called before tune generation do_grid: function() { var s, v, p_v, ix, cs, c_a_cs, bt, gch, voice_tb = this.get_voice_tb() if (this.cfmt().grid2) for (v = 0; v < voice_tb.length; v++) if (voice_tb[v].grid2 == undefined) voice_tb[v].grid2 = 1 for (v = 0; v < voice_tb.length; v++) { p_v = voice_tb[v] if (!p_v.grid2) continue curvoice = p_v this.set_v_param("stafflines", "...") // no staff p_v.clef.invis = true; // no clef p_v.key.k_sf = 0 // no key signature delete p_v.key.k_a_acc p_v.staffnonote = 2 // draw the staff for (s = p_v.sym; s; s = s.next) { delete s.a_dd // no decoration if (!s.dur) { if (s.bar_type) bt = s.time continue } // set all notes s.invis = true; // as invisible delete s.sl1; // with no slur delete s.ti1 // and no tie delete s.ti2 for (ix = 0; ix <= s.nhd; ix++) delete s.notes[ix].tie_ty if (s.tf) // don't show the tuplets s.tf[0] = 1 if (!s.a_gch) { if (s.time == bt) s.a_gch = [ this.clone(c_a_cs) ] continue } for (ix = 0; ix < s.a_gch.length; ix++) { gch = s.a_gch[ix] if (gch.type == 'g') { c_a_cs = gch break } } } } }, // do_grid() // draw the chord symbol in the middle of the staff draw_gchord: function(of, i, s, x, y) { var an if (s.p_v.grid2) { this.set_dscale(s.st) an = s.a_gch[i] if (an.type == 'g') { this.use_font(an.font) this.set_font(an.font) this.xy_str(s.x + an.x, 12 - an.font.size * .5, an.text) } } else { of(i, s, x, y) } }, output_music: function(of) { abc2svg.grid2.do_grid.call(this); of() }, set_fmt: function(of, cmd, param) { if (cmd == "grid2") { var curvoice = this.get_curvoice(), v = this.get_bool(param) if (curvoice) curvoice.grid2 = v else this.cfmt().grid2 = v return } of(cmd, param) }, set_hooks: function(abc) { abc.draw_gchord = abc2svg.grid2.draw_gchord.bind(abc, abc.draw_gchord); abc.output_music = abc2svg.grid2.output_music.bind(abc, abc.output_music); abc.set_format = abc2svg.grid2.set_fmt.bind(abc, abc.set_format) } } // grid2 if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.grid2 = abc2svg.grid2.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/jianpu-1.js0000644000400000040000000003115514565315274022141 0ustar jvjv// jianpu.js - module to output jiănpŭ (简谱) music sheets // // Copyright (C) 2020-2024 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 . // // This module is loaded when "%%jianpu" appears in a ABC source. // // Parameters (none) // %%jianpu 1 if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.jianpu = { k_tb: [ "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#" ], cde2fcg: new Int8Array([0, 2, 4, -1, 1, 3, 5]), cgd2cde: new Int8Array([0, -4, -1, -5, -2, -6, -3, 0, -4, -1, -5, -2, -6, -3, 0]), acc2: new Int8Array([-2, -1, 3, 1, 2]), acc_tb: ["\ue264", "\ue260", , "\ue262", "\ue263", "\ue261"], // don't calculate the beams calc_beam: function(of, bm, s1) { if (!s1.p_v.jianpu) return of(bm, s1) // return 0 }, // calc_beam() // adjust some symbols before the generation output_music: function(of) { var p_v, v, C = abc2svg.C, abc = this, cur_sy = abc.get_cur_sy(), voice_tb = abc.get_voice_tb() // handle the overlay voices function ov_def(v) { var s1, tim, s = p_v.sym while (s) { s1 = s.ts_prev if (!s.invis && s.dur && s1.v != v && s1.st == s.st // overlay start && s1.time == s.time) { while (1) { // go back to the previous bar if (!s1.prev || s1.prev.bar_type) break s1 = s1.prev } //add deco '{' on s1 while (!s1.bar_type) { s1.dy = 14 s1.notes[0].pit = 30 if (s1.type == C.REST) s1.combine = -1 s1 = s1.next } // add deco '}' on s1 while (1) { s.dy = -14 s.notes[0].pit = 20 if (!s.next || s.next.bar_type || s.next.time >= s1.time) break s = s.next } } s = s.next } } // ov_def() // output the key and time signatures function set_head() { var v, p_v, mt, s2, sk, s, tsfirst = abc.get_tsfirst() // search a jianpu voice for (v = 0; v < voice_tb.length; v++) { p_v = voice_tb[v] if (p_v.jianpu) break } if (v >= voice_tb.length) return mt = p_v.meter.a_meter[0] sk = p_v.key s2 = p_v.sym s = { type: C.BLOCK, subtype: "text", time: s2.time, dur: 0, v: 0, p_v: p_v, st: 0, fmt: s2.fmt, seqst: true, text: (sk.k_mode + 1) + "=" + (abc2svg.jianpu.k_tb[sk.k_sf + 7 + abc2svg.jianpu.cde2fcg[sk.k_mode]]), font: abc.get_font("text") } if (mt) s.text += ' ' + (mt.bot ? (mt.top + '/' + mt.bot) : mt.top) // insert the block after the first %%staves s2 = tsfirst s.next = s2.next if (s.next) s.next.prev = s s.prev = s2 s2.next = s s.ts_next = s2.ts_next s.ts_next.ts_prev = s s.ts_prev = s2 s2.ts_next = s } // set head() // expand a long note/rest function slice(s) { var n, s2, s3, jn = s.type == C.REST ? 0 : 8 // '0' or '-' if (s.dur >= C.BLEN) n = 3 else if (s.dur == C.BLEN / 2) n = 1 else n = 2 // duplicate the note/rest for display s2 = abc.clone(s) s2.invis = s2.play = 1 //true s2.next = s if (s2.prev) s2.prev.next = s2 s.prev = s2 s2.ts_next = s if (s2.ts_prev) s2.ts_prev.ts_next = s2 s.ts_prev = s2 delete s.seqst s.noplay = 1 // true // create the continuation symbols // s.notes[0].dur = s.dur = s.dur_orig = C.BLEN / 4 delete s.fmr while (--n >= 0) { s2 = { type: C.REST, v: s.v, p_v: s.p_v, st: s.st, dur: C.BLEN / 4, dur_orig: C.BLEN / 4, fmt: s.fmt, stem: 0, multi: 0, nhd: 0, notes: [{ dur: s.dur, pit: s.notes[0].pit, jn: jn }], xmx: 0, noplay: true, time: s.time + C.BLEN / 4, prev: s, next: s.next } s.next = s2 if (s2.next) s2.next.prev = s2 if (!s.ts_next) { s.ts_next = s2 if (s.soln) s.soln = false s2.ts_prev = s s2.seqst = true } else { for (s3 = s.ts_next; s3; s3 = s3.ts_next) { if (s3.time < s2.time) continue if (s3.time > s2.time) { s2.seqst = true s3 = s3.ts_prev } s2.ts_next = s3.ts_next s2.ts_prev = s3 if (s2.ts_next) s2.ts_next.ts_prev = s2 s3.ts_next = s2 break } } s = s2 } } // slice() function set_note(s, sf) { var i, m, note, p, pit, a, nn, delta = abc2svg.jianpu.cgd2cde[sf + 7] - 2 s.stem = -1 s.stemless = true if (s.sls) { for (i = 0; i < s.sls.length; i++) s.sls[i].ty = C.SL_ABOVE } for (m = 0; m <= s.nhd; m++) { note = s.notes[m] // note head p = note.pit pit = p + delta note.jn = ((pit + 77) % 7) + 1 // note number // set a fixed offset to the note for the decorations note.pit = 25 // "e" note.jo = (pit / 7) | 0 // octave number // accidentals a = note.acc if (a) { nn = abc2svg.jianpu.cde2fcg[(p + 5 + 16 * 7) % 7] - sf if (a != 3) nn += a * 7 nn = ((((nn + 1 + 21) / 7) | 0) + 2 - 3 + 32 * 5) % 5 note.acc = abc2svg.jianpu.acc2[nn] } // set the slurs and ties up if (note.sls) { for (i = 0; i < note.sls.length; i++) note.sls[i].ty = C.SL_ABOVE } if (note.tie_ty) note.tie_ty = C.SL_ABOVE } // change the long notes if (s.dur >= C.BLEN / 2 && !s.invis) slice(s) // replace the staccato dot if (s.a_dd) { for (i = 0; i < s.a_dd.length; i++) { if (s.a_dd[i].glyph == "stc") { abc.deco_put("gstc", s) s.a_dd[i] = s.a_dd.pop() } } } } // set_note() function set_sym(p_v) { var s, g, sf = p_v.key.k_sf delete p_v.key.k_a_acc // no accidental // no (visible) clef s = p_v.clef s.invis = true s.clef_type = 't' s.clef_line = 2 // scan the voice for (s = p_v.sym; s; s = s.next) { s.st = p_v.st switch (s.type) { case C.CLEF: s.invis = true s.clef_type = 't' s.clef_line = 2 // continue default: continue case C.KEY: sf = s.k_sf s.a_gch = [{ type: '@', font: abc.get_font("annotation"), wh: [10, 10], x: -5, y: 26, text: (s.k_mode + 1) + "=" + (abc2svg.jianpu.k_tb[sf + 7 + abc2svg.jianpu.cde2fcg[s.k_mode]]) }] continue case C.REST: if (s.notes[0].jn) continue s.notes[0].jn = 0 if (s.dur >= C.BLEN / 2 && !s.invis) slice(s) continue case C.NOTE: // change the notes set_note(s, sf) break case C.GRACE: for (g = s.extra; g; g = g.next) set_note(g, sf) break } } } // set_sym() // -- output_music -- set_head() for (v = 0; v < voice_tb.length; v++) { p_v = voice_tb[v] if (p_v.jianpu) { set_sym(p_v) if (p_v.second) ov_def(v) } } of() }, // output_music() draw_symbols: function(of, p_voice) { var s, s2, nl, y, C = abc2svg.C, abc = this, dot = "\ue1e7", anno_a = abc.anno_a, staff_tb = abc.get_staff_tb(), out_svg = abc.out_svg, out_sxsy = abc.out_sxsy, xypath = abc.xypath if (!p_voice.jianpu) { of(p_voice) return } // draw the duration lines under the notes function draw_dur(s1, x, y, s2, n, nl) { var s, s3, sc = s1.grace ? .5 : 1 xypath(x - 3, y + 5) out_svg('h' + ((s2.x - s1.x) / sc + 8).toFixed(1) + '"/>\n') // " y -= 2.5 while (++n <= nl) { s = s1 while (1) { if (s.nflags && s.nflags >= n) { s3 = s while (s != s2) { if (s.next.beam_br1 || (s.next.beam_br2 && n > 2) || (s.next.nflags && s.next.nflags < n)) break s = s.next } draw_dur(s3, s3.x, y, s, n, nl) } if (s == s2) break s = s.next } } } // draw_dur() function out_mus(x, y, p) { out_svg('' + p + '\n') } // out_mus() function out_txt(x, y, p) { out_svg('' + p + '\n') } // out_txt() function draw_hd(s, x, y) { var m, note, ym for (m = 0; m <= s.nhd; m++) { note = s.notes[m] out_txt(x - 3.5, y + 8, "01234567-"[note.jn]) if (note.acc) out_mus(x - 12, y + 12, abc2svg.jianpu.acc_tb[note.acc + 2]) if (note.jo > 2) { out_mus(x - 1, y + 22, dot) if (note.jo > 3) { y += 3 out_mus(x - 1, y + 22, dot) } } else if (note.jo < 2) { ym = y + 4 if (m == 0 && s.nflags > 0) ym -= 2.5 * s.nflags out_mus(x - 1, ym, dot) if (note.jo < 1) { ym -= 3 out_mus(x - 1, ym, dot) } } y += 20 } } // draw_hd() function draw_note(s) { var sc = 1, x = s.x, y = staff_tb[s.st].y if (s.dy) y += s.dy // voice overlay if (s.grace) { out_svg('\n') abc.stv_g().g++ // in container x = 0 y = 0 sc = .5 } draw_hd(s, x, y) if (s.nflags >= 0 && s.dots) out_mus(x + 8 * sc, y + 13 * sc, dot) if (s.grace) { out_svg('\n') abc.stv_g().g-- } anno_a.push(s) } // draw_note() // -- draw_symbols -- for (s = p_voice.sym; s; s = s.next) { if (s.invis) continue switch (s.type) { case C.METER: abc.draw_meter(s) break case C.NOTE: case C.REST: draw_note(s) break case C.GRACE: for (g = s.extra; g; g = g.next) draw_note(g) break } } // draw the (pseudo) beams for (s = p_voice.sym; s; s = s.next) { if (s.invis) continue switch (s.type) { case C.NOTE: case C.REST: nl = s.nflags if (nl <= 0) continue y = staff_tb[s.st].y s2 = s while (s.next && s.next.nflags > 0) { s = s.next if (s.nflags > nl) nl = s.nflags if (s.beam_end) break } if (s.dy) y += s.dy draw_dur(s2, s2.x, y, s, 1, nl) break } } }, // draw_symbols() // set some parameters set_fmt: function(of, cmd, param) { if (cmd == "jianpu") { this.set_v_param("jianpu", param) return } of(cmd, param) }, // set_fmt() // adjust some values set_pitch: function(of, last_s) { of(last_s) if (!last_s) return // first time var C = abc2svg.C for (var s = this.get_tsfirst(); s; s = s.ts_next) { if (!s.p_v.jianpu) continue switch (s.type) { // draw the key signature only in the first voice // and not at start of the staff case C.KEY: if (s.prev.type == C.CLEF || s.v != 0) s.a_gch = null break // adjust the vertical spacing above the note heads case C.NOTE: s.ymx = 20 * s.nhd + 22 if (s.notes[s.nhd].jo > 2) { s.ymx += 3 if (s.notes[s.nhd].jo > 3) s.ymx += 2 } s.ymn = 0 // bottom of line break } } }, // set_pitch() set_vp: function(of, a) { var i, p_v = this.get_curvoice() for (i = 0; i < a.length; i++) { if (a[i] == "jianpu=") { p_v.jianpu = this.get_bool(a[++i]) if (p_v.jianpu) this.set_vp([ "staffsep=", "20", "sysstaffsep=", "14", "stafflines=", "...", "tuplets=", "0 1 0 1" // [auto, slur, number, above] ]) break } } of(a) }, // set_vp() // set the width of some symbols set_width: function(of, s) { of(s) if (!s.p_v // (if voice_tb[v].clef/key/meter) || !s.p_v.jianpu) return var w, m, note, C = abc2svg.C switch (s.type) { case C.CLEF: case C.KEY: // case C.METER: s.wl = s.wr = .1 // (must not be null) break case C.NOTE: for (m = 0; m <= s.nhd; m++) { note = s.notes[m] if (note.acc && s.wl < 14) // room for the accidental s.wl = 14 } break } }, // set_width() set_hooks: function(abc) { abc.calculate_beam = abc2svg.jianpu.calc_beam.bind(abc, abc.calculate_beam) abc.draw_symbols = abc2svg.jianpu.draw_symbols.bind(abc, abc.draw_symbols) abc.output_music = abc2svg.jianpu.output_music.bind(abc, abc.output_music) abc.set_format = abc2svg.jianpu.set_fmt.bind(abc, abc.set_format) abc.set_pitch = abc2svg.jianpu.set_pitch.bind(abc, abc.set_pitch) abc.set_vp = abc2svg.jianpu.set_vp.bind(abc, abc.set_vp) abc.set_width = abc2svg.jianpu.set_width.bind(abc, abc.set_width) // big staccato dot abc.get_glyphs().gstc = '' abc.get_decos().gstc = "0 gstc 5 1 1" abc.add_style("\n.fj{font:15px sans-serif}") } // set_hooks() } // jianpu if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.jianpu = abc2svg.jianpu.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/abc2svg-1.js0000644000400000040000000177122214565315274022211 0ustar jvjv// abc2svg - abc2svg.js // // Copyright (C) 2014-2022 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 . // define the abc2svg object is not yet done if (typeof abc2svg == "undefined") var abc2svg = {}; // constants abc2svg.C = { BLEN: 1536, // symbol types BAR: 0, CLEF: 1, CUSTOS: 2, SM: 3, // sequence marker (transient) GRACE: 4, KEY: 5, METER: 6, MREST: 7, NOTE: 8, PART: 9, REST: 10, SPACE: 11, STAVES: 12, STBRK: 13, TEMPO: 14, BLOCK: 16, REMARK: 17, // note heads FULL: 0, EMPTY: 1, OVAL: 2, OVALBARS: 3, SQUARE: 4, // position types SL_ABOVE: 0x01, // position (3 bits) SL_BELOW: 0x02, SL_AUTO: 0x03, SL_HIDDEN: 0x04, SL_DOTTED: 0x08, // modifiers SL_ALI_MSK: 0x70, // align SL_ALIGN: 0x10, SL_CENTER: 0x20, SL_CLOSE: 0x40 }; // !! tied to the symbol types in abc2svg.js !! abc2svg.sym_name = ['bar', 'clef', 'custos', 'smark', 'grace', 'key', 'meter', 'Zrest', 'note', 'part', 'rest', 'yspace', 'staves', 'Break', 'tempo', '', 'block', 'remark'] // key table - index = number of accidentals + 7 abc2svg.keys = [ new Int8Array([-1,-1,-1,-1,-1,-1,-1 ]), // 7 flat signs new Int8Array([-1,-1,-1, 0,-1,-1,-1 ]), // 6 flat signs new Int8Array([ 0,-1,-1, 0,-1,-1,-1 ]), // 5 flat signs new Int8Array([ 0,-1,-1, 0, 0,-1,-1 ]), // 4 flat signs new Int8Array([ 0, 0,-1, 0, 0,-1,-1 ]), // 3 flat signs new Int8Array([ 0, 0,-1, 0, 0, 0,-1 ]), // 2 flat signs new Int8Array([ 0, 0, 0, 0, 0, 0,-1 ]), // 1 flat signs new Int8Array([ 0, 0, 0, 0, 0, 0, 0 ]), // no accidental new Int8Array([ 0, 0, 0, 1, 0, 0, 0 ]), // 1 sharp signs new Int8Array([ 1, 0, 0, 1, 0, 0, 0 ]), // 2 sharp signs new Int8Array([ 1, 0, 0, 1, 1, 0, 0 ]), // 3 sharp signs new Int8Array([ 1, 1, 0, 1, 1, 0, 0 ]), // 4 sharp signs new Int8Array([ 1, 1, 0, 1, 1, 1, 0 ]), // 5 sharp signs new Int8Array([ 1, 1, 1, 1, 1, 1, 0 ]), // 6 sharp signs new Int8Array([ 1, 1, 1, 1, 1, 1, 1 ]) // 7 sharp signs ] // base-40 representation of musical pitch // (http://www.ccarh.org/publications/reprints/base40/) abc2svg.p_b40 = new Int8Array( // staff pitch to base-40 // C D E F G A B [ 2, 8, 14, 19, 25, 31, 37 ]) abc2svg.b40_p = new Int8Array( // base-40 to staff pitch // C D [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, // E F G 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, // A B 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6 ]) abc2svg.b40_a = new Int8Array( // base-40 to accidental // C D [-2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, // E F G -2, -1, 0, 1, 2, -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2, -3, // A B -2, -1, 0, 1, 2, -3, -2, -1, 0, 1, 2 ]) abc2svg.b40_m = new Int8Array( // base-40 to midi // C D [-2, -1, 0, 1, 2, 0, 0, 1, 2, 3, 4, 0, // E F G 2, 3, 4, 5, 6, 3, 4, 5, 6, 7, 0, 5, 6, 7, 8, 9, 0, // A B 7, 8, 9, 10, 11, 0, 9, 10, 11, 12, 13 ]) abc2svg.b40l5 = new Int8Array([ // base-40 to line of fifth // C D -14, -7, 0, 7, 14, 0,-12, -5, 2, 9, 16, 0, // E F G -10, -3, 4, 11, 18,-15, -8, -1, 6, 13, 0,-13, -6, 1, 8, 15, 0, // A B -11, -4, 3, 10, 17, 0, -9, -2, 5, 12, 19 ]) abc2svg.isb40 = new Int8Array( // interval with sharp to base-40 interval [0, 1, 6,7,12,17,18,23,24,29,30,35]) abc2svg.pab40 = function(p, a) { p += 19 // staff pitch from C-1 var b40 = ((p / 7) | 0) * 40 + abc2svg.p_b40[p % 7] if (a && a != 3) // if some accidental, but not natural b40 += a return b40 } // pit2b40() abc2svg.b40p = function(b) { return ((b / 40) | 0) * 7 + abc2svg.b40_p[b % 40] - 19 } // b40p() abc2svg.b40a = function(b) { return abc2svg.b40_a[b % 40] } // b40a() abc2svg.b40m = function(b) { return ((b / 40) | 0) * 12 + abc2svg.b40_m[b % 40] } // b40m() // chord table // This table is used in various modules // to convert the types of chord symbols to a minimum set. // More chord types may be added by the command %%chordalias. abc2svg.ch_alias = { "maj": "", "min": "m", "-": "m", "°": "dim", "+": "aug", "+5": "aug", "maj7": "M7", "Δ7": "M7", "Δ": "M7", "min7": "m7", "-7": "m7", "ø7": "m7b5", "°7": "dim7", "min+7": "m+7", "aug7": "+7", "7+5": "+7", "7#5": "+7", "sus": "sus4", "7sus": "7sus4" } // ch_alias // global fonts abc2svg.font_tb = [] // fonts - index = font.fid abc2svg.font_st = {} // font style => font_tb index for incomplete user fonts // font weight // reference: // https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight abc2svg.ft_w = { thin: 100, extralight: 200, light: 300, regular: 400, medium: 500, semi: 600, demi: 600, semibold: 600, demibold: 600, bold: 700, extrabold: 800, ultrabold: 800, black: 900, heavy: 900 } abc2svg.ft_re = new RegExp('\ -?Thin|-?Extra Light|-?Light|-?Regular|-?Medium|\ -?[DS]emi|-?[DS]emi[ -]?Bold|\ -?Bold|-?Extra[ -]?Bold|-?Ultra[ -]?Bold|-?Black|-?Heavy/', "i") // simplify a rational number n/d abc2svg.rat = function(n, d) { var a, t, n0 = 0, d1 = 0, n1 = 1, d0 = 1 while (1) { if (d == 0) break t = d a = (n / d) | 0 d = n % d n = t t = n0 + a * n1 n0 = n1 n1 = t t = d0 + a * d1 d0 = d1 d1 = t } return [n1, d1] } // rat() // compare pitches // This function is used to sort the note pitches abc2svg.pitcmp = function(n1, n2) { return n1.pit - n2.pit } // start of the Abc object abc2svg.Abc = function(user) { "use strict"; // constants var C = abc2svg.C; // mask some unsafe functions var require = empty_function, system = empty_function, write = empty_function, XMLHttpRequest = empty_function, std = null, os = null // -- constants -- // staff system var OPEN_BRACE = 0x01, CLOSE_BRACE = 0x02, OPEN_BRACKET = 0x04, CLOSE_BRACKET = 0x08, OPEN_PARENTH = 0x10, CLOSE_PARENTH = 0x20, STOP_BAR = 0x40, FL_VOICE = 0x80, OPEN_BRACE2 = 0x0100, CLOSE_BRACE2 = 0x0200, OPEN_BRACKET2 = 0x0400, CLOSE_BRACKET2 = 0x0800, MASTER_VOICE = 0x1000, IN = 96, // resolution 96 PPI CM = 37.8, // 1 inch = 2.54 centimeter YSTEP // number of steps for y offsets // error texts var errs = { bad_char: "Bad character '$1'", bad_grace: "Bad character in grace note sequence", bad_transp: "Bad transpose value", bad_val: "Bad value in $1", bar_grace: "Cannot have a bar in grace notes", ignored: "$1: inside tune - ignored", misplaced: "Misplaced '$1' in %%score", must_note: "!$1! must be on a note", must_note_rest: "!$1! must be on a note or a rest", nonote_vo: "No note in voice overlay", not_ascii: "Not an ASCII character", not_enough_n: 'Not enough notes/rests for %%repeat', not_enough_m: 'Not enough measures for %%repeat', not_enough_p: "Not enough parameters in %%map", not_in_tune: "Cannot have '$1' inside a tune", notransp: "Cannot transpose with a temperament" } var self = this, // needed for modules glovar = { meter: { type: C.METER, // meter in tune header wmeasure: 1, // no M: a_meter: [] // default: none }, }, info = {}, // information fields parse = { ctx: {}, prefix: '%', state: 0, ottava: [], line: new scanBuf }, tunes = [], // first time symbol and voice array per tune for playing psvg // PostScript // utilities function clone(obj, lvl) { if (!obj) return obj var tmp = new obj.constructor for (var k in obj) if (obj.hasOwnProperty(k)) { if (lvl && typeof obj[k] == "object") tmp[k] = clone(obj[k], lvl - 1) else tmp[k] = obj[k] } return tmp } function errbld(sev, txt, fn, idx) { var i, j, l, c, h if (user.errbld) { switch (sev) { case 0: sev = "warn"; break case 1: sev = "error"; break default: sev= "fatal"; break } user.errbld(sev, txt, fn, idx) return } if (idx != undefined && idx >= 0) { i = l = 0 while (1) { j = parse.file.indexOf('\n', i) if (j < 0 || j > idx) break l++; i = j + 1 } c = idx - i } h = "" if (fn) { h = fn if (l) h += ":" + (l + 1) + ":" + (c + 1); h += " " } switch (sev) { case 0: h += "Warning: "; break case 1: h += "Error: "; break default: h += "Internal bug: "; break } user.errmsg(h + txt, l, c) } function error(sev, s, msg, a1, a2, a3, a4) { var i, j, regex, tmp if (!sev && cfmt.quiet) return if (s) { if (s.err) // only one error message per symbol return s.err = true } if (user.textrans) { tmp = user.textrans[msg] if (tmp) msg = tmp } if (arguments.length > 3) msg = msg.replace(/\$./g, function(a) { switch (a) { case '$1': return a1 case '$2': return a2 case '$3': return a3 default : return a4 } }) if (s && s.fname) errbld(sev, msg, s.fname, s.istart) else errbld(sev, msg) } // scanning functions function scanBuf() { // this.buffer = buffer this.index = 0; scanBuf.prototype.char = function() { return this.buffer[this.index] } scanBuf.prototype.next_char = function() { return this.buffer[++this.index] } scanBuf.prototype.get_int = function() { var val = 0, c = this.buffer[this.index] while (c >= '0' && c <= '9') { val = val * 10 + Number(c); c = this.next_char() } return val } } function syntax(sev, msg, a1, a2, a3, a4) { var s = { fname: parse.fname, istart: parse.istart + parse.line.index } error(sev, s, msg, a1, a2, a3, a4) } // inject javascript code function js_inject(js) { eval('"use strict";\n' + js) } // abc2svg - deco.js - decorations // // Copyright (C) 2014-2024 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 . // Decoration objects // dd { // decoration definition (static) // dd_en, // definition of the ending decoration // dd_st, // definition of the starting decoration // func, // function // glyph, // glyph // h, // height / ascent // hd, // descent // inv, // inverted glyph // name, // name // str, // string // wl, // left width // wr, // right width // } // de { // decoration elements (in an array - one list per music line) // dd, // definition of the decoration // defl { // flags // noen, // no end of this decoration // nost, // no start of this decoration // }, // has_val, // defined value // ix, // index of the decoration in the 'de' list // lden, // end of a long decoration // ldst, // start of a long decoration if true // m, // note index when note decoration // prev, // previous decoration (hack for 'tr~~~~~') // s, // symbol // start, // start of the decoration (in the ending element) // up, // above the symbol // val, // value // x, // x offset // y, // y offset // } var dd_tb = {}, // definition of the decorations a_de, // array of the decoration elements cross // cross voice decorations // decorations - populate with standard decorations var decos = { dot: "0 stc 6 1.5 1", tenuto: "0 emb 6 4 3", slide: "1 sld 3 7 1", arpeggio: "2 arp 12 10 3", roll: "3 roll 5,4 5 6", lowermordent: "3 lmrd 6,5 4 6", uppermordent: "3 umrd 6,5 4 6", trill: "3 trl 14 5 8", upbow: "3 upb 10,2 3 7", downbow: "3 dnb 9 4 6", gmark: "3 grm 7 4 6", wedge: "0 wedge 8 1.5 1", // (staccatissimo or spiccato) longphrase: "5 lphr 0 1 16", mediumphrase: "5 mphr 0 1 16", shortphrase: "5 sphr 0 1 16", turnx: "3 turnx 7,2.5 5 6", invertedturn: "3 turn 7,2 5 6", "0": "3 fng 5,5 3 3 0", "1": "3 fng 5,5 3 3 1", "2": "3 fng 5,5 3 3 2", "3": "3 fng 5,5 3 3 3", "4": "3 fng 5,5 3 3 4", "5": "3 fng 5,5 3 3 5", plus: "3 dplus 8,2 2 4", "+": "3 dplus 8,2 2 4", ">": "5 accent 3.5,3.5 4 4", accent: "5 accent 3.5,3.5 4 4", emphasis: "5 accent 3.5,3.5 4 4", marcato: "3 marcato 9 5 5", "^": "3 marcato 9 5 5", mordent: "3 lmrd 6,5 4 6", open: "3 opend 8 3 3", snap: "3 snap 10 3 3", thumb: "3 thumb 10 3 3", turn: "3 turn 7,2.5 5 6", "trill(": "5 ltr 8 0 0", "trill)": "5 ltr 8 0 0", "8va(": "5 8va 12 6 6", "8va)": "5 8va 12 6 6", "8vb(": "4 8vb 10,5 6 6", "8vb)": "4 8vb 10,5 6 6", "15ma(": "5 15ma 12 9 9", "15ma)": "5 15ma 12 9 9", "15mb(": "4 15mb 12 9 9", "15mb)": "4 15mb 12 9 9", breath: "5 brth 0 1 16", caesura: "5 caes 0 1 20", short: "5 short 0 1 16", tick: "5 tick 0 1 16", coda: "5 coda 22,5 10 10", dacapo: "5 dacs 16 20 20 Da Capo", dacoda: "5 dacs 16 20 20 Da Coda", "D.C.": "5 dcap 16,3 6 6", "D.S.": "5 dsgn 16,3 6 6", "D.C.alcoda": "5 dacs 16 32 32 D.C. al Coda", "D.S.alcoda": "5 dacs 16 32 32 D.S. al Coda", "D.C.alfine": "5 dacs 16 32 32 D.C. al Fine", "D.S.alfine": "5 dacs 16 32 32 D.S. al Fine", fermata: "5 hld 12 7.5 7.5", fine: "5 dacs 16 12 12 Fine", invertedfermata: "7 hld 12 8 8", segno: "5 sgno 22,2 5 5", f: "6 f 12,5 3 4", ff: "6 ff 12,5 8 5", fff: "6 fff 12,5 11 9", ffff: "6 ffff 12,5 15 12", mf: "6 mf 12,5 8 10", mp: "6 mp 12,5 9 10", p: "6 p 12,5 3 6", pp: "6 pp 12,5 8 9", ppp: "6 ppp 12,5 14 11", pppp: "6 pppp 12,5 14 17", pralltriller: "3 umrd 6,5 4 6", sfz: "6 sfz 12,5 9 9", ped: "6 ped 9 6 10", "ped-up": "6 pedoff 9 4 4", "ped(": "7 lped 14 1 1", "ped)": "7 lped 14 1 1", "crescendo(": "6 cresc 15,2 0 0", "crescendo)": "6 cresc 15,2 0 0", "<(": "6 cresc 15,2 0 0", "<)": "6 cresc 15,2 0 0", "diminuendo(": "6 dim 15,2 0 0", "diminuendo)": "6 dim 15,2 0 0", ">(": "6 dim 15,2 0 0", ">)": "6 dim 15,2 0 0", "-(": "8 gliss 0 0 0", "-)": "8 gliss 0 0 0", "~(": "8 glisq 0 0 0", "~)": "8 glisq 0 0 0", // internal // color: "10 0 0 0 0", invisible: "32 0 0 0 0", beamon: "33 0 0 0 0", trem1: "34 0 0 0 0", trem2: "34 0 0 0 0", trem3: "34 0 0 0 0", trem4: "34 0 0 0 0", xstem: "35 0 0 0 0", beambr1: "36 0 0 0 0", beambr2: "36 0 0 0 0", rbstop: "37 0 0 0 0", "/": "38 0 0 6 6", "//": "38 0 0 6 6", "///": "38 0 0 6 6", "beam-accel": "39 0 0 0 0", "beam-rall": "39 0 0 0 0", stemless: "40 0 0 0 0", rbend: "41 0 0 0 0", editorial: "42 0 0 0 0", "sacc-1": "3 sacc-1 6,4 4 4", sacc3: "3 sacc3 6,5 4 4", sacc1: "3 sacc1 6,4 4 4", courtesy: "43 0 0 0 0", "cacc-1": "3 cacc-1 0 0 0", cacc3: "3 cacc3 0 0 0", cacc1: "3 cacc1 0 0 0", "tie(": "44 0 0 0 0", "tie)": "44 0 0 0 0"}, // types of decoration per function f_near = [ d_near, // 0 - near the note d_slide, // 1 d_arp // 2 ], f_note = [ null, null, null, d_upstaff, // 3 - tied to note d_upstaff // 4 (below the staff) ], f_staff = [ null, null, null, null, null, d_upstaff, // 5 (above the staff) d_upstaff, // 6 - tied to staff (dynamic marks) d_upstaff // 7 (below the staff) ] /* -- get the max/min vertical offset -- */ function y_get(st, up, x, w) { var y, p_staff = staff_tb[st], i = (x / 2) | 0, j = ((x + w) / 2) | 0 if (i < 0) i = 0 if (j >= YSTEP) { j = YSTEP - 1 if (i > j) i = j } if (up) { y = p_staff.top[i++] while (i <= j) { if (y < p_staff.top[i]) y = p_staff.top[i]; i++ } } else { y = p_staff.bot[i++] while (i <= j) { if (y > p_staff.bot[i]) y = p_staff.bot[i]; i++ } } return y } /* -- adjust the vertical offsets -- */ function y_set(st, up, x, w, y) { var p_staff = staff_tb[st], i = (x / 2) | 0, j = ((x + w) / 2) | 0 /* (may occur when annotation on 'y' at start of an empty staff) */ if (i < 0) i = 0 if (j >= YSTEP) { j = YSTEP - 1 if (i > j) i = j } if (up) { while (i <= j) { if (p_staff.top[i] < y) p_staff.top[i] = y; i++ } } else { while (i <= j) { if (p_staff.bot[i] > y) p_staff.bot[i] = y; i++ } } } // get the staff position // - of the ornements function up3(s, pos) { switch (pos & 0x07) { case C.SL_ABOVE: return 1 // true case C.SL_BELOW: return 0 // false } // if (s.multi) // return s.multi > 0 // return 1 // true return !s.second } // up3() // - of the dynamic and volume marks function up6(s, pos) { switch (pos & 0x07) { case C.SL_ABOVE: return true case C.SL_BELOW: return false } if (s.multi) return s.multi > 0 if (!s.p_v.have_ly) return false /* above if the lyrics are below the staff */ return (s.pos.voc & 0x07) != C.SL_ABOVE } /* -- drawing functions -- */ /* 2: special case for arpeggio */ function d_arp(de) { var m, h, dx, s = de.s, dd = de.dd, xc = dd.wr if (s.type == C.NOTE) { for (m = 0; m <= s.nhd; m++) { if (s.notes[m].acc) { dx = s.notes[m].shac } else { dx = 1 - s.notes[m].shhd switch (s.head) { case C.SQUARE: dx += 3.5 break case C.OVALBARS: case C.OVAL: dx += 2 break } } if (dx > xc) xc = dx } } h = 3 * (s.notes[s.nhd].pit - s.notes[0].pit) + 4; m = dd.h /* minimum height */ if (h < m) h = m; de.has_val = true; de.val = h; // de.x = s.x - xc; de.x -= xc; de.y = 3 * ((s.notes[0].pit + s.notes[s.nhd].pit) / 2 - 18) - h / 2 - 3 } /* 0: near the note (dot, tenuto) */ function d_near(de) { var y, up = de.up, s = de.s, dd = de.dd y = up ? s.ymx : s.ymn if (y > 0 && y < 24) { y = (((y + 9) / 6) | 0) * 6 - 6 // between lines } if (up) { y += dd.hd s.ymx = y + dd.h } else if (dd.name[0] == 'w') { // wedge (no descent) de.inv = true y -= dd.h s.ymn = y } else { y -= dd.h s.ymn = y - dd.hd } de.x -= dd.wl de.y = y if (s.type == C.NOTE) de.x += s.notes[s.stem >= 0 ? 0 : s.nhd].shhd if (dd.name[0] == 'd') { // if dot (staccato) if (!(s.beam_st && s.beam_end)) { // if in a beam sequence if (up) { if (s.stem > 0) de.x += 3.5 // stem_xoff } else { if (s.stem < 0) de.x -= 3.5 } } else { if (up && s.stem > 0) { y = s.y + (y - s.y) * .6 if (y >= 27) { de.y = y // put the dot a bit lower s.ymx = de.y + dd.h } } } } } /* 1: special case for slide */ function d_slide(de) { var m, dx, s = de.s, yc = s.notes[0].pit, xc = 5 for (m = 0; m <= s.nhd; m++) { if (s.notes[m].acc) { dx = 4 + s.notes[m].shac } else { dx = 5 - s.notes[m].shhd switch (s.head) { case C.SQUARE: dx += 3.5 break case C.OVALBARS: case C.OVAL: dx += 2 break } } if (s.notes[m].pit <= yc + 3 && dx > xc) xc = dx } // de.x = s.x - xc; de.x -= xc; de.y = 3 * (yc - 18) } // special case for long decoration function d_trill(de) { if (de.ldst) return var y, w, tmp, dd = de.dd, de2 = de.prev, up = de.start.up, s2 = de.s, st = s2.st, s = de.start.s, x = s.x // shift the starting point of a long decoration // in the cases "T!trill(!" and "!pp!!<(!" // (side effect on x) function sh_st() { var de3, de2 = de.start, // start of the decoration s = de2.s, i = de2.ix // index of the current decoration while (--i >= 0) { de3 = a_de[i] if (!de3 || de3.s != s) break } while (1) { // loop on the decorations of the symbol i++ de3 = a_de[i] if (!de3 || de3.s != s) break if (de3 == de2) continue if (!(up ^ de3.up) && (de3.dd.name == "trill" || de3.dd.func == 6)) { // dynamic x += de3.dd.wr + 2 break } } } // sh_st() // shift the ending point of a long decoration // (side effect on w) function sh_en() { var de3, i = de.ix // index of the current decoration while (--i > 0) { de3 = a_de[i] if (!de3 || de3.s != s2) break } while (1) { // loop on the decorations of the symbol i++ de3 = a_de[i] if (!de3 || de3.s != s2) break // if (de3 == de || de3 == de2) if (de3 == de) continue if (!(up ^ de3.up) && de3.dd.func == 6) { // if dynamic mark w -= de3.dd.wl break } } } //sh_en() // d_trill() if (de2) { // same height x = de2.s.x + de.dd.wl + 2 de2.val -= de2.dd.wr if (de2.val < 8) de2.val = 8 } de.st = st de.up = up sh_st() // shift the starting point? if (de.defl.noen) { /* if no decoration end */ w = de.x - x if (w < 20) { x = de.x - 20 - 3; w = 20 } } else { w = s2.x - x - 4 sh_en(de) // shift the ending point? if (w < 20) w = 20 } y = y_get(st, up, x - dd.wl, w) if (up) { tmp = staff_tb[s.st].topbar + 2 if (y < tmp) y = tmp } else { tmp = staff_tb[s.st].botbar - 2 if (y > tmp) y = tmp y -= dd.h } if (de2) { // if same height if (up) { if (y < de2.y) y = de2.y // (only on one note) } else { if (y >= de2.y) { y = de2.y } else { do { de2.y = y de2 = de2.prev // go backwards } while (de2) } } } de.lden = false; de.has_val = true; de.val = w; de.x = x; de.y = y if (up) y += dd.h; else y -= dd.hd y_set(st, up, x, w, y) if (up) s.ymx = s2.ymx = y else s.ymn = s2.ymn = y } /* 3, 4, 5, 7: above (or below) the staff */ function d_upstaff(de) { // don't treat here the long decorations if (de.ldst) // if long deco start return if (de.start) { // if long decoration d_trill(de) return } var y, inv, up = de.up, s = de.s, dd = de.dd, x = de.x, w = dd.wl + dd.wr // glyphs inside the staff switch (dd.glyph) { case "brth": case "caes": case "lphr": case "mphr": case "sphr": case "short": case "tick": y = staff_tb[s.st].topbar + 2 + dd.hd if (s.type == C.BAR) { s.invis = 1 } else { if (dd.glyph == "brth" && y < s.ymx) y = s.ymx for (s = s.ts_next; s; s = s.ts_next) if (s.seqst) break x += ((s ? s.x : realwidth) - x) * .45 } de.x = x de.y = y return } if (s.nhd) x += s.notes[s.stem >= 0 ? 0 : s.nhd].shhd; switch (dd.ty) { case '@': case '<': case '>': y = de.y break } if (y == undefined) { if (up) { y = y_get(s.st, true, x - dd.wl, w) + dd.hd if (de.y > y) y = de.y s.ymx = y + dd.h } else { y = y_get(s.st, false, x - dd.wl, w) - dd.h if (de.y < y) y = de.y if (dd.name == "fermata" || dd.glyph == "accent" || dd.glyph == "roll") de.inv = 1 s.ymn = y - dd.hd } } if (dd.wr > 5 && x > realwidth - dd.wr) de.x = x = realwidth - dd.wr // if (dd.func == 6 // && ((de.pos & C.SL_ALI_MSK) == C.SL_ALIGN // || ((de.pos & C.SL_ALI_MSK) == 0 // && de.s.fmt.dynalign > 0))) // if align // ; // else if (up) y_set(s.st, 1, x - dd.wl, w, y + dd.h) else y_set(s.st, 0, x - dd.wl, w, y - dd.hd) de.y = y } // add a decoration /* syntax: * %%deco [] * "" may be followed by "," (descent) */ function deco_add(param) { var dv = param.match(/(\S*)\s+(.*)/); decos[dv[1]] = dv[2] } // define a decoration // nm is the name of the decoration // nmd is the name of the definition in the table 'decos' function deco_def(nm, nmd) { if (!nmd) nmd = nm var a, dd, dd2, nm2, c, i, elts, str, hd, text = decos[nmd] // check if a long decoration with number if (!text && /\d[()]$/.test(nmd)) text = decos[nmd.replace(/\d/, '')] if (!text) { if (cfmt.decoerr) error(1, null, "Unknown decoration '$1'", nm) return //undefined } // extract the values a = text.match(/(\d+)\s+(.+?)\s+([0-9.,]+)\s+([0-9.]+)\s+([0-9.]+)/) if (!a) { error(1, null, "Invalid decoration '$1'", nm) return //undefined } var c_func = Number(a[1]), // glyph = a[2], h = a[3], wl = parseFloat(a[4]), wr = parseFloat(a[5]) if (isNaN(c_func)) { error(1, null, "%%deco: bad C function value '$1'", a[1]) return //undefined } if (c_func > 10 && (c_func < 32 || c_func > 44)) { error(1, null, "%%deco: bad C function index '$1'", c_func) return //undefined } // if (c_func == 5) // old !trill(! // c_func = 3 // if (c_func == 7) // old !cresc(! // c_func = 6 if (h.indexOf(',') > 0) { h = h.split(',') hd = h[1] h = h[0] } else { hd = 0 } if (h > 50 || wl > 80 || wr > 80) { error(1, null, "%%deco: abnormal h/wl/wr value '$1'", text) return //undefined } // create/redefine the decoration dd = dd_tb[nm] if (!dd) { dd = { name: nm } dd_tb[nm] = dd } /* set the values */ dd.func = nm.indexOf("head-") == 0 ? 9 : c_func; dd.glyph = a[2]; dd.h = Number(h) dd.hd = Number(hd) dd.wl = wl; dd.wr = wr; str = text.replace(a[0], '').trim() if (str) { // optional string if (str[0] == '"') str = str.slice(1, -1); if (str[0] == '@') { c = str.match(/^@([0-9.-]+),([0-9.-]+);?/) if (!c) { error(1, null, "%%deco: bad position '$1'", str) return } dd.dx = +c[1] // x and y offsets dd.dy = +c[2] str = str.replace(c[0], '') } dd.str = str } /* compatibility */ if (dd.func == 6 && dd.str == undefined) dd.str = nm // link the start and end of long decorations c = nm.slice(-1) if (c == '(' || (c == ')' && nm.indexOf('(') < 0)) { // not (#) dd.str = null; // (no string) nm2 = nm.slice(0, -1) + (c == '(' ? ')' : '('); dd2 = dd_tb[nm2] if (dd2) { if (c == '(') { dd.dd_en = dd2; dd2.dd_st = dd } else { dd.dd_st = dd2; dd2.dd_en = dd } } } return dd } // define a cross-voice tie // @nm = decoration name // @s = note symbol // @nt1 = note function do_ctie(nm, s, nt1) { var nt2 = cross[nm], nm2 = nm.slice(0, -1) + (nm.slice(-1) == '(' ? ')' : '(') if (nt2) { error(1, s, "Conflict on !$1!", nm) return } nt1.s = s nt2 = cross[nm2] if (!nt2) { cross[nm] = nt1 // keep the start/end return } if (nm.slice(-1) == ')') { nt2 = nt1 nt1 = cross[nm2] } cross[nm2] = null if (nt1.midi != nt2.midi || nt1.s.time + nt1.s.dur != nt2.s.time) { error(1, s, "Bad tie") } else { nt1.tie_ty = C.SL_AUTO nt1.tie_e = nt2 nt2.tie_s = nt1 nt1.s.ti1 = nt2.s.ti2 = true } } // do_ctie() // get/create the definition of a decoration function get_dd(nm) { var ty, p, dd = dd_tb[nm] if (dd) return dd if ("<>^_@".indexOf(nm[0]) >= 0 // if position && !/^([>^]|[<>]\d?[()])$/.test(nm)) { ty = nm[0] if (ty == '@') { p = nm.match(/@([-\d]+),([-\d]+)/) if (p) ty = p[0] else ty = '' // accept decorations starting with '@' } dd = deco_def(nm, nm.replace(ty, '')) } else { dd = deco_def(nm) } if (!dd) return if (ty) { if (ty[0] == '@') { // if with x,y dd.x = Number(p[1]) dd.y = Number(p[2]) ty = '@' } dd.ty = ty } return dd } // get_dd() /* -- convert the decorations -- */ function deco_cnv(s, prev) { var i, j, dd, nm, note, s1, court while (1) { nm = a_dcn.shift() if (!nm) break dd = get_dd(nm) if (!dd) continue /* special decorations */ switch (dd.func) { case 0: // near if (s.type == C.BAR && nm == "dot") { s.bar_dotted = true continue } // fall thru case 1: // slide case 2: // arp // if (s.type != C.NOTE && s.type != C.REST) { if (!s.notes) { error(1, s, errs.must_note_rest, nm) continue } break case 4: // below the staff case 5: // above the staff i = nm.match(/1?[85]([vm])([ab])([()])/) if (i) { // if ottava j = i[1] == 'v' ? 1 : 2 if (i[2] == 'b') j = -j if (!s.ottava) s.ottava = [] s.ottava[i[3] == '(' ? 0 : 1] = j glovar.ottava = 1 //true } break case 8: // gliss if (s.type != C.NOTE) { error(1, s, errs.must_note, nm) continue } note = s.notes[s.nhd] // move to the upper note of the chord if (!note.a_dd) note.a_dd = [] note.a_dd.push(dd) continue case 9: // alternate head if (!s.notes) { error(1, s, errs.must_note_rest, nm) continue } // move the alternate head of the chord to the notes for (j = 0; j <= s.nhd; j++) { note = s.notes[j] note.invis = true if (!note.a_dd) note.a_dd = [] note.a_dd.push(dd) } continue case 10: /* color */ if (s.notes) { for (j = 0; j <= s.nhd; j++) s.notes[j].color = nm } else { s.color = nm } break case 32: /* invisible */ s.invis = true break case 33: /* beamon */ if (s.type != C.BAR) { error(1, s, "!beamon! must be on a bar") continue } s.beam_on = true break case 34: /* trem1..trem4 */ if (s.type != C.NOTE || !prev || prev.type != C.NOTE || s.dur != prev.dur) { error(1, s, "!$1! must be on the last of a couple of notes", nm) continue } s.trem2 = true; s.beam_end = true; s.beam_st = false; prev.beam_st = true; prev.beam_end = false; s.ntrem = prev.ntrem = Number(nm[4]); for (j = 0; j <= s.nhd; j++) s.notes[j].dur *= 2; for (j = 0; j <= prev.nhd; j++) prev.notes[j].dur *= 2 break case 35: /* xstem */ if (s.type != C.NOTE) { error(1, s, errs.must_note, nm) continue } s.xstem = true; break case 36: /* beambr1 / beambr2 */ if (s.type != C.NOTE) { error(1, s, errs.must_note, nm) continue } if (nm[6] == '1') s.beam_br1 = true else s.beam_br2 = true break case 37: /* rbstop */ s.rbstop = 1 // open break case 38: /* /, // and /// = tremolo */ if (s.type != C.NOTE) { error(1, s, errs.must_note, nm) continue } s.trem1 = true; s.ntrem = nm.length /* 1, 2 or 3 */ break case 39: /* beam-accel/beam-rall */ if (s.type != C.NOTE) { error(1, s, errs.must_note, nm) continue } s.feathered_beam = nm[5] == 'a' ? 1 : -1; break case 40: /* stemless */ s.stemless = true break case 41: /* rbend */ s.rbstop = 2 // with end break case 42: // editorial if (s.type != C.NOTE) { error(1, s, errs.must_note, nm) continue } if (!s.notes[0].acc) continue nm = "sacc" + s.notes[0].acc.toString() // small accidental dd = dd_tb[nm] if (!dd) { dd = deco_def(nm) if (!dd) { error(1, s, errs.bad_val, "!editorial!") continue } } delete s.notes[0].acc curvoice.acc[s.notes[0].pit + 19] = 0 // ignore the accidental break case 43: // courtesy if (s.type != C.NOTE) { error(1, s, errs.must_note, nm) continue } j = curvoice.acc[s.notes[0].pit + 19] if (s.notes[0].acc || !j) continue court = 1 // defer break case 44: // cross-voice ties if (s.type != C.NOTE) { error(1, s, errs.must_note, nm) continue } do_ctie(nm, s, s.notes[0]) // (only one note for now) continue // default: // break } // add the decoration in the symbol if (!s.a_dd) s.a_dd = [] s.a_dd.push(dd) } // handle the possible courtesy accidental if (court) { a_dcn.push("cacc" + j) dh_cnv(s, s.notes[0]) } } // -- convert head decorations -- // The decorations are in the global array a_dcn function dh_cnv(s, nt) { var k, nm, dd while (1) { nm = a_dcn.shift() if (!nm) break dd = get_dd(nm) if (!dd) continue switch (dd.func) { case 0: case 1: case 3: case 4: case 8: // gliss break default: // case 2: // arpeggio // case 5: // trill // case 7: // d_cresc error(1, s, "Cannot have !$1! on a head", nm) continue case 9: // head replacement nt.invis = true break case 32: // invisible nt.invis = true continue case 10: // color nt.color = nm continue case 40: // stemless chord (abcm2ps behaviour) s.stemless = true continue case 44: // cross-voice ties do_ctie(nm, s, nt) continue } // add the decoration in the note if (!nt.a_dd) nt.a_dd = [] nt.a_dd.push(dd) } } // dh_cnv() /* -- update the x position of a decoration -- */ // used to center the rests function deco_update(s, dx) { var i, de, nd = a_de.length for (i = 0; i < nd; i++) { de = a_de[i] if (de.s == s) de.x += dx } } /* -- adjust the symbol width -- */ function deco_width(s, wlnt) { var dd, i, w, wl = wlnt, wr = s.wr, a_dd = s.a_dd, nd = a_dd.length for (i = 0; i < nd; i++) { dd = a_dd[i] switch (dd.func) { case 1: /* slide */ case 2: /* arpeggio */ if (wl < 12) wl = 12 break case 3: switch (dd.glyph) { case "brth": case "lphr": case "mphr": case "sphr": if (s.wr < 20) s.wr = 20 break default: w = dd.wl + 2 if (wl < w) wl = w break } // fall thru default: switch (dd.ty) { case '<': w = wlnt + dd.wl + dd.wr + 6 if (wl < w) wl = w break case '>': w = wr + dd.wl + dd.wr + 6 if (s.wr < w) s.wr = w break } break } } return wl } // compute the width of decorations in chord function deco_wch(nt) { var i, w, dd, wl = 0, n = nt.a_dd.length for (i = 0; i < n; i++) { dd = nt.a_dd[i] if (dd.ty == '<') { w = dd.wl + dd.wr + 4 if (w > wl) wl = w } } return wl } // deco_wch() /* -- draw the decorations -- */ /* (the staves are defined) */ Abc.prototype.draw_all_deco = function() { if (!a_de.length) return var de, dd, s, note, f, st, x, y, y2, ym, uf, i, str, a, new_de = [], ymid = [] st = nstaff; y = staff_tb[st].y while (--st >= 0) { y2 = staff_tb[st].y; ymid[st] = (y + 24 + y2) * .5; y = y2 } while (1) { de = a_de.shift() if (!de) break dd = de.dd if (!dd) continue // deleted if (dd.dd_en) // start of long decoration continue // handle the stem direction s = de.s f = dd.glyph; i = f.indexOf('/') if (i > 0) { if (s.stem >= 0) f = f.slice(0, i) else f = f.slice(i + 1) } // no voice scale if staff decoration if (f_staff[dd.func]) set_sscale(s.st) else set_scale(s); st = de.st; if (!staff_tb[st].topbar) continue // invisible staff x = de.x + (dd.dx || 0) y = de.y + staff_tb[st].y + (dd.dy || 0) // update the coordinates if head decoration if (de.m != undefined) { note = s.notes[de.m]; if (note.shhd) x += note.shhd * stv_g.scale; /* center the dynamic marks between two staves */ /*fixme: KO when deco on other voice and same direction*/ } else if (dd.func == 6 && ((de.pos & C.SL_ALI_MSK) == C.SL_CENTER || ((de.pos & C.SL_ALI_MSK) == 0 && !s.fmt.dynalign)) && ((de.up && st > 0) || (!de.up && st < nstaff))) { if (de.up) ym = ymid[--st] else ym = ymid[st++]; ym -= dd.h * .5 if ((de.up && y < ym) || (!de.up && y > ym)) { // if (s.st > st) { // while (s.st != st) // s = s.ts_prev // } else if (s.st < st) { // while (s.st != st) // s = s.ts_next // } y2 = y_get(st, !de.up, de.x, de.val) + staff_tb[st].y if (de.up) y2 -= dd.h //fixme: y_set is not used later! if ((de.up && y2 > ym) || (!de.up && y2 < ym)) { y = ym; // y_set(st, de.up, de.x, de.val, // (de.up ? y + dd.h : y) // - staff_tb[st].y) if (stv_g.scale != 1) y += stv_g.dy / 2 } } } // check if user JS decoration if (user.deco) { uf = user.deco[f] if (uf && typeof(uf) == "function") { uf.call(self, x, y, de) continue } } // check if user PS definition if (self.psdeco(x, y, de)) continue anno_start(s, 'deco') // if (de.flags.grace) { // g_open(x, y, 0, .7, de.inv ? -.7 : 0); // x = y = 0 // } else if (de.inv) { y = y + dd.h - dd.hd g_open(x, y, 0, 1, -1); x = y = 0 } if (de.has_val) { if (dd.func != 2 // if not !arpeggio! || stv_g.st < 0) // or not staff scale // || voice_tb[s.v].scale != 1) out_deco_val(x, y, f, de.val / stv_g.scale, de.defl) else out_deco_val(x, y, f, de.val, de.defl) if (de.cont) new_de.push(de.start) // to be continued next line } else if (dd.str != undefined // string && !tgls[dd.glyph] && !glyphs[dd.glyph]) { // with a class out_deco_str(x, y, // - dd.h * .2, de) } else if (de.lden) { out_deco_long(x, y, de) } else { xygl(x, y, f) } if (stv_g.g) g_close(); anno_stop(s, 'deco') } // keep the long decorations which continue on the next line a_de = new_de } /* -- create the decorations and define the ones near the notes -- */ /* (the staves are not yet defined) */ /* (delayed output) */ /* this function must be called first as it builds the deco element table */ function draw_deco_near() { var s, g // update starting old decorations function ldeco_update(s) { var i, de, // x = s.ts_prev.x + s.ts_prev.wr x = s.x - s.wl, nd = a_de.length for (i = 0; i < nd; i++) { de = a_de[i]; de.ix = i; de.s.x = de.x = x; de.defl.nost = true } } /* -- create the deco elements, and treat the near ones -- */ function create_deco(s) { var dd, k, pos, de, x, y, up, nd = s.a_dd.length if (s.y == undefined) s.y = 0 // (no y in measure bars) /*fixme:pb with decorations above the staff*/ for (k = 0; k < nd; k++) { dd = s.a_dd[k] // adjust the position x = s.x y = s.y switch (dd.func) { default: if (dd.func >= 10) continue pos = 0 break case 3: /* d_upstaff */ case 4: case 5: // after slurs pos = s.pos.orn break case 6: /* dynamic */ pos = s.pos.dyn break } switch (dd.ty) { // explicit position case '^': pos = (pos & ~0x07) | C.SL_ABOVE break case '_': pos = (pos & ~0x07) | C.SL_BELOW break case '<': case '>': pos = (pos & 0x07) | C.SL_CLOSE if (dd.ty == '<') { x -= dd.wr + 8 if (s.notes[0].acc) x -= 8 } else { x += dd.wl + 8 } y = 3 * (s.notes[0].pit - 18) - (dd.h - dd.hd) / 2 break case '@': x += dd.x y += dd.y break } if ((pos & 0x07) == C.SL_HIDDEN) continue de = { s: s, dd: dd, st: s.st, ix: a_de.length, defl: {}, x: x, y: y } if (pos) de.pos = pos up = 0 //false if (dd.ty == '^') { up = 1 //true } else if (dd.ty == '_') { ; } else { switch (dd.func) { case 0: if (s.multi) up = s.multi > 0 else up = s.stem < 0 break case 3: case 5: up = up3(s, pos) break case 6: case 7: up = up6(s, pos) break } } de.up = up if (dd.name.indexOf("inverted") >= 0) de.inv = 1 if (s.type == C.BAR && !dd.ty) de.x -= s.wl / 2 - 2 a_de.push(de) if (dd.dd_en) { de.ldst = true } else if (dd.dd_st) { //fixme: pb with "()" de.lden = true; de.defl.nost = true } if (f_near[dd.func]) f_near[dd.func](de) } } // create_deco() // create the decorations of note heads function create_dh(s, m) { var de, k, dd, note = s.notes[m], nd = note.a_dd.length, x = s.x for (k = 0; k < nd; k++) { dd = note.a_dd[k] //fixme: check if hidden? de = { s: s, dd: dd, st: s.st, m: m, ix: 0, defl: {}, x: x, y: 3 * (note.pit - 18) - (dd.h - dd.hd) / 2 // dy: 0 } if (dd.ty) { // if explicit position if (dd.ty == '@') { de.x += dd.x de.y += dd.y } else { de.y -= (dd.h - dd.hd) / 2 // center if (dd.ty == '<') { de.x -= dd.wr + 8 if (s.notes[m].acc) x -= 8 } else if (dd.ty == '>') { de.x += dd.wl + 8 } } } a_de.push(de) if (dd.dd_en) { de.ldst = true } else if (dd.dd_st) { de.lden = true; de.defl.nost = true } } } // create_dh() // create all decorations of a note (chord and heads) function create_all(s) { if (s.invis && s.play) // play sequence: no decoration return if (s.a_dd) create_deco(s) if (s.notes) { for (var m = 0; m < s.notes.length; m++) { if (s.notes[m].a_dd) create_dh(s, m) } } } // create_all() // link the long decorations function ll_deco() { var i, j, de, de2, de3, dd, dd2, v, s, st, n_de = a_de.length // add ending decorations for (i = 0; i < n_de; i++) { de = a_de[i] if (!de.ldst) // not the start of long decoration continue dd = de.dd; dd2 = dd.dd_en; s = de.s; v = s.v // search later in the voice for (j = i + 1; j < n_de; j++) { de2 = a_de[j] if (!de2.start && de2.dd == dd2 && de2.s.v == v) break } if (j == n_de) { // no end, search in the staff st = s.st; for (j = i + 1; j < n_de; j++) { de2 = a_de[j] if (!de2.start && de2.dd == dd2 && de2.s.st == st) break } } if (j == n_de) { // no end, insert one de2 = { s: s, st: de.st, dd: dd2, ix: a_de.length - 1, x: realwidth - 6, y: s.y, cont: true, // keep for next line lden: true, defl: { noen: true } } if (de2.x < s.x + 10) de2.x = s.x + 10 if (de.m != undefined) de2.m = de.m; a_de.push(de2) } de2.start = de; de2.defl.nost = de.defl.nost // handle same decoration ending at a same time j = i while (--j >= 0) { de3 = a_de[j] if (!de3.start) continue if (de3.s.time < s.time) break if (de3.dd.name == de2.dd.name) { de2.prev = de3 break } } } // add starting decorations for (i = 0; i < n_de; i++) { de2 = a_de[i] if (!de2.lden // not the end of long decoration || de2.start) // start already found continue s = de2.s; de = { s: prev_scut(s), st: de2.st, dd: de2.dd.dd_st, ix: a_de.length - 1, // x: s.x - s.wl - 4, y: s.y, ldst: true } de.x = de.s.x + de.s.wr if (de2.m != undefined) de.m = de2.m; a_de.push(de); de2.start = de } } // ll_deco // update the long decorations started in the previous line for (s = tsfirst ; s; s = s.ts_next) { switch (s.type) { case C.CLEF: case C.KEY: case C.METER: continue } break } if (a_de.length) ldeco_update(s) for ( ; s; s = s.ts_next) { switch (s.type) { case C.BAR: case C.MREST: case C.NOTE: case C.REST: case C.SPACE: break case C.GRACE: for (g = s.extra; g; g = g.next) create_all(g) break default: continue } create_all(s) } ll_deco() // link the long decorations } /* -- define the decorations tied to a note -- */ /* (the staves are not yet defined) */ /* (delayed output) */ function draw_deco_note() { var i, de, dd, f, nd = a_de.length for (i = 0; i < nd; i++) { de = a_de[i]; dd = de.dd; f = dd.func if (f_note[f] && de.m == undefined) f_note[f](de) } } // -- define the music elements tied to the staff -- // - decoration tied to the staves // - chord symbols // - repeat brackets /* (the staves are not yet defined) */ /* (unscaled delayed output) */ function draw_deco_staff() { var s, p_voice, y, i, v, de, dd, w, minmax = new Array(nstaff + 1), nd = a_de.length /* draw the repeat brackets */ function draw_repbra(p_voice) { var s, s1, x, y, y2, i, p, w, wh, first_repeat; // search the max y offset of the line y = staff_tb[p_voice.st].topbar + 15 // 10 (vert bar) + 5 (room) for (s = p_voice.sym; s; s = s.next) { if (s.type != C.BAR) continue if (!s.rbstart || s.norepbra) continue /*fixme: line cut on repeat!*/ if (!s.next) break if (!first_repeat) { first_repeat = s; set_font("repeat") } s1 = s for (;;) { if (!s.next) break s = s.next if (s.rbstop) break } x = s1.x if (s1.xsh) // volta shift x += s1.xsh y2 = y_get(p_voice.st, true, x, s.x - x) + 2 if (y < y2) y = y2 // have room for the vertical lines and the repeat numbers if (s1.rbstart == 2) { y2 = y_get(p_voice.st, true, x, 3) + 10 if (y < y2) y = y2 } if (s.rbstop == 2) { y2 = y_get(p_voice.st, true, s.x - 3, 3) + 10 if (y < y2) y = y2 } if (s1.text) { wh = strwh(s1.text); y2 = y_get(p_voice.st, true, x + 4, wh[0]) + wh[1] if (y < y2) y = y2 } if (s.rbstart) s = s.prev } /* draw the repeat indications */ s = first_repeat if (!s) return set_dscale(p_voice.st, true); y2 = y * staff_tb[p_voice.st].staffscale for ( ; s; s = s.next) { if (!s.rbstart || s.norepbra) continue s1 = s while (1) { if (!s.next) break s = s.next if (s.rbstop) break } if (s1 == s) break x = s1.x if (s1.xsh) // volta shift x += s1.xsh if (cfmt.measurenb > 0 & s.bar_num && s.bar_num % cfmt.measurenb) x += 6 if (s.type != C.BAR) { w = s.rbstop ? 0 : s.x - realwidth + 4 } else if ((s.bar_type.length > 1 // if complex bar && s.bar_type != "[]") || s.bar_type == "]") { // if (s.bar_type == "]") // s.invis = true //fixme:%%staves: cur_sy moved? if (s1.st > 0 && !(cur_sy.staves[s1.st - 1].flags & STOP_BAR)) w = s.wl else if (s.bar_type.slice(-1) == ':') w = 12 else if (s.bar_type[0] != ':') // || s.bar_type == "]") w = 0 /* explicit repeat end */ else w = 8 } else { w = (s.rbstop && !s.rbstart) ? 0 : 8 } w = (s.x - x - w) // / staff_tb[p_voice.st].staffscale; if (!s.next // 2nd ending at end of line && !s.rbstop && !p_voice.bar_start) { // continue on next line p_voice.bar_start = _bar(s) p_voice.bar_start.bar_type = "" p_voice.bar_start.rbstart = 1 } if (s1.text) xy_str(x + 4, y2 - gene.curfont.size, s1.text); xypath(x, y2); if (s1.rbstart == 2) output += 'm0 10v-10'; output+= 'h' + w.toFixed(1) if (s.rbstop == 2) output += 'v10'; output += '"/>\n'; y_set(s1.st, true, x, w, y + 2) if (s.rbstart) s = s.prev } } // draw_repbra() /* create the decorations tied to the staves */ for (i = 0; i <= nstaff; i++) minmax[i] = { ymin: 0, ymax: 0 } for (i = 0; i < nd; i++) { de = a_de[i]; dd = de.dd if (!dd) // if error continue if (!f_staff[dd.func] /* if not tied to the staff */ || de.m != undefined // or head decoration || dd.ty == '<' || dd.ty == '>' || dd.ty == '@') continue f_staff[dd.func](de) if (dd.func != 6 || dd.dd_en) // if start continue if ((de.pos & C.SL_ALI_MSK) == C.SL_ALIGN || ((de.pos & C.SL_ALI_MSK) == 0 && de.s.fmt.dynalign > 0)) { // if align if (de.up) { if (de.y > minmax[de.st].ymax) minmax[de.st].ymax = de.y } else { if (de.y < minmax[de.st].ymin) minmax[de.st].ymin = de.y } } } // set the same vertical offset of the dynamic marks for (i = 0; i < nd; i++) { de = a_de[i]; dd = de.dd if (!dd) // if error continue // if @x,y offsets, update the top and bottom of the staff if (dd.ty == '@') { var y2 y = de.y if (y > 0) { y2 = y + dd.h + 2 if (y2 > staff_tb[de.st].ann_top) staff_tb[de.st].ann_top = y2 } else { y2 = y - dd.hd - 2 if (y2 < staff_tb[de.st].ann_bot) staff_tb[de.st].ann_bot = y2 } continue } if (dd.func != 6 || dd.ty == '<' || dd.ty == '>' || dd.dd_en) // if start continue w = de.val || (dd.wl + dd.wr) if ((de.pos & C.SL_ALI_MSK) == C.SL_ALIGN || ((de.pos & C.SL_ALI_MSK) == 0 && de.s.fmt.dynalign > 0)) { // if align if (de.up) y = minmax[de.st].ymax else y = minmax[de.st].ymin; de.y = y } else { y = de.y } if (de.up) y += dd.h; else y -= dd.hd y_set(de.st, de.up, de.x, w, y) } // second pass for pedal (under the staff) for (i = 0; i < nd; i++) { de = a_de[i] dd = de.dd if (!dd) // if error continue if (dd.dd_en // if start || dd.name.slice(0, 3) != "ped") continue w = de.val || 10 de.y = y_get(de.st, 0, de.x, w) - (dd.dd_st && cfmt.pedline ? 10 : dd.h) y_set(de.st, 0, de.x, w, de.y) // (no descent) } draw_all_chsy() // draw all chord symbols /* draw the repeat brackets */ for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v] if (p_voice.second || !p_voice.sym || p_voice.ignore) continue draw_repbra(p_voice) } } /* -- draw the measure bar numbers -- */ /* (scaled delayed output) */ function draw_measnb() { var s, st, bar_num, x, y, w, any_nb, font_size, w0, sy = cur_sy /* search the top staff */ for (st = 0; st <= nstaff; st++) { if (sy.st_print[st]) break } if (st > nstaff) return /* no visible staff */ set_dscale(st) /* leave the measure numbers as unscaled */ if (staff_tb[st].staffscale != 1) { font_size = get_font("measure").size; param_set_font("measurefont", "* " + (font_size / staff_tb[st].staffscale).toString()) } set_font("measure"); w0 = cwidf('0'); // (greatest) width of a number s = tsfirst; /* clef */ bar_num = gene.nbar if (bar_num > 1) { if (cfmt.measurenb == 0) { any_nb = true; y = y_get(st, true, 0, 20) if (y < staff_tb[st].topbar + 14) y = staff_tb[st].topbar + 14; xy_str(0, y - gene.curfont.size * .2, bar_num.toString()) y_set(st, true, 0, 20, y + gene.curfont.size + 2) } else if (bar_num % cfmt.measurenb == 0) { for ( ; ; s = s.ts_next) { switch (s.type) { case C.CLEF: case C.KEY: case C.METER: case C.STBRK: continue } break } // don't display the number twice if (s.type != C.BAR || !s.bar_num) { any_nb = true; w = w0 if (bar_num >= 10) w *= bar_num >= 100 ? 3 : 2 if (gene.curfont.pad) w += gene.curfont.pad * 2 x = (s.prev ? s.prev.x + s.prev.wr / 2 : s.x - s.wl) - w y = y_get(st, true, x, w) + 5 if (y < staff_tb[st].topbar + 6) y = staff_tb[st].topbar + 6; y += gene.curfont.pad xy_str(x, y - gene.curfont.size * .2, bar_num.toString()) y += gene.curfont.size + gene.curfont.pad y_set(st, true, x, w, y); // s.ymx = y } } } for ( ; s; s = s.ts_next) { switch (s.type) { case C.STAVES: sy = s.sy for (st = 0; st < nstaff; st++) { if (sy.st_print[st]) break } set_dscale(st) continue default: continue case C.BAR: if (!s.bar_num || s.bar_num <= 1) continue break } bar_num = s.bar_num if (cfmt.measurenb == 0 || (bar_num % cfmt.measurenb) != 0 || !s.next || s.bar_mrep) continue if (!any_nb) any_nb = true; w = w0 if (bar_num >= 10) w *= bar_num >= 100 ? 3 : 2 if (gene.curfont.pad) w += gene.curfont.pad * 2 x = s.x y = y_get(st, true, x, w) if (y < staff_tb[st].topbar + 6) y = staff_tb[st].topbar + 6 if (s.next.type == C.NOTE) { if (s.next.stem > 0) { if (y < s.next.ys - gene.curfont.size) y = s.next.ys - gene.curfont.size } else { if (y < s.next.y) y = s.next.y } } y += 2 + gene.curfont.pad xy_str(x, y - gene.curfont.size * .2, bar_num.toString()) y += gene.curfont.size + gene.curfont.pad y_set(st, true, x, w, y); // s.ymx = y } gene.nbar = bar_num if (font_size) param_set_font("measurefont", "* " + font_size.toString()); } /* -- draw the parts and the tempo information -- */ // (unscaled delayed output) function draw_partempo() { var s, s2, some_part, some_tempo, h, w, y, st, sy = cur_sy // search the top staff for (st = 0; st <= nstaff; st++) { if (sy.st_print[st]) break } if (st > nstaff) return // no visible staff set_dscale(st, 1) // no scale /* get the minimal y offset */ var ymin = staff_tb[st].topbar + 2, dosh = 0, shift = 1, x = -100, // (must be negative for %%soloffs) yn = 0 // y min when x < 0 // output the parts for (s = tsfirst; s; s = s.ts_next) { s2 = s.part if (!s2 || s2.invis) continue if (!some_part) { some_part = s; set_font("parts"); h = gene.curfont.size + 2 + gene.curfont.pad * 2 } if (s2.x == undefined) s2.x = s.x - 10 w = strwh(s2.text)[0] y = y_get(st, true, s2.x, w + 3) if (ymin < y) ymin = y } if (some_part) { set_sscale(-1) ymin *= staff_tb[st].staffscale for (s = some_part; s; s = s.ts_next) { s2 = s.part if (!s2 || s2.invis) continue w = strwh(s2.text)[0] if (user.anno_start || user.anno_stop) { s2.wl = 0 s2.wr = w s2.ymn = ymin s2.ymx = s2.ymn + h anno_start(s2) } xy_str(s2.x, ymin + 2 + gene.curfont.pad + gene.curfont.size * .22, s2.text) y_set(st, 1, s2.x, w + 3, ymin + 2 + h) if (s2.x < 0) yn = ymin + 2 + h anno_stop(s2) } } // output the tempos ymin = staff_tb[st].topbar + 6 for (s = tsfirst; s; s = s.ts_next) { if (s.type != C.TEMPO || s.invis) continue if (!some_tempo) some_tempo = s w = s.tempo_wh[0] // if (s.time == 0 && s.x > 40) // at start of tune and no %%soloffs, // s.x = 40 // shift the tempo over the key signature y = y_get(st, true, s.x - 16, w) if (s.x - 16 < 0) y = yn if (y > ymin) ymin = y if (x >= s.x - 16 && !(dosh & (shift >> 1))) dosh |= shift shift <<= 1 x = s.x - 16 + w } if (some_tempo) { set_sscale(-1) set_font("tempo") h = gene.curfont.size ymin += 2 ymin *= staff_tb[st].staffscale /* draw the tempo indications */ for (s = some_tempo; s; s = s.ts_next) { if (s.type != C.TEMPO || s.invis) // (displayed by %%titleformat) continue w = s.tempo_wh[0] y = ymin if (dosh & 1) y += h if (user.anno_start || user.anno_stop) { s.wl = 16 // s.wr = 30 s.wr = w - 16 s.ymn = y s.ymx = s.ymn + 14 anno_start(s) } writempo(s, s.x - 16, y) anno_stop(s) y_set(st, 1, s.x - 16, w, y + h + 2) dosh >>= 1 } } } // abc2svg - draw.js - draw functions // // Copyright (C) 2014-2023 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 . // constants var STEM_MIN = 16, /* min stem height under beams */ STEM_MIN2 = 14, /* ... for notes with two beams */ STEM_MIN3 = 12, /* ... for notes with three beams */ STEM_MIN4 = 10, /* ... for notes with four beams */ STEM_CH_MIN = 14, /* min stem height for chords under beams */ STEM_CH_MIN2 = 10, /* ... for notes with two beams */ STEM_CH_MIN3 = 9, /* ... for notes with three beams */ STEM_CH_MIN4 = 9, /* ... for notes with four beams */ BEAM_DEPTH = 3.2, /* width of a beam stroke */ BEAM_OFFSET = .25, /* pos of flat beam relative to staff line */ BEAM_SHIFT = 5, /* shift of second and third beams */ BEAM_STUB = 7, /* length of stub for flag under beam */ SLUR_SLOPE = .5, /* max slope of a slur */ GSTEM = 15, /* grace note stem length */ GSTEM_XOFF = 2.3 /* x offset for grace note stem */ var cache, anno_a = [] // symbols with annotations /* -- compute the best vertical offset for the beams -- */ function b_pos(grace, stem, nflags, b) { var top, bot, d1, d2, shift = !grace ? BEAM_SHIFT : 3.5, depth = !grace ? BEAM_DEPTH : 1.8 /* -- up/down shift needed to get k*6 -- */ function rnd6(y) { var iy = Math.round((y + 12) / 6) * 6 - 12 return iy - y } // rnd6() if (stem > 0) { bot = b - (nflags - 1) * shift - depth if (bot > 26) return 0 top = b } else { top = b + (nflags - 1) * shift + depth if (top < -2) return 0 bot = b } d1 = rnd6(top - BEAM_OFFSET); d2 = rnd6(bot + BEAM_OFFSET) return d1 * d1 > d2 * d2 ? d2 : d1 } /* duplicate a note for beaming continuation */ function sym_dup(s) { var m, note s = clone(s) s.invis = true delete s.extra; delete s.text delete s.a_gch delete s.a_ly delete s.a_dd; delete s.tp s.notes = clone(s.notes) for (m = 0; m <= s.nhd; m++) { note = s.notes[m] = clone(s.notes[m]) delete note.a_dd } return s } /* -- calculate a beam -- */ /* (the staves may be defined or not) */ var min_tb = [ [STEM_MIN, STEM_MIN, STEM_MIN2, STEM_MIN3, STEM_MIN4, STEM_MIN4], [STEM_CH_MIN, STEM_CH_MIN, STEM_CH_MIN2, STEM_CH_MIN3, STEM_CH_MIN4, STEM_CH_MIN4] ] // (possible hook) Abc.prototype.calculate_beam = function(bm, s1) { var s, s2, g, notes, nflags, st, v, two_staves, two_dir, n, x, y, ys, a, b, stem_err, max_stem_err, p_min, p_max, s_closest, stem_xoff, scale, visible, dy if (!s1.beam_st) { /* beam from previous music line */ s = sym_dup(s1); lkvsym(s, s1); lktsym(s, s1); s.x -= 12 if (s.x > s1.prev.x + 12) s.x = s1.prev.x + 12; s.beam_st = true delete s.beam_end; s.tmp = true delete s.sls; s1 = s } /* search last note in beam */ notes = nflags = 0; /* set x positions, count notes and flags */ two_staves = two_dir = false; st = s1.st; v = s1.v; stem_xoff = s1.grace ? GSTEM_XOFF : 3.5 for (s2 = s1; ;s2 = s2.next) { if (s2.type == C.NOTE) { if (s2.nflags > nflags) nflags = s2.nflags; notes++ if (s2.st != st) two_staves = true if (s2.stem != s1.stem) two_dir = true if (!visible && !s2.invis && (!s2.stemless || s2.trem2)) visible = true if (s2.beam_end) break } if (!s2.next) { /* beam towards next music line */ for (; ; s2 = s2.prev) { if (s2.type == C.NOTE) break } s = sym_dup(s2); s.next = s2.next if (s.next) s.next.prev = s; s2.next = s; s.prev = s2; s.ts_next = s2.ts_next if (s.ts_next) s.ts_next.ts_prev = s; s2.ts_next = s; s.ts_prev = s2 delete s.beam_st; s.beam_end = true; s.tmp = true delete s.sls; s.x += 12 if (s.x < realwidth - 12) s.x = realwidth - 12; s2 = s; notes++ break } } // at least, must have a visible note with a stem if (!visible) return false; bm.s2 = s2 /* (don't display the flags) */ if (staff_tb[st].y == 0) { /* staves not defined */ if (two_staves) return false } else { /* staves defined */ // if (!two_staves && !s1.grace) { if (!two_staves) { bm.s1 = s1; /* beam already calculated */ bm.a = (s1.ys - s2.ys) / (s1.xs - s2.xs); bm.b = s1.ys - s1.xs * bm.a + staff_tb[st].y; bm.nflags = nflags return true } } s_closest = s1; p_min = 100; p_max = 0 for (s = s1; ; s = s.next) { if (s.type != C.NOTE) continue if ((scale = s.p_v.scale) == 1) scale = staff_tb[s.st].staffscale if (s.stem >= 0) { x = stem_xoff + s.notes[0].shhd if (s.notes[s.nhd].midi > p_max) { p_max = s.notes[s.nhd].midi s_closest = s } } else { x = -stem_xoff + s.notes[s.nhd].shhd if (s.notes[0].midi < p_min) { p_min = s.notes[0].midi s_closest = s } } s.xs = s.x + x * scale; if (s == s2) break } // have flat beams on grace notes when asked if (s.grace && s1.fmt.flatbeams) a = 0 // if a note inside the beam is the closest to the beam, the beam is flat else if (!two_dir && notes >= 3 && s_closest != s1 && s_closest != s2) a = 0 y = s1.ys + staff_tb[st].y if (a == undefined) { if (two_dir && s1.stem != s2.stem && s1.st == s2.st) { // if inverted stems, y -= 5 * s1.stem // remove the beam depth s2.ys -= 5 * s2.stem } a = (s2.ys + staff_tb[s2.st].y - y) / (s2.xs - s1.xs) } if (a != 0) { a = s1.fmt.beamslope * a / (s1.fmt.beamslope + Math.abs(a)) // max steepness for beam if (a > -.04 && a < .04) a = 0 // slope too low } // center the main beam b = (y + s2.ys + staff_tb[s2.st].y) / 2 - a * (s2.xs + s1.xs) / 2 /* have room for the symbols in the staff */ max_stem_err = 0; /* check stem lengths */ // when 2 directions, check if all beams are on the same side of the main beam s = s1 if (two_dir) { n = 1 while (1) { if (s.stem != s1.stem && (s.nflags == 1 || s.beam_br1 || s.beam_br2)) { n = 0 break } if (s == s2) break s = s.next } if (n) // same side n = (s1.nflags + s2.nflags) * (s1.nflags >= s2.nflags ? s1.stem : s2.stem) / 4 else // different sides n = -(s1.nflags * s1.stem + s2.nflags * s2.stem) / 2 b += ((s1.grace ? 3.5 : BEAM_SHIFT) * n + BEAM_DEPTH * s1.stem) / 2 } else if (!s1.grace) { /* normal notes */ var beam_h = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1) //--fixme: added for abc2svg while (s.ts_prev && s.ts_prev.type == C.NOTE && s.ts_prev.time == s.time && s.ts_prev.x > s1.xs) s = s.ts_prev for (; s && s.time <= s2.time; s = s.ts_next) { if (s.type != C.NOTE || s.invis || (s.st != st && s.v != v)) { continue } x = s.v == v ? s.xs : s.x; ys = a * x + b - staff_tb[s.st].y if (s.v == v) { stem_err = min_tb[s.nhd == 0 ? 0 : 1][s.nflags] if (s.stem > 0) { if (s.notes[s.nhd].pit > 26) { stem_err -= 2 if (s.notes[s.nhd].pit > 28) stem_err -= 2 } stem_err -= ys - 3 * (s.notes[s.nhd].pit - 18) } else { if (s.notes[0].pit < 18) { stem_err -= 2 if (s.notes[0].pit < 16) stem_err -= 2 } stem_err -= 3 * (s.notes[0].pit - 18) - ys } stem_err += BEAM_DEPTH + BEAM_SHIFT * (s.nflags - 1) } else { /*fixme: KO when two_staves*/ if (s1.stem > 0) { if (s.stem > 0) { /*fixme: KO when the voice numbers are inverted*/ if (s.ymn > ys + 4 || s.ymx < ys - beam_h - 2) continue if (s.v > v) stem_err = s.ymx - ys else stem_err = s.ymn + 8 - ys } else { stem_err = s.ymx - ys } } else { if (s.stem < 0) { if (s.ymx < ys - 4 || s.ymn > ys - beam_h - 2) continue if (s.v < v) stem_err = ys - s.ymn else stem_err = ys - s.ymx + 8 } else { stem_err = ys - s.ymn } } stem_err += 2 + beam_h } if (stem_err > max_stem_err) max_stem_err = stem_err } } else { /* grace notes */ for ( ; ; s = s.next) { ys = a * s.xs + b - staff_tb[s.st].y; stem_err = GSTEM - 2 if (s.stem > 0) stem_err -= ys - (3 * (s.notes[s.nhd].pit - 18)) else stem_err += ys - (3 * (s.notes[0].pit - 18)); stem_err += 3 * (s.nflags - 1) if (stem_err > max_stem_err) max_stem_err = stem_err if (s == s2) break } } if (max_stem_err > 0) /* shift beam if stems too short */ b += s1.stem * max_stem_err // have room for the gracenotes and clefs if (!two_staves && !two_dir) for (s = s1.next; ; s = s.next) { switch (s.type) { case C.REST: /* cannot move rests in multi-voices */ if (!s.multi) break g = s.ts_next if (!g || g.st != st || (g.type != C.NOTE && g.type != C.REST)) break //fixme:too much vertical shift if some space above the note //fixme:this does not fix rest under beam in second voice (ts_prev) /*fall thru*/ // case C.BAR: if (s.invis) break /*fall thru*/ case C.CLEF: y = a * s.x + b if (s1.stem > 0) { y = s.ymx - y + BEAM_DEPTH + BEAM_SHIFT * (nflags - 1) + 2 if (y > 0) b += y } else { y = s.ymn - y - BEAM_DEPTH - BEAM_SHIFT * (nflags - 1) - 2 if (y < 0) b += y } break case C.GRACE: for (g = s.extra; g; g = g.next) { y = a * g.x + b if (s1.stem > 0) { y = g.ymx - y + BEAM_DEPTH + BEAM_SHIFT * (nflags - 1) + 2 if (y > 0) b += y } else { y = g.ymn - y - BEAM_DEPTH - BEAM_SHIFT * (nflags - 1) - 2 if (y < 0) b += y } } break } if (s == s2) break } if (a == 0) /* shift flat beams onto staff lines */ b += b_pos(s1.grace, s1.stem, nflags, b - staff_tb[st].y) /* adjust final stems and rests under beam */ for (s = s1; ; s = s.next) { switch (s.type) { case C.NOTE: s.ys = a * s.xs + b - staff_tb[s.st].y if (s.stem > 0) { s.ymx = s.ys + 2.5 //fixme: hack if (s.ts_prev && s.ts_prev.stem > 0 && s.ts_prev.st == s.st && s.ts_prev.ymn < s.ymx && s.ts_prev.x == s.x && s.notes[0].shhd == 0) { s.ts_prev.x -= 3; /* fix stem clash */ s.ts_prev.xs -= 3 } } else { s.ymn = s.ys - 2.5 } break case C.REST: y = a * s.x + b - staff_tb[s.st].y dy = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1) + (s.head != C.FULL ? 4 : 9) if (s1.stem > 0) { y -= dy if (s1.multi == 0 && y > 12) y = 12 if (s.y <= y) break } else { y += dy if (s1.multi == 0 && y < 12) y = 12 if (s.y >= y) break } if (s.head != C.FULL) y = (((y + 3 + 12) / 6) | 0) * 6 - 12; s.y = y break } if (s == s2) break } /* save beam parameters */ if (staff_tb[st].y == 0) /* if staves not defined */ return false bm.s1 = s1; bm.a = a; bm.b = b; bm.nflags = nflags return true } /* -- draw the beams for one word -- */ /* (the staves are defined) */ function draw_beams(bm) { var s, i, beam_dir, shift, bshift, bstub, bh, da, bsh, k, k1, k2, x1, osh = 0, // shift other side s1 = bm.s1, s2 = bm.s2 /* -- draw a single beam -- */ function draw_beam(x1, x2, dy, h, bm, n) { /* beam number (1..n) */ var y1, dy2, s = bm.s1, nflags = s.nflags if (s.ntrem) nflags -= s.ntrem if (s.trem2 && n > nflags) { if (s.dur >= C.BLEN / 2) { x1 = s.x + 6; x2 = bm.s2.x - 6 } else if (s.dur < C.BLEN / 4) { var dx = x2 - x1 if (dx < 16) { x1 += dx / 4 x2 -= dx / 4 } else { x1 += 5 x2 -= 6 } } } y1 = bm.a * x1 + bm.b - dy; x2 -= x1; x2 /= stv_g.scale; dy2 = bm.a * x2 * stv_g.scale xypath(x1, y1, true); output += 'l' + x2.toFixed(1) + ' ' + (-dy2).toFixed(1) + 'v' + h.toFixed(1) + 'l' + (-x2).toFixed(1) + ' ' + dy2.toFixed(1) + 'z"/>\n' } // draw_beam() anno_start(s1, 'beam') /*fixme: KO if many staves with different scales*/ // set_scale(s1) if (!s1.grace) { bshift = BEAM_SHIFT; bstub = BEAM_STUB; shift = .34; /* (half width of the stem) */ bh = BEAM_DEPTH } else { bshift = 3.5; bstub = 3.2; shift = .29; bh = 1.8 } bh /= stv_g.scale /*fixme: quick hack for stubs at end of beam and different stem directions*/ beam_dir = s1.stem if (s1.stem != s2.stem && s1.nflags > s2.nflags) beam_dir = s2.stem if (beam_dir < 0) bh = -bh; /* make first beam over whole word and adjust the stem lengths */ draw_beam(s1.xs - shift, s2.xs + shift, 0, bh, bm, 1); da = 0 for (s = s1; ; s = s.next) { if (s.type == C.NOTE && s.stem != beam_dir) s.ys = bm.a * s.xs + bm.b - staff_tb[s.st].y + bshift * (s.nflags - 1) * s.stem - bh if (s == s2) break } if (s1.feathered_beam) { da = bshift / (s2.xs - s1.xs) if (s1.feathered_beam > 0) { da = -da; bshift = da * s1.xs } else { bshift = da * s2.xs } da = da * beam_dir } /* other beams with two or more flags */ shift = 0 for (i = 2; i <= bm.nflags; i++) { shift += bshift if (da != 0) bm.a += da for (s = s1; ; s = s.next) { if (s.type != C.NOTE || s.nflags < i) { if (s == s2) break continue } if (s.trem1 && i > s.nflags - s.ntrem) { x1 = (s.dur >= C.BLEN / 2) ? s.x : s.xs; draw_beam(x1 - 5, x1 + 5, (shift + 2.5) * beam_dir, bh, bm, i) if (s == s2) break continue } k1 = s while (1) { if (s == s2) break k = s.next if (k.type == C.NOTE || k.type == C.REST) { if (k.trem1){ if (k.nflags - k.ntrem < i) break } else if (k.nflags < i) { break } } if (k.beam_br1 || (k.beam_br2 && i > 2)) break s = k } k2 = s while (k2.type != C.NOTE) k2 = k2.prev; x1 = k1.xs bsh = shift * beam_dir if (k1 == k2) { if (k1 == s1) { x1 += bstub } else if (k1 == s2) { x1 -= bstub } else if (k1.beam_br1 || (k1.beam_br2 && i > 2)) { x1 += bstub } else { k = k1.next while (k.type != C.NOTE) k = k.next if (k.beam_br1 || (k.beam_br2 && i > 2)) { x1 -= bstub } else { k1 = k1.prev while (k1.type != C.NOTE) k1 = k1.prev if (k1.nflags < k.nflags || (k1.nflags == k.nflags && k1.dots < k.dots)) x1 += bstub else x1 -= bstub } } if (k1.stem != beam_dir) { osh -= bshift bsh = osh * beam_dir k1.ys = bm.a * k1.xs + bm.b - staff_tb[k1.st].y - bh } } else if (k1.stem == k2.stem && k1.stem != beam_dir) { // inverted stems: put the beam on the other side osh -= bshift bsh = osh * beam_dir for (s = k1; ; s = s.next) { if (s.type == C.NOTE) s.ys = bm.a * s.xs + bm.b - staff_tb[s.st].y - bh if (s == k2) break } } draw_beam(x1, k2.xs, bsh, bh, bm, i) if (s == s2) break } } if (s1.tmp) unlksym(s1) else if (s2.tmp) unlksym(s2) anno_stop(s1, 'beam') } /* -- draw the left side of the staves -- */ function draw_lstaff(x) { var i, j, yb, h, fl, nst = cur_sy.nstaff, l = 0 /* -- draw a system brace or bracket -- */ function draw_sysbra(x, st, flag) { var i, st_end, yt, yb while (!cur_sy.st_print[st]) { if (cur_sy.staves[st].flags & flag) return st++ } i = st_end = st while (1) { if (cur_sy.st_print[i]) st_end = i if (cur_sy.staves[i].flags & flag) break i++ } yt = staff_tb[st].y + staff_tb[st].topbar * staff_tb[st].staffscale; yb = staff_tb[st_end].y + staff_tb[st_end].botbar * staff_tb[st_end].staffscale if (flag & (CLOSE_BRACE | CLOSE_BRACE2)) out_brace(x, yb, yt - yb) else out_bracket(x, yt, yt - yb) } for (i = 0; ; i++) { fl = cur_sy.staves[i].flags if (fl & (OPEN_BRACE | OPEN_BRACKET)) l++ if (cur_sy.st_print[i]) break if (fl & (CLOSE_BRACE | CLOSE_BRACKET)) l-- if (i == nst) break } for (j = nst; j > i; j--) { if (cur_sy.st_print[j]) break } if (i == j && l == 0) return yb = staff_tb[j].y + staff_tb[j].botbar * staff_tb[j].staffscale; h = staff_tb[i].y + staff_tb[i].topbar * staff_tb[i].staffscale - yb; xypath(x, yb); output += "v" + (-h).toFixed(1) + '"/>\n' for (i = 0; i <= nst; i++) { fl = cur_sy.staves[i].flags if (fl & OPEN_BRACE) draw_sysbra(x, i, CLOSE_BRACE) if (fl & OPEN_BRACKET) draw_sysbra(x, i, CLOSE_BRACKET) if (fl & OPEN_BRACE2) draw_sysbra(x - 6, i, CLOSE_BRACE2) if (fl & OPEN_BRACKET2) draw_sysbra(x - 6, i, CLOSE_BRACKET2) } } /* -- draw the time signature -- */ function draw_meter(s) { if (!s.a_meter) return var dx, i, j, meter, x, st = s.st, p_staff = staff_tb[st], y = p_staff.y; // adjust the vertical offset according to the staff definition if (p_staff.stafflines != '|||||') y += (p_staff.topbar + p_staff.botbar) / 2 - 12 // bottom for (i = 0; i < s.a_meter.length; i++) { meter = s.a_meter[i]; x = s.x + s.x_meter[i] if (meter.bot) { out_XYAB('\ \n\ A\n\ B\n\ \n', x, y + 6, m_gl(meter.top), m_gl(meter.bot)) } else { out_XYAB('\ A\n', x, y + 12, m_gl(meter.top)) } } } var acc_nd = {} // cache of the microtonal accidentals /* -- draw an accidental -- */ function draw_acc(x, y, a) { if (typeof a == "object") { // if microtone var c, n = a[0], d = a[1] c = n + '_' + d a = acc_nd[c] if (!a) { a = abc2svg.rat(Math.abs(n), d) d = a[1] a = (n < 0 ? -a[0] : a[0]).toString() if (d != 1) a += '_' + d acc_nd[c] = a } } xygl(x, y, "acc" + a) } // memorize the helper/ledger lines function set_hl(p_st, n, x, dx1, dx2) { var i, hl if (n >= 0) { hl = p_st.hlu[n] if (!hl) hl = p_st.hlu[n] = [] } else { hl = p_st.hld[-n] if (!hl) hl = p_st.hld[-n] = [] } for (i = 0; i < hl.length; i++) { if (x >= hl[i][0]) break } if (i == hl.length) { hl.push([x, dx1, dx2]) } else if (x > hl[i][0]) { hl.splice(++i, 0, [x, dx1, dx2]) } else { if (dx1 < hl[i][1]) hl[i][1] = dx1 if (dx2 > hl[i][2]) hl[i][2] = dx2 } } // set_hl() // draw helper lines // (possible hook) Abc.prototype.draw_hl = function(s) { var i, j, n, note, hla = [], st = s.st, p_staff = staff_tb[st] // check if any helper line if (!p_staff.hll || s.invis) return // no helper line (no line) for (i = 0; i <= s.nhd; i++) { note = s.notes[i] if (!p_staff.hlmap[note.pit - p_staff.hll]) hla.push([note.pit - 18, note.shhd * stv_g.scale]) } n = hla.length if (!n) return // no // handle the helper lines out of the staff var dx1, dx2, hl, shhd,hlp, stafflines = p_staff.stafflines, top = stafflines.length - 1, yu = top, bot = p_staff.botline / 6, yl = bot, dx = s.grace ? 4 : hw_tb[s.head] * 1.3 // get the x start and x stop of the intermediate helper lines note = s.notes[s.stem < 0 ? s.nhd : 0] shhd = note.shhd for (i = 0; i < hla.length; i++) { hlp = hla[i][0] dx1 = (hla[i][1] < shhd ? hla[i][1] : shhd) - dx dx2 = (hla[i][1] > shhd ? hla[i][1] : shhd) + dx if (hlp < bot * 2) { if (hlp < yl * 2) yl = ++hlp >> 1 n-- } else if (hlp > top * 2) { yu = hlp >> 1 n-- } set_hl(p_staff, hlp >> 1, s.x, dx1, dx2) } dx1 = shhd - dx dx2 = shhd + dx while (++yl < bot) set_hl(p_staff, yl, s.x, dx1, dx2) while (--yu > top) set_hl(p_staff, yu, s.x, dx1, dx2) if (!n) return // no more helper lines // draw the helper lines inside the staff i = yl; j = yu while (i > bot && stafflines[i] == '-') i-- while (j < top && stafflines[j] == '-') j++ for ( ; i < j; i++) { if (stafflines[i] == '-') set_hl(p_staff, i, s.x, dx1, dx2) } } /* -- draw a key signature -- */ // (possible hook) var sharp_cl = new Int8Array([24, 9, 15, 21, 6, 12, 18]), flat_cl = new Int8Array([12, 18, 24, 9, 15, 21, 6]), sharp1 = new Int8Array([-9, 12, -9, -9, 12, -9]), sharp2 = new Int8Array([12, -9, 12, -9, 12, -9]), flat1 = new Int8Array([9, -12, 9, -12, 9, -12]), flat2 = new Int8Array([-12, 9, -12, 9, -12, 9]) Abc.prototype.draw_keysig = function(x, s) { var old_sf = s.k_old_sf, st = s.st, staffb = staff_tb[st].y, i, shift, p_seq, clef_ix = s.k_y_clef, a_acc = s.k_a_acc // accidental list [pit, acc] // set the accidentals when K: with modified accidentals function set_k_acc(a_acc, sf) { var i, j, n, nacc, p_acc, accs = [], pits = [] if (sf > 0) { for (nacc = 0; nacc < sf; nacc++) { accs[nacc] = 1 // sharp pits[nacc] = [26, 23, 27, 24, 21, 25, 22][nacc] } } else { for (nacc = 0; nacc < -sf; nacc++) { accs[nacc] = -1 // flat pits[nacc] = [22, 25, 21, 24, 20, 23, 26][nacc] } } n = a_acc.length for (i = 0; i < n; i++) { p_acc = a_acc[i] for (j = 0; j < nacc; j++) { if (pits[j] == p_acc.pit) { accs[j] = p_acc.acc break } } if (j == nacc) { accs[j] = p_acc.acc pits[j] = p_acc.pit nacc++ } } for (i = 0; i < nacc; i++) { p_acc = a_acc[i] if (!p_acc) p_acc = a_acc[i] = {} p_acc.acc = accs[i] p_acc.pit = pits[i] } } // set_k_acc() // ---- draw_keysig --- if (clef_ix & 1) clef_ix += 7; clef_ix /= 2 while (clef_ix < 0) clef_ix += 7; clef_ix %= 7 /* normal accidentals */ if (a_acc && !s.exp) // if added accidentals set_k_acc(a_acc, s.k_sf) // merge them into the key if (!a_acc) { /* put neutrals if 'accidental cancel' */ if (s.fmt.cancelkey || s.k_sf == 0) { /* when flats to sharps, or sharps to flats, */ if (s.k_sf == 0 || old_sf * s.k_sf < 0) { /* old sharps */ shift = sharp_cl[clef_ix]; p_seq = shift > 9 ? sharp1 : sharp2 for (i = 0; i < old_sf; i++) { xygl(x, staffb + shift, "acc3"); shift += p_seq[i]; x += 5.5 } /* old flats */ shift = flat_cl[clef_ix]; p_seq = shift < 18 ? flat1 : flat2 for (i = 0; i > old_sf; i--) { xygl(x, staffb + shift, "acc3"); shift += p_seq[-i]; x += 5.5 } if (s.k_sf != 0) x += 3 /* extra space */ } } /* new sharps */ if (s.k_sf > 0) { shift = sharp_cl[clef_ix]; p_seq = shift > 9 ? sharp1 : sharp2 for (i = 0; i < s.k_sf; i++) { xygl(x, staffb + shift, "acc1"); shift += p_seq[i]; x += 5.5 } if (s.fmt.cancelkey && i < old_sf) { x += 2 for (; i < old_sf; i++) { xygl(x, staffb + shift, "acc3"); shift += p_seq[i]; x += 5.5 } } if (s.k_bagpipe == 'p') { // K:Hp - add the g natural xygl(x, staffb + 27, "acc3") x += 5.5 } } /* new flats */ if (s.k_sf < 0) { shift = flat_cl[clef_ix]; p_seq = shift < 18 ? flat1 : flat2 for (i = 0; i > s.k_sf; i--) { xygl(x, staffb + shift, "acc-1"); shift += p_seq[-i]; x += 5.5 } if (s.fmt.cancelkey && i > old_sf) { x += 2 for (; i > old_sf; i--) { xygl(x, staffb + shift, "acc3"); shift += p_seq[-i]; x += 5.5 } } } } else if (a_acc.length) { /* explicit accidentals */ var acc, last_acc = a_acc[0].acc, last_shift = 100, s2 = { st: st, nhd: 0, notes: [{}] } for (i = 0; i < a_acc.length; i++) { acc = a_acc[i]; shift = (s.k_y_clef // clef shift + acc.pit - 18) * 3 while (shift < -3) // let the accidentals inside the staff shift += 21 while (shift > 27) shift -= 21 if (i != 0 && (shift > last_shift + 18 || shift < last_shift - 18)) x -= 5.5 // no clash else if (acc.acc != last_acc) x += 3; last_acc = acc.acc; s2.x = x s2.notes[0].pit = shift / 3 + 18; // is this useful? // s2.head = C.FULL // s2.notes[0].shhd = 0 // self.draw_hl(s2) last_shift = shift; draw_acc(x, staffb + shift, acc.acc) x += 5.5 } } } // output the measure repeat number function nrep_out(x, y, n) { y -= 3 if (n < 10) { xygl(x - 4, y, "mtr" + n) } else { xygl(x - 10, y, "mtr" + ((n / 10) | 0)) xygl(x - 2, y, "mtr" + (n % 10)) } } // nrep_out() // if rest alone in the measure or measure repeat, // change the head and center function center_rest(s) { var s2, x if (s.dur < C.BLEN * 2) s.nflags = -2 // semibreve / whole else if (s.dur < C.BLEN * 4) s.nflags = -3 else s.nflags = -4 s.dots = 0 /* don't use next/prev: there is no bar in voice overlay */ s2 = s.ts_next while (s2.time != s.time + s.dur && s2.ts_next) s2 = s2.ts_next x = s2.x - s2.wl s2 = s while (!s2.seqst) s2 = s2.ts_prev s2 = s2.ts_prev x = (x + s2.x + s2.wr) / 2 /* center the associated decorations */ if (s.a_dd) deco_update(s, x - s.x) s.x = x } // center_rest() /* -- draw a rest -- */ /* (the staves are defined) */ var rest_tb = [ "r128", "r64", "r32", "r16", "r8", "r4", "r2", "r1", "r0", "r00"] function draw_rest(s) { var s2, i, j, y, bx, p_staff = staff_tb[s.st], yb = p_staff.y, // bottom of staff x = s.x if (s.notes[0].shhd) x += s.notes[0].shhd * stv_g.scale if (s.rep_nb) { set_sscale(s.st); anno_start(s); if (p_staff.stafflines == '|||||') yb += 12 else yb += (p_staff.topbar + p_staff.botbar) / 2 if (s.rep_nb < 0) { xygl(x, yb, "srep") } else { xygl(x, yb, "mrep") if (s.rep_nb > 2 && s.v == cur_sy.top_voice && s.fmt.measrepnb > 0 && !(s.rep_nb % s.fmt.measrepnb)) nrep_out(x, yb + p_staff.topbar, s.rep_nb) } anno_a.push(s) return } set_scale(s); anno_start(s); if (s.notes[0].color) set_color(s.notes[0].color); y = s.y; i = 5 - s.nflags /* rest_tb index (5 = C_XFLAGS) */ if (i == 7 && y == 12 && p_staff.stafflines.length <= 2) y -= 6 /* semibreve a bit lower */ // draw the rest if (!s.notes[0].invis) // if not head replacement xygl(x, y + yb, rest_tb[i]) if (s.dots) { x += 8; y += yb + 3 j = s.dots i = (s.dur_orig / 12) >> ((5 - s.nflags) - j) while (j-- > 0) { xygl(x, y, (i & (1 << j)) ? "dot" : "dot+") x += 3.5 } } set_color(); anno_a.push(s) } // -- draw a multi-measure rest -- // (the staves are defined) function draw_mrest(s) { var x1, x2, prev, p_st = staff_tb[s.st], y = p_st.y + (p_st.topbar + p_st.botbar) / 2, p = s.nmes.toString() // output an old multimeasure rest function omrest() { var x = s.x, y = p_st.y + 12, n = s.nmes, k = n >> 2 // number of rests if (n & 3) { k++ if (n & 3 == 3) k++ } //dx = 6 x -= 3 * (k - 1) while (n >= 4) { xygl(x, y, "r00") n -= 4 x += 6 } if (n >= 2) { xygl(x, y, "r0") n -= 2 x += 6 } if (n) xygl(x + 2, y, "r1") } // omrest() if (!s.next) { error(1, s, "Lack of bar after multi-measure rest") return } set_scale(s) prev = s // search the start of the previous time sequence while (!prev.seqst) prev = prev.ts_prev prev = prev.ts_prev while (!prev.seqst) prev = prev.ts_prev x1 = prev.x + 20 x2 = s.next.x - 20 s.x = (x1 + x2) / 2 anno_start(s) if (!cfmt.oldmrest || s.nmes > cfmt.oldmrest) { out_XYAB('\n' } else { omrest() // old multirest } if (s.tacet) out_XYAB('A\n', s.x, y + 18, s.tacet) else out_XYAB('A\n', s.x, y + 22, m_gl(p)) anno_a.push(s) } // draw_mrest() function grace_slur(s) { var yy, x0, y0, x3, y3, bet1, bet2, dy1, dy2, last, below, so = s, g = s.extra while (1) { if (!g.next) break /* (keep the last note) */ g = g.next } last = g below = ((g.stem >= 0 || s.multi < 0) && g.notes[0].pit <= 28) || g.notes[0].pit < 16 if (below) { yy = 127 for (g = s.extra; g; g = g.next) { if (g.y < yy) { yy = g.y; last = g } } x0 = last.x; y0 = last.y - 5 if (s.extra != last) { x0 -= 4; y0 += 1 } s = s.next; x3 = s.x - 1 if (s.stem < 0 && s.nflags > -2) x3 -= 4; y3 = 3 * (s.notes[0].pit - 18) - 5; dy1 = (x3 - x0) * .4 if (dy1 > 3) dy1 = 3; dy2 = dy1; bet1 = .2; bet2 = .8 if (y0 > y3 + 7) { x0 = last.x - 1; y0 += .5; y3 += 6.5; x3 = s.x - 5.5; dy1 = (y0 - y3) * .8; dy2 = (y0 - y3) * .2; bet1 = 0 } else if (y3 > y0 + 4) { y3 = y0 + 4; x0 = last.x + 2; y0 = last.y - 4 } } else { // slur above yy = -127 for (g = s.extra; g; g = g.next) { if (g.y > yy) { yy = g.y; last = g } } x0 = last.x; y0 = last.y + 5 if (s.extra != last) { x0 -= 4; y0 -= 1 } s = s.next; x3 = s.x - 1 if (s.stem >= 0 && s.nflags > -2) x3 -= 2; y3 = 3 * (s.notes[s.nhd].pit - 18) + 5; dy1 = (x0 - x3) * .4 if (dy1 < -3) dy1 = -3; dy2 = dy1; bet1 = .2; bet2 = .8 if (y0 < y3 - 7) { x0 = last.x - 1; y0 -= .5; y3 -= 6.5; x3 = s.x - 5.5; dy1 = (y0 - y3) * .8; dy2 = (y0 - y3) * .2; bet1 = 0 } else if (y3 < y0 - 4) { y3 = y0 - 4; x0 = last.x + 2; y0 = last.y + 4 } } so.slur = { x0: x0, y0: y0, x1: bet1 * x3 + (1 - bet1) * x0 - x0, y1: y0 - bet1 * y3 - (1 - bet1) * y0 + dy1, x2: bet2 * x3 + (1 - bet2) * x0 - x0, y2: y0 - bet2 * y3 - (1 - bet2) * y0 + dy2, x3: x3 - x0, y3: y0 - y3 } y0 -= so.slur.y1 g = so.extra //fixme: useless? // y_set(s.st, !below, x0, x3 - x0, y0) if (below) { if (y0 < g.ymn) g.ymn = y0 } else { if (y0 > g.ymx) g.ymx = y0 } } // grace_slur() /* -- draw grace notes -- */ /* (the staves are defined) */ function draw_gracenotes(s) { var x1, y1, last, note, bm = {}, g = s.extra /* draw the notes */ // bm.s2 = undefined /* (draw flags) */ while (1) { if (g.beam_st && !g.beam_end) { if (self.calculate_beam(bm, g)) draw_beams(bm) } anno_start(g) draw_note(g, !bm.s2) if (g == bm.s2) bm.s2 = null /* (draw flags again) */ anno_a.push(s) // if (g.sls || g.sl2) // slur = true if (!g.next) break /* (keep the last note) */ g = g.next } last = g // if an acciaccatura, draw a bar if (s.sappo) { g = s.extra if (!g.next) { /* if one note */ x1 = 9 y1 = g.stem > 0 ? 5 : -5 } else { /* many notes */ x1 = (g.next.x - g.x) * .5 + 4 y1 = (g.ys + g.next.ys) * .5 - g.y if (g.stem > 0) y1 -= 1 else y1 += 1 } note = g.notes[g.stem < 0 ? 0 : g.nhd] out_acciac(x_head(g, note), y_head(g, note), x1, y1, g.stem > 0) } /* slur */ g = s.slur if (g) { anno_start(s, 'slur') xypath(g.x0, g.y0 + staff_tb[s.st].y) output += 'c' + g.x1.toFixed(1) + ' ' + g.y1.toFixed(1) + ' ' + g.x2.toFixed(1) + ' ' + g.y2.toFixed(1) + ' ' + g.x3.toFixed(1) + ' ' + g.y3.toFixed(1) + '"/>\n' anno_stop(s, 'slur') } } /* -- set the y offset of the dots -- */ function setdoty(s, y_tb) { var m, m1, y /* set the normal offsets */ for (m = 0; m <= s.nhd; m++) { y = 3 * (s.notes[m].pit - 18) /* note height on staff */ if ((y % 6) == 0) { if (s.dot_low) y -= 3 else y += 3 } y_tb[m] = y } /* dispatch and recenter the dots in the staff spaces */ for (m = 0; m < s.nhd; m++) { if (y_tb[m + 1] > y_tb[m]) continue m1 = m while (m1 > 0) { if (y_tb[m1] > y_tb[m1 - 1] + 6) break m1-- } if (3 * (s.notes[m1].pit - 18) - y_tb[m1] < y_tb[m + 1] - 3 * (s.notes[m + 1].pit - 18)) { while (m1 <= m) y_tb[m1++] -= 6 } else { y_tb[m + 1] = y_tb[m] + 6 } } } // get the x and y position of a note head // (when the staves are defined) function x_head(s, note) { return s.x + note.shhd * stv_g.scale } function y_head(s, note) { return staff_tb[s.st].y + 3 * (note.pit - 18) } /* -- draw m-th head with accidentals and dots -- */ /* (the staves are defined) */ // sets {x,y}_note function draw_basic_note(s, m, y_tb) { var i, p, yy, dotx, doty, inv, old_color = false, note = s.notes[m], staffb = staff_tb[s.st].y, /* bottom of staff */ x = s.x, y = 3 * (note.pit - 18), /* note height on staff */ shhd = note.shhd * stv_g.scale, x_note = x + shhd, y_note = y + staffb // /* special case for voice unison */ // if (s.nohdi1 != undefined // && m >= s.nohdi1 && m < s.nohdi2) // return var elts = identify_note(s, note.dur), head = elts[0], dots = elts[1], nflags = elts[2] /* draw the head */ if (note.invis) { ; } else if (s.grace) { // don't apply %%map to grace notes p = "ghd"; x_note -= 4.5 * stv_g.scale } else if (note.map && note.map[0]) { i = s.head; p = note.map[0][i] // heads if (!p) p = note.map[0][note.map[0].length - 1] i = p.indexOf('/') if (i >= 0) { // stem dependant if (s.stem >= 0) p = p.slice(0, i) else p = p.slice(i + 1) } } else if (s.type == C.CUSTOS) { p = "custos" } else { switch (head) { case C.OVAL: p = "HD" break case C.OVALBARS: if (s.head != C.SQUARE) { p = "HDD" break } // fall thru case C.SQUARE: if (nflags > -4) { p = "breve" } else { p = "longa" inv = s.stem > 0 } /* don't display dots on last note of the tune */ if (!tsnext && s.next && s.next.type == C.BAR && !s.next.next) dots = 0 x_note += 1 break case C.EMPTY: p = "Hd" // white note break default: // black note p = "hd" break } } if (note.color != undefined) old_color = set_color(note.color) if (p) { if (inv) { g_open(x_note, y_note, 0, 1, -1); x_note = y_note = 0 } if (!self.psxygl(x_note, y_note, p)) xygl(x_note, y_note, p) if (inv) g_close() } /* draw the dots */ /*fixme: to see for grace notes*/ if (dots) { dotx = x + (7.7 + s.xmx) * stv_g.scale if (y_tb[m] == undefined) { y_tb[m] = 3 * (s.notes[m].pit - 18) if ((s.notes[m].pit & 1) == 0) y_tb[m] += 3 } doty = y_tb[m] + staffb i = (note.dur / 12) >> ((5 - nflags) - dots) while (dots-- > 0) { xygl(dotx, doty, (i & (1 << dots)) ? "dot" : "dot+") dotx += 3.5 } } /* draw the accidental */ if (note.acc) { x -= note.shac * stv_g.scale if (!s.grace) { draw_acc(x, y + staffb, note.acc) } else { g_open(x, y + staffb, 0, .75); draw_acc(0, 0, note.acc) g_close() } } if (old_color != false) set_color(old_color) } /* -- draw a note or a chord -- */ /* (the staves are defined) */ function draw_note(s, fl) { // draw flags var s2, i, m, y, slen, c, nflags, y_tb = new Array(s.nhd + 1), note = s.notes[s.stem < 0 ? s.nhd : 0], // master note head x = x_head(s, note), y = y_head(s, note), staffb = staff_tb[s.st].y if (s.dots) setdoty(s, y_tb) /* draw the stem and flags */ if (!s.stemless) { slen = s.ys - s.y; nflags = s.nflags if (s.ntrem) nflags -= s.ntrem if (!fl || nflags <= 0) { /* stem only */ if (s.nflags > 0) { /* (fix for PS low resolution) */ if (s.stem >= 0) slen -= 1 else slen += 1 } out_stem(x, y, slen, s.grace) } else { /* stem and flags */ out_stem(x, y, slen, s.grace, nflags, s.fmt.straightflags) } } else if (s.xstem) { /* cross-staff stem */ s2 = s.ts_prev; slen = (s2.stem > 0 ? s2.y : s2.ys) - s.y; slen += staff_tb[s2.st].y - staffb; out_stem(x, y, slen) } /* draw the tremolo bars */ if (fl && s.trem1) { var ntrem = s.ntrem || 0, x1 = x; slen = 3 * (s.notes[s.stem > 0 ? s.nhd : 0].pit - 18) if (s.head == C.FULL || s.head == C.EMPTY) { x1 += (s.grace ? GSTEM_XOFF : 3.5) * s.stem if (s.stem > 0) slen += 6 + 5.4 * ntrem else slen -= 6 + 5.4 } else { if (s.stem > 0) slen += 5 + 5.4 * ntrem else slen -= 5 + 5.4 } slen /= s.p_v.scale; out_trem(x1, staffb + slen, ntrem) } /* draw the note heads */ for (m = 0; m <= s.nhd; m++) draw_basic_note(s, m, y_tb) } // find where to start a long decoration function prev_scut(s) { while (s.prev) { s = s.prev if (s.rbstart) return s } /* return a symbol of any voice starting before the start of the voice */ s = s.p_v.sym while (s.type != C.CLEF) s = s.ts_prev /* search a main voice */ if (s.next && s.next.type == C.KEY) s = s.next if (s.next && s.next.type == C.METER) return s.next return s } /* -- decide whether a slur goes up or down (same voice) -- */ function slur_direction(k1, k2) { var s, some_upstem, low, dir // check if slur sequence in a multi-voice staff function slur_multi(s1, s2) { // while (1) { // if (s1.multi) // if multi voice // //fixme: may change // return s1.multi // if (s1 == s2) // break // s1 = s1.next // } if (s1.multi) return s1.multi if (s2.multi) return s2.multi return 0 } // slur_multi() if (k1.grace && k1.stem > 0) return -1 dir = slur_multi(k1, k2) if (dir) return dir for (s = k1; ; s = s.next) { if (s.type == C.NOTE) { if (!s.stemless) { if (s.stem < 0) return 1 some_upstem = true } if (s.notes[0].pit < 22) /* if under middle staff */ low = true } // if (s == k2) if (s.time == k2.time) // (k2 may be a grace note) break } if (!some_upstem && !low) return 1 return -1 } /* -- output a slur / tie -- */ function slur_out(x1, y1, x2, y2, dir, height, dotted) { var dx, dy, dz, alfa = .3, beta = .45; /* for wide flat slurs, make shape more square */ dy = y2 - y1 if (dy < 0) dy = -dy; dx = x2 - x1 if (dx > 40. && dy / dx < .7) { alfa = .3 + .002 * (dx - 40.) if (alfa > .7) alfa = .7 } /* alfa, beta, and height determine Bezier control points pp1,pp2 * * X====alfa===|===alfa=====X * / | \ * pp1 | pp2 * / height \ * beta | beta * / | \ * p1 m p2 * */ var mx = .5 * (x1 + x2), my = .5 * (y1 + y2), xx1 = mx + alfa * (x1 - mx), yy1 = my + alfa * (y1 - my) + height; xx1 = x1 + beta * (xx1 - x1); yy1 = y1 + beta * (yy1 - y1) var xx2 = mx + alfa * (x2 - mx), yy2 = my + alfa * (y2 - my) + height; xx2 = x2 + beta * (xx2 - x2); yy2 = y2 + beta * (yy2 - y2); // dy = 1.6 * dir dy = 2 * dir; dz = .2 + .001 * dx if (dz > .6) dz = .6; dz *= dir dx *= .03 // if (dx > 10.) // dx = 10. // var scale_y = stv_g.st < 0 ? stv_g.scale : 1 var scale_y = 1 // (see set_dscale()) if (!dotted) output += '\n' } // draw a slur between two chords / notes /* (the staves are not yet defined) */ /* (delayed output) */ /* (not a pretty routine, this) */ function draw_slur(path, // list of symbols under the slur sl, // slur variables: type, end symbol, note recurr) { // recurrent call when slur on two staves var i, k, g, x1, y1, x2, y2, height, addy, s_st2, a, y, z, h, dx, dy, ty = sl.ty, dir = (ty & 0x07) == C.SL_ABOVE ? 1 : -1, n = path.length, i1 = 0, i2 = n - 1, not1 = sl.nts, // if the slur starts on a note k1 = path[0], k2 = path[i2], nn = 1 set_dscale(k1.st) for (i = 1; i < n; i++) { k = path[i] if (k.type == C.NOTE || k.type == C.REST) { nn++ if (k.st != k1.st && !s_st2) s_st2 = k } } // if slur on 2 staves, define it, but don't output it now // this will be done in draw_sl2() if (s_st2 && !recurr) { // if not 2nd call to draw_slur() if (!gene.a_sl) gene.a_sl = [] // replace the symbols of the other staff // by symbols in the current staff but with updated y offsets h = 24 + k1.fmt.sysstaffsep // delta y if (s_st2.st > k1.st) h = -h for (i = 0; i < n; i++) { k = path[i] if (k.st == k1.st) { if (k.dur) a = k // (used for types // and \\) continue } k = clone(k) if (path[i] == s_st2) s_st2 = k path[i] = k if (k.dur) { k.notes = clone(k.notes) k.notes[0] = clone(k.notes[0]) if (sl.ty & C.SL_CENTER) { if (k.st != a.st) { sl.ty = (sl.ty & ~0x07) | (a.st < k.st ? C.SL_BELOW : C.SL_ABOVE) z = k1.ymn h = k2.ymx if (k.st < a.st) { for (i1 = 1; i1 < i; i1++) { a = path[i1] if (a.ymn < z) z = a.ymn } for (i1 = i; i1 < i2; i1++) { a = path[i1] if (a.ymx > h) h = a.ymx } } else { for (i1 = 1; i1 < i; i1++) { a = path[i1] if (a.ymx > h) h = a.ymx } for (i1 = i; i1 < i2; i1++) { a = path[i1] if (a.ymn < z) z = a.ymn } } h += z a = k } k.y = h - k.y k.notes[0].pit = (k.y / 3 | 0) + 18 k.ys = h - k.ys y = k.ymx k.ymx = h - k.ymn k.ymn = h - y k.stem = -k.stem } else { k.notes[0].pit += h / 3 | 0 k.ys += h k.y += h k.ymx += h k.ymn += h } } // k.st = k1.st // keep the staff number for draw_sl2() } ty = k1.st > s_st2.st ? '/' : '\\' if (sl.ty & C.SL_CENTER) ty = ty + ty // type = // or \\ else if (k1.st == k2.st) ty = ty == '/' ? '/\\' : '\\/' // type = /\ or \/ else ty += dir > 0 ? '+' : '-' // type = .+ or .- var savout = output output = "" draw_slur(path, sl, 1 /*true*/) gene.a_sl.push([k1, s_st2, ty, output]) output = savout return } /* fix endpoints */ x1 = k1.x if (k1.notes && k1.notes[0].shhd) x1 += k1.notes[0].shhd; x2 = k2.x if (k2.notes) x2 += k2.notes[0].shhd if (not1) { // start on a note y1 = 3 * (not1.pit - 18) + 2 * dir x1 += 3 } else { // start on a chord y1 = dir > 0 ? k1.ymx + 2 : k1.ymn - 2 if (k1.type == C.NOTE) { if (dir > 0) { if (k1.stem > 0) { x1 += 5 if (k1.beam_end && k1.nflags >= -1 /* if with a stem */ //fixme: check if at end of tuplet && !k1.in_tuplet) { if (k1.nflags > 0) { x1 += 2; y1 = k1.ys - 3 } else { y1 = k1.ys - 6 } } else { y1 = k1.ys + 3 } } else { y1 = k1.y + 8 } } else { if (k1.stem < 0) { x1 -= 1 if (k2.grace) { y1 = k1.y - 8 } else if (k1.beam_end && k1.nflags >= -1 && (!k1.in_tuplet || k1.ys < y1 + 3)) { if (k1.nflags > 0) { x1 += 2; y1 = k1.ys + 3 } else { y1 = k1.ys + 6 } } else { y1 = k1.ys - 3 } } else { y1 = k1.y - 8 } } } } if (sl.nte) { // slur ending on a note y2 = 3 * (sl.nte.pit - 18) + 2 * dir x2 -= 3 } else { // end on a chord y2 = dir > 0 ? k2.ymx + 2 : k2.ymn - 2 if (k2.type == C.NOTE) { if (dir > 0) { if (k2.stem > 0) { x2 += 1 if (k2.beam_st && k2.nflags >= -1 && !k2.in_tuplet) y2 = k2.ys - 6 else y2 = k2.ys + 3 } else { y2 = k2.y + 8 } } else { if (k2.stem < 0) { x2 -= 5 if (k2.beam_st && k2.nflags >= -1 && !k2.in_tuplet) // || k2.ys < y2 + 3)) y2 = k2.ys + 6 else y2 = k2.ys - 3 } else { y2 = k2.y - 8 } } } } if (k1.type != C.NOTE) { y1 = y2 + 1.2 * dir; x1 = k1.x + k1.wr * .5 if (x1 > x2 - 12) x1 = x2 - 12 } if (k2.type != C.NOTE) { if (k1.type == C.NOTE) y2 = y1 + 1.2 * dir else y2 = y1 if (k1 != k2) x2 = k2.x - k2.wl * .3 } if (nn >= 3) { k = path[1] if (k.type != C.BAR && k.x < x1 + 48) { if (dir > 0) { y = k.ymx - 2 if (y1 < y) y1 = y } else { y = k.ymn + 2 if (y1 > y) y1 = y } } k = path[i2 - 1] if (k.type != C.BAR && k.x > x2 - 48) { if (dir > 0) { y = k.ymx - 2 if (y2 < y) y2 = y } else { y = k.ymn + 2 if (y2 > y) y2 = y } } } a = (y2 - y1) / (x2 - x1) /* slur steepness */ if (a > SLUR_SLOPE || a < -SLUR_SLOPE) { a = a > SLUR_SLOPE ? SLUR_SLOPE : -SLUR_SLOPE if (a * dir > 0) y1 = y2 - a * (x2 - x1) else y2 = y1 + a * (x2 - x1) } /* for big vertical jump, shift endpoints */ y = y2 - y1 if (y > 8) y = 8 else if (y < -8) y = -8 z = y if (z < 0) z = -z; dx = .5 * z; dy = .3 * y if (y * dir > 0) { x2 -= dx; y2 -= dy } else { x1 += dx; y1 += dy } /* special case for grace notes */ if (k1.grace) x1 = k1.x - GSTEM_XOFF * .5 if (k2.grace) x2 = k2.x + GSTEM_XOFF * 1.5; h = 0; a = (y2 - y1) / (x2 - x1) if (k1 != k2 && k1.v == k2.v) { addy = y1 - a * x1 for (i = 1; i < i2; i++) { k = path[i] switch (k.type) { case C.NOTE: case C.REST: if (dir > 0) { y = 3 * (k.notes[k.nhd].pit - 18) + 6 if (y < k.ymx) y = k.ymx; y -= a * k.x + addy if (y > h) h = y } else { y = 3 * (k.notes[0].pit - 18) - 6 if (y > k.ymn) y = k.ymn; y -= a * k.x + addy if (y < h) h = y } break case C.GRACE: for (g = k.extra; g; g = g.next) { if (dir > 0) { y = 3 * (g.notes[g.nhd].pit - 18) + 6 if (y < g.ymx) y = g.ymx; y -= a * g.x + addy if (y > h) h = y } else { y = 3 * (g.notes[0].pit - 18) - 6 if (y > g.ymn) y = g.ymn; y -= a * g.x + addy if (y < h) h = y } } break } } y1 += .45 * h; y2 += .45 * h; h *= .65 } if (nn > 3) height = (.08 * (x2 - x1) + 12) * dir else height = (.03 * (x2 - x1) + 8) * dir if (dir > 0) { if (height < 3 * h) height = 3 * h if (height > 40) height = 40 } else { if (height > 3 * h) height = 3 * h if (height < -40) height = -40 } y = y2 - y1 if (y < 0) y = -y if (dir > 0) { if (height < .8 * y) height = .8 * y } else { if (height > -.8 * y) height = -.8 * y } height *= k1.fmt.slurheight; // anno_start(k1_o, 'slur'); slur_out(x1, y1, x2, y2, dir, height, ty & C.SL_DOTTED); // anno_stop(k1_o, 'slur'); /* have room for other symbols */ dx = x2 - x1; a = (y2 - y1) / dx; /*fixme: it seems to work with .4, but why?*/ // addy = y1 - a * x1 + .4 * height //fixme: the following code seems better! addy = y1 - a * x1 if (height > 0) addy += 4 * Math.sqrt(height) - 2 else addy -= 4 * Math.sqrt(-height) - 2 for (i = 0; i < i2; i++) { k = path[i] if (k.st != k1.st || k.type == C.BAR) continue y = a * k.x + addy if (k.ymx < y) k.ymx = y else if (k.ymn > y) k.ymn = y if (recurr) // no room when slur on 2 staves continue if (i == i2 - 1) { dx = x2 if (sl.nte) dx -= 5; } else { dx = k.x + k.wr } if (i != 0) x1 = k.x if (!i || i == i2) y -= height / 3 dx -= x1 - k.wl y_set(k1.st, dir > 0, x1 - k.wl, dx, y) } } /* -- draw the slurs between 2 symbols --*/ function draw_slurs(s, last) { var gr1, i, m, note, sls, nsls // draw a slur knowing the start and stop elements function draw_sls(s, // start symbol sl) { // slur variables var k, v, i, dir, s3, path = [], s2 = sl.se // slur end if (last && s2.time > last.time) return // will be drawn next time // handle slurs without start or end switch (sl.loc) { case 'i': // no start s = prev_scut(s) break case 'o': // no end for (s3 = s; s3.ts_next; s3 = s3.ts_next) ; s2 = s3 for (; s3; s3 = s3.ts_prev) { if (s3.v == s.v) { s2 = s3 break } if (s3.st == s.st) s2 = s3 if (s3.ts_prev.time != s2.time) break } break } // if the slur continues on the next music line, // stop it at the end of the current line if (s.p_v.s_next && s2.time >= tsnext.time) { if (s2.time == tsnext.time) { if (s2.grace) { for (s3 = tsnext; s3 && s3.time == s2.time; s3 = s3.ts_next) { if (s3.type == C.GRACE) { s3 = null break } } } else { for (s3 = tsnext; s3.time == s2.time; s3 = s3.ts_next) { if (s3 == s2) { s3 = null // end of slur in next line break } } } } else { s3 = null } if (!s3) { s.p_v.sls.push(sl); // continuation on next line s2 = s.p_v.s_next.prev // one voice while (s2.next) s2 = s2.next; // search the ending bar sl = Object.create(sl); // new slur } } // set the slur position switch (sl.ty & 0x07) { case C.SL_ABOVE: dir = 1; break case C.SL_BELOW: dir = -1; break default: dir = s.v != s2.v ? 1 : // always above ? slur_direction(s, s2) sl.ty &= ~0x07 sl.ty |= dir > 0 ? C.SL_ABOVE : C.SL_BELOW break } // build the path of the symbols under the slur if (s.v == s2.v) { v = s.v } if (!cur_sy.voices[s.v] || !cur_sy.voices[s2.v]) { v = s.v > s2.v ? s.v : s2.v } else if (dir * // if slur on first voice (cur_sy.voices[s.v].range <= cur_sy.voices[s2.v].range ? 1 : -1) > 0) v = s.v else v = s2.v if (gr1 // if start on a grace note && !(s2.grace // and not end in the same && s.v == s2.v // grace note sequence && s.time == s2.time)) { do { path.push(s); // add all grace notes s = s.next } while (s); s = gr1.next } else { path.push(s); if (s.grace) s = s.next else s = s.ts_next } if (!s2.grace) { // if end on a normal note while (s) { if (s.v == v) path.push(s) if (s == s2) break s = s.ts_next } } else if (s.grace) { // if start/end in the same sequence while (1) { // if (s.v == v) path.push(s) if (s == s2) break s = s.next } } else { // end on a grace note k = s2 while (k.prev) k = k.prev // .extra pointer while (1) { if (s.v == v) path.push(s) if (s.extra == k) break s = s.ts_next } s = k while (1) { path.push(s) if (s == s2) break s = s.next } } // if some nested slurs/tuplets, draw them for (i = 1; i < path.length - 1; i++) { s = path[i] if (s.sls) draw_slurs(s, last) if (s.tp) draw_tuplet(s) } draw_slur(path, sl) return 1 // slur drawn, remove it } // draw_sls() // code of draw_slurs() while (1) { if (!s || s == last) { if (!gr1 // if end of grace notes || !(s = gr1.next) // return to normal notes || s == last) break gr1 = null } if (s.type == C.GRACE) { // if start of grace notes gr1 = s; // continue in the grace note sequence s = s.extra continue } if (s.sls) { // slurs from the chord sls = s.sls s.sls = null nsls = [] for (i = 0; i < sls.length; i++) { if (!draw_sls(s, sls[i])) nsls.push(sls[i]) } if (nsls.length) s.sls = nsls } s = s.next } } /* -- draw a tuplet -- */ /* (the staves are not yet defined) */ /* (delayed output) */ /* See http://moinejf.free.fr/abcm2ps-doc/tuplets.html * for the value of 'tp.f' */ function draw_tuplet(s1) { var s2, s3, g, stu, std, nb_only, x1, x2, y1, y2, xm, ym, a, s0, yy, yx, dy, a, dir, r, tp = s1.tp.shift() // tuplet parameters if (!s1.tp.length) delete s1.tp // last tuplet // treat the slurs and the nested tuplets stu = std = s1.st for (s2 = s1; s2; s2 = s2.next) { switch (s2.type) { case C.GRACE: if (!s2.sl1) continue for (g = s2.extra; g; g = g.next) { if (g.sls) draw_slurs(g) } // fall thru default: continue case C.NOTE: case C.REST: break } if (s2.sls) draw_slurs(s2) if (s2.st < stu) { std = stu stu = s2.st } else if (s2.st > std) { std = s2.st } if (s2.tp) draw_tuplet(s2) if (s2.tpe) break } if (s2) s2.tpe-- if (tp.f[0] == 1) // if 'when' == never return // accept tuplets on many lines if (!s2) { error(1, s1, "No end of tuplet in this music line") return } dir = tp.f[3] // 'where' if (!dir) { // if auto s3 = s1 while (s3 && !s3.stem) // (may have tuplets of rests!) s3 = s3.next dir = (s3 && s3.stem < 0) ? C.SL_BELOW : C.SL_ABOVE } set_dscale(dir == C.SL_ABOVE ? stu : std) if (s1 == s2 // tuplet with 1 note (!) || tp.f[1] == 2) { // what == nothing nb_only = true } else if (tp.f[1] == 1) { /* 'what' == slur */ nb_only = true; draw_slur([s1, s2], {ty: dir}) } else { /* search if a bracket is needed */ if (tp.f[0] != 2 // if 'when' != always && s1.type == C.NOTE && s2.type == C.NOTE) { nb_only = true for (s3 = s1; ; s3 = s3.next) { if (s3.type != C.NOTE && s3.type != C.REST) { if (s3.type == C.GRACE || s3.type == C.SPACE) continue nb_only = false break } if (s3 == s2) break if (s3.beam_end) { nb_only = false break } } if (nb_only && !s1.beam_st && !s1.beam_br1 && !s1.beam_br2) { for (s3 = s1.prev; s3; s3 = s3.prev) { if (s3.type == C.NOTE || s3.type == C.REST) { if (s3.nflags >= s1.nflags) nb_only = false break } } } if (nb_only && !s2.beam_end) { for (s3 = s2.next; s3; s3 = s3.next) { if (s3.type == C.NOTE || s3.type == C.REST) { if (!s3.beam_br1 && !s3.beam_br2 && s3.nflags >= s2.nflags) nb_only = false break } } } } } /* if number only, draw it */ if (nb_only) { if (tp.f[2] == 1) /* if 'which' == none */ return set_font("tuplet") xm = (s2.x + s1.x) / 2 if (dir == C.SL_ABOVE) // 8 = width around the middle ym = y_get(stu, 1, xm - 4, 8) else ym = y_get(std, 0, xm - 4, 8) - gene.curfont.size if (s1.stem * s2.stem > 0) { if (s1.stem > 0) xm += 4 else xm -= 4 } yy = ym + gene.curfont.size * .22 if (tp.f[2] == 0) // if 'which' == number xy_str(xm, yy, tp.p.toString(), 'c') else xy_str(xm, yy, tp.p + ':' + tp.q, 'c') for (s3 = s1; ; s3 = s3.next) { if (s3.x >= xm) break } if (dir == C.SL_ABOVE) { ym += gene.curfont.size if (s3.ymx < ym) s3.ymx = ym; y_set(stu, 1, xm - 3, 6, ym) } else { if (s3.ymn > ym) s3.ymn = ym; y_set(std, 0, xm - 3, 6, ym) } return } // here, 'what' is square bracket /*fixme: two staves not treated*/ /*fixme: to optimize*/ // first, get the x offsets x1 = s1.x - 4 // end the bracket according to the last note duration if (s2.dur > s2.prev.dur) { s3 = s2.next if (!s3 // maybe a note in an overlay voice || s3.time != s2.time + s2.dur) { for (s3 = s2.ts_next; s3; s3 = s3.ts_next) { if (s3.seqst && s3.time >= s2.time + s2.dur) break } } //fixme: s3 cannot be null (bar at end of staff) x2 = s3 ? s3.x - s3.wl - 5 : realwidth - 6 } else { x2 = s2.x + 4 r = s2.stem >= 0 ? 0 : s2.nhd if (s2.notes[r].shhd > 0) x2 += s2.notes[r].shhd if (s2.st == stu && s2.stem > 0) x2 += 3.5 } // above if (dir == C.SL_ABOVE) { if (s1.st >= s2.st) { if (s1.stem > 0) x1 += 3 ym = y_get(s1.st, 1, x1 - 4, 8) y1 = ym > staff_tb[s1.st].topbar + 2 ? ym : staff_tb[s1.st].topbar + 2 } else { y1 = staff_tb[s1.st].topbar + 2 } if (s2.st >= s1.st) { ym = y_get(s2.st, 1, x2 - 4, 8) y2 = ym > staff_tb[s2.st].topbar + 2 ? ym : staff_tb[s2.st].topbar + 2 } else { y2 = staff_tb[s2.st].topbar + 2 } xm = .5 * (x1 + x2); ym = .5 * (y1 + y2); a = (y2 - y1) / (x2 - x1); s0 = 3 * (s2.notes[s2.nhd].pit - s1.notes[s1.nhd].pit) / (x2 - x1) if (s0 > 0) { if (a < 0) a = 0 else if (a > s0) a = s0 } else { if (a > 0) a = 0 else if (a < s0) a = s0 } a = s1.fmt.beamslope * a / (s1.fmt.beamslope + Math.abs(a)) if (a * a < .1 * .1) a = 0 /* shift up bracket if needed */ dy = 0 for (s3 = s1; ; s3 = s3.next) { if (!s3.dur /* not a note or a rest */ || s3.st != stu) { if (s3 == s2) break continue } yy = ym + (s3.x - xm) * a; yx = y_get(stu, 1, s3.x - 4, 8) + 2 if (yx - yy > dy) dy = yx - yy if (s3 == s2) break } ym += dy; y1 = ym + a * (x1 - xm); y2 = ym + a * (x2 - xm); /* shift the slurs / decorations */ ym += 6 for (s3 = s1; ; s3 = s3.next) { if (s3.st == stu) { yy = ym + (s3.x - xm) * a if (s3.ymx < yy) s3.ymx = yy y_set(stu, 1, s3.x - 3, 6, yy) } if (s3 == s2) break } // below } else { /* lower voice of the staff: the bracket is below the staff */ if (s1.st <= s2.st) { ym = y_get(s1.st, 0, x1 - 4, 8) y1 = ym < -2 ? ym : -2 } else { y1 = -2 } if (s2.st <= s1.st) { if (s2.stem < 0) x2 -= 3 ym = y_get(s2.st, 0, x2 - 4, 8) y2 = ym < -2 ? ym : -2 } else { y2 = -2 } xm = .5 * (x1 + x2); ym = .5 * (y1 + y2); a = (y2 - y1) / (x2 - x1); s0 = 3 * (s2.notes[0].pit - s1.notes[0].pit) / (x2 - x1) if (s0 > 0) { if (a < 0) a = 0 else if (a > s0) a = s0 if (a > .35) a = .35 } else { if (a > 0) a = 0 else if (a < s0) a = s0 if (a < -.35) a = -.35 } if (a * a < .1 * .1) a = 0 /* shift down the bracket if needed */ dy = 0 for (s3 = s1; ; s3 = s3.next) { if (!s3.dur /* not a note nor a rest */ || s3.st != std) { if (s3 == s2) break continue } yy = ym + (s3.x - xm) * a; yx = y_get(std, 0, s3.x - 4, 8) if (yx - yy < dy) dy = yx - yy if (s3 == s2) break } ym += dy - 8 y1 = ym + a * (x1 - xm); y2 = ym + a * (x2 - xm); /* shift the slurs / decorations */ ym -= 2 for (s3 = s1; ; s3 = s3.next) { if (s3.st == std) { yy = ym + (s3.x - xm) * a if (s3.ymn > yy) s3.ymn = yy; y_set(std, 0, s3.x - 3, 6, yy) } if (s3 == s2) break } } /* lower voice */ if (tp.f[2] == 1) { /* if 'which' == none */ out_tubr(x1, y1 + 4, x2 - x1, y2 - y1, dir == C.SL_ABOVE); return } out_tubrn(x1, y1, x2 - x1, y2 - y1, dir == C.SL_ABOVE, tp.f[2] == 0 ? tp.p.toString() : tp.p + ':' + tp.q); if (dir == C.SL_ABOVE) y_set(stu, 1, xm - 3, 6, yy + 2) else y_set(std, 0, xm - 3, 6, yy) } // -- draw a ties -- function draw_tie(not1, not2, job) { // -: start and end // 1: no starting note // 2: no ending note // 3: no start for clef or staff change var m, x1, s, y, h, time, p = job == 2 ? not1.pit : not2.pit, dir = (not1.tie_ty & 0x07) == C.SL_ABOVE ? 1 : -1, s1 = not1.s, st = s1.st, s2 = not2.s, x2 = s2.x, sh = not1.shhd // head shift for (m = 0; m < s1.nhd; m++) if (s1.notes[m] == not1) break if (dir > 0) { if (m < s1.nhd && p + 1 == s1.notes[m + 1].pit) if (s1.notes[m + 1].shhd > sh) sh = s1.notes[m + 1].shhd } else { if (m > 0 && p == s1.notes[m - 1].pit + 1) if (s1.notes[m - 1].shhd > sh) sh = s1.notes[m - 1].shhd } x1 = s1.x + sh // * .6 if (job != 2) { for (m = 0; m < s2.nhd; m++) if (s2.notes[m] == not2) break sh = s2.notes[m].shhd if (dir > 0) { if (m < s2.nhd && p + 1 == s2.notes[m + 1].pit) if (s2.notes[m + 1].shhd < sh) sh = s2.notes[m + 1].shhd } else { if (m > 0 && p == s2.notes[m - 1].pit + 1) if (s2.notes[m - 1].shhd < sh) sh = s2.notes[m - 1].shhd } x2 += sh // * .6 } switch (job) { default: if (p < not2.pit || dir < 0) p = not1.pit break case 3: dir = -dir case 1: /* no starting note */ x1 = s2.prev ? (s2.prev.x + s2.wr) : s1.x if (s1.st != s2.st) st = s2.st x1 += (x2 - x1) * .4 if (x1 > x2 - 20) x1 = x2 - 20 break case 2: // no ending note x2 = s1.next ? s1.next.x : realwidth if (x2 != realwidth) x2 -= (x2 - x1) * .4 if (x2 < x1 + 16) x2 = x1 + 16 break } if (x2 - x1 > 20) { x1 += 3.5 x2 -= 3.5 } else { x1 += 1.5 x2 -= 1.5 } if (s1.dots && !(not1.pit & 1) && ((dir > 0 && !s1.dot_low) || (dir < 0 && s1.dot_low))) x1 += 5 y = staff_tb[st].y + 3 * (p - 18) + /* 1.0 * */ dir h = (.03 * (x2 - x1) + 16) * dir * s1.fmt.tieheight // anno_start(k1, 'slur') slur_out(x1, y, x2, y, dir, h, not1.tie_ty & C.SL_DOTTED) // anno_stop(k1, 'slur') } // draw_tie() /* -- draw all ties between neighboring notes -- */ function draw_all_ties(p_voice) { var s, s1, s2, clef_chg, x, dx, m, not1, not2, tim2 = 0 /* search the start of ties */ // clef_chg = false s1 = p_voice.sym set_color(s1.color) for ( ; s1; s1 = s1.next) { if (s1.ti2 // if end of tie && s1.time != tim2) { // and new end for (m = 0; m <= s1.nhd; m++) { not2 = s1.notes[m] not1 = not2.tie_s if (!not1 || not1.s.v != s1.v) // (already done) continue draw_tie(not1, not2, 1) } } if (!s1.ti1) // if not start of tie continue // get the end of the tie(s) if (s1.type == C.GRACE) { for (s = s1.extra; s; s = s.next) { for (m = 0; m <= s1.nhd; m++) { not1 = s.notes[m] not2 = not1.tie_e if (!not2) continue draw_tie(not1, not2) tim2 = not2.s.time } } continue } for (m = 0; m <= s1.nhd; m++) { not1 = s1.notes[m] not2 = not1.tie_e if (!not2) { if (not1.tie_ty) draw_tie(not1, not1, 2) continue } s2 = not2.s if (tsnext && s2.time >= tsnext.time) { // if end in next line draw_tie(not1, not2, 2) continue } tim2 = s2.time // check if some clef changes (can occur in an other voice) for (s = s1.ts_next; s != s2; s = s.ts_next) { if (s.st != s1.st) continue if (s.type == C.CLEF) { clef_chg = true break } } /* ties with clef or staff change */ if (clef_chg || s1.st != s2.st) { draw_tie(not1, not2, 2) draw_tie(not1, not2, 3) clef_chg = false } else { draw_tie(not1, not2) } } } } /* -- draw the symbols near the notes -- */ /* (the staves are not yet defined) */ /* order: * - scaled * - beams * - decorations near the notes * - decorations tied to the notes * - tuplets and slurs * - not scaled * - measure numbers * - lyrics * - staff decorations * - chord symbols * - repeat brackets * - parts and tempos * The buffer output is delayed until the definition of the staff system */ function draw_sym_near() { var p_voice, p_st, s, v, st, y, g, w, i, st, dx, top, bot, ymn, output_sav = output; // set the staff offsets of a beam function set_yab(s1, s2) { var y, k = realwidth / YSTEP, i = (s1.x / k) | 0, j = (s2.x / k) | 0, a = (s1.ys - s2.ys) / (s1.xs - s2.xs), b = s1.ys - s1.xs * a, p_st = staff_tb[s1.st] k *= a if (s1.stem > 0) { while (i <= j) { y = k * i + b if (p_st.top[i] < y) p_st.top[i] = y i++ } } else { while (i <= j) { y = k * i + b if (p_st.bot[i] > y) p_st.bot[i] = y i++ } } } // set_yab() output = "" YSTEP = Math.ceil(realwidth / 2) // initialize the min/max vertical offsets in the staves for (st = 0; st <= nstaff; st++) { p_st = staff_tb[st] p_st.top = new Float32Array(YSTEP) p_st.bot = new Float32Array(YSTEP) for (i = 0; i < YSTEP; i++) { p_st.top[i] = 0 p_st.bot[i] = 24 } // p_st.top.fill(0.) // p_st.bot.fill(24.) } /* calculate the beams but don't draw them (the staves are not yet defined) */ for (v = 0; v < voice_tb.length; v++) { var bm = {}, first_note = true; p_voice = voice_tb[v] for (s = p_voice.sym; s; s = s.next) { switch (s.type) { case C.GRACE: for (g = s.extra; g; g = g.next) { if (g.beam_st && !g.beam_end) { self.calculate_beam(bm, g) if (bm.s2) set_yab(g, bm.s2) } } if (!s.p_v.ckey.k_bagpipe // no slur when bagpipe && s.fmt.graceslurs && !s.gr_shift // tied to previous note && !s.sl1 // explicit slur && !s.ti1 // some tie && s.next && s.next.type == C.NOTE) grace_slur(s) break } } for (s = p_voice.sym; s; s = s.next) { switch (s.type) { case C.NOTE: if ((s.beam_st && !s.beam_end) || (first_note && !s.beam_st)) { first_note = false; self.calculate_beam(bm, s) if (bm.s2) set_yab(s, bm.s2) } break } } } set_tie_room(); draw_deco_near() /* set the min/max vertical offsets */ for (s = tsfirst; s; s = s.ts_next) { if (s.invis) continue switch (s.type) { case C.GRACE: for (g = s.extra; g; g = g.next) { y_set(s.st, true, g.x - 2, 4, g.ymx + 1); y_set(s.st, false, g.x - 2, 4, //fixme: hack for slur/accidental far under the staff // g.ymn - 1) g.ymn - 5) } continue case C.MREST: y_set(s.st, true, s.x + 16, 32, s.ymx + 2) continue default: y_set(s.st, true, s.x - s.wl, s.wl + s.wr, s.ymx + 2); y_set(s.st, false, s.x - s.wl, s.wl + s.wr, s.ymn - 2) continue case C.NOTE: break } // (permit closer staves) if (s.stem > 0) { if (s.stemless) { dx = -5; w = 10 } else if (s.beam_st) { dx = 3; w = s.beam_end ? 4 : 10 } else { dx = -8; w = s.beam_end ? 11 : 16 } y_set(s.st, true, s.x + dx, w, s.ymx); ymn = s.ymn if (s.notes[0].acc // set room for the accidental && ymn > 3 * (s.notes[0].pit - 18) - 9) ymn = 3 * (s.notes[0].pit - 18) - 9 y_set(s.st, false, s.x - s.wl, s.wl + s.wr, ymn) } else { y_set(s.st, true, s.x - s.wl, s.wl + s.wr, s.ymx); if (s.stemless) { dx = -5; w = 10 } else if (s.beam_st) { dx = -6; w = s.beam_end ? 4 : 10 } else { dx = -8; w = s.beam_end ? 5 : 16 } dx += s.notes[0].shhd; y_set(s.st, false, s.x + dx, w, s.ymn) } /* have room for the accidentals */ if (s.notes[s.nhd].acc) { y = 3 * (s.notes[s.nhd].pit - 18) + (s.notes[s.nhd].acc == -1 // flat ? 11 : 10) y_set(s.st, true, s.x - 10, 10, y) } if (s.notes[0].acc) { y = 3 * (s.notes[0].pit - 18) - (s.notes[0].acc == -1 // flat ? 5 : 10) y_set(s.st, false, s.x - 10, 10, y) } } draw_deco_note() for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v]; s = p_voice.sym if (!s) continue set_color(s.color); st = p_voice.st; // if (st == undefined) { //error(1, s, "BUG: no staff for voice " + p_voice.id) // continue // } // draw the slurs and tuplets for ( ; s; s = s.next) { if (s.play) continue if (s.tp) draw_tuplet(s) if (s.sls || s.sl1) draw_slurs(s) } } set_color() /* set the top and bottom out of the staves */ for (st = 0; st <= nstaff; st++) { p_st = staff_tb[st]; top = p_st.topbar + 2; bot = p_st.botbar - 2 /*fixme:should handle stafflines changes*/ for (i = 0; i < YSTEP; i++) { if (top > p_st.top[i]) p_st.top[i] = top if (bot < p_st.bot[i]) p_st.bot[i] = bot } } if (cfmt.measurenb >= 0) draw_measnb(); /* if any lyric, draw them now as unscaled */ set_dscale(-1) // set_sscale(-1) for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v] if (p_voice.have_ly) { draw_all_lyrics() break } } draw_deco_staff() draw_partempo() // draw the parts and tempo indications if any set_dscale(-1); output = output_sav } /* -- draw the name/subname of the voices -- */ function draw_vname(indent, stl) { var p_voice, n, st, v, a_p, p, y, h, h2, staff_d = [] if (!gene.vnt) return // for (st = stl.length; st >= 0; st--) { for (st = stl.length; --st >= 0; ) { if (stl[st]) break } if (st < 0) return for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v] if (!cur_sy.voices[v]) continue st = cur_sy.voices[v].st if (!stl[st]) continue if (!gene.vnt) continue p = gene.vnt == 2 ? p_voice.nm : p_voice.snm if (!p) continue delete p_voice.new_name if (!staff_d[st]) staff_d[st] = p else staff_d[st] += "\n" + p } if (!staff_d.length) return set_font("voice"); h = gene.curfont.size h2 = h / 2 indent = -indent * .5 /* center */ for (st = 0; st < staff_d.length; st++) { if (!staff_d[st]) continue a_p = staff_d[st].split("\n"); y = staff_tb[st].y + staff_tb[st].topbar * .5 * staff_tb[st].staffscale + h2 * (a_p.length - 2) // if instrument with 2 staves, center the voice name if ((cur_sy.staves[st].flags & OPEN_BRACE) && st + 1 < staff_tb.length && (cur_sy.staves[st + 1].flags & CLOSE_BRACE) && !staff_d[st + 1]) y -= (staff_tb[st].y - staff_tb[st + 1].y) * .5 for (n = 0; n < a_p.length; n++) { p = a_p[n]; xy_str(indent, y, p, "c"); y -= h } } } // -- set the y offset of the staves and return the height of the whole system -- function set_staff() { var i, st, prev_staff, v, fmt, s, y, staffsep, dy, maxsep, mbot, val, p_voice, p_staff, sy = cur_sy // the last values of {,max}{,sys}staffsep are in the last format fmt = tsnext ? tsnext.fmt : cfmt /* set the scale of the voices */ for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v] if (p_voice.scale != 1) p_voice.scale_str = 'transform="scale(' + p_voice.scale.toFixed(2) + ')"' } // search the top staff for (st = 0; st <= nstaff; st++) { if (gene.st_print[st]) break } y = 0 if (st > nstaff) return y p_staff = staff_tb[st] for (i = 0; i < YSTEP; i++) { val = p_staff.top[i] if (y < val) y = val } /* set the vertical offset of the 1st staff */ y *= p_staff.staffscale; staffsep = tsfirst.fmt.staffsep / 2 + p_staff.topbar * p_staff.staffscale if (y < staffsep) y = staffsep if (y < p_staff.ann_top) // absolute annotation y = p_staff.ann_top; p_staff.y = -y; /* set the offset of the other staves */ for (prev_staff = 0; prev_staff < st; prev_staff++) staff_tb[prev_staff].y = -y if (!gene.st_print[st]) // no staff return y var sy_staff_prev = sy.staves[prev_staff] for (st++; st <= nstaff; st++) { if (!gene.st_print[st]) continue p_staff = staff_tb[st] staffsep = sy_staff_prev.sep || fmt.sysstaffsep; maxsep = sy_staff_prev.maxsep || fmt.maxsysstaffsep; dy = 0 if (p_staff.staffscale == staff_tb[prev_staff].staffscale) { for (i = 0; i < YSTEP; i++) { val = p_staff.top[i] - staff_tb[prev_staff].bot[i] if (dy < val) dy = val } dy *= p_staff.staffscale } else { for (i = 0; i < YSTEP; i++) { val = p_staff.top[i] * p_staff.staffscale - staff_tb[prev_staff].bot[i] * staff_tb[prev_staff].staffscale if (dy < val) dy = val } } staffsep += p_staff.topbar * p_staff.staffscale if (dy < staffsep) dy = staffsep; maxsep += p_staff.topbar * p_staff.staffscale if (dy > maxsep) dy = maxsep; y += dy; p_staff.y = -y; while (!gene.st_print[++prev_staff]) staff_tb[prev_staff].y = -y while (1) { sy_staff_prev = sy.staves[prev_staff] if (sy_staff_prev) break sy = sy.next } } mbot = 0 for (i = 0; i < YSTEP; i++) { val = staff_tb[prev_staff].bot[i] if (mbot > val) mbot = val } if (mbot > p_staff.ann_bot) // absolute annotation mbot = p_staff.ann_bot; mbot *= staff_tb[prev_staff].staffscale /* output the staff offsets */ for (st = 0; st <= nstaff; st++) { p_staff = staff_tb[st]; dy = p_staff.y if (p_staff.staffscale != 1) { p_staff.scale_str = 'transform="translate(0,' + (posy - dy).toFixed(1) + ') ' + 'scale(' + p_staff.staffscale.toFixed(2) + ')"' } } if (mbot == 0) { for (st = nstaff; st >= 0; st--) { if (gene.st_print[st]) break } if (st < 0) /* no symbol in this system ! */ return y } dy = -mbot; staffsep = fmt.staffsep * .5 if (dy < staffsep) dy = staffsep; maxsep = fmt.maxstaffsep * .5 if (dy > maxsep) dy = maxsep; // return the height of the whole staff system return y + dy } /* -- draw the staff systems and the measure bars -- */ function draw_systems(indent) { var s, s2, st, x, x2, res, sy, xstaff = [], stl = [], // all staves in the line bar_bot = [], bar_height = [], ba = [], // bars [symbol, bottom, height] sb = "", thb = "" /* -- set the bottom and height of the measure bars -- */ function bar_set() { var st, staffscale, top, bot, dy = 0 for (st = 0; st <= cur_sy.nstaff; st++) { if (xstaff[st] < 0) { bar_bot[st] = bar_height[st] = 0 continue } staffscale = staff_tb[st].staffscale; top = staff_tb[st].topbar * staffscale; bot = staff_tb[st].botbar * staffscale if (dy == 0) dy = staff_tb[st].y + top; bar_bot[st] = staff_tb[st].y + bot; bar_height[st] = dy - bar_bot[st]; dy = (cur_sy.staves[st].flags & STOP_BAR) ? 0 : bar_bot[st] } } // bar_set() /* -- draw a staff -- */ function draw_staff(st, x1, x2) { var w, i, dy, ty, y = 0, ln = "", stafflines = staff_tb[st].stafflines, l = stafflines.length, il = 6 * staff_tb[st].staffscale // interline if (!/[\[|]/.test(stafflines)) return // no line w = x2 - x1; set_sscale(-1) // check if default staff if (cache && cache.st_l == stafflines && staff_tb[st].staffscale == 1 && cache.st_w == (w | 0)) { xygl(x1, staff_tb[st].y, 'stdef' + cfmt.fullsvg) return } for (i = 0; i < l; i++, y -= il) { if (stafflines[i] == '.') continue dy = 0 for (; i < l; i++, y -= il, dy -= il) { switch (stafflines[i]) { case '.': case '-': continue case ty: ln += 'm-' + w.toFixed(1) + ' ' + dy + 'h' + w.toFixed(1); dy = 0 continue } if (ty != undefined) ln += '"/>\n'; ty = stafflines[i] ln += '' } y = staff_tb[st].y if (!cache && w > get_lwidth() - 10 && staff_tb[st].staffscale == 1) { cache = { st_l: stafflines, st_w: w | 0 } i = 'stdef' + cfmt.fullsvg; if (ln.indexOf('\n' + ln + '\n'; xygl(x1, y, i) return } out_XYAB('\n' + ln + '\n\n', x1, y) } // draw_staff() // draw a measure bar function draw_bar(s, bot, h) { var i, s2, yb, w, bar_type = s.bar_type, st = s.st, p_staff = staff_tb[st], x = s.x // don't put a line between the staves if there is no bar above if (st != 0 && s.ts_prev //fixme: 's.ts_prev.st != st - 1' when floating voice in lower staff // && (s.ts_prev.type != C.BAR || s.ts_prev.st != st - 1)) && s.ts_prev.type != C.BAR) h = p_staff.topbar * p_staff.staffscale; s.ymx = s.ymn + h; set_sscale(-1) anno_start(s) if (s.color) set_color(s.color); // compute the middle vertical offset of the staff yb = p_staff.y + 12; if (p_staff.stafflines != '|||||') yb += (p_staff.topbar + p_staff.botbar) / 2 - 12 // bottom // if measure repeat, draw the '%' like glyphs if (s.bar_mrep) { set_sscale(st) if (s.bar_mrep == 1) { for (s2 = s.prev; s2.type != C.REST; s2 = s2.prev) ; xygl(s2.x, yb, "mrep") } else { xygl(x, yb, "mrep2") if (s.v == cur_sy.top_voice) nrep_out(x, yb + p_staff.topbar, s.bar_mrep) } set_sscale(-1) } if (bar_type == '||:') bar_type = '[|:' for (i = bar_type.length; --i >= 0; ) { switch (bar_type[i]) { case "|": if (s.bar_dotted) { w = (5 * p_staff.staffscale).toFixed(1); out_XYAB( '\n', x, bot, w, h) } else if (s.color) { out_XYAB('\n', x, bot, h) } else { sb += 'M' + sx(x).toFixed(1) + ' ' + self.sy(bot).toFixed(1) + 'v-' + h.toFixed(1) } break default: // case "[": // case "]": x -= 3; if (s.color) out_XYAB('\n', x + 1.5, bot, h) else thb += 'M' + sx(x + 1.5).toFixed(1) + ' ' + self.sy(bot).toFixed(1) + 'v-' + h.toFixed(1) break case ":": x -= 2; set_sscale(st); xygl(x + 1, yb - 12, "rdots") set_sscale(-1) break } x -= 3 } set_color(); anno_stop(s) } // draw_bar() // output all the bars function out_bars() { var i, b, bx, l = ba.length set_font("annotation"); bx = gene.curfont.box if (bx) gene.curfont.box = 0 for (i = 0; i < l; i++) { b = ba[i]; // symbol, bottom, height draw_bar(b[0], b[1], b[2]) } if (bx) gene.curfont.box = bx set_sscale(-1) if (sb) // single bars output += '\n' if (thb) // thick bars [x, y, h] output += '\n' } // out_bars() // set the helper lines of rests function hl_rest(s) { var j, p_st = staff_tb[s.st], i = 5 - s.nflags, // rest_tb index (5 = C_XFLAGS) x = s.x, y = s.y if (i < 6) // no ledger line if rest smaller than minim return if (i == 7 && y == 12 && p_st.stafflines.length <= 2) y -= 6 // semibreve a bit lower j = y / 6 switch (i) { default: switch (p_st.stafflines[j + 1]) { case '|': case '[': break default: set_hl(p_st, j + 1, x, -7, 7) break } if (i == 9) { // longa y -= 6 j-- } break case 7: // semibreve y += 6 j++ case 6: // minim break } switch (p_st.stafflines[j]) { case '|': case '[': break default: set_hl(p_st, j, x, -7, 7) break } } // hl_rest() // return the left x offset of a new staff // s is the %%staves function st1(st, s) { var tim = s.time do { // search a voice of this staff s = s.ts_next } while (s.st != st) while (s.prev // search the first symbol of this voice && s.prev.time >= tim) s = s.prev if (s.bar_type) return s.x return s.x - s.wl } // st1() // ---- draw_systems() ---- /* draw the staff, skipping the staff breaks */ for (st = 0; st <= nstaff; st++) { stl[st] = cur_sy.st_print[st] // staff at start of line xstaff[st] = !stl[st] ? -1 : 0; } bar_set(); draw_lstaff(0) for (s = tsfirst; s; s = s.ts_next) { switch (s.type) { case C.STAVES: sy = s.sy for (st = 0; st <= nstaff; st++) { x = xstaff[st] if (x < 0) { // no staff yet if (sy.st_print[st]) { xstaff[st] = st1(st, s) stl[st] = true } continue } if (sy.st_print[st] // if not staff stop && cur_sy.staves[st] && sy.staves[st].stafflines == cur_sy.staves[st].stafflines) continue if (s.ts_prev.bar_type) { x2 = s.ts_prev.x } else { x2 = (s.ts_prev.x + s.x) / 2 xstaff[st] = -1 } draw_staff(st, x, x2) xstaff[st] = sy.st_print[st] ? x2 : -1 } cur_sy = sy; bar_set() continue case C.BAR: // display the bars after the staves if (s.invis || !s.bar_type || !cur_sy.st_print[s.st]) break if (s.second && (!s.ts_prev || (s.ts_prev.type == C.BAR && s.ts_prev.st == s.st))) break ba.push([s, bar_bot[s.st], bar_height[s.st]]) break case C.STBRK: if (cur_sy.voices[s.v] && cur_sy.voices[s.v].range == 0) { if (s.xmx > 14 && s.next // if not at end of line && s.next.type == C.CLEF) { // and before a clef /* draw the left system if stbrk in all voices */ var nv = 0 for (var i = 0; i < voice_tb.length; i++) { if (cur_sy.voices[i] && cur_sy.voices[i].range > 0) nv++ } for (s2 = s.ts_next; s2; s2 = s2.ts_next) { if (s2.type != C.STBRK) break nv-- } if (nv == 0) draw_lstaff(s.x) } } st = s.st; x = xstaff[st] if (x >= 0) { s2 = s.prev if (!s2) break x2 = s2.type == C.BAR ? s2.x : s.x - s.xmx if (x >= x2) break draw_staff(st, x, x2) xstaff[st] = s.x } break case C.GRACE: for (s2 = s.extra; s2; s2 = s2.next) self.draw_hl(s2) break case C.NOTE: if (!s.invis) self.draw_hl(s) break case C.REST: if (s.fmr // if full measure rest || (s.rep_nb && s.rep_nb >= 0)) center_rest(s) if (!s.invis) hl_rest(s) break // default: //fixme:does not work for "%%staves K: M: $" */ //removed for K:/M: in empty staves // if (!cur_sy.st_print[st]) // s.invis = true // break } } // draw the end of the staves for (st = 0; st <= nstaff; st++) { x = xstaff[st] if (x < 0 || x >= realwidth) continue draw_staff(st, x, realwidth) } // the ledger lines draw_all_hl() // and the bars out_bars() draw_vname(indent, stl) // set_sscale(-1) } /* -- draw remaining symbols when the staves are defined -- */ // (possible hook) Abc.prototype.draw_symbols = function(p_voice) { var bm = {}, s, x, y, st; // bm.s2 = undefined for (s = p_voice.sym; s; s = s.next) { if (s.invis) { switch (s.type) { case C.CLEF: if (s.time >= staff_tb[s.st].clef.time) staff_tb[s.st].clef = s continue case C.KEY: p_voice.ckey = s default: continue case C.NOTE: // (beams may start on invisible notes) break } } st = s.st x = s.x; set_color(s.color) switch (s.type) { case C.NOTE: //--fixme: recall set_scale if different staff set_scale(s) if (s.beam_st && !s.beam_end) { if (self.calculate_beam(bm, s)) draw_beams(bm) } if (!s.invis) { anno_start(s); draw_note(s, !bm.s2); anno_a.push(s) } if (s == bm.s2) bm.s2 = null break case C.REST: if (!gene.st_print[st]) break draw_rest(s); break case C.BAR: break /* drawn in draw_systems */ case C.CLEF: if (s.time >= staff_tb[st].clef.time) staff_tb[st].clef = s if (s.second || !staff_tb[st].topbar || !gene.st_print[st]) break set_color(); set_sscale(st); anno_start(s); y = staff_tb[st].y if (s.clef_name) xygl(x, y + s.y, s.clef_name) else if (!s.clef_small) xygl(x, y + s.y, s.clef_type + "clef") else xygl(x, y + s.y, "s" + s.clef_type + "clef") if (s.clef_octave) { /*fixme:break the compatibility and avoid strange numbers*/ if (s.clef_octave > 0) { y += s.ymx - 10 if (s.clef_small) y -= 1 } else { y += s.ymn + 6 if (s.clef_small) y += 1 } xygl(x - 2, y, (s.clef_octave == 7 || s.clef_octave == -7) ? "oct" : "oct2") } anno_a.push(s) break case C.METER: p_voice.meter = s if (s.second || !staff_tb[s.st].topbar) break set_color(); set_sscale(s.st); anno_start(s); draw_meter(s); anno_a.push(s) break case C.KEY: p_voice.ckey = s if (s.second || !staff_tb[s.st].topbar) break set_color(); set_sscale(s.st); anno_start(s); self.draw_keysig(x, s); anno_a.push(s) break case C.MREST: draw_mrest(s) break case C.GRACE: set_scale(s); draw_gracenotes(s) break case C.SPACE: case C.STBRK: break /* nothing */ case C.CUSTOS: set_scale(s); draw_note(s, 0) break case C.BLOCK: // no width case C.REMARK: case C.STAVES: case C.TEMPO: break default: error(2, s, "draw_symbols - Cannot draw symbol " + s.type) break } } set_scale(p_voice.sym); } /* -- draw all symbols -- */ function draw_all_sym() { var p_voice, v, n = voice_tb.length // draw the slurs on 2 staves // sl = [symbol of 1st staff, symbol of 2nd staff, slur type, function draw_sl2() { var i, a, d, dy, dy2, dy2o, dz, n, sl while (1) { sl = gene.a_sl.shift() if (!sl) break // extract the path header and the values i = sl[3].indexOf('d="M') + 4 output += sl[3].slice(0, i) // d="M a = new Float32Array(sl[3].slice(i).match(/[\d.-]+/g)) // update the starting point of the slur a[1] -= staff_tb[sl[0].st].y // absolute vertical offset // [0][1] = M // [2][3] [4][5] [6][7] = c // [8] = v second curve if not dotted // [9][10] [11][12] [13][14] = c // // y: 3-------5 // / 12---10 \ // / / \ \ // / / \ \ 7 // 1 14 \| 8 // // x: 0 2 4 6 // 13 11 9 // deltas between staves, original and now dy2o = sl[0].fmt.sysstaffsep + 24 dy2 = staff_tb[sl[1].st].y - staff_tb[sl[0].st].y switch (sl[2]) { // slur type case "//": // '~' like case "\\\\": // get the middle of the '~' slur (* 2) d = -(sl[1].prev.prev.y + staff_tb[sl[0].st].y + sl[1].prev.next.y + staff_tb[sl[1].st].y) - 2 * (a[1] - posy) a[5] = d - a[5] a[7] = d - a[7] if (a.length > 8) { d = sl[2][0] == '/' ? 3 : -3 a[8] = -a[8] a[10] = -a[3] + d a[12] = -a[5] + d a[14] = -a[7] } break case "/\\": case "\\/": d = sl[2][0] == '/' ? dy2 - dy2o - 10 : dy2 + dy2o + 10 a[3] += d a[5] += d if (a.length > 8) { a[10] += d a[12] += d } break default: // /+, /-, \+ or \- d = sl[2][0] == '/' ? dy2 - dy2o : -dy2 - dy2o a[5] += d a[7] += d if (a.length > 8) { a[12] -= d a[14] -= d } break } // output the slur output += a[0].toFixed(1) + ' ' + a[1].toFixed(1) + 'c' + a[2].toFixed(1) + ' ' + a[3].toFixed(1) + ' ' + a[4].toFixed(1) + ' ' + a[5].toFixed(1) + ' ' + a[6].toFixed(1) + ' ' + a[7].toFixed(1) if (a.length > 8) output += 'v' + a[8].toFixed(1) + 'c' + a[9].toFixed(1) + ' ' + a[10].toFixed(1) + ' ' + a[11].toFixed(1) + ' ' + a[12].toFixed(1) + ' ' + a[13].toFixed(1) + ' ' + a[14].toFixed(1) output += '"/>\n' } } // draw_sl2() for (v = 0; v < n; v++) { p_voice = voice_tb[v] if (p_voice.sym && p_voice.sym.x != undefined) { self.draw_symbols(p_voice) draw_all_ties(p_voice); // no need to reset the scale as in abcm2ps set_color() } } self.draw_all_deco() glout() // output the symbols anno_put() // before outputting the symbol annotations set_sscale(-1) /* restore the scale */ if (gene.a_sl) // if slurs on two staves draw_sl2() } /* -- set the tie directions for one voice -- */ function set_tie_dir(s) { var i, ntie, dir, sec, pit, ty, s2 for ( ; s; s = s.next) { if (!s.ti1) continue sec = ntie = 0; pit = 128 for (i = 0; i <= s.nhd; i++) { if (s.notes[i].tie_ty) { ntie++ if (pit < 128 && s.notes[i].pit <= pit + 1) sec++; pit = s.notes[i].pit s2 = s.notes[i].tie_e } } if (s2 && s.stem * s2.stem < 0) dir = pit >= 22 // up if above middle staff ? C.SL_ABOVE : C.SL_BELOW else if (s.multi) dir = s.multi > 0 ? C.SL_ABOVE : C.SL_BELOW else dir = s.stem < 0 ? C.SL_ABOVE : C.SL_BELOW // if other voice, set the ties in opposite direction if (s.multi) { for (i = 0; i <= s.nhd; i++) { ty = s.notes[i].tie_ty if (!((ty & 0x07) == C.SL_AUTO)) continue s.notes[i].tie_ty = (ty & C.SL_DOTTED) | dir } continue } /* if one note, set the direction according to the stem */ if (ntie <= 1) { for (i = 0; i <= s.nhd; i++) { ty = s.notes[i].tie_ty if (ty) { if ((ty & 0x07) == C.SL_AUTO) s.notes[i].tie_ty = (ty & C.SL_DOTTED) | dir break } } continue } if (!sec) { if (ntie & 1) { /* in chords with an odd number of notes, the outer noteheads are paired off * center notes are tied according to their position in relation to the * center line */ ntie = (ntie - 1) / 2; dir = C.SL_BELOW for (i = 0; i <= s.nhd; i++) { ty = s.notes[i].tie_ty if (!ty) continue if (ntie == 0) { /* central tie */ if (s.notes[i].pit >= 22) dir = C.SL_ABOVE } if ((ty & 0x07) == C.SL_AUTO) s.notes[i].tie_ty = (ty & C.SL_DOTTED) | dir if (ntie-- == 0) dir = C.SL_ABOVE } continue } /* even number of notes, ties divided in opposite directions */ ntie /= 2; dir = C.SL_BELOW for (i = 0; i <= s.nhd; i++) { ty = s.notes[i].tie_ty if (!ty) continue if ((ty & 0x07) == C.SL_AUTO) s.notes[i].tie_ty = (ty & C.SL_DOTTED) | dir if (--ntie == 0) dir = C.SL_ABOVE } continue } /*fixme: treat more than one second */ /* if (nsec == 1) { */ /* When a chord contains the interval of a second, tie those two notes in * opposition; then fill in the remaining notes of the chord accordingly */ pit = 128 for (i = 0; i <= s.nhd; i++) { if (s.notes[i].tie_ty) { if (pit < 128 && s.notes[i].pit <= pit + 1) { ntie = i break } pit = s.notes[i].pit } } dir = C.SL_BELOW for (i = 0; i <= s.nhd; i++) { ty = s.notes[i].tie_ty if (!ty) continue if (ntie == i) dir = C.SL_ABOVE if ((ty & 0x07) == C.SL_AUTO) s.notes[i].tie_ty = (ty & C.SL_DOTTED) | dir } /*fixme.. continue } ..*/ /* if a chord contains more than one pair of seconds, the pair farthest * from the center line receives the ties drawn in opposition */ } } /* -- have room for the ties out of the staves -- */ function set_tie_room() { var p_voice, s, s2, v, dx, y, dy for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v]; s = p_voice.sym if (!s) continue s = s.next if (!s) continue set_tie_dir(s) for ( ; s; s = s.next) { if (!s.ti1) continue if (s.notes[0].pit < 20 && s.notes[0].tie_ty && (s.notes[0].tie_ty & 0x07) == C.SL_BELOW) ; else if (s.notes[s.nhd].pit > 24 && s.notes[s.nhd].tie_ty && (s.notes[s.nhd].tie_ty & 0x07) == C.SL_ABOVE) ; else continue s2 = s.next while (s2 && s2.type != C.NOTE) s2 = s2.next if (s2) { if (s2.st != s.st) continue dx = s2.x - s.x - 10 } else { dx = realwidth - s.x - 10 } if (dx < 100) dy = 9 else if (dx < 300) dy = 12 else dy = 16 if (s.notes[s.nhd].pit > 24) { y = 3 * (s.notes[s.nhd].pit - 18) + dy if (s.ymx < y) s.ymx = y if (s2 && s2.ymx < y) s2.ymx = y; y_set(s.st, true, s.x + 5, dx, y) } if (s.notes[0].pit < 20) { y = 3 * (s.notes[0].pit - 18) - dy if (s.ymn > y) s.ymn = y if (s2 && s2.ymn > y) s2.ymn = y; y_set(s.st, false, s.x + 5, dx, y) } } } } // abc2svg music font var musicfont = 'url("data:application/octet-stream;base64,\ AAEAAAAOAIAAAwBgRkZUTZHVOuoAAFgMAAAAHEdERUYAFQAUAABX8AAAABxPUy8yWMFdCgAAAWgA\ AABWY21hcHlUuFMAAAQIAAAD5mN2dCAAIgKIAAAH8AAAAARnYXNw//8AAwAAV+gAAAAIZ2x5ZqUq\ QgUAAAkcAABF/GhlYWQVmDJzAAAA7AAAADZoaGVhCWn/GwAAASQAAAAkaG10eNm9+0EAAAHAAAAC\ SGxvY2HHqraAAAAH9AAAASZtYXhwANkBEgAAAUgAAAAgbmFtZeq7sB0AAE8YAAADFXBvc3SIfxKM\ AABSMAAABbUAAQAAAAEAANGbj/xfDzz1AAsEAAAAAADRlyIXAAAAAOBezej/OPzvBUsEiAAAAAgA\ AgAAAAAAAAABAAAEiPzvAFwEJf84/XQFSwABAAAAAAAAAAAAAAAAAAAAkgABAAAAkgDhAAUAAAAA\ AAIAAAABAAEAAABAAC4AAAAAAAEBgAGQAAUACAKZAswAAACPApkCzAAAAesAMwEJAAACAAUDAAAA\ AAAAAAAAARAAAAAAAAAAAAAAAFBmRWQAQAAA7LcDOP84AFwEiAMRAAAAAQAAAAAAAAF2ACIAAAAA\ AVUAAAGQAAACWAAAAFcAAAAjAAAAJQAAACT//wBkAAAEIwAABCUAAAHg/9wDugAAAwsAAALSAAAC\ v/+6AdYAAAMLAAADDgAAAyf/yADIAAABaAAAAa4AAAEiAAABkAAAAXwAAAGQAAABkAAAAYEAAAGQ\ AAABkAAAAYEAAAGZAAkBmAAJAfQAAAEEABQBBAAKAmsAJAISAAABwgAAAUIAAAFAAAABSv/+ASwA\ AAIwAAABSgAAAUoAAABkAAABQAAAAUAAAAFAAAABQAAAAGQAAAE2AAAA5gAAATYAAAE7AAABOwAA\ ATsAAAE7AAABOwAAATsAAAE7AAABOwAAATsAAAE7AAABDQAAAMgAAAD/AAABCwAUAW4AAACMAAAA\ jAAAAQ0AMgFu//UAqQAAAToAAAFA//0AUAAAAVQAAABkAAABGAAAAlgAAAC2AAABkAAFAIIAAACC\ AAABLAAAASwAAADuAAAA/wAAAUkAAAGPAAAB2AAAAdgAAAIz//ADIP/hAXv/tAG4/9sBFv9+ARP/\ 2wDcAAAA6P/kAr//tAIz/7QCv/+0Ayv/2wFf/9sCaf9+AV//fgJp/34BXwAAAf0ABQG1AAABtQAA\ AkQADQJEAA0BGAAAATYAAAEs//8BLAAAAPoAAADIAAABGP84APoAAADIAAAEDQAAAhwADAH0AAAB\ 9AAAAfQAAAH0AAAB9AAAAfQAAAB4AAAALQAAAhwAAAD6AAAA+v/oAcIAAAFIAAABQAAAAgoAAAIK\ AAAAZAAAAAAAAwAAAAMAAAAcAAEAAAAAAuAAAwABAAAAHAAEAsQAAABgAEAABQAgAAAAIOAA4DDg\ OeBI4FDgXOBi4GngjOCV4KTgqeCz4QHhu+Hn4gDiSeJk4mvig+Ss5MDk0eTq5QHlMeU55W3lguXQ\ 5eLmGOYk5jDmUOZV6RjpIOkl6V3qAuqk7Knst///AAAAAAAg4ADgMOA44EPgUOBc4GLgaeB64JTg\ oOCp4LPhAeG54efh8uJA4mDiauKA5KDkwOTO5OHlAOUg5TnlZuWC5dDl4uYQ5iTmMOZQ5lXpEOkg\ 6SXpXeoC6qTsouy3//8AA//kIAUf1gAAAAAfvh+zH64fqAAAAAAAAB+CH3kfLAAAHkkAAAAAAAAA\ AAAAAAAbkwAAAAAAAAAAGzcAABr0GqcalgAAGlkaThovGisAABdnF2MXLBaIFecAABPaAAEAAAAA\ AAAAAABYAFoAAAAAAAAAAABcAIAAggAAAAAAAACEAAAAhgCiALQAvAC+AMQAAADaAOAA8gD0AAAB\ FAAAAAAAAAEcAAAAAAAAAAABJAAAAAAAAAAAAAABKgAAAAAABwAIAAkAAAAKAAsADAANABIAEwAU\ ABUAFgAAABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgAuAAAALwAxAAAA\ MgAAAAAAMwAAADQAAAAAADUAAAA2ADcAOAA5ADoAOwA8AD0APgA/AEAAQQBCAEMARABFAEYARwBI\ AEkASgBLAEwATQBOAAAATwAAAFAAAAAAAAAAUQAAAAAAAABSAFQAAAAAAFUAVgBXAFgAWQBaAFsA\ XABdAF4AXwBgAGEAYgBjAGQAZQBmAGcAAAAAAAAAaABpAGoAawBsAAAAbQBuAG8AcQByAAAAcwAA\ AAAAdAB1AHkAAAB6AAAAewAAAAAAAAB8AIEAggCDAAAAhACFAAAAAACGAIwAjQAAAI4AAACPAAAA\ kAAAAQYAAAMAAAAAAAAAAQIAAAACAAAAAAAAAAAAAAAAAAAAAQAABAAAAAAAAAAAAAAAAAAAAAAA\ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAogAAAAqACoAKgA2AD4AbgB6\ AIYAkgCwASQBgAHwAjQCwANSA7IDxgRkBPYFTgWOBhAGMgZGBpgG6AcIB04HjgfICBQIVAiaCPQJ\ CAkuCVQJhgmiCcQJ7AoCChwKUgpgCmwKeAqKCqYKyArWCuoK/AsMC1gLaAuCC5wL1AwMDFoMpg0M\ DW4N7A5kDooOpA7UDv4PVg9wD4oPsBAKECoQaBCQEKQQsBC+EM4Q9hEeEVYRYhFuEXoRhhGoEc4S\ BBJSErITJhNME3oTzhRCFJYU1hUaFXAWihdiF/gYqhlsGf4a0BvkHNAdEh1WHaAduh3eHfIeBh42\ HkYeWB50Hooerh7eH7ogdiCeILwg7iEsIV4hoCGyIcAh3CH2IhwiPiJsIoYisCLsIv4AAAACACIA\ AAEyAqoAAwAHAC6xAQAvPLIHBADtMrEGBdw8sgMCAO0yALEDAC88sgUEAO0ysgcGAfw8sgECAO0y\ MxEhESczESMiARDuzMwCqv1WIgJmAAABAAAAAAGRAZAAAwAAMREhEQGRAZD+cAABAAAAAAAAAAAA\ AAAAMQAAAQAAAAAAVwQDACAAABE1NjU0JyY1NDcGFRQXFhUUBxYVFAcGFRQXJjU0NzY1NDUjElc/\ FSVNTSUVP1cSIwIDAhhDNmA5NGY6MksiOWFNYRgYZkxgOSVKMjpmNDlgNkgAAQAAAAAAIwPoAAMA\ ABEzESMjIwPo/BgAAQAAAfQAJQPoAAMAABEzESMlJQPo/gwAAf//A2QAJARgAAMAABEzByMkASQE\ YPwAAgAAAUAAZAKeAAcADwAAEiImNDYyFhQCIiY0NjIWFEcqHR0qHR0qHR0qHQI6HSodHSr+6R0q\ HR0qAAAABQAAAAAEJAGuAC8ANwA/AEcAUwAAITUzHgEzMjY1NCcuBDU0NjMyFhc3MxcjLgEjIgYV\ FB4DFx4BFRQGIyInByAiJjQ2MhYUBCImNDYyFhQBETMyNjQmIwM1MxEjNTMyFhUUIwJOHhVPMik7\ lBkaKhYRWT0kJxkeHgceD0owHzkQIhkyCE5NW09FLiMBmyodHSod/eoqHR0qHf6JKDxGRjzcRkbc\ cYn6oDxLICEtKAcIFBQjFUNNCw4ZmzpIKBsPFw8JCwIVNzM6TiAgHSodHSodHSodHSoBaf6YYaZh\ /noeAWgeZ2vSAAUAAAAABCQBrgAaACIAKgAyAD4AACEiJjU0NjMyFhc3MxcHJiMiBhQWMzI2NxcO\ ATIiJjQ2MhYUBCImNDYyFhQBETMyNjQmIwM1MxEjNTMyFhUUIwMCZ3WCWiUpGx4eCCEkXjg2Njg2\ TREjFFmxKh0dKh396iodHSod/okoPEZGPNxGRtxxifprZ2V3DBIepgSMbZhtST4KSlEdKh0dKh0d\ Kh0dKgFp/phhpmH+eh4BaB5na9IAAAAD/9wAAgHeArMABwAPAE0AAAAiJjQ2MhYUBCImNDYyFhQX\ NDYzMhYVFAcWMzI2NTQvAQMnEy4BNTQ+ATc2MzIWFRQGIyImNTQ3JiMiBhUUHwETFwMeARUUDgEH\ BiMiJgGeIBgYIBj+fiAYGCAYWxsUEx4sFykmNiZ7zyvRWkgcExQmMzA4GxQTHiwXKSY2JnrUK9Va\ SBwTFCYzMDgBUhggGBggVBggGBgg0xIcGhEdDhctJi0mZf7eIAElR3E4Fi0RERM4IRIcGhEdDhct\ Ji0mZQEmH/7XR3I4Fi0RERM4AAUAAP9WA7wDYgAXABsAHwAjACcAAAEzFR4BFzMVIw4BBxUjNS4B\ JyM1Mz4BNxEjFhcTETY3JzMmJwMRBgcBxDRwnwmsrAmfcDRwnwmsrAmecZQHjTSOBpSUBo40jgYD\ YqsLtoA0f7cLq6sLt380grQL/ov0FAEI/vgU9DT0FP74AQoU9gAAAAAEAAD9bwKnBIgACwBEAE4A\ YwAAAQYVFBc+ATU0Jw4BExcVFAYjIiY1NDYzMhYUBgcWMzI2PQEnBiMiJjU0Nz4GPwEmNTQ2NxYV\ FAYHFzYzMhYVFCc0JiMiBiMTPgEnDgEVFBYXLgE1NDcnDgEHHgEzMjcBbAcFSHU2OUJLF09NUl9A\ Mi9BPy8tGC9AFxwNmNs6DSQgLh0xEhcWDWxJYlx5FBQLcYhOYlACBwIhW0HiNkYnHT0/phGPbAEC\ oXkKFgNTNy0jWjGVSHINB177bOwDTFxTQS1IO1g3ARk8RgPpAsedhWYXMCUtGicOERCWZoyhBzrl\ iKlfzwKcc866PmYB/p0SXfILRjEgQhIORUepLr1yl2eNowIAAgAA/fwC0gIAAGQAaAAAATI2NTQn\ JiMiBw4CByYnJicRIxEzETY3NjceAxcWMzI2NTQnJiMiBxYXFBYVFAYrASY1NDc2NzYzMhcWFxUU\ BgcGIyInBxc2MzIWFxYdAQYHBiMiJjU0NzMyFhUUBhUGBxYBMxEjAd4+Sg0aSUY8AgYKBCIaHi4c\ HC4eGiIGFAwZDyclMT0SJFIvMTINAjMhBUQFGlUnI15VNwhaSB8tND8iIj80QmYeKAg3Vl1MckQF\ ITMCDzA1/k17e/4geEotLWxJBQ4bCmInKx/+BAQA/hEfKydiCyoXHQkbe0IxNmIaECgDDQQeKxky\ FAtEGQ1PNFMSTm8cDRdLShc+LDlDElM0UE48MhkrHgQOBCYQHAPe/AAAAAP/uv2lAsMA/wAqADYA\ QgAANzQ2MzIWFxYVFAYHDgEHNjc+ATc2NTQmJy4BIyIGBz4BMzIWFRQHBiMiJgUiJjU0NjMyFhUU\ BiciJjU0NjMyFhUUBhOLZ1VrKy9GVWbPleWFMTQTChEdHDQzO2EWGCMbLj0nITEzRQKCFh0aFBUe\ GhoXGxwVFBwbHWKANDk+cn+6T2BNCD2FMWNQK2ZHUCMiF09GHhdBLzIgHlGKHBcWHB0VFh3yHhkV\ GhsUGR4AAAIAAP8GAXIA+gADAAcAADczEyMDMxMj3JQCltyUApb6/gwB9P4MAAAEAAD+CgIfA6oA\ CQAgAGMAbgAAJRYXPgE1NCYjIgMCJw4BFRQXLgE1NDY3JicOAQceATMyFx4BHwEwHQEUIyImNTQ2\ MzIWFRQGBxYzMjY1NC8BBiMiJjU0Nz4BNz4CNyY1NDY3HgEVFAYHHgEXNjMyFxYVFAcGAwYVFBc+\ ATU0JwYBSRMGTUdWQg4NGAEsOR8gKkw7BQlxVQEFbYIDIgMGAgJ6NlMzKCU1MScVIiMsAQ0JFY2Z\ Lg5MHgUjJxIOWUAvGUlhAggEEghcOTJjNmQDBjdeKV91xFsSTzM2Vv7gAQsQCTQnKSYSQSk4ThA+\ Wlp5U3F+GiBAFxcbB39IMSQzNyQiKAEMMzUPCY0BkopqURxQGAQgIQ3CB25/EzNiW22HTBFuJAJD\ NmJ3MBsDVh4iOSQlezNGJiYAAAAAAgAA/mMCQgGaAGMAZwAAATI2NTQnJiMiBw4BByYnJicRIxEz\ ETY3NjceAxcWMzI2NTQnJiMiBxYXFBYVFAYrASY1NDc2NzYzMhcWFxUUBgcGIyInBxc2MzIWFxYd\ AQYHBiMiJjU0NzMyFhUUBhUGBxYBMxEjAX4yOwoVOjgwAgwEHRMYJRYWJRgTHQQRChMMIB0nMQ4c\ QyUnKAoCKhoENgQVRCMYSUYtBkg6GCUrMRsbNCg1UhggBi1GST1bNgQaKgIMJiv+o2Ji/oBgOyQk\ VzsFHQtSHCIZ/moDM/50GSIcUggiEhcIFmI1KyhOFQ0gAgsDGCIUKBAJNRULQCtBDj5aFgoSPDsT\ MiMuNQ9BK0A/MCgUIxgDCwMfDRYDGPzNAAAAAAP/yP4eAjYAzAAmAC8AOwAANzQ2MzIXFhUUBw4B\ BzY3Njc2NTQnLgEjIgYHPgEzMhYVFAcGIyImBSImNDYyFhQGJyImNTQ2MzIWFRQGD3BSgjwjeEPG\ acBeRx4JLRgtIDBSChIWFiU5IBklLDkCAhIXFCIYFRUSFhUSERYVIU1eVzNaxmw7VwYzZEqMLylm\ MxoUQjYXDTwmKBoYU3UXJBYXIhjCGBQRFBUQExkAAAAAAwAAAAAAyADwAAkAFQArAAA3BhUUFjMy\ NjU0JyIGFRQeARc2NTQmByImNTQ2Ny4BNTQ2MzIWFRQHFhUUBlYsHhARFAIOEwoJECUUNCU1IyIR\ DCgeKTE3IzR0EB4UHhsRF4URDgoQCAwLHQ4X3CgeFxoLDxMQGiIfHSUOHB8gJgACAAD//wFnAQMA\ MABiAAAXIjU0MzIVFAYVFDMyNjU0IyIHBiMiNTQ3NjM2MzIWFRQHBgcOAQcGFjc2MzIWFRQGJyI0\ OwEyNzI0MzY3PgE1NiMiBwYnJjc+ATc2OwIyFxYzMjc2BwYHBhUUMzIVFCsC1D4YGxIdFyweChQW\ CAsmBgkjSQ8FFhpICgYBAgUIDA4gI0P6CQkJFAUBAQ0XAQIIDgkIDA0KDwYRAjIEAgMGDgwIAggX\ BkcKAhUMDCcnATchGwcWBg5CIxwMDQ8WVAsBAgUQBwkQAgYLCgUCAyQgKDkBEgwEHz0BBQIVCQ4G\ BhIHGAJABwYCCBKtHQUFCAoIAAAAAAIAAP8GAa4A+gALABQAADMUFjMyNjU0JiMiBgc0NjIWFAYi\ JooqIyIrJyYlKIp9tH19tH1ieHlhZXV2YWeQkdKRkgAAAQAA/wYBIgD6AAkAADE3MxEXFSM1NxFk\ fUHwQfr+Ph4UFB4BLAAAAAEAAP8GAY8A+gA8AAA3MhUUBw4DBzYzMhYzMjc+AjMOAgcGBwYjIiYj\ IgYjIjU0Jz4FNTQnIgcyFhUUBiMiNTQ+AcfIBQ02QG82EyAbZBwYHgUQDAEBBQUBBxAaKRp0FR9W\ AgcBAiw+RzwoU04aHCk3Hkw8WPp+Gg4hLh1ELQwjDgMNCwUWFgMpDhgnJhABAiFFODwyOBhiATUl\ Hh8pZyg5GQAAAQAA/wYBdQD6ADkAADcyFhUUBiMiJjU0NzYzMhcWFRQGBx4BFRQHBiMiJicmNDYz\ MhYVFAYjFjMyNjU0JicmNDc+ATQmIyJmGyInIRsyHzNZRiZERj0+UUskTSdXGCMyICIqJRsMPyQr\ SC4WFi9LKSQ8qhwXGyMrIy8aKhMiSC5ECwtFLUMnExYUHUwuIRsZHikxJyY6CAQiBAk3UDAAAAEA\ AP8GAZAA+gARAAAFFyM3NSM1NjUzATM/AREzFSMBRTLIMuGTo/77sAFjS0vRKSkxKPKA/o6Wkf7Z\ KAAAAAABAAD/BwF+APoALwAAFzYzMhYVFAYjFjMyNz4BNTQnJiMiBxMhDgErAQc2MzIXHgEVFAcO\ AiMiJy4BNTQSHCEbKiAcGiQxHBMJHhwoTkgKAWILNSXVBjlCUzEhK0MWQCslPCsQHl8gIBccICEe\ FB8gORwaNQEiJDp5Hh8VQSVPLxAQAhQJMhIjAAAAAAIAAP8GAYEA+gAJACwAABcyNjU0JiMiBxYT\ FhUUBiMiJjU0NjMmIyIGFT4CMzIWFRQGIyImJz4BMzIWyCktKigsMAfdGyMYHiIbEBY3NS8VGC0e\ TE9xSGFmAQFsWzA/0kUsIjAlngGfGiYZKB4bDB4jeF8LCglANkRZgnhpkRIAAQAA/wYBkAD7ACgA\ ADciDgMHNz4IMzIWMzI2Nw4EFSM2NzY3NjcGIyImYRQaFAkRBQoBDAILBQsJDRAJL3UjGjsRG0Ua\ HgiCAQgRaB4tER4lYKEFDgkbBnQBCwIJAQYBAwEmFw5Dp0FcQitFGzaHJzkKKAADAAD/BgGEAPoA\ DgAcADQAABcOARUUFjMyNjU0LgM3PgE1NCYiBhUUHgMHLgE1NDY3MhYVFAYHHgEVFAYjIiY1NDaa\ NixYLCo/DyAdMT4zI0RSMwocEjFwMSlmSktlKjA6MnVNTHY5KhkkGx0wKR8OFxINE1oaIhwdMCgg\ DxcTChU/GDw1M00BRjInNBcaOjU3SkgwJDUAAAIAAP8GAYEA+gAJACwAADciBhUUFjMyNyYDJjU0\ NjMyFhUUBiMWMzI2NQ4CIyImNTQ2MzIWFw4BIyImuSktKigsMAfdGyMYHiIbEBY3NS8VGC0eTE9x\ SGFmAQFsWzA/0kUsIjAlnv5hGiYZKB4bDB4jeF8LCglANkRZgnhpkRIAAQAJ/woBmQD5ADAAACUw\ FzY1NCYjDgEVFBcWMzI3NjcUHgEVDgEHIicmJzQmNTQ3MhYXFhUUBiMiJjU+ATMBLxIEPB8zQCch\ MCsoHCoJCBtVVk87OwQB2yRAESIkHCApAiAapAMFCBQeAmJqjjMqIhhYAQQDAVVQATk5ZgIrAuYC\ HhQnJCU5LhwWJgACAAn+ogGZAV4AOAA/AAAlMBc2NTQmIyIHERYzMjc2NxQeARUOAQcjFSM1Jicm\ JzQmNTQ3NTMVMjYzMhYXFhUUBiMiJjU+ATMDEQYVFBcWAS8SBDwfAxAMDCsoHCoJCBtRVQEjPS47\ BAGrIwMHAyRAESIkHCApAiAaej0nCqQDBQgUIgT+SwQiGFgBBAMBVFEBaGsJLTlmAisCyxlpZgEe\ FCckJTkuHBYm/pcBli6RjjMNAAABAAD/BgH0APoACwAANTM1MxUzFSMVIzUj10bX10bXI9fXRtfX\ AAAAAQAU/gYA5AIAABMAABMWBwYnJgI1NBI3NhcWBwYCFRQS3AcNCQVJa2tJCQsHBzxGR/4WCAUD\ BlcBIH18ASJWCwcFCUn+54iG/uUAAAEACv4CANwB/QATAAATNhI1NAInJjc2FxYSFRQCBwYnJhQ7\ R0Y8CQwKB0lra0kICwb+FkkBG4aIARlJCwQECVb+3nx9/uBXCQkEAAAEACT/VgJMAKoACwAPABMA\ HgAABTQmIyIGFRQWMzI2NzMRIwEzESMkFAYjIiY1NDYzMgGoVDwgNFc9IS9yMjL+CjIyAfV7ZmV8\ eWhmIDVOJx81SyPq/qwBVP6s5nhGSTk/QwAAAgAA/yQCEgDcAAMADwAANxUhNSUzFSE1MxEjNSEV\ Ix4B1v4MHgHWHh7+Kh5BgoKbNzf+SDc3AAACAAD/fwHCAIEACwATAAAFNCYjIgYVFBYzMj4BFAYi\ JjQ2MgFRWjYgNF03IS9xfch9fcgeNE8nHzRMI3RsS0tsSwAAAAIAAP92AUIAigAMABgAACU0JiMi\ BhUUHgEzMjYnMhYVFAYjIiY1NDYBIxocO5MEGxc7k2FAQHtHQEB7Lg4ZWygEDhVbhEwpP2BMKT9g\ AAAAAQAA/3kBQACHAAsAACUUBiMiJjU0NjMyFgFAeVkyPHpYMjwoRmk4J0VqOAAB//7/bwFMAJEA\ CwAAJzcXNxcHFwcnByc3AhuMjBuGhhyLixyGcSB2dSBwcCF1dSFwAAAABQAA/2oBLACWAAUACwAR\ ABcAHwAAFwcWMzI3LwEGFRQXPwEmIyIHHwE2NTQnBjQ2MhYUBiKWRx0qKR9dRx0dXEgfKSodXEcd\ HfJYfFhYfBJIHR1aSR8pKh1YSB0dWkgfKSodhXxYWHxYAAAAAQAA/wYCMAD6AAMAABUBMwEBuHj+\ R/oB9P4MAAEAAP90AUoAjAADAAAxNxcHpaWljIyMAAEAAP90AUoAjAACAAAVGwGlpYwBGP7oAAEA\ AP/OAGQAMgAHAAAWIiY0NjIWFEcqHR0qHTIdKh0dKgAAAAEAAP95AUACqAAPAAARIREUBiMiJjU0\ NjMyFxEhAUB5WTI8elgxH/7eAqj9gEZpOCdFahwBxQAAAQAA/3kBQAKoABMAABEhERQGIyImNTQ2\ MzIXESE1ITUhAUB5WTI8elgxH/7eASL+3gKo/YBGaTgnRWocARF4PAAAAAABAAACMAFAAqgAAwAA\ ESEVIQFA/sACqHgAAAAAAgAAAXwBQAKoAAMABwAAESEVIREhFSEBQP7AAUD+wAH0eAEseAAAAAEA\ AP/aAGQAPgAHAAAWIiY0NjIWFEcqHR0qHSYdKh0dKgAAAAEAAAMCATYDwAAFAAARIRUhFSMBNv7o\ HgPAHqAAAAAAAQAAAyoA5gQ4ADkAABMiJjU0NjMyFhUUDwEUMzI2NTQmKwEiNTQ7ATI2NTQmIyIV\ FBYVFCMiJjU0NjMyFhUUDgEVFBYVFAZQJykQDg8SCwwhFSQRDxIUFCgXJQ4OFQIhDBIlKyMtIyMU\ MwMqHhgOFhAMEgcHDSofDxkOECUVDRMNAwgIHBEMFR4iGhgjEwIBIg8jLQAAAAABAAADAgE2A8AA\ BQAAARUjNSE1ATYe/ugDwL6gHgAAAAEAAPzvATsAAAAPAAAVNTMeBBUUBzY1NCYnHgY/UVA3LhKR\ cO/vNXBlbIlJYGlBSY/fKgABAAAAAAE7AxEADwAAMTUzPgE1NCcWFRQOAwcecJITLjdQUT8G7x/h\ k0dIZ15Iim1ncTUAAgAA/UQBPAAAABgAJgAAGQEzHgYVFAcWFRQHNjU0LgMjNR4DFzQ2NTQuAx4G\ JzM7OC4cEhMeBSk+SEAWCURNVhUBKT5IQf6pAVcbOTM3Oj1IJSwrKSs2ORkhN2RFNBirJFRDXSkE\ DAQ3ZUUzGQAAAAIAAP//ATwCvAAYACYAABURMzI+AzU0JxYVFAcWFRQOBQc1Mj4DNTQmNQ4DHhZA\ SD4pBR4TEhwuODszJwYWQUg+KQEVVk1EAQFXGDRFZDchGTk2KykrLCVIPTo3MzkbqxkzRWU3BAwD\ KV1DUwAAAAADAAD9KgE8AJEAGwApADcAABkBMx4GFRQHFhUUBxYVFAc2NTQuAiM1HgMXNDY1NC4C\ IyceAxc0NjU0LgIjHgYnMzs4LhwSEhITHgU9V1UcCURNVhUBPFZVHQIJRE1WFQE8VlUd/pACARs5\ Mzc6PUglLCsnLSwrKSs2ORkhRHdIKakkVEFdKQQNA0R2SCmrJFNCXSkEDQNEdkgpAAADAAD/VgE8\ Ar0AHAApADYAABEzMj4DNTQnFhUUBxYVFAcWFRQOBQcjNzI+AjU0JjUOAycyPgI1NCY1DgMeFkBI\ PikFHhMSEhIcLjg7MycGHh4dVVc9ARVWTUQJHVVXPQEVVk1EAVcYNEVkNyEZOTYrKSssLScrLCVI\ PTo3MzkbqilJeEQEDAQpXUNUhilJeEQDDQQpXUNUAAQAAP1CATwBVAAgAC4APABKAAAZATMeBhUU\ BxYVFAcWFRQHFhUUBzY1NC4DIzUeAxc0NjU0LgMnHgMXNDY1NC4DJx4DFzQ2NTQuAx4GJzM7OC4c\ EhISEhITHgUpPkhAFglETVYVASk+SEEWCURNVhUBKT5IQRYJRE1WFQEpPkhB/qgCrBs5Mzc6PUgl\ LCsnLSwrJy0sKykrNjkZITdkRTQYqyRUQ10pBAwEN2VFMxmrJFRDXSkDDgM3ZUUzGaskVENdKQQM\ BDdlRTMZAAQAAP6OATwCoAAfACwAOQBGAAAZATMyPgI1NCcWFRQHFhUUBxYVFAcWFRQOBQc1Mj4C\ NTQmNQ4DJzI+AjU0JjUOAycyPgI1NCY1DgMeHFVXPQUeExISEhISHC44OzMnBh1VVz0BFVZNRAkd\ VVc9ARVWTUQJHVVXPQEVVk1E/o4CrClId0QhGTk2KykrLC0nKywtJyssJUg9OjczORuqKUl4RAMN\ BCldQ1SGKUl4RAMNBCldQ1SGKUl4RAMNBCldQ1QAAAAFAAD9VQE8AhIAJAAyAEAATgBcAAAZATMe\ BhUUBxYVFAcWFRQHFhUUBxYVFAc2NTQuAyM1HgMXNDY1NC4DJx4DFzQ2NTQuAyceAxc0NjU0LgMn\ HgMXNDY1NC4DHgYnMzs4LhwSEhISEhISEx4FKT5IQBYJRE1WFQEpPkhBFglETVYVASk+SEEWCURN\ VhUBKT5IQRYJRE1WFQEpPkhB/rsDVxs5Mzc6PUglLCsnLSwrJy0sKyctLCspKzY5GSE3ZEU0GKsk\ VENdKQMNBDdlRTMZqyRUQ10pBAwEN2VFMxmrJFRDXSkDDgM3ZUUzGaskVENdKQQMBDdlRTMZAAAF\ AAD9vAE8AnkAIwAwAD0ASgBXAAAZATMyPgI1NCcWFRQHFhUUBxYVFAcWFRQHFhUUDgUHNTI+AjU0\ JjUOAycyPgI1NCY1DgMnMj4CNTQmNQ4DJzI+AjU0JjUOAx4cVVc9BR4TEhISEhISEhwuODszJwYd\ VVc9ARVWTUQJHVVXPQEVVk1ECR1VVz0BFVZNRAkdVVc9ARVWTUT9vANXKUh3RCEZOTYrKSssLScr\ LC0nKywtJyssJUg9OjczORuqKUl4RAMNBCldQ1SGKUl4RAMNBCldQ1SGKUl4RAMNBCldQ1SGKUl4\ RAMNBCldQ1QAAAACAAD/ZADhAbAACgAWAAA3IgYdATY3NjU0JjcyFhUUBwYjETMRNmcUKyImKx0H\ IzlLUkQoI3omE7kPODsvGyYmMiNJTFICTP68NAAAAgAA/oYAxQF6AAMADAAAFzc1BxEVNxEjNQcR\ NxyQkKkZrAFlLZYtAUnoNP3A4jMCQwEAAAIAAP6YAP8BaAADAB8AADcVNzUDIzUHNTc1BzU3NTMV\ NzUzFTcVBxU3FQcVIzUHU1paHjU1NTUeWh01NTU1HVpGpxun/jejD1wPpw9aD6ifHKujD1wPpw9a\ D6ifHAAAAAEAFP+EAQsAegAeAAAXNSYnBzAVIzUzNycwIzUzFRYXNzA1MxUjBgcXMDMVwygMM0g5\ MzM5SCESNEg5IRM0OXw7Jg0zO0oyMkg5IhEzOUciETRIAAQAAP9qAWwBsAAOABwAKwA6AAA3DgEd\ ATI3Njc2NTQnJiM3MhYVFAcGBwYjETMRNhcOAR0BMjc2NzY1NCcmIzcyFhUUBwYHDgEjETMRNk4R\ Hg4eHwwEChARGR0rCRgrNS8fGdERHRAeHQsGCxAPFh8qCxkoFjcWHht9AR4QxikrNA0ZHhQVJjkh\ EiA5NEACRv7BMiYBHRHGKS8wExMcFhUmNiQWHD4vGyUCRv7BMgAAAQAA/wYAjAD6AA4AADcVBhUU\ FxUuAzQ+AoxQUBolMhsbMiX6FEOjpkAUDx86WnBaOh8AAAEAAP8GAIwA+gAOAAA1HgMUDgIHNTY1\ NCcaJTIbGzIlGlBQ+g8fOlpwWjofDxRApqNDAAACADL/ZAENAbAACgAWAAA3IgYVFBcWFzU0Jicy\ FxEzESInJjU0NqsXIC4uGiozOiMjOlJPOXomGzBFQgPLEh4mNAFE/bRSUEUjMgAABP/1/2oBbAGw\ AA4AHQAsADsAADcjIgcGFRQXFhcWMzU0JicyFxEzESImJyYnJjU0NhcOAR0BMjc2NzY1NCcmIzcy\ FhUUBwYHDgEjETMRNlcBERAKBAwfHg4eKzAZHxY3FysYCSvpER0QHh0LBgsQDxYfKgsZKBY3Fh4b\ fRUUHhkNNCspxhAeJzIBP/26JRs0OSASITkmAR0RxikvMBMTHBYVJjYkFhw+LxslAkb+wTIAAAAA\ AQAA/sAAqQFAABMAABMzFTcVBxU3FQcVIzUHNTc1BzU3RB5HR0dHHkREREQBQKIOXA5/DloPqKIO\ XA5/DloPAAMAAP6YAToBaAAjACcAKwAANzUzFTcVBxU3FQcVIzUHFSM1DwEjNQc1NzUHNTc1MxU3\ NTMVAzUHFTcVNzXpHjMzMzMePR48AR4zMzMzHj0eHj1bPcCong9cD58PWg+1qhKspBCong9cD58P\ Wg+1qhKspP79nhGfuJ4RnwAB//0AAAE/APQAGAAANwYjIiY1ND8BNi8BJjU0NjMyMRcFFhUUBxIC\ AwcJBs8ODs0ICwcBAgEfDg4BARAICgNJBwZPAwsKEgFrBg4NBQAAAAEAAAAAAFAAUAAJAAA1NDYy\ FhQGIyImFyIXFxEQGCgRFxciFxgAAAABAAAAAAFUADIAAwAAMTUhFQFUMjIAAAABAAAAAABkARgA\ AwAAMwMzAygoZCgBGP7oAAAAAQAAAAABGAE1AAUAADEbASMnB4yMQVhaATX+y8bGAAACAAAAAAJY\ AUoADgAZAAAxNDYzMh4CFSMuASIGByEiJjQ2MzIWFRQGs3k5a1UzDwui4KILARwXJSUXGSMjmLIs\ UYBNboaGbiQwJCQYGSMAAAEAAAAAALYBLQAYAAATMhYXFhUUBw4BIzAnJjU0NjU0Iy4BNTQ2Vhsb\ EBoyGUQQBgFHFBsoLQEtDBEdMD08HS0DAQIIaxMPASYcHjEAAAAAAgAFAAABjgH1ABEAIwAANxM2\ MzIXFhUUBwMGIyInJjU0JxM2MzIXFhUUBwMGIyInJjU0mM0LCgYDCwXMCQsEBguLzQoLBgMLBcwI\ DAQGCyMBwBICBQoGDP4/EQMGDAcHAcASAgUKBgz+PxEDBgwHAAEAAP8GAIIA+gADAAA1MxEjgoL6\ /gwAAAEAAAAAAIIA+gADAAA1MxUjgoL6+gAAAAEAAP+DASwAAAADAAAxIRUhASz+1H0AAAEAAAAA\ ASwAfQADAAA1IRUhASz+1H19AAEAAP5+AOsBhwATAAATFwcXJiMiBhUUFyY1NDYzMhcnNym9Z2wy\ NB8mOHg0JSIih2QBh+XZzy4kHTU0S00jLRW8tAAAAQAA/w0BAADAABYAADcOAiMiJjU0NjIWFRQH\ MjY3NjIXAyerAxkaEys3JjgpFyIzIQIVA5YwPAEHBCkoHyAeGR0bISwCAv5vEAAAAAEAAP4MAUgA\ wAAkAAAXBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyNzYyFwMnqyghKzcnGxwpF0ELPDYY\ KzcnGxwpF0guAhUDxS3EDCgoICAfGR0bIsoMKSgfIB4ZHRtNAgL9bgwAAAEAAP4MAY8BwAA2AAA3\ BiMiJjU0NjMyFhUUBzI/ASIOASMiJjU0NjMyFhUUBzI3NjIXAScTBiMiJjU0NjMyFhUUBzI39igf\ KzcnGxwpFz8LOgEgHBMrNycbHCkXSC4BFgP+9C1VKCErNycbHCkXQQs8DCgoICAfGR0bIssJBCko\ HyAeGR0bTQIC/G4MASQMKCggIB8ZHRsiAAAAAAEAAP0MAdoBwABFAAATBiMiJjU0NjMyFhUUBzI/\ AQYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMj8BIg4BIyImNTQ2MhYVFAcyNzYyFwEnqygh\ KzcnGxwpF0ELOighKzcnGxwpF0ELOigfKzcnGxwpFz8LOgEgHBMrNyY4KRdILgIVA/6pLf48DCgo\ ICAfGR0bIsoMKCggIB8ZHRsiygwoKCAgHxkdGyLLCQQpKB8gHhkdG00CAvtuDAAAAAEAAP0MAhkC\ rgBWAAAlBiMiJjU0NjMyFhUUBzI/AQYjIiY1NDYzMhYVFAcyPwEiDgEjIiY1NDYzMhYVFAcyNzYy\ FwEnEwYjIiY1NDYzMhYVFAcyPwEGIyImNTQ2MzIWFRQHMjcBPyghKzcnGxwpF0ELNigfKzcnGxwp\ Fz8LNAEgHBMrNycbHCkXSC4BFgP+ai1VKCErNycbHCkXQQs6KCErNycbHCkXQQs0DCgoICAfGR0b\ IsQMKCggIB8ZHRsixwkEKSgfIB4ZHRtNAgL6gAwBJAwoKCAgHxkdGyLIDCgoICAfGR0bIgAD//D/\ BgImAPoABwAPABMAADYiJjQ2MhYUACImNDYyFhQFATMBUDIjIzIjAYgyIyMyI/3SAbh+/kdLIzIj\ IzL+zyMyIyMyWgH0/gwABP/h/wYDBwD6AAcADwATABcAADYiJjQ2MhYUACImNDYyFhQFATMBMwEz\ AUEyIyMyIwJ3MiMjMiP84wG4e/5HeQG4e/5HSyMyIyMy/s8jMiMjMloB9P4MAfT+DAAC/7T/iAF8\ ARgAEQA7AAA3FjMyNjc2NTQnJiMiBgcGFRQXIicHMzIUKwEiNDsBEzY1NCMiDgMHBiY3Njc2MzIW\ Fz4BMzIWFRQGxQIFEjIODQ8CBBI1Cw8nKhktNAsL4QsLS2gGCwgMDwsZCgUbBTEPFyUjJAcdJiMe\ LVkoATUkICYlBQEzHCckKS4geh4eAR0SDA8HFBIsEAgPCVgQGRMaHg80MElrAAAB/9v/9gG+ARgA\ UwAANwYHBisBIj8BNiYjIgYHBiY3PgMzMhc2MzIXPgEzMhYVFA8BBhUUMzI3PgU3NhYHDgIjIiY1\ ND8BNjU0IyIPAQYHJwYmPwE2NTQjIgdQBwgEBDUNDUIEBggNFiQFFQQUECIfEjcLJCQtCQspExkk\ BS8ECAEEBQsHDAMNAQYVBhIXLR8VGQU0ARUbCEEIDyYNCARDARUbCBURAwEapw0PGjkIDAklGjAS\ KCgoEBgjGgsPfQsJDgIDCQYPBRIBCQ0LHx8aFhMNDYgCBA4UqxYBAQEPCKsCBA4UAAAAAf9+/2AB\ XgG4AEEAAAciJjU0NjMyFhUUBwYVFDMyPgc3IyI1NDsBPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIU\ KwEOATIgMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGQcMCgcIBQYDBgE2ExM/IXag\ JiAaIhQPDgsHDQ4GERMlJDw3VScVE0tfJiAaIhQPDgsGHAULChQNGg0eBijFwQAAAf/bAAABEwET\ ACoAADc+ASYjIgYPAQYHBisBIj8BNiYjIgYHBiY3PgEzMhYXNjMyFhUUIyImNTTQBQMDBRQkCT8H\ CAQENQ0NQgQGCA4XIgUVBCE4IhsdBB8kGiArDxvnAwcELBicEQMBGqMNDxo1CAwJPEAaDSceGTcW\ DRQAAAABAAAAAADcARgAMQAAMyImNTQ2MhYVFAcWMzI2NTQuAicmNTQ2MzIWFRQGIyImNTQ3JiMi\ BhUUHgIXFhUUUB4yFRoXEAwTFiEJCxgGOjctIjYWEA0WBw8RDxkREh4FMCsbEBYQDBIQEBYSCw8H\ DQQlMCMoJBgQGBQOAw4TEQ0JEwwRAyArVQAAAf/k//wA5wEPADwAACcGLgE/ASIGIyImDgEHBicm\ Nz4BNx4BMzI2MzIXFhQPAQYVFDMyNjMyFxY3NiciNTQ2MzIVFAYrAS4BIyIKBgsBBbAFHwwDFAwf\ BQ8EAwgMCwEOLxUiKwQIBQsHngICAQ0GGiMNCAUHJBINJigeHRglCA4DBAgNBsEGBgIyBQ0LCRIe\ MAIBBgsBAREHpwYCAwMaCg4NAx0OFTceLwMYAAX/tP+IBUsBGAAQACIAtQDGANcAACUWMzI2NzY0\ JyYjIgYHBhUUBRYzMjY3NjU0JyYjIgYHBhUUFyInBzMyFCsBIjQ7ARM2NTQjIg4DBwYmNzY3NjMy\ Fhc+ATMyFz4BMzIWFz4BMzIXPgEzMhYXPgEzMhc+ATMyFhc+ATMyFhUUBiMiJwczMhQrASI0OwET\ NjU0IyIGBxUUBiMiJwczMhQrASI0OwETNjU0IyIGBxUUBiMiJwczMhQrASI0OwETNjU0IyIGBxUU\ BiUWMzI2NzY0JyYjIgYHBhUUBRYzMjY3NjQnJiMiBgcGFRQCCgIFEjIODQ8CBBI1Cw/+ywIFEjIO\ DQ8CBBI1Cw8TFhktNAsL4QsLS2gGCwgMDwsZCgUbBTEPFyUjJAcdJiMvExIiGyMkBx0mIy8TEiIb\ IyQHHSYjLxMSIhsjJAcdJiMeLWtJFhktNAsL4QsLS2gGCw4UFGtJFhktNAsL4QsLS2gGCw4UFGtJ\ FhktNAsL4QsLS2gGCw4UFGsDgwIFEjIODQ8CBBI1Cw/+ywIFEjIODQ8CBBI1Cw8oATUkIUoFATMc\ JyQpBgE1JCAmJQUBMxwnJCkuIHoeHgEdEgwPBxQSLBAIDwlYEBkTGh4PMhwWExoeDzIcFhMaHg8y\ HBYTGh4PNDBHbSB6Hh4BHRIMDxgjAUdtIHoeHgEdEgwPGCMBR20geh4eAR0SDA8YIwFHbSgBNSQh\ SgUBMxwnJCkGATUkIUoFATMcJyQpAAT/tP+IBAYBGABvAIAAkQCjAAAhIicHMzIUKwEiNDsBEzY1\ NCMiBgcVFAYjIicHMzIUKwEiNDsBEzY1NCMiBgcVFAYjIicHMzIUKwEiNDsBEzY1NCMiDgMHBiY3\ Njc2MzIWFz4BMzIXPgEzMhYXPgEzMhc+ATMyFhc+ATMyFhUUBicWMzI2NzY0JyYjIgYHBhUUBRYz\ MjY3NjQnJiMiBgcGFRQFFjMyNjc2NTQnJiMiBgcGFRQDUhYZLTQLC+ELC0toBgsOFBRrSRYZLTQL\ C+ELC0toBgsOFBRrSRYZLTQLC+ELC0toBgsIDA8LGQoFGwUxDxclIyQHHSYjLxMSIhsjJAcdJiMv\ ExIiGyMkBx0mIx4ta0wCBRIyDg0PAgQSNQsP/ssCBRIyDg0PAgQSNQsP/ssCBRIyDg0PAgQSNQsP\ IHoeHgEdEgwPGCMBR20geh4eAR0SDA8YIwFHbSB6Hh4BHRIMDwcUEiwQCA8JWBAZExoeDzIcFhMa\ Hg8yHBYTGh4PNDBHbSgBNSQhSgUBMxwnJCkGATUkIUoFATMcJyQpBgE1JCAmJQUBMxwnJCkAAAP/\ tP+IAsEBGABMAF4AbwAAMyInBzMyFCsBIjQ7ARM2NTQjIg4DBwYmNzY3NjMyFhc+ATMyFz4BMzIW\ Fz4BMzIWFRQGIyInBzMyFCsBIjQ7ARM2NTQjIgYHFRQGJxYzMjY3NjU0JyYjIgYHBhUUBRYzMjY3\ NjQnJiMiBgcGFRTIFhktNAsL4QsLS2gGCwgMDwsZCgUbBTEPFyUjJAcdJiMvExIiGyMkBx0mIx4t\ a0kWGS00CwvhCwtLaAYLDhQUa0wCBRIyDg0PAgQSNQsPAVUCBRIyDg0PAgQSNQsPIHoeHgEdEgwP\ BxQSLBAIDwlYEBkTGh4PMhwWExoeDzQwR20geh4eAR0SDA8YIwFHbSgBNSQgJiUFATMcJyQpBgE1\ JCFKBQEzHCckKQAAAAL/2/+IAysBGAByAIMAACU2NzYzMhYXPgEzMhYVFAYjIicHMzIUKwEiNDsB\ EzY1NCMiDgMHDgEjIiY1ND8BNjU0IyIPAQYHJwYmPwE2NTQjIg8BBgcGKwEiPwE2JiMiBgcGJjc+\ AzMyFzYzMhc+ATMyFhUUDwEGFRQzMjc+ARcWMzI2NzY0JyYjIgYHBhUUAa0xFBgiIyQHHSYjHi1r\ SRYZLTQLC+ELC0toBgsHDxIOGAgaPC0VGQU0ARUbCEEIDyYNCARDARUbCEQHCAQENQ0NQgQGCA0W\ JAUVBBQQIh8SNwskJC0JCykTGSQFLwQIAQQPJdECBRIyDg0PAgQSNQsPd2wYHRMaHg80MEdtIHoe\ HgEdEgwPDR8bNRA2OBYTDQ2IAgQOFKsWAQEBDwirAgQOFKwRAwEapw0PGjkIDAklGjASKCgoEBgj\ GgsPfQsJDgIINTkBNSQhSgUBMxwnJCkAAAAAAv/b/2ADGQG4AFMAlQAANwYHBisBIj8BNiYjIgYH\ BiY3PgMzMhc2MzIXPgEzMhYVFA8BBhUUMzI3PgU3NhYHDgIjIiY1ND8BNjU0IyIPAQYHJwYmPwE2\ NTQjIgcTIiY1NDYzMhYVFAcGFRQzMj4HNyMiNTQ7AT4BMzIWFRQGIyImNTQ3NjQjIg4HBzMyFCsB\ DgFQBwgEBDUNDUIEBggNFiQFFQQUECIfEjcLJCQtCQspExkkBS8ECAEEBQsHDAMNAQYVBhIXLR8V\ GQU0ARUbCEEIDyYNCARDARUbCPUgMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGQcM\ CgcIBQYDBgE2FBQ/IXYVEQMBGqcNDxo5CAwJJRowEigoKBAYIxoLD30LCQ4CAwkGDwUSAQkNCx8f\ GhYTDQ2IAgQOFKsWAQEBDwirAgQOFP6fJiAaIhQPDgsHDQ4GERMlJDw3VScVE0tfJiAaIhQPDgsG\ HAULChQNGg0eBijFwQAAAAH/fv9gAmkBuAB0AAAlIw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjIjU0\ OwE+ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0\ NjMyFhUUBwYVFDMyPgcBX5shdl8gMBcTEhcSChkLEA8LDQoODRQKNRMRQRRpNCAwFxMSFxIKGSYX\ AwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYUFD8hdl8gMBcTEhcSChkLEA8LDQoPDRTmxcEmIBoi\ FA8OCwcNDgYREyUkPDdVJxUTS18mIBoiFA8OCwYccwwGAUtfJiAaIhQPDgsGHAULChQNGg0eBijF\ wSYgGiIUDw4LBw0OBhETJSQ8N1UAAAAB/37/YAN0AbgAqgAAEzM+ATMyFhUUBiMiJjU0NzY0IyIH\ BgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0NjMyFhUUBwYVFDMyPgc3Iw4BIyIm\ NTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQzMj4HNyMiNTQ7AT4BMzIWFRQGIyIm\ NTQ3NjQjIg4CBwbMnBRpNCAwFxMSFxIKGSYXAwGbFGk0IDAXExIXEgoZBwwKBwgFBgMGATYTEz8h\ dl8gMBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFQqbIXZfIDAXExIXEgoZ\ CxAPCw0KDg0UCjUTEUEUaTQgMBcTEhcSChkOFQ4IBgIBDktfJiAaIhQPDgsGHHMMBgFLXyYgGiIU\ Dw4LBhwFCwoUDRoNHgYoxcEmIBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlJDs4\ VSfFwSYgGiIUDw4LBw0OBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhwTJyIbCgAB/37/YASAAbgA4AAA\ ARc+ATMyFhUUBiMiJjU0NzY0IyIHBgcXPgEzMhYVFAYjIiY1NDc2NCMiDgcHMzIUKwEOASMiJjU0\ NjMyFhUUBwYVFDMyPgc3Iw4BIyImNTQ2MzIWFRQHBhUUMzI+BzcjDgEjIiY1NDYzMhYVFAcGFRQz\ Mj4HNyMOASMiJjU0NjMyFhUUBwYVFDMyPgc3IyI1NDsBPgEzMhYVFAYjIiY1NDc2NCMiBwYHFz4B\ MzIWFRQGIyImNTQ3NjQjIg4HAdicFGk0IDAXExIXEgoZJhcDAZsUaTQgMBcTEhcSChkHDAoHCAUG\ AwYBNhMTPyF2XyAwFxMSFxIKGQsQDwsNCg8NFAqbIXZfIDAXExIXEgoZCxAPCw0KDg0UCpshdl8g\ MBcTEhcSChkLEA8LDQoPDRQKmyF2XyAwFxMSFxIKGQsQDwsNCg4NFAo1ExFBFGk0IDAXExIXEgoZ\ JhcDAZsUaTQgMBcTEhcSChkHDAoHCAUGAwYBDwFLXyYgGiIUDw4LBhxzDAYBS18mIBoiFA8OCwYc\ BQsKFA0aDR4GKMXBJiAaIhQPDgsHDQ4GERMlJDw3VSfFwSYgGiIUDw4LBw0OBhETJSM8N1YnxcEm\ IBoiFA8OCwcNDgYREyUkPDdVJ8XBJiAaIhQPDgsHDQ4GERMlJDw3VScVE0tfJiAaIhQPDgsGHHMM\ BgFLXyYgGiIUDw4LBhwFCgsTDhkOHQAAAAADAAD/YALfAbgANgBxALYAADMiJjU0NjMyFhUUBxYz\ MjY1NC4DJy4CNTQ2MzIWFRQGIyImNTQ3JiMiBhUUHgIXHgEVFCUGLgE/ASYjIgYjIg4BBwYnJjc+\ ATcWMzI2MzIXFhQPAQYVFB4BFxY+AScuATU0NjMyFRQGKwEuASMiBSImNTQ2MzIWFRQHDgEeARUU\ FjI+BzcjIjU0OwE+ATMyFhUUBiMiJjU0NzY0IyIOBwczMhQrAQ4BUB4yEgwOFwwGGBYhAwwFFwMU\ GBQ3LSI2FhANFgUQDg8ZERIeBRsXASYGCwEFrgoUBRoJBRASBRAFAwcLDAExGiAoBwIUCweaBBoq\ DQUJBQIDJBQMJisgBhc1CRP+tCAwFxMSFxIEAQECBhYQDwsNCg4NFAo1ExFBFGk0IDAXExIXEgoZ\ BwwKBwgFBgMGATYTEz8hdisbEBYNCxgMEhYSCQwNBA4CDRIeDyMtJBgQGBQOBgoUEQ0JEwwRAxIf\ FVoDBAgNBr8FARgfBA0LCBMeLgQDCAIBEQejBQUHAwcLAwQNBggIFAsTNx4qAg2yJiAaIhQPDgsC\ BQQGAwgGBhETJSQ8N1UnFRNLXyYgGiIUDw4LBhwFCwoUDRoNHgYoxcEAAgAF//sB/AGaAAkALAAA\ AQ8BBhUUMzI2NwcOASMiJjU0PwEjNTM/AQc3MhU2MzIWFRQGIiY1NDcGDwEjATZ/OQIUGEQSDCky\ HyIiAzdpcxZZJpgZIDUYHRggFgk3EEJIAQkHuAgDFRcPKBkUJBoLC7MgSi54Ci0pHBUSGxEOEhMP\ J9gAAQAA//0BtQDUAC8AACUyNTQnBiImNT4BMzIWFRQHBiMiLwEmIyIVFBc2MzIWFRQGByInJjU0\ NzYzFh8BFgFwLRoQHBQBFwkkLSkXHiYeohoRLhkQDw0VFA0dGBwoFiIqF6IcMTkgExAWDQ4WMzUz\ JBUVehI6IBIQFw4PEgIaIi0zJBMCEHoTAAEAAP/NAbUBAwA2AAAXIiY1NDc2MxYfATUzFRcWMzI2\ NTQnBiMiNT4BMzIWFRQHBiMiLwEVIzUnJiMiBhUUFzYzMhUUTSAtKBYgKBcuHlwiDxUcFhQSHAEP\ CSAtKRccJB4uHlwgDxYcFRQTHANDJjMkEwIQJGmARxcpHCcQDBsPFUImMyQVFSRsg0cWKB4nDwwd\ IAABAA0AAAJFAOAACwAANyc3FzcXNxcHJwcnIhWOZXhqTRaSaXRpLhmZfHx8VBehfHx8AAAAAQAN\ /8sCRQERABMAACUHJwcnNxc3NTMXNxc3FwcnBxUjARZAaUsVjmUWGwFGak8UkmkZG0ZGfE4ZmXwX\ lntKfFUYoXwblgAAAQAAAAABGAEYAAsAADM1IzUzNTMVMxUjFXt7eyJ7e3sie3siewAAAAEAAAAA\ ATYBcgAKAAAxNT4ENzMUBiQxTDQ0Dx7APAcPKz1vSY7aAAAB//8AAAEtAKAAHQAANz4CMzIeARcW\ MzI3NhYHDgIjIi4BJyYjIgcGJgEJECkcGCYmDwkKHhgEDgIIESkcGCQlEgcIHR0FDUYZISAhMQoG\ JAYHBxkiHyExCgQjBggAAAABAAAAAAEsASwABwAAMREhESM1IxUBLCPmASz+1LS0AAEAAAAAAPoB\ wgAGAAAzAzMbATMDaWkoVVUoaQHC/pgBaP4+AAIAAAAAAMgAyAAHAA8AADYyNjQmIgYUFiImNDYy\ FhRFPiwsPix0Ujs7UjsZLD4sLD5FO1I7O1IAAf84AAAAyADIAAsAACM0NjIWFSM0JiIGFch2pHYe\ YJRgUnZ2UkpgYEoAAAACAAAAAAC0ASwABwAVAAA2MjY0JiIGFBc1LgE1NDYyFhUUBgcVSx4bGx4b\ GB0rN0Y3Kx14NTY1NTatZAg1Jyg8PCgnNQhkAAACAAAAAADIASwADwAfAAA3LgE1NDYyFhUUBgcd\ ASM1Nz4BNTQmIgYVFBYXPQEzFVQkMDtSOzAkICAZIiw+LCIZIGYFOCUpOzspJTgFAWVlGQYpGx8s\ LB8bKQYBSUkAAAAEAAD//AP0An8AgwCNAJkAowAANz4BNTQnLgE1ND4CPwIOARUUMzI3Fw4BIyIm\ NTQ+AjMyFhUUBiMiJic3HgEzMjU0LgInBwYVFB4CFRQGDwEeAjsBMjcmNTQ3NjMyFhUUBwYHHgEz\ MjY1NDY3LgI9AR4BFRQGIyInBiMiLgEnDgEiJicmIyIOAQcGIyI1NDYFIiY0NjMyFhQGJT4BNTQn\ JiMiBhUUBTY1NCYnDgEVFJg2RQUDUgMEBQECPWJqIB4dGhUpJR4tHz5wSH1yMi4cNhIYERQUMwwc\ QC0bCR0iHR8QEA4pHwwNESIhHCJMGyMKHTwTIBsWMUNdIIFeurRRQkEjJDwhMxYOLCggGxwaEgoU\ GQZaIQowA1wLExMLDBIR/g8zJAQFFRwuATxaFxI/MFoYPyAKDAh0IgUODgwEBKUEYywjPA1CLyYa\ IUdELFE3MDUwKw4eETIKGiIaAkIkGx42ISwVJk8UFAopGh41KDUlLisgIRM5MBcSKhlSXxksWTQG\ ATyxa0ZfOToVFBImGRYgLQsVBDoHDThPEhgSEhgSgC8vGwcOEjkrHnQFgSJJFR1YRD8AAAIADAAK\ AdMBzwAKAI8AACU0JiMiBhQWMzI2Jw4BIyImNDYzMhYXNjU0JyYjIiY0NjMyFx4BFxYzMjU0Jy4B\ NTQ2MzIWFRQGBxQzMjc+ATc2MzIWFRQGIyIGBwYVFDMyNjMyFhQGIyImIyIGFRQXHgEXFhUUBiMi\ Jy4BJyYjIhUUFhUUBiMiJjQ2NTQjIgcOAQcGIyImNTQ3NjMyNzY1NAEWGA8QFRYPEBeJGSYOGRsa\ GQ0qGCUMFBgdHBkXEhALAxQLEhQBAiceFBIbIwEWEA0RAQ0MGhMeGxIbFQ0QIRsrDhwbHRkPJxQX\ EgsUOA0OGRcTFAwBExEKEiogExIdJhcNDhIDERIMFBoNDBYjEg3uEBQTIhYVCgElGSobJQIDFg4K\ ExwoHQwLPhYNGA0IFycPFxobFhEjGSoQEzgPDhsUESMGCw4PFicdKBomCAoWChIDCw0ZExsOCzkV\ Dx8eMBMUGBokMRckDhI9DAcWFBgODRINDBoAAAADAAD/BgH0APoABwAPABcAADYUFjI2NCYiAjQ2\ MhYUBiI2IiY0NjIWFC14qnh4qqWS0JKS0IEyIyMyI1WqeHiqeP7L0JKS0JK+IzIjIzIAAAIAAP8G\ AfQA+gAHAA8AADYUFjI2NCYiAjQ2MhYUBiIteKp4eKqlktCSktBVqnh4qnj+y9CSktCSAAAAAAMA\ AP6iAfQBXgARABcAHQAAEzMVHgEVFAYHFSM1LgE1NDY3GQEOARQWFz4BNCYn5C1gg4NgLWCEg2FO\ aWl7TWlpTQFeZAqOYmGPCmRlCI9iY44I/jsBmAh1nnUICHacdggAAAACAAD/BgHSAPoAIQApAAAl\ FhUUBwYjIicmIyIGFBYzMjc2MzIXFhUUBwYjIiY0NjMyAiImNDYyFhQBzwILBQYNCjN3VXh4VXgy\ BhIHAwwDQJVokpJolHsyIyMyI4IGBA8GAw5feKp4Xw0CBwwGBniS0JL+yiMyIyMyAAAAAQAA/wYB\ 0gD6ACEAACUWFRQHBiMiJyYjIgYUFjMyNzYzMhcWFRQHBiMiJjQ2MzIBzwILBQYNCjN3VXh4VXgy\ BhIHAwwDQJVokpJolIIGBA8GAw5feKp4Xw0CBwwGBniS0JIAAAIAAP6iAdIBXgAkACoAACUWFxYV\ FAcGIyInJicRNjc2MzIXFhUUBwYHFSM1LgE1NDY3NTMDEQ4BFBYBEYQ6AgsFBg0KLWZlLgYSBwMM\ AzyCLWCEg2EtLU5pafkKbQYEDwYDDlQK/mgHVw0CBwwGBnAHZWUIj2Jjjghl/dYBmAh1nnUAAAEA\ AP/EAHgAPAAHAAAWIiY0NjIWFFUyIyMyIzwjMiMjMgAAAAEAAP6iAC0BXgADAAATESMRLS0BXv1E\ ArwAAAACAAD9EgFKAL4AAwAPAAA3FSE1JTMVITUzESMRIRUjHgEO/tQeAQ4eHv7yHkaMjHgyMvxU\ AmIyAAEAAAAAAlYBcgALAAA1Nxc3FzcXAScHJweJVFZSryL+/FRWUzVBuXNzcekW/qR0dHBHAAAB\ /+gAvQESATcAFwAAJj4BMzIWMjc2MzIVFA4BIyImIgcGIyI1GDAhHBRVKBQDBw4wIRwUVSgUBAcN\ 9jARPRQDBwwwET0UBAgAAAAAAgAA/4kBwgCLAAsAEwAABTQmIyIGFRQWMzI+ARQGIiY0NjIBUVo2\ IDRdNyEvcX3IfX3IFDRPJx80TCN0bEtLbEsAAAACAAD/bAFIAqgADQAcAAAlJiMiBhUUFxYzMjY1\ NBMzERQGIyInJjU0NjMyFwEkDSU8lwYLJjyXAh1+SE8kD35IQCREF2ErCgkXYSsJAm79bkNnQx4d\ Q2ctAAAAAAEAAP95AUACqAANAAABMxEUBiMiJjU0NjMyFwEiHnlZMjx6WDEfAqj9gEZpOCdFahwA\ AAABAAD/eQIKAqgAGgAAAREUBiMiJjU0NjMyFxE1Mx4EFRQHNjU0AUB5WTI8elgxHx4GLjk4JTIS\ Abn+b0ZpOCdFahwBTu81XEdOdUlIckFJ7wAAAAACAAD/eQILAqgAHwAoAAAlNREzHgQVFAcWFRQH\ NjU0JiMRFAYjIiY1NDYzMhMeARc0NjU0JgEiHgkwODYjFRYiBXY4eVkyPHpYMT0OhRsBdmvmAVck\ RztCWDMlMjAkLkEZIV+e/tZGaTgnRWoBdjuiNQMOA1+fAAABAAD/zgBkADIABwAAFiImNDYyFhRH\ Kh0dKh0yHSodHSoAAAAAAA4ArgABAAAAAAAAAIMBCAABAAAAAAABAAcBnAABAAAAAAACAAcBtAAB\ AAAAAAADAB8B/AABAAAAAAAEAAcCLAABAAAAAAAFAAgCRgABAAAAAAAGAAcCXwADAAEECQAAAQYA\ AAADAAEECQABAA4BjAADAAEECQACAA4BpAADAAEECQADAD4BvAADAAEECQAEAA4CHAADAAEECQAF\ ABACNAADAAEECQAGAA4CTwBDAG8AcAB5AHIAaQBnAGgAdAAgAFwAMgA1ADEAIAAyADAAMQA4AC0A\ MgAwADEAOQAgAEoAZQBhAG4ALQBGAHIAYQBuAGMAbwBpAHMAIABNAG8AaQBuAGUALgAgAFQAaABp\ AHMAIABmAG8AbgB0ACAAaQBzACAAbABpAGMAZQBuAHMAZQBkACAAdQBuAGQAZQByACAAdABoAGUA\ IABTAEkATAAgAE8AcABlAG4AIABGAG8AbgB0ACAATABpAGMAZQBuAHMAZQAgAFwAKABoAHQAdABw\ ADoALwAvAHMAYwByAGkAcAB0AHMALgBzAGkAbAAuAG8AcgBnAC8ATwBGAEwAXAApAC4AAENvcHly\ aWdodCBcMjUxIDIwMTgtMjAxOSBKZWFuLUZyYW5jb2lzIE1vaW5lLiBUaGlzIGZvbnQgaXMgbGlj\ ZW5zZWQgdW5kZXIgdGhlIFNJTCBPcGVuIEZvbnQgTGljZW5zZSBcKGh0dHA6Ly9zY3JpcHRzLnNp\ bC5vcmcvT0ZMXCkuAABhAGIAYwAyAHMAdgBnAABhYmMyc3ZnAABSAGUAZwB1AGwAYQByAABSZWd1\ bGFyAABGAG8AbgB0AEYAbwByAGcAZQAgADoAIABhAGIAYwAyAHMAdgBnACAAOgAgADEANAAtADQA\ LQAyADAAMgAzAABGb250Rm9yZ2UgOiBhYmMyc3ZnIDogMTQtNC0yMDIzAABhAGIAYwAyAHMAdgBn\ AABhYmMyc3ZnAABWAGUAcgBzAGkAbwBuACAAAFZlcnNpb24gAABhAGIAYwAyAHMAdgBnAABhYmMy\ c3ZnAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJIAAAABAAIBAgADAQMBBAEF\ AQYBBwEIAQkBCgELAQwBDQEOAQ8BEAERARIBEwEUARUBFgEXARgBGQEaARsBHAEdAR4BHwEgASEB\ IgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgEvATABMQEyATMBNAE1ATYBNwE4ATkBOgE7ATwBPQE+\ AT8BQAFBAUIBQwFEAUUBRgFHAUgBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQBVQFWAVcBWAFZAVoB\ WwFcAV0BXgFfAWABYQFiAWMBZAFlAWYBZwFoAWkBagFrAWwBbQFuAW8BcAFxAXIBcwF0AXUBdgF3\ AXgBeQF6AXsBfAF9AX4BfwGAAYEBggGDAYQBhQGGAYcBiAGJAYoBiwGMAY0BjgGPBi5ub2RlZgd1\ bmlFMDAwB3VuaUUwMzAHdW5pRTAzOAd1bmlFMDM5B3VuaUUwNDMHdW5pRTA0NQd1bmlFMDQ2B3Vu\ aUUwNDcHdW5pRTA0OAd1bmlFMDUwB3VuaUUwNUMHdW5pRTA2Mgd1bmlFMDY5B3VuaUUwN0EHdW5p\ RTA3Qgd1bmlFMDdDB3VuaUUwN0QHdW5pRTA3RQd1bmlFMDgwB3VuaUUwODEHdW5pRTA4Mgd1bmlF\ MDgzB3VuaUUwODQHdW5pRTA4NQd1bmlFMDg2B3VuaUUwODcHdW5pRTA4OAd1bmlFMDg5B3VuaUUw\ OEEHdW5pRTA4Qgd1bmlFMDhDB3VuaUUwOTQHdW5pRTA5NQd1bmlFMEEwB3VuaUUwQTEHdW5pRTBB\ Mgd1bmlFMEEzB3VuaUUwQTQHdW5pRTBBOQd1bmlFMEIzB3VuaUUxMDEHdW5pRTFCOQd1bmlFMUJC\ B3VuaUUxRTcHdW5pRTFGMgd1bmlFMUY0B3VuaUUxRjcHdW5pRTFGOQd1bmlFMUZDB3VuaUUxRkUH\ dW5pRTFGRgd1bmlFMjAwB3VuaUUyNDAHdW5pRTI0MQd1bmlFMjQyB3VuaUUyNDMHdW5pRTI0NAd1\ bmlFMjQ1B3VuaUUyNDYHdW5pRTI0Nwd1bmlFMjQ4B3VuaUUyNDkHdW5pRTI2MAd1bmlFMjYxB3Vu\ aUUyNjIHdW5pRTI2Mwd1bmlFMjY0B3VuaUUyNmEHdW5pRTI2Ygd1bmlFMjgwB3VuaUUyODEHdW5p\ RTI4Mgd1bmlFMjgzB3VuaUU0QTAHdW5pRTRBMgd1bmlFNEE0B3VuaUU0QTgHdW5pRTRBQwd1bmlF\ NEMwB3VuaUU0Q0UHdW5pRTREMQd1bmlFNEUxB3VuaUU0RTIHdW5pRTRFMwd1bmlFNEU0B3VuaUU0\ RTUHdW5pRTRFNgd1bmlFNEU3B3VuaUU0RTgHdW5pRTRFOQd1bmlFNEVBB3VuaUU1MDAHdW5pRTUw\ MQd1bmlFNTIwB3VuaUU1MjEHdW5pRTUyMgd1bmlFNTIzB3VuaUU1MjQHdW5pRTUyNQd1bmlFNTI5\ B3VuaUU1MkEHdW5pRTUyQgd1bmlFNTJDB3VuaUU1MkQHdW5pRTUyRgd1bmlFNTMwB3VuaUU1MzEH\ dW5pRTUzOQd1bmlFNTY2B3VuaUU1NjcHdW5pRTU2OQd1bmlFNTZDB3VuaUU1NkQHdW5pRTU4Mgd1\ bmlFNUQwB3VuaUU1RTIHdW5pRTYxMAd1bmlFNjEyB3VuaUU2MTQHdW5pRTYxOAd1bmlFNjI0B3Vu\ aUU2MzAHdW5pRTY1MAd1bmlFNjU1B3VuaUU5MTAHdW5pRTkxMQd1bmlFOTEyB3VuaUU5MTQHdW5p\ RTkxNQd1bmlFOTE4B3VuaUU5MjAHdW5pRTkyNQd1bmlFOTVEB3VuaUVBMDIHdW5pRUFBNAd1bmlF\ Q0EyB3VuaUVDQTMHdW5pRUNBNQd1bmlFQ0E3B3VuaUVDQTkHdW5pRUNCNwAAAAAAAAH//wACAAEA\ AAAAAAAADAAUAAQAAAACAAAAAQAAAAEAAAAAAAEAAAAA399K6gAAAADRlyIXAAAAAOBezeg=\ ") format("truetype")' // abc2svg - format.js - formatting functions // // Copyright (C) 2014-2023 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 . 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) 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}, subtitlespace: 3, sysstaffsep: 34, systnames: -1, tempofont: {name: txt_ff, weight: "bold", size: 12}, textfont: {name: txt_ff, size: 16}, // textoption: undefined, textspace: 14, tieheight: 1.0, titlefont: {name: txt_ff, size: 20}, // titleleft: false, titlespace: 6, titletrim: true, // transp: 0, // global transpose // topmargin: .7 * IN, topspace: 22, tuplets: [0, 0, 0, 0], tupletfont: {name: txt_ff, style: "italic", size: 10}, vocalfont: {name: txt_ff, weight: "bold", size: 13}, vocalspace: 10, voicefont: {name: txt_ff, weight: "bold", size: 13}, // voicescale: 1, writefields: "CMOPQsTWw", wordsfont: {name: txt_ff, size: 16}, wordsspace: 5, "writeout-accidentals": "n" } // parameters that are used in the symbols var sfmt = { bardef: true, barsperstaff: true, beamslope: true, breaklimit: true, bstemdown: true, cancelkey: true, dynalign: true, flatbeams: true, gracespace: true, hyphencont: true, keywarn: true, maxshrink: true, maxstaffsep: true, measrepnb: true, rbmax: true, rbmin: true, shiftunison: true, slurheight: true, squarebreve: true, staffsep: true, systnames: 1, // stemheight: true, stretchlast: true, stretchstaff: true, tieheight: true, timewarn: true, trimsvg: 1, //true vocalspace: true } // sfmt function get_bool(param) { return !param || !/^(0|n|f)/i.test(param) // accept void as true ! } // %%font [] [] function get_font_scale(param) { var i, font, a = info_split(param) // a[0] = font name if (a.length <= 1) return var scale = +a[a.length - 1] if (isNaN(scale) || scale <= 0.5) { syntax(1, "Bad scale value in %%font") return } font_scale_tb[a[0]] = scale } // set the width factor of a font function set_font_fac(font) { var scale = font_scale_tb[font.fname || font.name] if (!scale) scale = 1.1; font.swfac = font.size * scale } // %%xxxfont fontname|* [encoding] [size|*] function param_set_font(xxxfont, p) { var font, n, a, ft2, k // "setfont-" goes to "ufont" if (xxxfont[xxxfont.length - 2] == '-') { n = xxxfont[xxxfont.length - 1] if (n < '1' || n > '9') return xxxfont = "u" + n + "font" } // fill the values font = {} a = p.match(/\s+(no)?box(\s|$)/) if (a) { // if box if (a[1]) { font.box = false // nobox font.pad = 0 } else { font.box = true font.pad = 2.5 } p = p.replace(a[0], a[2]) } a = p.match(/\s+padding=([\d.]+)(\s|$)/) if (a) { // if padding font.pad = a[1] ? +a[1] : 0 p = p.replace(a[0], a[2]) } a = p.match(/\s+class=(.*?)(\s|$)/) if (a) { font.class = a[1]; p = p.replace(a[0], a[2]) } a = p.match(/\s+wadj=(.*?)(\s|$)/) if (a) { if (typeof document == "undefined") // useless if in browser switch (a[1]) { case 'none': font.wadj = '' break case 'space': font.wadj = 'spacing' break case 'glyph': font.wadj = 'spacingAndGlyphs' break default: syntax(1, errs.bad_val, "%%" + xxxfont) break } p = p.replace(a[0], a[2]) } // the font size is the last item a = p.match(/\s+([0-9.]+|\*)$/) if (a) { if (a[1] != "*") font.size = +a[1] p = p.replace(a[0], "") } // accept local(..) and url(...) as the font source if ((p[0] == 'u' && p.slice(0, 4) == "url(") || (p[0] == 'l' && p.slice(0, 6) == "local(")) { n = p.indexOf(')', 1) if (n < 0) { syntax(1, "No end of url in font family") return } font.src = p.slice(0, n + 1) font.fid = abc2svg.font_tb.length abc2svg.font_tb.push(font) font.name = 'ft' + font.fid p = p.replace(font.src, '') } // extract the font attributes a = p.match(/[- ]?[nN]ormal/) if (a) { font.normal = true p = p.replace(a[0], '') } // font weight a = p.match(abc2svg.ft_re) if (a) { font.weight = abc2svg.ft_w[a[0].replace(/[ -]/, '') .toLowerCase()] p = p.replace(a[0], '') } a = p.match(/[- ]?[iI]talic/) if (a) { font.style = "italic" p = p.replace(a[0], '') } a = p.match(/[- ]?[oO]blique/) if (a) { font.style = "oblique" p = p.replace(a[0], '') } if (!font.src) { // if no url(...) if (p[0] == '"') { n = p.indexOf('"', 1) if (n < 0) { syntax(1, "No end of string in font family") return } p = p.slice(1, n) } // here is the font family p = p.trim() switch (p) { case "": case "*": p = ""; break case "Times-Roman": case "Times": p = "serif"; break case "Helvetica": p = "sans-serif"; break case "Courier": p = "monospace"; break case "music": p = cfmt.musicfont.name; break default: //hack: the font "Figurato" is used for figured bass if (p.indexOf("Fig") > 0) font.figb = true break } } if (p && !font.name) font.name = p if (font.size) set_font_fac(font) // keep the previous attributes if no font name or no size if (!font.name || !font.size) { ft2 = cfmt[xxxfont] for (k in ft2) { if (!ft2.hasOwnProperty(k) || font[k] != undefined) continue switch (k) { case "fid": case "used": case "src": break case "style": case "weight": if (font.normal) break // fall thru default: font[k] = ft2[k] break } } if (!font.swfac) set_font_fac(font) } if (font.pad == undefined) font.pad = 0 font.fname = font.name if (font.weight >= 700) font.fname += 'Bold' cfmt[xxxfont] = font } // get a length with a unit - return the number of pixels function get_unit(param) { var v = param.toLowerCase().match(/(-?[\d.]+)(.*)/) if (!v) return NaN v[1] = +v[1] switch (v[2]) { case "cm": return v[1] * CM case "in": return v[1] * IN case "pt": // paper point in 1/72 inch return v[1] / .75 case "px": // screen pixel in 1/96 inch case "": return v[1] } return NaN } // set the infoname function set_infoname(param) { //fixme: check syntax: ' ["string"]' var tmp = cfmt.infoname.split("\n"), letter = param[0] for (var i = 0; i < tmp.length; i++) { var infoname = tmp[i] if (infoname[0] != letter) continue if (param.length == 1) tmp.splice(i, 1) else tmp[i] = param cfmt.infoname = tmp.join('\n') return } cfmt.infoname += "\n" + param } // get the text option var textopt = { align: 'j', center: 'c', fill: 'f', justify: 'j', obeylines: 'l', ragged: 'f', right: 'r', skip: 's', // abcm2ps compatibility "0": 'l', "1": 'j', "2": 'f', "3": 'c', "4": 's', "5": 'r' } function get_textopt(v) { var i = v.indexOf(' ') if (i > 0) v = v.slice(0, i) return textopt[v] } /* -- position of a voice element -- */ var posval = { above: C.SL_ABOVE, auto: 0, // !! not C.SL_AUTO !! below: C.SL_BELOW, down: C.SL_BELOW, hidden: C.SL_HIDDEN, opposite: C.SL_HIDDEN, under: C.SL_BELOW, up: C.SL_ABOVE } /* -- set the position of elements in a voice -- */ function set_pos(k, v) { // keyword, value k = k.slice(0, 3) if (k == "ste") k = "stm" set_v_param("pos", '"' + k + ' ' + v + '"') } // set/unset the fields to write function set_writefields(parm) { var c, i, a = parm.split(/\s+/) if (get_bool(a[1])) { for (i = 0; i < a[0].length; i++) { // set c = a[0][i] if (cfmt.writefields.indexOf(c) < 0) cfmt.writefields += c } } else { for (i = 0; i < a[0].length; i++) { // unset c = a[0][i] if (cfmt.writefields.indexOf(c) >= 0) cfmt.writefields = cfmt.writefields.replace(c, '') } } } // set a voice specific parameter function set_v_param(k, v) { k = [k + '=', v] if (parse.state < 3) memo_kv_parm(curvoice ? curvoice.id : '*', k) else if (curvoice) set_kv_parm(k) else memo_kv_parm('*', k) } function set_page() { if (!img.chg) return img.chg = false; img.lm = cfmt.leftmargin - cfmt.printmargin if (img.lm < 0) img.lm = 0; img.rm = cfmt.rightmargin - cfmt.printmargin if (img.rm < 0) img.rm = 0; img.width = cfmt.pagewidth - 2 * cfmt.printmargin // must have 100pt at least as the staff width if (img.width - img.lm - img.rm < 100) { error(0, undefined, "Bad staff width"); img.width = img.lm + img.rm + 150 } set_posx() } // set_page() // set a format parameter // (possible hook) Abc.prototype.set_format = function(cmd, param) { var f, f2, v, i //fixme: should check the type and limits of the parameter values if (/.+font(-[\d])?$/.test(cmd)) { if (cmd == "soundfont") cfmt.soundfont = param else param_set_font(cmd, param) return } // duplicate the global parameters if already used by symbols if (sfmt[cmd] && parse.ufmt) cfmt = Object.create(cfmt) switch (cmd) { case "aligncomposer": case "barsperstaff": case "infoline": case "measurenb": case "rbmax": case "rbmin": case "measrepnb": case "shiftunison": case "systnames": v = parseInt(param) if (isNaN(v)) { syntax(1, "Bad integer value"); break } cfmt[cmd] = v break case "abc-version": case "bgcolor": case "fgcolor": case "propagate-accidentals": case "titleformat": 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 "quiet": 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)) syntax(1, errs.bad_val, '%%' + cmd) else cfmt[cmd] = f break case "page-format": user.page_format = get_bool(param) break case "print-leftmargin": // to remove syntax(0, "$1 is deprecated - use %%printmargin instead", '%%' + cmd) cmd = "printmargin" // fall thru case "printmargin": // case "botmargin": case "leftmargin": // case "pageheight": case "pagewidth": case "rightmargin": // case "topmargin": f = get_unit(param) // normally unit in cm or in - 96 DPI if (isNaN(f)) { syntax(1, errs.bad_val, '%%' + cmd) break } cfmt[cmd] = f; img.chg = true break case "concert-score": if (cfmt.sound != "play") cfmt.sound = "concert" break case "writefields": set_writefields(param) break case "volume": cmd = "dynamic" // fall thru case "dynamic": case "gchord": case "gstemdir": case "ornament": case "stemdir": case "vocal": set_pos(cmd, param) break case "font": get_font_scale(param) break case "fullsvg": if (parse.state != 0) { syntax(1, errs.not_in_tune, "%%fullsvg") break } //fixme: should check only alpha, num and '_' characters cfmt[cmd] = param break case "gracespace": v = param.split(/\s+/) for (i = 0; i < 3; i++) if (isNaN(+v[i])) { syntax(1, errs.bad_val, "%%gracespace") break } if (parse.ufmt) cfmt[cmd] = new Float32Array(3) for (i = 0; i < 3; i++) cfmt[cmd][i] = +v[i] break case "tuplets": v = param.split(/\s+/) f = v[3] if (f) // if 'where' f = posval[f] // translate the keyword if (f) v[3] = f if (curvoice) curvoice.tup = v else cfmt[cmd] = v break case "infoname": set_infoname(param) break case "notespacingfactor": v = param.match(/([.\d]+)[,\s]*(\d+)?/) if (v) { f = +v[1] if (isNaN(f) || f < 1 || f > 2) { f = 0 } else if (v[2]) { f2 = +v[2] if (isNaN(f)) f = 0 } else { f2 = cfmt.spatab[5] } } if (!f) { syntax(1, errs.bad_val, "%%" + cmd) break } cfmt[cmd] = param // (for dump) // in the table 'spatab', // the width of notes is indexed by log2(note_length) cfmt.spatab = new Float32Array(10) i = 5; // index of crotchet do { cfmt.spatab[i] = f2 f2 /= f } while (--i >= 0) i = 5; f2 = cfmt.spatab[i] for ( ; ++i < cfmt.spatab.length; ) { f2 *= f; cfmt.spatab[i] = f2 } break case "play": cfmt.sound = "play" // without clef break case "pos": cmd = param.match(/(\w*)\s+(.*)/) if (!cmd || !cmd[2]) { syntax(1, "Error in %%pos") break } if (cmd[1].slice(0, 3) == 'tup' // special case for %%pos tuplet && curvoice) { // inside tune if (!curvoice.tup) curvoice.tup = cfmt.tuplets else curvoice.tup = Object.create(curvoice.tup) v = posval[cmd[2]] switch (v) { case C.SL_ABOVE: curvoice.tup[3] = 1 break case C.SL_BELOW: curvoice.tup[3] = 2 break case C.SL_HIDDEN: curvoice.tup[2] = 1 break } break } if (cmd[1].slice(0, 3) == "vol") cmd[1] = "dyn" // compatibility set_pos(cmd[1], cmd[2]) break case "sounding-score": if (cfmt.sound != "play") cfmt.sound = "sounding" break case "staffwidth": v = get_unit(param) if (isNaN(v)) { syntax(1, errs.bad_val, '%%' + cmd) break } if (v < 100) { syntax(1, "%%staffwidth too small") break } v = cfmt.pagewidth - v - cfmt.leftmargin if (v < 2) { syntax(1, "%%staffwidth too big") break } cfmt.rightmargin = v; img.chg = true break case "textoption": cfmt[cmd] = get_textopt(param) break case "dynalign": case "singleline": case "stretchlast": case "titletrim": v = +param if (isNaN(v)) v = get_bool(param) ? 0 : 1 if (cmd[1] == 't') { // stretchlast if (v < 0 || v > 1) { syntax(1, errs.bad_val, '%%' + cmd) break } } cfmt[cmd] = v break case "combinevoices": syntax(1, "%%combinevoices is deprecated - use %%voicecombine instead") break case "voicemap": set_v_param("map", param) break case "voicescale": set_v_param("scale", param) break case "unsizedsvg": if (get_bool(param)) user.imagesize = "" else delete user.imagesize break // deprecated case "rbdbstop": v = get_bool(param) if (v && cfmt["abc-version"] >= "2.2") cfmt["abc-version"] = "1" else if (!v && cfmt["abc-version"] < "2.2") cfmt["abc-version"] = "2.2" break default: // memorize all global commands if (!parse.state) // (needed for modules) cfmt[cmd] = param break } // check if already a same format if (sfmt[cmd] && parse.ufmt) { // to do... parse.ufmt = false } } // font stuff // build a font style function st_font(font) { var n = font.name, r = "" if (font.weight) r += font.weight + " " if (font.style) r += font.style + " " if (n.indexOf('"') < 0 && n.indexOf(' ') > 0) n = '"' + n + '"' return r + font.size.toFixed(1) + 'px ' + n } function style_font(font) { return 'font:' + st_font(font) } Abc.prototype.style_font = style_font // build a font class function font_class(font) { var f = 'f' + font.fid + cfmt.fullsvg if (font.class) f += ' ' + font.class if (font.box) f += ' ' + 'box' return f } // use the font function use_font(font) { if (!font.used) { font.used = true; if (font.fid == undefined) { // if default font font.fid = abc2svg.font_tb.length abc2svg.font_tb.push(font) if (!font.swfac) set_font_fac(font) if (!font.pad) font.pad = 0 // set the pointer to the width of the characters font.cw_tb = !font.name ? ssw_tb : font.name.indexOf("ans") > 0 ? ssw_tb // sans-serif : font.name.indexOf("ono") > 0 ? mw_tb // monospace : sw_tb // serif } add_fstyle(".f" + font.fid + (cfmt.fullsvg || "") + "{" + style_font(font) + "}") if (font.src) add_fstyle("@font-face{\n\ font-family:" + font.name + ";\n\ src:" + font.src + "}") if (font == cfmt.musicfont) // add more music font style add_fstyle(".f" + font.fid + (cfmt.fullsvg || "") + ' text,tspan{white-space:pre}') } } // get the font of the 'xxxfont' parameter function get_font(fn) { var font, font2, fid, st fn += "font" font = cfmt[fn] if (!font) { syntax(1, "Unknown font $1", fn) return gene.curfont } if (!font.name || !font.size) { // if incomplete user font font2 = Object.create(gene.deffont) if (font.name) font2.name = font.name if (font.normal) { if (font2.weight) // !! don't use delete !! font2.weight = null if (font2.style) font2.style = null } else { if (font.weight) font2.weight = font.weight if (font.style) font2.style = font.style } if (font.src) font2.src = font.src if (font.size) font2.size = font.size st = st_font(font2) if (font.class) { font2.class = font.class st += ' '+ font.class } fid = abc2svg.font_st[st] if (fid != undefined) return abc2svg.font_tb[fid] abc2svg.font_st[st] = abc2svg.font_tb.length // will be the font id font2.fid = font2.used = undefined font = font2 } use_font(font) return font } // abc2svg - front.js - ABC parsing front-end // // Copyright (C) 2014-2024 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 . var sav = {}, // save global (between tunes) definitions mac = {}, // macros (m:) maci = {}, // first letters of macros modone = {} // hooks done by module // translation table from the ABC draft version 2.2 var abc_utf = { "=D": "Đ", "=H": "Ħ", "=T": "Ŧ", "=d": "đ", "=h": "ħ", "=t": "ŧ", "/O": "Ø", "/o": "ø", // "/D": "Đ", // "/d": "đ", "/L": "Ł", "/l": "ł", "vL": "Ľ", "vl": "ľ", "vd": "ď", ".i": "ı", "AA": "Å", "aa": "å", "AE": "Æ", "ae": "æ", "DH": "Ð", "dh": "ð", // "ng": "ŋ", "OE": "Œ", "oe": "œ", "ss": "ß", "TH": "Þ", "th": "þ" } // accidentals as octal values (abcm2ps compatibility) var oct_acc = { "1": "\u266f", "2": "\u266d", "3": "\u266e", "4": "𝄪", "5": "𝄫" } // convert the escape sequences to utf-8 function cnv_escape(src, flag) { var c, c2, dst = "", i, j = 0 while (1) { i = src.indexOf('\\', j) if (i < 0) break dst += src.slice(j, i); c = src[++i] if (!c) return dst + '\\' switch (c) { case '0': case '2': if (src[i + 1] != '0') break c2 = oct_acc[src[i + 2]] if (c2) { dst += c2; j = i + 3 continue } break case 'u': j = Number("0x" + src.slice(i + 1, i + 5)); if (isNaN(j) || j < 0x20) { dst += src[++i] + "\u0306" // breve accent j = i + 1 continue } c = String.fromCharCode(j) if (c == '\\') { i += 4 break } dst += c j = i + 5 continue case 't': // TAB dst += '\t'; j = i + 1 continue case 'n': // new line (voice name) dst += '\n'; j = i + 1 continue default: c2 = abc_utf[src.slice(i, i + 2)] if (c2) { dst += c2; j = i + 2 continue } // try unicode combine characters c2 = src[i + 1] if (!c2) break // !! the next test is true if c2 is undefined !! if (!/[A-Za-z]/.test(c2)) break switch (c) { case '`': dst += c2 + "\u0300" // grave j = i + 2 continue case "'": dst += c2 + "\u0301" // acute j = i + 2 continue case '^': dst += c2 + "\u0302" // circumflex j = i + 2 continue case '~': dst += c2 + "\u0303" // tilde j = i + 2 continue case '=': dst += c2 + "\u0304" // macron j = i + 2 continue case '_': dst += c2 + "\u0305" // overline j = i + 2 continue case '.': dst += c2 + "\u0307" // dot j = i + 2 continue case '"': dst += c2 + "\u0308" // dieresis j = i + 2 continue case 'o': dst += c2 + "\u030a" // ring j = i + 2 continue case 'H': dst += c2 + "\u030b" // hungarumlaut j = i + 2 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 = 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() function end_tune() { 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(); } // 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 "select": if (parse.state != 0) { syntax(1, errs.not_in_tune, "%%select") continue } select = uncomment(text.slice(7)) 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); info.X = text; parse.state = 1 // tune header if (user.page_format && blkdiv < 1) // (if no newpage) blkdiv = 1 // the tune starts by the next SVG if (parse.tune_opts) tune_filter() continue case 'T': switch (parse.state) { case 0: continue case 1: case 2: 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) { syntax(1, errs.ignored, line0) continue } a = text.match(/(.*?)[= ]+(.*)/) if (!a || !a[2]) { syntax(1, errs.bad_val, "m:") continue } mac[a[1]] = a[2]; maci[a[1][0]] = true // first letter break // info fields in tune body only case 's': if (parse.state != 3 || cfmt.writefields.indexOf(line0) < 0) break get_sym(text, txt_add == ' ') break case 'w': if (parse.state != 3 || cfmt.writefields.indexOf(line0) < 0) break get_lyrics(text, txt_add == ' ') break case '|': // "|:" starts a music line if (parse.state < 2) continue parse.line.buffer = text parse_music_line() continue default: if ("ABCDFGHNOSZ".indexOf(line0) >= 0) { if (parse.state >= 2) { syntax(1, errs.ignored, line0) continue } // if (cfmt.writefields.indexOf(c) < 0) // break if (!info[line0]) info[line0] = text else info[line0] += txt_add + text break } // info field which may be embedded do_info(line0, text) continue } txt_add = '\n'; last_info = line0 } if (include) return if (parse.state == 1) { syntax(1, "End of file in tune header") get_key("C") } if (parse.state >= 2) end_tune(); parse.state = 0 } Abc.prototype.tosvg = tosvg // abc2svg - music.js - music generation // // Copyright (C) 2014-2024 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 . var gene, staff_tb, nstaff, // current number of staves tsnext, // next line when cut realwidth, // real staff width while generating insert_meter, // insert the time signature spf_last, // spacing for last short line smallest_duration /* -- decide whether to shift heads to other side of stem on chords -- */ /* this routine is called only once per tune */ // distance for no overlap - index: [prev acc][cur acc] //var dt_tb = [ // [5, 5, 5, 5], /* dble sharp */ // [5, 6, 6, 6], /* sharp */ // [5, 6, 5, 6], /* natural */ // [5, 5, 5, 5] /* flat / dble flat */ //] // accidental x offset - index = note head type var dx_tb = new Float32Array([ 10, // FULL 10, // EMPTY 11, // OVAL 13, // OVALBARS 15 // SQUARE ]) // head width - index = note head type var hw_tb = new Float32Array([ 4.7, // FULL 5, // EMPTY 6, // OVAL 7.2, // OVALBARS 7.5 // SQUARE ]) /* head width for voice overlap - index = note head type */ var w_note = new Float32Array([ 3.5, // FULL 3.7, // EMPTY 5, // OVAL 6, // OVALBARS 7 // SQUARE ]) // get head type, dots, flags of note/rest for a duration function identify_note(s, dur_o) { var head, flags, dots = 0, dur = dur_o if (dur % 12 != 0) error(1, s, "Invalid note duration $1", dur); dur /= 12 /* see C.BLEN for values */ if (!dur) error(1, s, "Note too short") for (flags = 5; dur; dur >>= 1, flags--) { if (dur & 1) break } dur >>= 1 if ((dur + 1) & dur) { if (s.type != C.REST || dur_o != s.p_v.wmeasure) error(0, s, "Non standard note duration $1", dur_o) } while (dur >> dots > 0) dots++ flags -= dots if (flags >= 0) { head = C.FULL } else switch (flags) { default: error(1, s, "Note too long") flags = -4 /* fall thru */ case -4: head = C.SQUARE break case -3: head = s.fmt.squarebreve ? C.SQUARE : C.OVALBARS break case -2: head = C.OVAL break case -1: head = C.EMPTY break } return [head, dots, flags] } function set_head_shift(s) { var i, i1, i2, d, ps, dx, dx_head = dx_tb[s.head], dir = s.stem, n = s.nhd if (!n) return // single note /* set the head shifts */ dx = dx_head * .74 if (s.grace) dx *= .6 if (dir >= 0) { i1 = 1; i2 = n + 1; ps = s.notes[0].pit } else { dx = -dx; i1 = n - 1; i2 = -1; ps = s.notes[n].pit } var shift = false, dx_max = 0 for (i = i1; i != i2; i += dir) { d = s.notes[i].pit - ps; ps = s.notes[i].pit if (!d) { if (shift) { /* unison on shifted note */ var new_dx = s.notes[i].shhd = s.notes[i - dir].shhd + dx if (dx_max < new_dx) dx_max = new_dx continue } if (i + dir != i2 /* second after unison */ //fixme: should handle many unisons after second && ps + dir == s.notes[i + dir].pit) { s.notes[i].shhd = -dx if (dx_max < -dx) dx_max = -dx continue } } if (d < 0) d = -d if (d > 3 || (d >= 2 && s.head != C.SQUARE)) { shift = false } else { shift = !shift if (shift) { s.notes[i].shhd = dx if (dx_max < dx) dx_max = dx } } } s.xmx = dx_max /* shift the dots */ } // set the accidental shifts for a set of chords function acc_shift(notes, dx_head) { var i, i1, i2, dx, dx1, dx2, ps, p1, acc, n = notes.length // set the shifts from the head shifts for (i = n - 1; --i >= 0; ) { // (no shift on top) dx = notes[i].shhd if (!dx || dx > 0) continue dx = dx_head - dx; ps = notes[i].pit for (i1 = n; --i1 >= 0; ) { if (!notes[i1].acc) continue p1 = notes[i1].pit if (p1 < ps - 3) break if (p1 > ps + 3) continue if (notes[i1].shac < dx) notes[i1].shac = dx } } // set the shifts of the highest and lowest notes for (i1 = n; --i1 >= 0; ) { if (notes[i1].acc) { p1 = notes[i1].pit // top note with accidental dx1 = notes[i1].shac if (!dx1) { dx1 = notes[i1].shhd if (dx1 < 0) dx1 = dx_head - dx1 else dx1 = dx_head } break } } if (i1 < 0) // no accidental return for (i2 = 0; i2 < i1; i2++) { if (notes[i2].acc) { ps = notes[i2].pit // bottom note with accidental dx2 = notes[i2].shac if (!dx2) { dx2 = notes[i2].shhd if (dx2 < 0) dx2 = dx_head - dx2 else dx2 = dx_head } break } } if (i1 == i2) { // only one accidental notes[i1].shac = dx1 return } if (p1 > ps + 4) { // if interval greater than a sixth if (dx1 > dx2) dx2 = dx1 // align the accidentals notes[i1].shac = notes[i2].shac = dx2 } else { notes[i1].shac = dx1 if (notes[i1].pit != notes[i2].pit) dx1 += 7 notes[i2].shac = dx2 = dx1 } dx2 += 7 // shift the remaining accidentals for (i = i1; --i > i2; ) { // from top to bottom acc = notes[i].acc if (!acc) continue dx = notes[i].shac if (dx < dx2) dx = dx2 ps = notes[i].pit for (i1 = n; --i1 > i; ) { if (!notes[i1].acc) continue p1 = notes[i1].pit if (p1 >= ps + 4) { // pitch far enough if (p1 > ps + 4 // if more than a fifth || acc < 0 // if flat/dble flat || notes[i1].acc < 0) continue } if (dx > notes[i1].shac - 6) { dx1 = notes[i1].shac + 7 if (dx1 > dx) dx = dx1 } } notes[i].shac = dx } } /* set the horizontal shift of accidentals */ /* this routine is called only once per tune */ function set_acc_shft() { var s, s2, st, i, acc, st, t, dx_head, notes // search the notes with accidentals at the same time s = tsfirst while (s) { if (s.type != C.NOTE || s.invis) { s = s.ts_next continue } st = s.st; t = s.time; acc = false for (s2 = s; s2; s2 = s2.ts_next) { if (s2.time != t || s2.type != C.NOTE || s2.st != st) break if (acc) continue for (i = 0; i <= s2.nhd; i++) { if (s2.notes[i].acc) { acc = true break } } } if (!acc) { s = s2 continue } dx_head = dx_tb[s.head] // build a pseudo chord and shift the accidentals notes = [] for ( ; s != s2; s = s.ts_next) { if (!s.invis) Array.prototype.push.apply(notes, s.notes) } notes.sort(abc2svg.pitcmp) acc_shift(notes, dx_head) } } // link a symbol before an other one function lkvsym(s, next) { // voice linkage s.next = next; s.prev = next.prev if (s.prev) s.prev.next = s else s.p_v.sym = s; next.prev = s } function lktsym(s, next) { // time linkage var old_wl s.ts_next = next if (next) { s.ts_prev = next.ts_prev if (s.ts_prev) s.ts_prev.ts_next = s; next.ts_prev = s } else { //fixme error(2, s, "Bad linkage") s.ts_prev = null } s.seqst = !s.ts_prev || s.time != s.ts_prev.time || (w_tb[s.ts_prev.type] != w_tb[s.type] && !!w_tb[s.ts_prev.type]) if (!next || next.seqst) return next.seqst = next.time != s.time || (w_tb[s.type] != w_tb[next.type] && !!w_tb[s.type]) if (next.seqst) { old_wl = next.wl self.set_width(next) if (next.a_ly) ly_set(next) if (!next.shrink) { next.shrink = next.wl if (next.prev) next.shrink += next.prev.wr } else { next.shrink += next.wl - old_wl } next.space = 0 } } /* -- unlink a symbol -- */ function unlksym(s) { if (s.next) s.next.prev = s.prev if (s.prev) s.prev.next = s.next else s.p_v.sym = s.next if (s.ts_next) { if (s.seqst) { if (s.ts_next.seqst) { s.ts_next.shrink += s.shrink; s.ts_next.space += s.space } else { s.ts_next.seqst = true; s.ts_next.shrink = s.shrink; s.ts_next.space = s.space } } else { if (s.ts_next.seqst && s.ts_prev && s.ts_prev.seqst && !w_tb[s.ts_prev.type]) { s.ts_next.seqst = false s.shrink = s.ts_next.shrink s.space = s.ts_next.space } } s.ts_next.ts_prev = s.ts_prev } if (s.ts_prev) s.ts_prev.ts_next = s.ts_next if (tsfirst == s) tsfirst = s.ts_next if (tsnext == s) tsnext = s.ts_next } /* -- insert a clef change (treble or bass) before a symbol -- */ function insert_clef(s, clef_type, clef_line) { var p_voice = s.p_v, new_s, st = s.st /* don't insert the clef between two bars */ if (s.type == C.BAR && s.prev && s.prev.type == C.BAR && s.prev.bar_type[0] != ':') s = s.prev; /* create the symbol */ p_voice.last_sym = s.prev if (!p_voice.last_sym) p_voice.sym = null; p_voice.time = s.time; new_s = sym_add(p_voice, C.CLEF); new_s.next = s; s.prev = new_s; new_s.clef_type = clef_type; new_s.clef_line = clef_line; new_s.st = st; new_s.clef_small = true delete new_s.second; new_s.notes = [] new_s.notes[0] = { pit: s.notes[0].pit } new_s.nhd = 0; /* link in time */ while (!s.seqst) s = s.ts_prev; lktsym(new_s, s) if (s.soln) { // move the start of line new_s.soln = true delete s.soln } return new_s } /* -- set the staff of the floating voices -- */ /* this function is called only once per tune */ function set_float() { var p_voice, st, staff_chg, v, s, s1, up, down for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v] // if (!p_voice.floating) // continue staff_chg = false; st = p_voice.st for (s = p_voice.sym; s; s = s.next) { if (!s.floating) { while (s && !s.floating) s = s.next if (!s) break staff_chg = false } if (!s.dur) { if (staff_chg) s.st++ continue } if (s.notes[0].pit >= 19) { /* F */ staff_chg = false continue } if (s.notes[s.nhd].pit <= 12) { /* F, */ staff_chg = true s.st++ continue } up = 127 for (s1 = s.ts_prev; s1; s1 = s1.ts_prev) { if (s1.st != st || s1.v == s.v) break if (s1.type == C.NOTE) if (s1.notes[0].pit < up) up = s1.notes[0].pit } if (up == 127) { if (staff_chg) s.st++ continue } if (s.notes[s.nhd].pit > up - 3) { staff_chg = false continue } down = -127 for (s1 = s.ts_next; s1; s1 = s1.ts_next) { if (s1.st != st + 1 || s1.v == s.v) break if (s1.type == C.NOTE) if (s1.notes[s1.nhd].pit > down) down = s1.notes[s1.nhd].pit } if (down == -127) { if (staff_chg) s.st++ continue } if (s.notes[0].pit < down + 3) { staff_chg = true s.st++ continue } up -= s.notes[s.nhd].pit down = s.notes[0].pit - down if (!staff_chg) { if (up < down + 3) continue staff_chg = true } else { if (up < down - 3) { staff_chg = false continue } } s.st++ } } } /* -- set the x offset of the grace notes -- */ function set_graceoffs(s) { var next, m, dx, x, gspleft = s.fmt.gracespace[0], gspinside = s.fmt.gracespace[1], gspright = s.fmt.gracespace[2], g = s.extra; if (s.prev && s.prev.type == C.BAR) gspleft -= 3; x = gspleft; g.beam_st = true for ( ; ; g = g.next) { set_head_shift(g) acc_shift(g.notes, 6.5) dx = 0 for (m = g.nhd; m >= 0; m--) { if (g.notes[m].shac - 2 > dx) dx = g.notes[m].shac - 2 } x += dx; g.x = x if (g.nflags <= 0) g.beam_st = g.beam_end = true next = g.next if (!next) { g.beam_end = true break } if (next.nflags <= 0) g.beam_end = true if (g.beam_end) { next.beam_st = true; x += gspinside / 4 } if (g.nflags <= 0) x += gspinside / 4 if (g.y > next.y + 8) x -= 1.5 x += gspinside } next = s.next if (next && next.type == C.NOTE) { /* if before a note */ if (g.y >= 3 * (next.notes[next.nhd].pit - 18)) gspright -= 1 // above, a bit closer else if (g.beam_st && g.y < 3 * (next.notes[next.nhd].pit - 18) - 4) gspright += 2 // below with flag, a bit further } x += gspright; /* return the whole width */ return x } // Compute the smallest spacing between symbols according to chord symbols // so that they stay at the same offset // and, also, adjust the spacing due to the lyric words. // Constraints: // - assume the chord symbols are only in the first staff // - treat only the first chord symbol of each symbol // - the chord symbols under the staff are ignored function set_w_chs(s) { var i, ch, w0, s0, dw, x = 0, n = 0 set_font("vocal") for ( ; s; s = s.ts_next) { if (s.shrink) { x += s.shrink; n++ } if (s.a_ly) // if some lyric ly_set(s) if (!s.a_gch) continue for (i = 0; i < s.a_gch.length; i++) { ch = s.a_gch[i] if (ch.type != 'g' || ch.y < 0) // upper chord symbol only continue if (w0) { // width of the previous chord symbol if (w0 > x + ch.x) { if (s.prev // (if not at start of a secondary voice) && s.prev.seqst && s.prev.type == C.BAR) // don't move away n-- // the symbol from a bar dw = (w0 - x - ch.x) / n while (1) { s0 = s0.ts_next if (s0.shrink) s0.shrink += dw if (s0 == s || s0.type == C.BAR) break } } } s0 = s; w0 = ch.text.wh[0]; n = 0; // x = ch.font.box ? -2 : 0 x = 0 break } } } // compute the width needed by the left and right annotations function gchord_width(s, wlnote, wlw) { var gch, w, ix, arspc = 0 for (ix = 0; ix < s.a_gch.length; ix++) { gch = s.a_gch[ix] switch (gch.type) { case '<': /* left */ w = gch.text.wh[0] + wlnote if (w > wlw) wlw = w break case '>': /* right */ w = gch.text.wh[0] + s.wr if (w > arspc) arspc = w break } } if (s.wr < arspc) s.wr = arspc return wlw } /* -- set the width of a symbol -- */ /* This routine sets the minimal left and right widths wl,wr * so that successive symbols are still separated when * no extra glue is put between them */ // (possible hook) Abc.prototype.set_width = function(s) { var s2, i, m, xx, w, wlnote, wlw, acc, nt, bar_type, meter, last_acc, n1, n2, esp, tmp if (s.play) { // if play symbol s.wl = s.wr = 0 return } switch (s.type) { case C.NOTE: case C.REST: /* set the note widths */ s.wr = wlnote = s.invis ? 0 : hw_tb[s.head] /* room for shifted heads and accidental signs */ if (s.xmx > 0) s.wr += s.xmx + 4; for (s2 = s.prev; s2; s2 = s2.prev) { if (w_tb[s2.type]) break } if (s2) { switch (s2.type) { case C.BAR: case C.CLEF: case C.KEY: case C.METER: wlnote += 3 break case C.STBRK: wlnote += 8 break } } for (m = 0; m <= s.nhd; m++) { nt = s.notes[m] xx = nt.shhd if (xx < 0) { if (wlnote < -xx + 5) wlnote = -xx + 5 } acc = nt.acc if (acc) { tmp = nt.shac + (typeof acc == "object" ? 5.5 : 3.5) if (wlnote < tmp) wlnote = tmp } if (nt.a_dd) // if decoration in chord wlnote += deco_wch(nt) } if (s2) { switch (s2.type) { case C.BAR: case C.CLEF: case C.KEY: case C.METER: wlnote -= 3 break } } /* room for the decorations */ if (s.a_dd) wlnote = deco_width(s, wlnote) /* space for flag if stem goes up on standalone note */ if (s.beam_st && s.beam_end && s.stem > 0 && s.nflags > 0) { if (s.wr < s.xmx + 9) s.wr = s.xmx + 9 } /* leave room for dots and set their offset */ if (s.dots) { if (s.wl == undefined) // don't recompute if new music line switch (s.head) { case C.SQUARE: case C.OVALBARS: s.xmx += 3 break case C.OVAL: s.xmx += 1 break } if (s.wr < s.xmx + 8) s.wr = s.xmx + 8 if (s.dots >= 2) s.wr += 3.5 * (s.dots - 1) } /* if a tremolo on 2 notes, have space for the small beam(s) */ if (s.trem2 && s.beam_end && wlnote < 20) wlnote = 20 wlw = wlnote if (s2) { switch (s2.type) { case C.NOTE: // change the spacing when stems in reverse directions if (s.stem * s2.stem < 0) { if (s.stem < 0) wlw += 5 else wlw -= 3 } /* make sure helper lines don't overlap */ if ((s.y > 27 && s2.y > 27) || (s.y < -3 && s2.y < -3)) { if (wlw < 6) wlw = 6 } /* have ties wide enough */ if (s2.tie) { if (wlw < 14) wlw = 14 } break case C.CLEF: /* extra space at start of line */ if (s2.second || s2.clef_small) break // fall thru case C.KEY: if (s.a_gch) wlw += 4 // have some room for the chord symbols // fall thru case C.METER: wlw += 3 break } } /* leave room for guitar chord */ if (s.a_gch) wlw = gchord_width(s, wlnote, wlw) // ignore the lyrics for now /* if preceeded by a grace note sequence, adjust */ if (s.prev && s.prev.type == C.GRACE) { s.prev.wl += wlnote - 4.5 s.wl = s.prev.wl } else { s.wl = wlw } return case C.SPACE: xx = s.width / 2; s.wr = xx if (s.a_gch) xx = gchord_width(s, xx, xx) if (s.a_dd) xx = deco_width(s, xx) s.wl = xx return case C.BAR: bar_type = s.bar_type switch (bar_type) { case "|": w = 5 // 3 + 2 break case "[": // repeat number on secondary staff w = 0 break default: w = 2 + 2.8 * bar_type.length for (i = 0; i < bar_type.length; i++) { switch (bar_type[i]) { case "[": case "]": w += 1 // fall thru case ":": w += 2 break } } break } s.wl = w if (s.next && s.next.type != C.METER) s.wr = 7 else s.wr = 5 // s.notes[0].shhd = (w - 5) * -.5 // special case for (mainly) "|| !invisible! |:" if (s.invis) //fixme // && s.prev && s.prev.bar_type) s.wl = s.wr = 2 /* if preceeded by a grace note sequence, adjust */ s2 = s.prev if (s2 && s2.type == C.GRACE) s.wl -= 6 for ( ; s2; s2 = s2.prev) { if (w_tb[s2.type]) { if (s2.type == C.STBRK) s.wl -= 12 break } } if (s.a_dd) s.wl = deco_width(s, s.wl) /* have room for the repeat numbers / chord indication */ if (s.text && s.text.length < 4 && s.next && s.next.a_gch) { set_font("repeat"); s.wr += strwh(s.text)[0] + 2 if (cfmt.measurenb > 0 & s.bar_num && s.bar_num % cfmt.measurenb) s.wr += 4 } return case C.CLEF: // (there may be invisible clefs in empty staves) if (s.invis) { s.wl = s.wr = 1 // (!! not 0 !!) return } if (s.prev && s.prev.type == C.STBRK) { s.wl = 6 s.wr = 13 delete s.clef_small return } s.wl = s.clef_small ? 11 : 12 s.wr = s.clef_small ? 8 : 13 return case C.KEY: if (s.invis) { // if no accidental s.wl = s.wr = 0 // no width return } s.wl = 0 esp = 3 n1 = s.k_sf /* new key sig */ if (s.k_old_sf && (s.fmt.cancelkey || n1 == 0)) n2 = s.k_old_sf /* old key */ else n2 = 0 if (n1 * n2 >= 0) { /* if no natural */ if (n1 < 0) n1 = -n1 if (n2 < 0) n2 = -n2 if (n2 > n1) n1 = n2 } else { n1 -= n2 if (n1 < 0) n1 = -n1; esp += 3 /* see extra space in draw_keysig() */ } if (s.k_bagpipe == 'p') // K:Hp - add the g natural n1++ if (s.k_a_acc) { n2 = s.k_a_acc.length if (s.exp) n1 = n2 // no key signature else n1 += n2 if (n2) last_acc = s.k_a_acc[0].acc for (i = 1; i < n2; i++) { acc = s.k_a_acc[i] if (acc.pit > s.k_a_acc[i - 1].pit + 6 || acc.pit < s.k_a_acc[i - 1].pit - 6) n1-- // no clash else if (acc.acc != last_acc) esp += 3; last_acc = acc.acc } } if (!n1) break // no width s.wr = 5.5 * n1 + esp if (s.prev && !s.prev.bar_type) s.wl += 2 return case C.METER: s.x_meter = [] if (!s.a_meter.length) break // no width wlw = 0 for (i = 0; i < s.a_meter.length; i++) { meter = s.a_meter[i] switch (meter.top[0]) { case 'C': case 'c': case 'o': s.x_meter[i] = wlw + 6; wlw += 12 break case '.': case '|': s.x_meter[i] = s.x_meter[i - 1] break default: w = 0 if (!meter.bot || meter.top.length > meter.bot.length) meter = meter.top else meter = meter.bot; for (m = 0; m < meter.length; m++) { switch (meter[m]) { case '(': wlw += 4 // fall thru case ')': case '1': w += 4 break default: w += 12 break } } s.x_meter[i] = wlw + w / 2 wlw += w } } s.wl = 1 s.wr = wlw + 7 return case C.MREST: s.wl = 6; s.wr = 66 return case C.GRACE: if (s.invis) break s.wl = set_graceoffs(s); s.wr = 0 if (s.a_ly) ly_set(s) return case C.STBRK: s.wl = s.xmx s.wr = 8 return case C.CUSTOS: s.wl = s.wr = 4 return case C.TEMPO: // no width, but build the tempo string tempo_build(s) break case C.BLOCK: // no width case C.REMARK: case C.STAVES: break default: error(2, s, "set_width - Cannot set width for symbol $1", s.type) break } s.wl = s.wr = 0 } // convert delta time to natural spacing function time2space(s, len) { var i, l, space if (smallest_duration >= C.BLEN / 2) { if (smallest_duration >= C.BLEN) len /= 4 else len /= 2 } else if (!s.next && len >= C.BLEN) { len /= 2 } if (len >= C.BLEN / 4) { if (len < C.BLEN / 2) i = 5 else if (len < C.BLEN) i = 6 else if (len < C.BLEN * 2) i = 7 else if (len < C.BLEN * 4) i = 8 else i = 9 } else { if (len >= C.BLEN / 8) i = 4 else if (len >= C.BLEN / 16) i = 3 else if (len >= C.BLEN / 32) i = 2 else if (len >= C.BLEN / 64) i = 1 else i = 0 } l = len - ((C.BLEN / 16 / 8) << i) space = cfmt.spatab[i] if (l) { if (l < 0) { space = cfmt.spatab[0] * len / (C.BLEN / 16 / 8) } else { if (i >= 9) i = 8 space += (cfmt.spatab[i + 1] - cfmt.spatab[i]) * l / ((C.BLEN / 16 / 8) << i) } } return space } // set the natural space function set_space(s, ptime) { var space, len, s2, stemdir len = s.time - ptime // time skip if (!len) { switch (s.type) { case C.MREST: return s.wl ///*fixme:do same thing at start of line*/ // case C.NOTE: // case C.REST: // if (s.ts_prev.type == C.BAR) { // if (s.nflags < -2) // return cfmt.spatab[0] // return cfmt.spatab[2] // } // break } return 0 } if (s.ts_prev.type == C.MREST) // return s.ts_prev.wr + 16 // + 3 // (bar wl=5 wr=8) return 71 // 66 (mrest.wl) + 5 (bar.wl) space = time2space(s, len) while (!s.dur) { switch (s.type) { case C.BAR: // (hack to have quite the same note widths between measures) if (!s.next) space *= .9 return space * .9 - 3 case C.CLEF: return space - s.wl - s.wr case C.BLOCK: // no space case C.REMARK: case C.STAVES: case C.TEMPO: s = s.ts_next if (!s) return space continue } break } /* reduce spacing within a beam */ if (s.dur && len <= C.BLEN / 4) { s2 = s while (s2) { if (!s2.beam_st) { space *= .9 // ex fnnp break } s2 = s2.ts_next if (!s2 || s2.seqst) break } } /* decrease spacing when stem down followed by stem up */ /*fixme:to be done later, after x computed in sym_glue*/ if (s.type == C.NOTE && s.nflags >= -1 && s.stem > 0) { stemdir = true for (s2 = s.ts_prev; s2 && s2.time == ptime; s2 = s2.ts_prev) { if (s2.type == C.NOTE && (s2.nflags < -1 || s2.stem > 0)) { stemdir = false break } } if (stemdir) { for (s2 = s.ts_next; s2 && s2.time == s.time; s2 = s2.ts_next) { if (s2.type == C.NOTE && (s2.nflags < -1 || s2.stem < 0)) { stemdir = false break } } if (stemdir) space *= .9 } } return space } // set the spacing inside tuplets or L: factor function set_sp_tup(s, s_et) { var tim = s.time, ttim = s_et.time - tim, sp = time2space(s, ttim), // whole time spacing s2 = s, wsp = 0 // compute the whole spacing while (1) { s2 = s2.ts_next if (s2.seqst) { wsp += s2.space if (s2.bar_type) wsp += 10 // (fixme: not exact) } if (s2 == s_et) break } sp = (sp + wsp) / 2 / ttim // mean spacing per time unit while (1) { s = s.ts_next if (s.seqst) { s.space = sp * (s.time - tim) tim = s.time } if (s == s_et) break } } // return an empty bar function _bar(s) { return { type: C.BAR, bar_type: "|", fname: s.fname, istart: s.istart, iend: s.iend, v: s.v, p_v: s.p_v, st: s.st, dur: 0, time: s.time + (s.dur || 0), nhd: 0, notes: [{ pit: s.notes ? s.notes[0].pit : 22 }], seqst: true, invis: true, prev: s, fmt: s.fmt } } // _bar() // create an invisible bar for end of music lines function add_end_bar(s) { var b = _bar(s), sn = s.ts_next // start of next line b.wl = 0 b.wr = 0 b.ts_prev = s b.next = s.next b.ts_next = s.ts_next b.shrink = s.type == C.STBRK ? 0 : (s.wr + 3) if (s.next) // (must not be the end of the voice) s.next.prev = b // if (s.ts_next) s.ts_next.ts_prev = b s.next = s.ts_next = b b.space = sn.space * .9 - 3 return b } /* -- set the width and space of all symbols -- */ // this function is called once for the whole tune // and once more for each new music line function set_allsymwidth(first) { var val, st, s_chs, stup, itup, s = tsfirst, s2 = s, xa = 0, xl = [], wr = [], maxx = xa, tim = s.time /* loop on all symbols */ while (1) { itup = 0 do { if ((s.a_gch || s.a_ly) && !s_chs) s_chs = s; self.set_width(s); st = s.st if (xl[st] == undefined) xl[st] = 0 if (wr[st] == undefined) wr[st] = 0; if (s.prev && s.prev.st != st) { xl[st] = xl[s.prev.st] wr[st] = wr[s.prev.st] } val = xl[st] + wr[st] + s.wl if (val > maxx) maxx = val if (s.dur && s.dur != s.notes[0].dur // if in tuplet && first) // (first time only) itup = 1 s = s.ts_next } while (s && !s.seqst); // set the spaces of the time sequence s2.shrink = maxx - xa s2.space = s2.ts_prev ? set_space(s2, tim) : 0 // adjust the spacing when after a spacer (y) if (s2.space == 0 && s2.ts_prev && s2.ts_prev.type == C.SPACE && s2.ts_prev.seqst) s2.space = s2.ts_prev.space /= 2 if (itup) { if (!stup) stup = s2 } else if (stup && stup.v == s2.v) { set_sp_tup(stup, s2) stup = null } if (!s2.shrink) { if (s2.type == C.CLEF && !s2.ts_prev.bar_type) { delete s2.seqst; /* no space */ s2.time = tim } else { s2.shrink = 10 // cannot be null } } tim = s2.time if (!s) break // update the min left space per staff s = s2 do { wr[s.st] = 0 s = s.ts_next } while (!s.seqst) xa = maxx do { st = s2.st; xl[st] = xa if (s2.wr > wr[st]) wr[st] = s2.wr s2 = s2.ts_next } while (!s2.seqst) } if (stup) set_sp_tup(stup, s2) // let the chord symbols at the same offset // and adjust the spacing due to the lyrics if (first && s_chs) set_w_chs(s_chs) } // insert a rest, this one replacing a sequence or a measure function to_rest(so) { var s = clone(so) s.prev.next = so.ts_prev = so.prev = s.ts_prev.ts_next = s s.next = s.ts_next = so so.seqst = false so.invis = so.play = true s.type = C.REST // just keep nl and seqst delete s.in_tuplet delete s.tp delete s.a_dd delete s.a_gch delete s.sls //fixme: what if chord / slur in notes / ... ? /*fixme: should set many parameters for set_width*/ // set_width(s) return s } /* -- set the repeat sequences / measures -- */ function set_repeat(s) { // first note var s2, s3, i, j, dur, n = s.repeat_n, k = s.repeat_k, st = s.st, v = s.v s.repeat_n = 0 // treated /* treat the sequence repeat */ if (n < 0) { /* number of notes / measures */ n = -n; i = n /* number of notes to repeat */ for (s3 = s.prev; s3; s3 = s3.prev) { if (!s3.dur) { if (s3.type == C.BAR) { error(1, s3, "Bar in repeat sequence") return } continue } if (--i <= 0) break } if (!s3) { error(1, s, errs.not_enough_n) return } dur = s.time - s3.time; i = k * n /* whole number of notes/rests to repeat */ for (s2 = s; s2; s2 = s2.next) { if (!s2.dur) { if (s2.type == C.BAR) { error(1, s2, "Bar in repeat sequence") return } continue } if (--i <= 0) break } if (!s2 || !s2.next) { /* should have some symbol */ error(1, s, errs.not_enough_n) return } for (s2 = s.prev; s2 != s3; s2 = s2.prev) { if (s2.type == C.NOTE) { s2.beam_end = true break } } for (j = k; --j >= 0; ) { i = n /* number of notes/rests */ if (s.dur) i--; s2 = s.ts_next while (i > 0) { if (s2.st == st) { s2.invis = s2.play = true if (s2.seqst && s2.ts_next.seqst) s2.seqst = false if (s2.v == v && s2.dur) i-- } s2 = s2.ts_next } s = to_rest(s) s.dur = s.notes[0].dur = dur; s.rep_nb = -1; // single repeat s.beam_st = true; self.set_width(s) s.head = C.SQUARE; for (s = s2; s; s = s.ts_next) { if (s.st == st && s.v == v && s.dur) break } } return } /* check the measure repeat */ i = n /* number of measures to repeat */ for (s2 = s.prev.prev ; s2; s2 = s2.prev) { if (s2.type == C.BAR || s2.time == tsfirst.time) { if (--i <= 0) break } } if (!s2) { error(1, s, errs.not_enough_m) return } dur = s.time - s2.time /* repeat duration */ if (n == 1) i = k /* repeat number */ else i = n /* check only 2 measures */ for (s2 = s; s2; s2 = s2.next) { if (s2.type == C.BAR) { if (--i <= 0) break } } if (!s2) { error(1, s, errs.not_enough_m) return } /* if many 'repeat 2 measures' * insert a new %%repeat after the next bar */ i = k /* repeat number */ if (n == 2 && i > 1) { s2 = s2.next if (!s2) { error(1, s, errs.not_enough_m) return } s2.repeat_n = n; s2.repeat_k = --i } /* replace */ dur /= n if (n == 2) { /* repeat 2 measures (once) */ s3 = s for (s2 = s.ts_next; ; s2 = s2.ts_next) { if (s2.st != st) continue if (s2.type == C.BAR) { if (s2.v == v) break continue } s2.invis = s2.play = true if (s2.seqst && s2.ts_next.seqst) s2.seqst = false } s3 = to_rest(s3) s3.dur = s3.notes[0].dur = dur; s3.invis = true s2.bar_mrep = 2 s3 = s2.next; for (s2 = s3.ts_next; ; s2 = s2.ts_next) { if (s2.st != st) continue if (s2.type == C.BAR) { if (s2.v == v) break continue } if (!s2.dur) continue s2.invis = s2.play = true if (s2.seqst && s2.ts_next.seqst) s2.seqst = false } s3 = to_rest(s3) s3.dur = s3.notes[0].dur = dur; s3.invis = true; self.set_width(s3) return } /* repeat 1 measure */ s3 = s for (j = k; --j >= 0; ) { for (s2 = s3.ts_next; ; s2 = s2.ts_next) { if (s2.st != st) continue if (s2.type == C.BAR) { if (s2.v == v) break continue } if (!s2.dur) continue s2.invis = s2.play = true if (s2.seqst && s2.ts_next.seqst) s2.seqst = false } s3 = to_rest(s3) s3.dur = s3.notes[0].dur = dur; s3.beam_st = true if (k == 1) { s3.rep_nb = 1 break } s3.rep_nb = k - j + 1; // number to print above the repeat rest s3 = s2.next } } /* add a custos before the symbol of the next line */ function custos_add(s) { var p_voice, new_s, i, s2 = s while (1) { if (s2.type == C.NOTE) break s2 = s2.next if (!s2) return } p_voice = s.p_v; p_voice.last_sym = s.prev; // 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 (!s.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.invis) // 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 // take care of big key signatures at end of line if (s.fmt.keywarn && next && next.type == C.KEY && !last.dur) { ws[0] += next.wr ws[1] += next.wr } // check if the symbols can enter in one line if (ws[0] + indent < lwidth) { if (next) next = set_nl(next) return next || last } /* loop on cutting the tune into music lines */ wwidth = ws[0] + indent while (1) { nlines = Math.ceil(wwidth / lwidth) if (nlines <= 1) { if (next) next = set_nl(next) return next || last } s2 = first = s; xmin = s.x - s.shrink - indent; xmax = xmin + lwidth; xmid = xmin + wwidth / nlines; xmin += wwidth / nlines * s.fmt.breaklimit; for (s = s.ts_next; s != next ; s = s.ts_next) { if (!s.x) continue if (s.type == C.BAR) s2 = s if (s.x >= xmin) break } s4 = s // keep first symbol with x greater than xmin //fixme: can this occur? if (s == next) { if (s) s = set_nl(s) return s } /* try to cut on a measure bar */ s3 = null for ( ; s != next; s = s.ts_next) { x = s.x if (!x) continue if (x > xmax) break if (s.type != C.BAR) continue // cut on the bar closest to the middle if (x < xmid) { s3 = s // closest bar before middle continue } if (!s3 || x - xmid < xmid - s3.x) s3 = s // closest bar after middle break } // no bar, try to avoid to cut a beam or a tuplet */ if (!s3) { s = s4 // restart after xmin var beam = 0, bar_time = s2.time xmax -= 8; // (left width of the inserted bar in set_allsymwidth) s5 = s for ( ; s != next; s = s.ts_next) { if (s.seqst) { x = s.x if (x + s.wr >= xmax) break if (!beam && !s.in_tuplet && (xmid - s5.x > x - xmid || (s.time - bar_time) % (C.BLEN / 4) == 0)) s3 = s } if (s.beam_st) beam |= 1 << s.v if (s.beam_end) beam &= ~(1 << s.v) s5 = s // start of new time sequence } if (s3) { do { // cut on the previous sequence s3 = s3.ts_prev } while (!s3.seqst) } } // cut anyhere if (!s3) { s3 = s = s4 for ( ; s != next; s = s.ts_next) { x = s.x if (!x) continue if (x + s.wr >= xmax) break if (s3 && x >= xmid) { if (xmid - s3.x > x - xmid) s3 = s break } s3 = s } } s = s3 while (s.ts_next) { s = s.ts_next if (s.seqst) break } if (s.nl) { /* already set here - advance */ error(0, s, "Line split problem - adjust maxshrink and/or breaklimit"); nlines = 2 for (s = s.ts_next; s != next; s = s.ts_next) { if (!s.x) continue if (--nlines <= 0) break } } s = set_nl(s) if (!s || (next && s.time >= next.time)) break wwidth -= s.x - first.x; indent = 0 } return s } /* -- cut the tune into music lines -- */ function cut_tune(lwidth, lsh) { var s2, i, mc, pg_sav = { // save the page parameters leftmargin: cfmt.leftmargin, rightmargin: cfmt.rightmargin, pagewidth: cfmt.pagewidth, scale: cfmt.scale }, indent = lsh[0] - lsh[1], // extra width of the first line ckw = get_ck_width(), // width of the starting symbols s = tsfirst lwidth -= lsh[1] // width of the lines if (cfmt.indent && cfmt.indent > lsh[0]) indent += cfmt.indent // adjust the line width according to the starting symbols lwidth -= ckw[0] indent += ckw[1] if (cfmt.custos && voice_tb.length == 1) lwidth -= 12 /* if asked, count the measures and set the EOLNs */ i = s.fmt.barsperstaff if (i) { for (s2 = s; s2; s2 = s2.ts_next) { if (s2.type != C.BAR || !s2.bar_num || --i > 0) continue while (s2.ts_next && s2.ts_next.type == C.BAR) s2 = s2.ts_next if (s2.ts_next) s2.ts_next.soln = true i = s.fmt.barsperstaff } } /* cut at explicit end of line, checking the line width */ s2 = s for ( ; s; s = s.ts_next) { if (s.type == C.BLOCK) { switch (s.subtype) { case "leftmargin": case "rightmargin": case "pagescale": case "pagewidth": case "scale": case "staffwidth": if (!s.soln) self.set_format(s.subtype, s.param) break case "mc_start": mc = { lm: cfmt.leftmargin, rm: cfmt.rightmargin } break case "mc_new": case "mc_end": if (!mc) break cfmt.leftmargin = mc.lm cfmt.rightmargin = mc.rm img.chg = 1 //true break } } if (!s.ts_next) { s = null } else if (!s.soln) { continue } else { s.soln = false //fixme what if new line wanted? if (s.time == s2.time) continue // empty music line! while (!s.seqst) s = s.ts_prev } set_page() lwidth = get_lwidth() - lsh[1] - ckw[0] s2 = set_lines(s2, s, lwidth, indent) if (!s2) break s = s2.type == C.BLOCK ? s2.ts_prev // don't miss a parameter : s indent = 0 } // restore the page parameters at start of line cfmt.leftmargin = pg_sav.leftmargin cfmt.rightmargin = pg_sav.rightmargin cfmt.pagewidth = pg_sav.pagewidth cfmt.scale = pg_sav.scale img.chg = 1 set_page() } /* -- set the y values of some symbols -- */ function set_yval(s) { //fixme: staff_tb is not yet defined // var top = staff_tb[s.st].topbar // var bot = staff_tb[s.st].botbar switch (s.type) { case C.CLEF: if (s.second || s.invis) { // s.ymx = s.ymn = (top + bot) / 2 s.ymx = s.ymn = 12 break } s.y = (s.clef_line - 1) * 6 switch (s.clef_type) { default: /* treble / perc */ s.ymx = s.y + 28 s.ymn = s.y - 14 break case "c": s.ymx = s.y + 13 s.ymn = s.y - 11 break case "b": s.ymx = s.y + 7 s.ymn = s.y - 12 break } if (s.clef_small) { s.ymx -= 2; s.ymn += 2 } if (s.ymx < 26) s.ymx = 26 if (s.ymn > -1) s.ymn = -1 // s.y += s.clef_line * 6 // if (s.y > 0) // s.ymx += s.y // else if (s.y < 0) // s.ymn += s.y if (s.clef_octave) { if (s.clef_octave > 0) s.ymx += 12 else s.ymn -= 12 } break case C.KEY: if (s.k_sf > 2) s.ymx = 24 + 10 else if (s.k_sf > 0) s.ymx = 24 + 6 else s.ymx = 24 + 2; s.ymn = -2 break default: // s.ymx = top; s.ymx = 24; s.ymn = 0 break } } // set the pitch of the notes under an ottava sequence function set_ottava() { var s, s1, st, o, d, m = nstaff + 1, staff_d = new Int8Array(m) // update the pitches of a symbol function sym_ott(s, d) { var g, m, note switch (s.type) { case C.REST: if (voice_tb.length == 1) break case C.NOTE: if (!s.p_v.ckey.k_drum) { for (m = s.nhd; m >= 0; m--) { note = s.notes[m]; if (!note.opit) note.opit = note.pit; note.pit += d } } break case C.GRACE: for (g = s.extra; g; g = g.next) { if (!s.p_v.ckey.k_drum) { for (m = 0; m <= g.nhd; m++) { note = g.notes[m] if (!note.opit) note.opit = note.pit note.pit += d } } } break } } // sym_ott() // remove the ottava decorations of a symbol function deco_rm(s) { for (var i = s.a_dd.length; --i >= 0;) { if (s.a_dd[i].name.match(/1?[85][vm][ab]/)) s.a_dd.splice(i, 1) } } // deco_rm() for (s = tsfirst; s; s = s.ts_next) { st = s.st o = s.ottava if (o) { // some ottava start or stop if (o[0]) { if (staff_d[st] && !o[1]) { sym_ott(s, staff_d[st]) deco_rm(s) continue // same ottava } } else if (!staff_d[st]) { deco_rm(s) continue // already no ottava } s1 = s while (s1 && !s1.seqst) s1 = s1.ts_prev if (s1) { // update the previous symbols while (s1 != s) { if (s1.st == st) { if (o[1]) sym_ott(s1, -staff_d[st]) if (o[0]) sym_ott(s1, -o[0] * 7) } s1 = s1.ts_next } } if (o[0]) { // ottava start staff_d[st] = -o[0] * 7 } else { staff_d[st] = 0 } } if (staff_d[st]) sym_ott(s, staff_d[st]) } } // expand the multi-rests as needed function mrest_expand() { var s, s2 // expand a multi-rest into a set of rest + bar function mexp(s) { var bar, s3, s4, tim, nbar, nb = s.nmes, dur = s.dur / nb, s2 = s.next // get the bar (there may be some other symbols before the bar) while (!s2.bar_type) s2 = s2.next bar = s2 while (!s2.bar_num) // get the bar number s2 = s2.ts_prev nbar = s2.bar_num - s.nmes // change the multi-rest into a single rest s.type = C.REST s.notes[0].dur = s.dur = s.dur_orig = dur s.nflags = -2 s.head = C.FULL s.fmr = 1 // full measure rest /* add the bar(s) and rest(s) */ tim = s.time + dur s3 = s while (--nb > 0) { // add the bar s2 = clone(bar) delete s2.soln delete s2.a_gch delete s2.a_dd delete s2.text delete s2.rbstart delete s2.rbstop lkvsym(s2, s.next) // before symbol at end of rests s2.time = tim while (s3.time < tim) s3 = s3.ts_next // bar at end of measure while (s3 && s3.v < s.v && s3.type == C.BAR) s3 = s3.ts_next // keep in order if (s3) { if (s3.bar_type) s3.seqst = 0 //false lktsym(s2, s3) if (s3.type == C.BAR) delete s3.bar_num } else { s3 = s while (s3.ts_next) s3 = s3.ts_next s3.ts_next = s2 s2.ts_prev = s3 s2.ts_next = null } nbar++ if (s2.seqst) { s2.bar_num = nbar s4 = s2.ts_next } else { delete s2.bar_num s4 = s2.ts_prev } s2.bar_type = s4.bar_type || "|" if (s4.bar_num && !s4.seqst) delete s4.bar_num // add the rest s4 = clone(s) delete s4.a_dd delete s4.soln delete s4.a_gch delete s4.part if (s2.next) { s4.next = s2.next s4.next.prev = s4 } else { s4.next = null } s2.next = s4 s4.prev = s2 s4.time = tim while (s3 && !s3.dur && s3.time == tim) s3 = s3.ts_next while (s3 && s3.v < s.v) { s3 = s3.ts_next // keep in order if (s3 && s3.seqst) break } if (s3) { if (s3.dur) s3.seqst = 0 //false lktsym(s4, s3) } else { s3 = s while (s3.ts_next) s3 = s3.ts_next s3.ts_next = s4 s4.ts_prev = s3 s4.ts_next = null } tim += dur s = s3 = s4 } } // mexp() for (s = tsfirst; s; s = s.ts_next) { if (s.type != C.MREST) continue if (!s.seqst && w_tb[s.ts_prev.type]) { s2 = s } else { s2 = s.ts_next while (!s2.seqst) { if (s2.type != C.MREST || s2.nmes != s.nmes) break s2 = s2.ts_next } } if (!s2.seqst) { while (s.type == C.MREST) { mexp(s) s = s.ts_next } } else { s = s2.ts_prev } } } // mrest_expand() // set the clefs (treble or bass) in a 'auto clef' sequence // return the starting clef type function set_auto_clef(st, s_start, clef_type_start) { var s, time, s2, s3, max = 12, /* "F," */ min = 20 /* "G" */ /* get the max and min pitches in the sequence */ for (s = s_start; s; s = s.ts_next) { if (s.type == C.STAVES && s != s_start) break if (s.st != st) continue if (s.type != C.NOTE) { if (s.type == C.CLEF) { if (s.clef_type != 'a') break unlksym(s) } continue } if (s.notes[0].pit < min) min = s.notes[0].pit if (s.notes[s.nhd].pit > max) max = s.notes[s.nhd].pit } if (min >= 19 /* upper than 'F' */ || (min >= 13 && clef_type_start != 'b')) /* or 'G,' */ return 't' if (max <= 13 /* lower than 'G,' */ || (max <= 19 && clef_type_start != 't')) /* or 'F' */ return 'b' /* set clef changes */ if (clef_type_start == 'a') { if ((max + min) / 2 >= 16) clef_type_start = 't' else clef_type_start = 'b' } var clef_type = clef_type_start, s_last = s, s_last_chg = null for (s = s_start; s != s_last; s = s.ts_next) { if (s.type == C.STAVES && s != s_start) break if (s.st != st || s.type != C.NOTE) continue /* check if a clef change may occur */ time = s.time if (clef_type == 't') { if (s.notes[0].pit > 12 /* F, */ || s.notes[s.nhd].pit > 20) { /* G */ if (s.notes[0].pit > 20) s_last_chg = s continue } s2 = s.ts_prev if (s2 && s2.time == time && s2.st == st && s2.type == C.NOTE && s2.notes[0].pit >= 19) /* F */ continue s2 = s.ts_next if (s2 && s2.st == st && s2.time == time && s2.type == C.NOTE && s2.notes[0].pit >= 19) /* F */ continue } else { if (s.notes[0].pit <= 12 /* F, */ || s.notes[s.nhd].pit < 20) { /* G */ if (s.notes[s.nhd].pit <= 12) s_last_chg = s continue } s2 = s.ts_prev if (s2 && s2.time == time && s2.st == st && s2.type == C.NOTE && s2.notes[0].pit <= 13) /* G, */ continue s2 = s.ts_next if (s2 && s2.st == st && s2.time == time && s2.type == C.NOTE && s2.notes[0].pit <= 13) /* G, */ continue } /* if first change, change the starting clef */ if (!s_last_chg) { clef_type = clef_type_start = clef_type == 't' ? 'b' : 't'; s_last_chg = s continue } /* go backwards and search where to insert a clef change */ s3 = s for (s2 = s.ts_prev; s2 != s_last_chg; s2 = s2.ts_prev) { if (s2.st != st) continue if (s2.type == C.BAR) { s3 = s2.bar_type[0] != ':' ? s2 : s2.next break } if (s2.type != C.NOTE) continue /* have a 2nd choice on beam start */ if (s2.beam_st && !s2.p_v.second) s3 = s2 } /* no change possible if no insert point */ if (s3.time == s_last_chg.time) { s_last_chg = s continue } s_last_chg = s; /* insert a clef change */ clef_type = clef_type == 't' ? 'b' : 't'; s2 = insert_clef(s3, clef_type, clef_type == "t" ? 2 : 4); s2.clef_auto = true // s3.prev.st = st } return clef_type_start } /* set the clefs */ /* this function is called once at start of tune generation */ /* * global variables: * - staff_tb[st].clef = clefs at start of line (here, start of tune) * (created here, updated on clef draw) * - voice_tb[v].clef = clefs at end of generation * (created on voice creation, updated here) */ function set_clefs() { var s, s2, st, v, p_voice, g, new_type, new_line, p_staff, pit, staff_clef = new Array(nstaff + 1), // st -> { clef, autoclef } sy = cur_sy, mid = [] // create the staff table staff_tb = new Array(nstaff + 1) for (st = 0; st <= nstaff; st++) { staff_clef[st] = { autoclef: true } staff_tb[st] = { output: "", sc_out: "" } } for (st = 0; st <= sy.nstaff; st++) mid[st] = (sy.staves[st].stafflines.length - 1) * 3 for (s = tsfirst; s; s = s.ts_next) { if (s.repeat_n) set_repeat(s) switch (s.type) { case C.STAVES: sy = s.sy // new system for (st = 0; st <= nstaff; st++) staff_clef[st].autoclef = true for (v = 0; v < voice_tb.length; v++) { if (!sy.voices[v]) continue p_voice = voice_tb[v]; st = sy.voices[v].st if (!sy.voices[v].second) { sy.staves[st].staffnonote = p_voice.staffnonote if (p_voice.staffscale) sy.staves[st].staffscale = p_voice.staffscale if (sy.voices[v].sep) sy.staves[st].sep = sy.voices[v].sep if (sy.voices[v].maxsep) sy.staves[st].maxsep = sy.voices[v].maxsep } s2 = p_voice.clef if (!s2.clef_auto) staff_clef[st].autoclef = false } for (st = 0; st <= sy.nstaff; st++) mid[st] = (sy.staves[st].stafflines.length - 1) * 3 for (v = 0; v < voice_tb.length; v++) { if (!sy.voices[v] || sy.voices[v].second) // main voices continue p_voice = voice_tb[v]; st = sy.voices[v].st; s2 = p_voice.clef if (s2.clef_auto) { //fixme: the staff may have other voices with explicit clefs... // if (!staff_clef[st].autoclef) // ??? new_type = set_auto_clef(st, s, staff_clef[st].clef ? staff_clef[st].clef.clef_type : 'a'); new_line = new_type == 't' ? 2 : 4 } else { new_type = s2.clef_type; new_line = s2.clef_line } if (!staff_clef[st].clef) { // new staff if (s2.clef_auto) { if (s2.clef_type != 'a') p_voice.clef = clone(p_voice.clef); p_voice.clef.clef_type = new_type; p_voice.clef.clef_line = new_line } staff_tb[st].clef = staff_clef[st].clef = p_voice.clef continue } // old staff if (new_type == staff_clef[st].clef.clef_type && new_line == staff_clef[st].clef.clef_line) continue g = s.ts_prev while (g && g.time == s.time && (g.v != v || g.st != st)) g = g.ts_prev if (!g || g.time != s.time) { g = s.ts_next while (g && (g.v != v || g.st != st)) g = g.ts_next if (!g || g.time != s.time) g = s } if (g.type != C.CLEF) { g = insert_clef(g, new_type, new_line) if (s2.clef_auto) g.clef_auto = true } staff_clef[st].clef = p_voice.clef = g } continue default: s.mid = mid[s.st] continue case C.CLEF: break } if (s.clef_type == 'a') { s.clef_type = set_auto_clef(s.st, s.ts_next, staff_clef[s.st].clef.clef_type); s.clef_line = s.clef_type == 't' ? 2 : 4 } p_voice = s.p_v; p_voice.clef = s if (s.second) { /*fixme:%%staves:can this happen?*/ // if (!s.prev) // break unlksym(s) continue } st = s.st // may have been inserted on %%staves // if (s.clef_auto) { // unlksym(s) // continue // } if (staff_clef[st].clef) { if (s.clef_type == staff_clef[st].clef.clef_type && s.clef_line == staff_clef[st].clef.clef_line) { // unlksym(s) continue } } else { // the voice moved to a new staff with a forced clef staff_tb[st].clef = s } staff_clef[st].clef = s } /* set a pitch to the symbols of voices with no note */ sy = cur_sy for (v = 0; v < voice_tb.length; v++) { if (!sy.voices[v]) continue s2 = voice_tb[v].sym if (!s2 || s2.notes[0].pit != 127) continue st = sy.voices[v].st switch (staff_tb[st].clef.clef_type) { default: pit = 22 /* 'B' */ break case "c": pit = 16 /* 'C' */ break case "b": pit = 10 /* 'D,' */ break } for (s = s2; s; s = s.next) s.notes[0].pit = pit } } /* set the pitch of the notes according to the clefs * and set the vertical offset of the symbols */ /* this function is called at start of tune generation and * then, once per music line up to the old sequence */ var delta_tb = { t: 0 - 2 * 2, c: 6 - 3 * 2, b: 12 - 4 * 2, p: 0 - 3 * 2 } /* upper and lower space needed by rests */ var rest_sp = [ [18, 18], [12, 18], [12, 12], [10, 12], [10, 10], [10, 10], /* crotchet */ [8, 4], [9, 0], [9, 4], [6, 8] ] // set the offsets of a rest function roffs(s) { s.ymx = s.y + rest_sp[5 - s.nflags][0] s.ymn = s.y - rest_sp[5 - s.nflags][1] } // roffs() // (possible hook) Abc.prototype.set_pitch = function(last_s) { var s, s2, g, st, delta, pitch, note, dur = C.BLEN, m = nstaff + 1, staff_delta = new Int16Array(m * 2), // delta clef sy = cur_sy // set the starting clefs of the staves for (st = 0; st <= nstaff; st++) { s = staff_tb[st].clef; staff_delta[st] = delta_tb[s.clef_type] + s.clef_line * 2 if (s.clefpit) staff_delta[st] += s.clefpit if (cfmt.sound) { if (s.clef_octave && !s.clef_oct_transp) staff_delta[st] += s.clef_octave } else { if (s.clef_oct_transp) staff_delta[st] -= s.clef_octave } } for (s = tsfirst; s != last_s; s = s.ts_next) { st = s.st switch (s.type) { case C.CLEF: staff_delta[st] = delta_tb[s.clef_type] + s.clef_line * 2 if (s.clefpit) staff_delta[st] += s.clefpit if (cfmt.sound) { if (s.clef_octave && !s.clef_oct_transp) staff_delta[st] += s.clef_octave } else { if (s.clef_oct_transp) staff_delta[st] -= s.clef_octave } set_yval(s) break case C.GRACE: for (g = s.extra; g; g = g.next) { delta = staff_delta[g.st] if (delta && !s.p_v.ckey.k_drum) { for (m = 0; m <= g.nhd; m++) { note = g.notes[m]; note.opit = note.pit note.pit += delta } } g.ymn = 3 * (g.notes[0].pit - 18) - 2; g.ymx = 3 * (g.notes[g.nhd].pit - 18) + 2 } set_yval(s) break case C.KEY: s.k_y_clef = staff_delta[st] /* keep the y delta */ /* fall thru */ default: set_yval(s) break case C.MREST: if (s.invis) break s.y = 12; s.ymx = 24 + 15; s.ymn = -2 break case C.REST: s.y = 12 if (s.rep_nb > 1 // if measure repeat || s.bar_mrep) { s.ymx = 38 // (24 + 14) s.ymn = 0 break } roffs(s) // fall thru case C.NOTE: delta = staff_delta[st] if (delta && !s.p_v.ckey.k_drum) { for (m = s.nhd; m >= 0; m--) { note = s.notes[m] note.opit = note.pit note.pit += delta } } if (s.dur < dur) dur = s.dur break } } if (!last_s) smallest_duration = dur } /* -- set the stem direction when multi-voices -- */ /* this function is called only once per tune */ // (possible hook) Abc.prototype.set_stem_dir = function() { var t, u, i, st, rvoice, v, v_st, // voice -> staff 1 & 2 st_v, vobj, // staff -> (v, ymx, ymn)* v_st_tb, // array of v_st st_v_tb = [], // array of st_v s = tsfirst, sy = cur_sy, nst = sy.nstaff while (s) { for (st = 0; st <= nst; st++) st_v_tb[st] = [] v_st_tb = [] /* get the max/min offsets in the delta time */ /*fixme: the stem height is not calculated yet*/ for (u = s; u; u = u.ts_next) { if (u.type == C.BAR) break; if (u.type == C.STAVES) { if (u != s) break sy = s.sy for (st = nst; st <= sy.nstaff; st++) st_v_tb[st] = [] nst = sy.nstaff continue } if ((u.type != C.NOTE && u.type != C.REST) || u.invis) continue st = u.st; /*fixme:test*/ if (st > nst) { var msg = "*** fatal set_stem_dir(): bad staff number " + st + " max " + nst; error(2, null, msg); throw new Error(msg) } v = u.v; v_st = v_st_tb[v] if (!v_st) { v_st = { st1: -1, st2: -1 } v_st_tb[v] = v_st } if (v_st.st1 < 0) { v_st.st1 = st } else if (v_st.st1 != st) { if (st > v_st.st1) { if (st > v_st.st2) v_st.st2 = st } else { if (v_st.st1 > v_st.st2) v_st.st2 = v_st.st1; v_st.st1 = st } } st_v = st_v_tb[st]; rvoice = sy.voices[v].range; for (i = st_v.length; --i >= 0; ) { vobj = st_v[i] if (vobj.v == rvoice) break } if (i < 0) { vobj = { v: rvoice, ymx: 0, ymn: 24 } for (i = 0; i < st_v.length; i++) { if (rvoice < st_v[i].v) { st_v.splice(i, 0, vobj) break } } if (i == st_v.length) st_v.push(vobj) } if (u.type != C.NOTE) continue if (u.ymx > vobj.ymx) vobj.ymx = u.ymx if (u.ymn < vobj.ymn) vobj.ymn = u.ymn if (u.xstem) { if (u.ts_prev.st != st - 1 || u.ts_prev.type != C.NOTE) { error(1, s, "Bad !xstem!"); u.xstem = false /*fixme:nflags KO*/ } else { u.ts_prev.multi = 1; u.multi = 1; u.stemless = true } } } for ( ; s != u; s = s.ts_next) { if (s.multi) continue switch (s.type) { default: continue case C.REST: // handle %%voicecombine 0 if ((s.combine != undefined && s.combine < 0) || !s.ts_next || s.ts_next.type != C.REST || s.ts_next.st != s.st || s.time != s.ts_next.time || s.dur != s.ts_next.dur || (s.a_dd && s.ts_next.a_dd) || (s.a_gch && s.ts_next.a_gch) || s.invis) break if (s.ts_next.a_dd) s.a_dd = s.ts_next.a_dd if (s.ts_next.a_gch) s.a_gch = s.ts_next.a_gch unlksym(s.ts_next) // fall thru case C.NOTE: case C.GRACE: break } st = s.st; v = s.v; v_st = v_st_tb[v]; st_v = st_v_tb[st] if (v_st && v_st.st2 >= 0) { if (st == v_st.st1) s.multi = -1 else if (st == v_st.st2) s.multi = 1 continue } if (st_v.length <= 1) { /* voice alone on the staff */ // if (s.multi) // continue /*fixme:could be done in set_var()*/ if (s.floating) s.multi = st == voice_tb[v].st ? -1 : 1 continue } rvoice = sy.voices[v].range for (i = st_v.length; --i >= 0; ) { if (st_v[i].v == rvoice) break } if (i < 0) continue /* voice ignored */ if (i == st_v.length - 1) { s.multi = -1 /* last voice */ } else { s.multi = 1 /* first voice(s) */ /* if 3 voices, and vertical space enough, * have stems down for the middle voice */ if (i && i + 2 == st_v.length) { if (st_v[i].ymn - s.fmt.stemheight >= st_v[i + 1].ymx) s.multi = -1; /* special case for unison */ t = s.ts_next //fixme: pb with ../lacerda/evol-7.5.5.abc if (s.ts_prev && s.ts_prev.time == s.time && s.ts_prev.st == s.st && s.notes[s.nhd].pit == s.ts_prev.notes[0].pit && s.beam_st && s.beam_end && (!t || t.st != s.st || t.time != s.time)) s.multi = -1 } } } while (s && s.type == C.BAR) s = s.ts_next } } /* -- adjust the offset of the rests when many voices -- */ /* this function is called only once per tune */ function set_rest_offset() { var s, s2, v, v_s, ymax, ymin, d, v_s_tb = [], sy = cur_sy // set a vertical offset on a line function loffs(d) { return d > 0 ? Math.ceil(d / 6) * 6 : -Math.ceil(-d / 6) * 6 } // loffs() // shift a rest to the right function rshift() { var dx = s2.dots ? 15 : 10 s2.notes[0].shhd = dx s2.xmx = dx d = (d + v_s.d) / 2 d = loffs(d) s2.y += d s2.ymx += d s2.ymn += d v_s.d = 0 } // rshift() // -- set_rest_off -- for (s = tsfirst; s; s = s.ts_next) { if (s.invis) continue switch (s.type) { case C.STAVES: sy = s.sy // fall thru default: continue case C.REST: if (s.invis || !s.multi) continue v_s = v_s_tb[s.v] if (!v_s) { v_s_tb[s.v] = v_s = { d: 0} } else if (v_s.d) { s2 = v_s.s // set the offsets of the previous rest d = loffs(v_s.d) s2.y += d s2.ymx += d s2.ymn += d v_s.d = 0 } d = s.multi > 0 ? 0 : 24 if (s.prev && s.prev.type == C.NOTE) d = (s.next && s.next.type == C.NOTE) ? (s.prev.y + s.next.y) / 2 : s.prev.y else if (s.next && s.next.type == C.NOTE) d = s.next.y else if (s.prev && s.prev.type == C.REST) d = s.prev.y if (s.multi > 0) { if (d >= 12) v_s.d = d - s.y } else { if (d <= 12) v_s.d = d - s.y } v_s.s = s v_s.st = s.st v_s.end_time = s.time + s.dur if (s.fmr) // if full meeasure rest v_s.end_time -= s.p_v.wmeasure * .3 if (!s.seqst) { s2 = s.ts_prev if (s2.st == s.st && s.ymx > s2.ymn && !s2.invis) { if (s2.type == C.NOTE) { v_s.d = s2.ymn - s.ymx break } if (s2.type == C.REST && s2.y < 18 && s.y >= 6) v_s.d = -6 } } continue case C.NOTE: if (s.invis || !s.multi) continue break } // check if any clash with a rest for (v = 0; v < v_s_tb.length; v++) { v_s = v_s_tb[v] if (!v_s || v_s.st != s.st || v == s.v || v_s.end_time <= s.time) continue s2 = v_s.s // rest if (sy.voices[v].range > sy.voices[s.v].range) { if (s2.ymx + v_s.d <= s.ymn) continue d = s.ymn - s2.ymx // rest must go down // if (s2.time < s.time) { if (s.type == C.REST) { if (!v_s_tb[s.v]) v_s_tb[s.v] = {d: 0} if (v_s_tb[s.v].d < 6) v_s_tb[s.v].d = 6 d = -6 } else { d /= 2 if (s2.fmr) d -= 6 } // } if (v_s.d) { if (v_s.d > 0) { // if it was go up rshift() // shift the rest continue } if (d >= v_s.d) continue } } else { if (s2.ymn + v_s.d >= s.ymx) continue d = s.ymx - s2.ymn // rest must go up if (s.type == C.REST // if rest && s2 == s.ts_prev // just under a rest && s.y == s2.y) { // at a same offset if (!v_s_tb[s.v]) v_s_tb[s.v] = {d: 0} if (v_s_tb[s.v].d > -6) v_s_tb[s.v].d = -6 d = 6 } else if (s2.time < s.time) { d = s.ymx - s2.y } if (v_s.d) { if (v_s.d < 0) { // if it was go down rshift() // shift the rest continue } if (d <= v_s.d) continue } } v_s.d = d } } // update the offsets of the last rests for (v = 0; v < v_s_tb.length; v++) { v_s = v_s_tb[v] if (v_s && v_s.d) { s2 = v_s.s d = loffs(v_s.d) s2.y += d s2.ymx += d s2.ymn += d } } } /* -- create a starting symbol -- */ // last_s = symbol at same time function new_sym(s, p_v, last_s) { s.p_v = p_v s.v = p_v.v s.st = p_v.st s.time = last_s.time if (p_v.last_sym) { s.next = p_v.last_sym.next if (s.next) s.next.prev = s; p_v.last_sym.next = s; s.prev = p_v.last_sym } p_v.last_sym = s; lktsym(s, last_s) } /* -- init the symbols at start of a music line -- */ function init_music_line() { var p_voice, s, s1, s2, s3, last_s, v, st, shr, shrmx, shl, shlp, p_st, top, nv = voice_tb.length, fmt = tsfirst.fmt /* initialize the voices */ for (v = 0; v < nv; v++) { if (!cur_sy.voices[v]) continue p_voice = voice_tb[v]; p_voice.st = cur_sy.voices[v].st p_voice.second = cur_sy.voices[v].second; p_voice.last_sym = p_voice.sym; // move the first clefs, key signatures and time signatures // to the staves for (s = p_voice.sym; s && s.time == tsfirst.time; s = s.next) { switch (s.type) { case C.CLEF: case C.KEY: case C.METER: switch (s.type) { case C.CLEF: staff_tb[s.st].clef = s break case C.KEY: s.p_v.ckey = s break case C.METER: s.p_v.meter = s insert_meter = cfmt.writefields.indexOf('M') >= 0 && s.a_meter.length break } if (s.part) s.next.part = s.part unlksym(s) // fall thru case C.TEMPO: case C.BLOCK: case C.REMARK: continue } break } } // generate the starting clefs, key signatures and time signatures // add a clef at start of the main voices last_s = tsfirst for (v = 0; v < nv; v++) { p_voice = voice_tb[v] if (!cur_sy.voices[v] || (cur_sy.voices[v].second && !p_voice.bar_start)) // needed for correct linkage continue st = cur_sy.voices[v].st if (!staff_tb[st] || !staff_tb[st].clef) continue s = clone(staff_tb[st].clef); s.v = v; s.p_v = p_voice; s.st = st; s.time = tsfirst.time; s.prev = null; s.next = p_voice.sym if (s.next) s.next.prev = s; p_voice.sym = p_voice.last_sym = s s.ts_next = last_s; if (last_s) s.ts_prev = last_s.ts_prev else s.ts_prev = null if (!s.ts_prev) { tsfirst = s; } else { s.ts_prev.ts_next = s delete s.seqst } if (last_s) last_s.ts_prev = s delete s.clef_small; delete s.part s.second = cur_sy.voices[v].second // (fixme: needed for sample5 X:3 Fugue & staffnonote.html) if (!cur_sy.st_print[st]) s.invis = true else if (!s.clef_none) delete s.invis s.fmt = fmt } /* add keysig */ for (v = 0; v < nv; v++) { if (!cur_sy.voices[v] || cur_sy.voices[v].second || !cur_sy.st_print[cur_sy.voices[v].st]) continue p_voice = voice_tb[v] s2 = p_voice.ckey if (s2.k_sf || s2.k_a_acc) { s = clone(s2) new_sym(s, p_voice, last_s) delete s.invis delete s.part s.k_old_sf = s2.k_sf // no key cancel s.fmt = fmt } } /* add time signature (meter) if needed */ if (insert_meter) { for (v = 0; v < nv; v++) { p_voice = voice_tb[v]; s2 = p_voice.meter if (!cur_sy.voices[v] || cur_sy.voices[v].second || !cur_sy.st_print[cur_sy.voices[v].st]) // || !s2.a_meter.length) continue s = clone(s2) new_sym(s, p_voice, last_s) delete s.part s.fmt = fmt } insert_meter = false // no meter any more } // add an invisible bar for the various continued elements for (v = 0; v < nv; v++) { p_voice = voice_tb[v] if (p_voice.sls.length) { s = { type: C.BAR, fname: last_s.fname, bar_type: "|", dur: 0, multi: 0, invis: true, sls: p_voice.sls, fmt: fmt } new_sym(s, p_voice, last_s) p_voice.sls = [] } } // add a bar for the continuation of repeat brackets for (v = 0; v < nv; v++) { p_voice = voice_tb[v]; s2 = p_voice.bar_start; p_voice.bar_start = null // check if bracket stop at this time for (s = last_s; s && s.time == last_s.time; s = s.ts_next) { if (s.rbstop) { s2 = null break } } if (!s2) continue if (!cur_sy.voices[v] || !cur_sy.st_print[cur_sy.voices[v].st]) continue if (p_voice.last_sym.type == C.BAR) { if (!p_voice.last_sym.rbstop) p_voice.last_sym.rbstart = 1 } else { new_sym(s2, p_voice, last_s) s2.fmt = fmt } } // compute the spacing of the added symbols self.set_pitch(last_s); s = tsfirst s.seqst = true for (s = last_s; s.ts_next && !s.ts_next.seqst; s = s.ts_next) ; if (s.ts_next // a bit further in case different keys per voice && s.ts_next.type != C.CLEF // (the clef may move in allsymwidth) && !s.tp // (start of a tuplet) && !s.ts_next.a_ly) // (don't update next .shrink) for (s = s.ts_next; s.ts_next && !s.ts_next.seqst; s = s.ts_next) ; s2 = s.ts_next s.ts_next = null set_allsymwidth() s.ts_next = s2 } // init_music_line() // check if the tune ends on a measure bar function check_end_bar() { var s2, s = tsfirst while (s.ts_next) s = s.ts_next if (s.type != C.BAR) { s2 = _bar(s) s2.ts_prev = s s.next = s.ts_next = s2 } } // check_end_bar() /* -- set a pitch in all symbols and the start/stop of the beams -- */ // and sort the pitches in the chords // and build the chord symbols / annotations // this function is called only once per tune function set_words(p_voice) { var s, s2, nflags, lastnote, res, start_flag = true, pitch = 127 /* no note */ // adjust the duration of the notes in a decoration !trem1..4! function trem_adj(s) { s.prev.trem2 = true s.prev.head = ++s.head if (--s.nflags > 0) { s.nflags += s.ntrem } else { if (s.nflags <= -2) { s.stemless = true s.prev.stemless = true } s.nflags = s.ntrem } s.prev.nflags = s.nflags } // trem_adj() for (s = p_voice.sym; s; s = s.next) { if (s.type == C.NOTE) { pitch = s.notes[0].pit break } } for (s = p_voice.sym; s; s = s.next) { if (s.a_gch) self.gch_build(s) switch (s.type) { case C.MREST: start_flag = true break case C.BAR: res = s.fmt.bardef[s.bar_type] if (res) s.bar_type = res if (!s.beam_on) start_flag = true if (!s.next && s.prev && !s.invis && s.prev.head == C.OVALBARS) s.prev.head = C.SQUARE break case C.GRACE: for (s2 = s.extra; s2; s2 = s2.next) { s2.notes.sort(abc2svg.pitcmp) res = identify_note(s2, s2.dur_orig) s2.head = res[0] s2.dots = res[1] s2.nflags = res[2] if (s2.trem2 && (!s2.next || s2.next.trem2)) trem_adj(s2) } break case C.NOTE: case C.REST: res = identify_note(s, s.dur_orig); s.head = res[0]; s.dots = res[1]; s.nflags = res[2] if (s.nflags <= -2) s.stemless = true if (s.xstem) s.nflags = 0 // beam break if (s.trem1) { if (s.nflags > 0) s.nflags += s.ntrem else s.nflags = s.ntrem } if (s.next && s.next.trem2) break if (s.trem2) { trem_adj(s) break } nflags = s.nflags if (s.ntrem) nflags -= s.ntrem if (s.type == C.REST && s.beam_end && !s.beam_on) { // s.beam_end = false; start_flag = true } if (start_flag || nflags <= 0) { if (lastnote) { lastnote.beam_end = true; lastnote = null } if (nflags <= 0) { s.beam_st = s.beam_end = true } else if (s.type == C.NOTE || s.beam_on) { s.beam_st = true; start_flag = false } } if (s.beam_end) start_flag = true if (s.type == C.NOTE || s.beam_on) lastnote = s break } if (s.type == C.NOTE) { if (s.nhd) s.notes.sort(abc2svg.pitcmp) pitch = s.notes[0].pit // if (s.prev // && s.prev.type != C.NOTE) { // s.prev.notes[0].pit = (s.prev.notes[0].pit // + pitch) / 2 for (s2 = s.prev; s2; s2 = s2.prev) { if (s2.type != C.REST) break s2.notes[0].pit = pitch } } else { if (!s.notes) { s.notes = [] s.notes[0] = {} s.nhd = 0 } s.notes[0].pit = pitch } } if (lastnote) lastnote.beam_end = true } /* -- set the end of the repeat sequences -- */ function set_rb(p_voice) { var s2, n, s = p_voice.sym while (s) { if (s.type != C.BAR || !s.rbstart || s.norepbra) { s = s.next continue } n = 0; s2 = null for (s = s.next; s; s = s.next) { if (s.type != C.BAR) continue if (s.rbstop) break if (!s.next) { s.rbstop = 2 // right repeat with end break } n++ if (n == s.fmt.rbmin) s2 = s if (n == s.fmt.rbmax) { if (s2) s = s2; s.rbstop = 1 // right repeat without end break } } } } /* -- initialize the generator -- */ // this function is called only once per tune var delpit = [0, -7, -14, 0] function set_global() { var p_voice, v, nv = voice_tb.length, sy = cur_sy, st = sy.nstaff insert_meter = cfmt.writefields.indexOf('M') >= 0 /* get the max number of staves */ while (1) { sy = sy.next if (!sy) break if (sy.nstaff > st) st = sy.nstaff } nstaff = st; // there must be a bar at end of tune check_end_bar() /* set the pitches, the words (beams) and the repeat brackets */ for (v = 0; v < nv; v++) { p_voice = voice_tb[v]; set_words(p_voice) p_voice.ckey = p_voice.key // starting key // (test removed because v.second may change after %%staves) // if (!p_voice.second && !p_voice.norepbra) set_rb(p_voice) } /* set the staff of the floating voices */ if (nv > 1) { set_float() // expand the multi-rests as needed if (glovar.mrest_p) mrest_expand() } if (glovar.ottava && cfmt.sound != "play") set_ottava(); // set the clefs and adjust the pitches of all symbol set_clefs(); self.set_pitch(null) } // get the left offsets of the first and other staff systems // return [lsh1, lsho] function get_lshift() { var st, v, p_v, p1, po, fnt, w, sy = cur_sy, lsh1 = 0, lsho = 0, nv = voice_tb.length // get the max width of a voice name/subname function get_wx(p, wx) { var w, j, i = 0 p += '\n' while (1) { j = p.indexOf("\n", i) if (j < 0) break w = strwh(p.slice(i, j))[0] + 12 if (w > wx) wx = w if (j < 0) break i = j + 1 } return wx } // get_wx() for (v = 0; v < nv; v++) { p_v = voice_tb[v] p1 = p_v.nm po = p_v.snm if ((p1 || po) && !fnt) { set_font("voice") fnt = gene.deffont } if (p1) { w = get_wx(p1, lsh1) if (w > lsh1) lsh1 = w } if (po) { w = get_wx(po, lsho) if (w > lsho) lsho = w } } // add the width of the braces/brackets w = 0 while (sy) { for (st = 0; st <= sy.nstaff; st++) { if (sy.staves[st].flags & (OPEN_BRACE2 | OPEN_BRACKET2)) { w = 12 break } if (sy.staves[st].flags & (OPEN_BRACE | OPEN_BRACKET)) w = 6 } if (w == 12) break sy = sy.next } lsh1 += w lsho += w return [lsh1, lsho] } // get_lshift() /* -- return the left indentation of the staves -- */ function set_indent(lsh) { var st, v, w, p_voice, p, i, j, font, vnt = 0, fmt = tsnext ? tsnext.fmt : cfmt // name or subname? if (fmt.systnames) { // display the names in the staff system for (v = voice_tb.length; --v >= 0; ) { p_voice = voice_tb[v] if (!cur_sy.voices[v] || !gene.st_print[p_voice.st]) continue if (p_voice.nm && (p_voice.new_name || fmt.systnames == 2)) { vnt = 2 // full name break } if (p_voice.snm) vnt = 1 // subname } } gene.vnt = vnt // voice name type for draw return vnt == 2 ? lsh[0] : lsh[1] } /* -- decide on beams and on stem directions -- */ /* this routine is called only once per tune */ function set_beams(sym) { var s, t, g, beam, s_opp, n, m, mid_p, pu, pd, laststem = -1 for (s = sym; s; s = s.next) { if (s.type != C.NOTE) { if (s.type != C.GRACE) continue g = s.extra if (g.stem == 2) { /* opposite gstem direction */ s_opp = s continue } if (!s.stem) s.stem = s.multi || 1 for (; g; g = g.next) { g.stem = s.stem; g.multi = s.multi } continue } if (!s.stem && s.multi) s.stem = s.multi if (!s.stem) { // if note alone on the staff mid_p = s.mid / 3 + 18 /* notes in a beam have the same stem direction */ if (beam) { s.stem = laststem } else if (s.beam_st && !s.beam_end) { // beam start beam = true; // the stem direction is the one of the note // farthest from the middle line pu = s.notes[s.nhd].pit; pd = s.notes[0].pit for (g = s.next; g; g = g.next) { if (g.type != C.NOTE) continue if (g.stem || g.multi) // if forced direction s.stem = g.stem || g.multi if (g.notes[g.nhd].pit > pu) pu = g.notes[g.nhd].pit if (g.notes[0].pit < pd) pd = g.notes[0].pit if (g.beam_end) break } if (!s.stem && g.beam_end) { if (pu + pd < mid_p * 2) { s.stem = 1 } else if (pu + pd > mid_p * 2) { s.stem = -1 } else { if (s.fmt.bstemdown) s.stem = -1 } } if (!s.stem) s.stem = laststem } else { // no beam n = (s.notes[s.nhd].pit + s.notes[0].pit) / 2 if (n == mid_p && s.nhd > 1) { for (m = 0; m < s.nhd; m++) { if (s.notes[m].pit >= mid_p) break } n = m * 2 < s.nhd ? mid_p - 1 : mid_p + 1 } if (n < mid_p) s.stem = 1 else if (n > mid_p || s.fmt.bstemdown) s.stem = -1 else s.stem = laststem } } else { /* stem set by set_stem_dir */ if (s.beam_st && !s.beam_end) beam = true } if (s.beam_end) beam = false; laststem = s.stem; if (s_opp) { /* opposite gstem direction */ for (g = s_opp.extra; g; g = g.next) g.stem = -laststem; s_opp.stem = -laststem; s_opp = null } } } // check if there may be one head for unison when voice overlap function same_head(s1, s2) { var i1, i2, l1, l2, head, i11, i12, i21, i22, sh1, sh2, shu = s1.fmt.shiftunison || 0 if (shu >= 3) return false if ((l1 = s1.dur) >= C.BLEN) return false if ((l2 = s2.dur) >= C.BLEN) return false if (s1.stemless && s2.stemless) return false if (s1.dots != s2.dots) { if (shu & 1 || s1.dots * s2.dots != 0) return false } if (s1.stem * s2.stem > 0) return false /* check if a common unison */ i1 = i2 = 0 if (s1.notes[0].pit > s2.notes[0].pit) { //fixme:dots if (s1.stem < 0) return false while (s2.notes[i2].pit != s1.notes[0].pit) { if (++i2 > s2.nhd) return false } } else if (s1.notes[0].pit < s2.notes[0].pit) { //fixme:dots if (s2.stem < 0) return false while (s2.notes[0].pit != s1.notes[i1].pit) { if (++i1 > s1.nhd) return false } } if (s2.notes[i2].acc != s1.notes[i1].acc) return false; i11 = i1; i21 = i2; sh1 = s1.notes[i1].shhd; sh2 = s2.notes[i2].shhd do { //fixme:dots i1++; i2++ if (i1 > s1.nhd) { //fixme:dots // if (s1.notes[0].pit < s2.notes[0].pit) // return false break } if (i2 > s2.nhd) { //fixme:dots // if (s1.notes[0].pit > s2.notes[0].pit) // return false break } if (s2.notes[i2].acc != s1.notes[i1].acc) return false if (sh1 < s1.notes[i1].shhd) sh1 = s1.notes[i1].shhd if (sh2 < s2.notes[i2].shhd) sh2 = s2.notes[i2].shhd } while (s2.notes[i2].pit == s1.notes[i1].pit) //fixme:dots if (i1 <= s1.nhd) { if (i2 <= s2.nhd) return false if (s2.stem > 0) return false } else if (i2 <= s2.nhd) { if (s1.stem > 0) return false } i12 = i1; i22 = i2; head = 0 if (l1 != l2) { if (l1 < l2) { l1 = l2; l2 = s1.dur } if (l1 < C.BLEN / 2) { if (s2.dots) head = 2 else if (s1.dots) head = 1 } else if (l2 < C.BLEN / 4) { /* (l1 >= C.BLEN / 2) */ // if (shu == 2) // || s1.dots != s2.dots) if (shu & 2) return false head = s2.dur >= C.BLEN / 2 ? 2 : 1 } else { return false } } if (!head) head = s1.p_v.scale < s2.p_v.scale ? 2 : 1 if (head == 1) { for (i2 = i21; i2 < i22; i2++) { s2.notes[i2].invis = true delete s2.notes[i2].acc } for (i2 = 0; i2 <= s2.nhd; i2++) s2.notes[i2].shhd += sh1 } else { for (i1 = i11; i1 < i12; i1++) { s1.notes[i1].invis = true delete s1.notes[i1].acc } for (i1 = 0; i1 <= s1.nhd; i1++) s1.notes[i1].shhd += sh2 } return true } /* handle unison with different accidentals */ function unison_acc(s1, s2, i1, i2) { var m, d, acc acc = s2.notes[i2].acc if (!acc) { d = w_note[s2.head] * 2 + s2.xmx + s1.notes[i1].shac + 2 acc = s1.notes[i1].acc if (typeof acc == "object") // microtone d += 2 if (s2.dots) d += 6 for (m = 0; m <= s1.nhd; m++) { s1.notes[m].shhd += d; s1.notes[m].shac -= d } s1.xmx += d } else { d = w_note[s1.head] * 2 + s1.xmx + s2.notes[i2].shac + 2 if (typeof acc == "object") // microtone d += 2 if (s1.dots) d += 6 for (m = 0; m <= s2.nhd; m++) { s2.notes[m].shhd += d; s2.notes[m].shac -= d } s2.xmx += d } } var MAXPIT = 48 * 2 /* set the left space of a note/chord */ function set_left(s) { var m, i, j, shift, w_base = w_note[s.head], w = w_base, left = [] for (i = 0; i < MAXPIT; i++) left.push(-100) /* stem */ if (s.nflags > -2) { if (s.stem > 0) { w = -w; i = s.notes[0].pit * 2; j = (Math.ceil((s.ymx - 2) / 3) + 18) * 2 } else { i = (Math.ceil((s.ymn + 2) / 3) + 18) * 2; j = s.notes[s.nhd].pit * 2 } if (i < 0) i = 0 if (j >= MAXPIT) j = MAXPIT - 1 while (i <= j) left[i++] = w } /* notes */ shift = s.notes[s.stem > 0 ? 0 : s.nhd].shhd; /* previous shift */ for (m = 0; m <= s.nhd; m++) { w = -s.notes[m].shhd + w_base + shift; i = s.notes[m].pit * 2 if (i < 0) i = 0 else if (i >= MAXPIT - 1) i = MAXPIT - 2 if (w > left[i]) left[i] = w if (s.head != C.SQUARE) w -= 1 if (w > left[i - 1]) left[i - 1] = w if (w > left[i + 1]) left[i + 1] = w } return left } /* set the right space of a note/chord */ function set_right(s) { var m, i, j, k, shift, w_base = w_note[s.head], w = w_base, flags = s.nflags > 0 && s.beam_st && s.beam_end, right = [] for (i = 0; i < MAXPIT; i++) right.push(-100) /* stem and flags */ if (s.nflags > -2) { if (s.stem < 0) { w = -w; i = (Math.ceil((s.ymn + 2) / 3) + 18) * 2; j = s.notes[s.nhd].pit * 2; k = i + 4 } else { i = s.notes[0].pit * 2; j = (Math.ceil((s.ymx - 2) / 3) + 18) * 2 } if (i < 0) i = 0 if (j > MAXPIT) j = MAXPIT while (i < j) right[i++] = w } if (flags) { if (s.stem > 0) { if (s.xmx == 0) i = s.notes[s.nhd].pit * 2 else i = s.notes[0].pit * 2; i += 4 if (i < 0) i = 0 for (; i < MAXPIT && i <= j - 4; i++) right[i] = 11 } else { i = k if (i < 0) i = 0 for (; i < MAXPIT && i <= s.notes[0].pit * 2 - 4; i++) right[i] = 3.5 } } /* notes */ shift = s.notes[s.stem > 0 ? 0 : s.nhd].shhd /* previous shift */ for (m = 0; m <= s.nhd; m++) { w = s.notes[m].shhd + w_base - shift; i = s.notes[m].pit * 2 if (i < 0) i = 0 else if (i >= MAXPIT - 1) i = MAXPIT - 2 if (w > right[i]) right[i] = w if (s.head != C.SQUARE) w -= 1 if (w > right[i - 1]) right[i - 1] = w if (w > right[i + 1]) right[i + 1] = w } return right } /* -- shift the notes horizontally when voices overlap -- */ /* this routine is called only once per tune */ function set_overlap() { var s, s1, s2, s3, i, i1, i2, m, sd, t, dp, d, d2, dr, dr2, dx, left1, right1, left2, right2, right3, pl, pr, sy = cur_sy // invert the voices function v_invert() { s1 = s2; s2 = s; d = d2; pl = left1; pr = right1; dr2 = dr } for (s = tsfirst; s; s = s.ts_next) { if (s.type != C.NOTE || s.invis) { if (s.type == C.STAVES) sy = s.sy continue } /* treat the stem on two staves with different directions */ if (s.xstem && s.ts_prev.stem < 0) { for (m = 0; m <= s.nhd; m++) { s.notes[m].shhd -= 7; // stem_xoff s.notes[m].shac += 16 } } /* search the next note at the same time on the same staff */ s2 = s while (1) { s2 = s2.ts_next if (!s2) break if (s2.time != s.time) { s2 = null break } if (s2.type == C.NOTE && !s2.invis && s2.st == s.st) break } if (!s2) continue s1 = s /* set the dot vertical offset */ if (sy.voices[s1.v].range < sy.voices[s2.v].range) s2.dot_low = true else s1.dot_low = true /* no shift if no overlap */ if (s1.ymn > s2.ymx || s1.ymx < s2.ymn) continue if (same_head(s1, s2)) continue // special case when only a second and no dots if (!s1.dots && !s2.dots) if ((s1.stem > 0 && s2.stem < 0 && s1.notes[0].pit == s2.notes[s2.nhd].pit + 1) || (s1.stem < 0 && s2.stem > 0 && s1.notes[s1.nhd].pit + 1 == s2.notes[0].pit)) { if (s1.stem < 0) { s1 = s2; s2 = s } d = s1.notes[0].shhd + 7 for (m = 0; m <= s2.nhd; m++) // shift the lower note(s) s2.notes[m].shhd += d s2.xmx += d s1.xmx = s2.xmx // align the dots continue } /* compute the minimum space for 's1 s2' and 's2 s1' */ right1 = set_right(s1); left2 = set_left(s2); s3 = s1.ts_prev if (s3 && s3.time == s1.time && s3.st == s1.st && s3.type == C.NOTE && !s3.invis) { right3 = set_right(s3) for (i = 0; i < MAXPIT; i++) { if (right3[i] > right1[i]) right1[i] = right3[i] } } else { s3 = null } d = -10 for (i = 0; i < MAXPIT; i++) { if (left2[i] + right1[i] > d) d = left2[i] + right1[i] } if (d < -3 // no clash if no dots clash && ((s2.notes[0].pit & 1) || !(s1.dots || s2.dots) || (!(s1.notes[s1.nhd].pit == s2.notes[0].pit + 2 && s1.dot_low) && !(s1.notes[s1.nhd].pit + 2 == s2.notes[0].pit && s2.dot_low)))) continue right2 = set_right(s2); left1 = set_left(s1) if (s3) { right3 = set_left(s3) for (i = 0; i < MAXPIT; i++) { if (right3[i] > left1[i]) left1[i] = right3[i] } } d2 = dr = dr2 = -100 for (i = 0; i < MAXPIT; i++) { if (left1[i] + right2[i] > d2) d2 = left1[i] + right2[i] if (right2[i] > dr2) dr2 = right2[i] if (right1[i] > dr) dr = right1[i] } /* check for unison with different accidentals * and clash of dots */ t = 0; i1 = s1.nhd; i2 = s2.nhd while (1) { dp = s1.notes[i1].pit - s2.notes[i2].pit switch (dp) { case 2: if (!(s1.notes[i1].pit & 1)) s1.dot_low = false break case 1: if (s1.notes[i1].pit & 1) s2.dot_low = true else s1.dot_low = false break case 0: if (s1.notes[i1].acc != s2.notes[i2].acc) { t = -1 break } if (s2.notes[i2].acc) { if (!s1.notes[i1].acc) s1.notes[i1].acc = s2.notes[i2].acc s2.notes[i2].acc = 0 } if (s1.dots && s2.dots && (s1.notes[i1].pit & 1)) t = 1 break case -1: if (s1.notes[i1].pit & 1) s2.dot_low = false else s1.dot_low = true break case -2: if (!(s1.notes[i1].pit & 1)) s2.dot_low = false break } if (t < 0) break if (dp >= 0) { if (--i1 < 0) break } if (dp <= 0) { if (--i2 < 0) break } } if (t < 0) { /* unison and different accidentals */ unison_acc(s1, s2, i1, i2) continue } sd = 0; if (s1.dots) { if (s2.dots) { if (!t) /* if no dot clash */ sd = 1 /* align the dots */ } else { v_invert() // shift the first voice } } else if (s2.dots) { if (d2 + dr < d + dr2) sd = 1 /* align the dots */ } pl = left2; pr = right2 if (!s3 && d2 + dr < d + dr2) v_invert() d += 3 if (d < 0) d = 0; // (not return!) /* handle the previous shift */ m = s1.stem >= 0 ? 0 : s1.nhd; d += s1.notes[m].shhd; m = s2.stem >= 0 ? 0 : s2.nhd; d -= s2.notes[m].shhd /* * room for the dots * - if the dots of v1 don't shift, adjust the shift of v2 * - otherwise, align the dots and shift them if clash */ if (s1.dots) { dx = 7.7 + s1.xmx + // x 1st dot 3.5 * s1.dots - 3.5 + // x last dot 3; // some space if (!sd) { d2 = -100; for (i1 = 0; i1 <= s1.nhd; i1++) { i = s1.notes[i1].pit if (!(i & 1)) { if (!s1.dot_low) i++ else i-- } i *= 2 if (i < 1) i = 1 else if (i >= MAXPIT - 1) i = MAXPIT - 2 if (pl[i] > d2) d2 = pl[i] if (pl[i - 1] + 1 > d2) d2 = pl[i - 1] + 1 if (pl[i + 1] + 1 > d2) d2 = pl[i + 1] + 1 } if (dx + d2 + 2 > d) d = dx + d2 + 2 } else { if (dx < d + dr2 + s2.xmx) { d2 = 0 for (i1 = 0; i1 <= s1.nhd; i1++) { i = s1.notes[i1].pit if (!(i & 1)) { if (!s1.dot_low) i++ else i-- } i *= 2 if (i < 1) i = 1 else if (i >= MAXPIT - 1) i = MAXPIT - 2 if (pr[i] > d2) d2 = pr[i] if (pr[i - 1] + 1 > d2) d2 = pr[i - 1] = 1 if (pr[i + 1] + 1 > d2) d2 = pr[i + 1] + 1 } if (d2 > 4.5 && 7.7 + s1.xmx + 2 < d + d2 + s2.xmx) s2.xmx = d2 + 3 - 7.7 } } } for (m = s2.nhd; m >= 0; m--) { s2.notes[m].shhd += d // if (s2.notes[m].acc // && s2.notes[m].pit < s1.notes[0].pit - 4) // s2.notes[m].shac -= d } s2.xmx += d if (sd) s1.xmx = s2.xmx // align the dots } } /* -- set the stem height -- */ /* this routine is called only once per tune */ // (possible hook) Abc.prototype.set_stems = function() { var s, s2, g, slen, scale,ymn, ymx, nflags, ymin, ymax for (s = tsfirst; s; s = s.ts_next) { if (s.type != C.NOTE) { if (s.type != C.GRACE) continue ymin = ymax = s.mid for (g = s.extra; g; g = g.next) { slen = GSTEM if (g.nflags > 1) slen += 1.2 * (g.nflags - 1); ymn = 3 * (g.notes[0].pit - 18); ymx = 3 * (g.notes[g.nhd].pit - 18) if (s.stem >= 0) { g.y = ymn; g.ys = ymx + slen; ymx = Math.round(g.ys) } else { g.y = ymx; g.ys = ymn - slen; ymn = Math.round(g.ys) } ymx += 4 ymn -= 4 if (ymn < ymin) ymin = ymn else if (ymx > ymax) ymax = ymx; g.ymx = ymx; g.ymn = ymn } s.ymx = ymax; s.ymn = ymin continue } /* shift notes in chords (need stem direction to do this) */ set_head_shift(s); /* if start or end of beam, adjust the number of flags * with the other end */ nflags = s.nflags if (s.beam_st && !s.beam_end) { if (s.feathered_beam) nflags = ++s.nflags for (s2 = s.next; /*s2*/; s2 = s2.next) { if (s2.type == C.NOTE) { if (s.feathered_beam) s2.nflags++ if (s2.beam_end) break } } /* if (s2) */ if (s2.nflags > nflags) nflags = s2.nflags } else if (!s.beam_st && s.beam_end) { //fixme: keep the start of beam ? for (s2 = s.prev; /*s2*/; s2 = s2.prev) { if (s2.beam_st) break } /* if (s2) */ if (s2.nflags > nflags) nflags = s2.nflags } /* set height of stem end */ slen = s.fmt.stemheight switch (nflags) { case 2: slen += 0; break case 3: slen += 4; break case 4: slen += 8; break case 5: slen += 12; break } if ((scale = s.p_v.scale) != 1) slen *= (scale + 1) * .5; ymn = 3 * (s.notes[0].pit - 18) if (s.nhd > 0) { slen -= 2; ymx = 3 * (s.notes[s.nhd].pit - 18) } else { ymx = ymn } if (s.ntrem) slen += 2 * s.ntrem /* tremolo */ if (s.stemless) { if (s.stem >= 0) { s.y = ymn; s.ys = ymx } else { s.ys = ymn; s.y = ymx } s.ymx = ymx + 4; s.ymn = ymn - 4 } else if (s.stem >= 0) { if (s.notes[s.nhd].pit > 26 && (nflags <= 0 || !s.beam_st || !s.beam_end)) { slen -= 2 if (s.notes[s.nhd].pit > 28) slen -= 2 } s.y = ymn if (s.notes[0].tie) ymn -= 3; s.ymn = ymn - 4; s.ys = ymx + slen if (s.ys < s.mid) s.ys = s.mid; s.ymx = (s.ys + 2.5) | 0 } else { /* stem down */ if (s.notes[0].pit < 18 && (nflags <= 0 || !s.beam_st || !s.beam_end)) { slen -= 2 if (s.notes[0].pit < 16) slen -= 2 } s.ys = ymn - slen if (s.ys > s.mid) s.ys = s.mid; s.ymn = (s.ys - 2.5) | 0; s.y = ymx /*fixme:the tie may be lower*/ if (s.notes[s.nhd].tie) ymx += 3; s.ymx = ymx + 4 } } } // generate a block symbol var blocks = [] // array of delayed block symbols // (possible hook) Abc.prototype.block_gen = function(s) { switch (s.subtype) { case "leftmargin": case "rightmargin": case "pagescale": case "pagewidth": case "scale": case "staffwidth": self.set_format(s.subtype, s.param) break case "mc_start": // multicol start if (multicol) { error(1, s, "No end of the previous %%multicol") break } multicol = { state: parse.state, posy: posy, maxy: posy, lm: cfmt.leftmargin, rm: cfmt.rightmargin, w: cfmt.pagewidth, sc: cfmt.scale } break case "mc_new": // multicol new if (!multicol || multicol.state != parse.state) { error(1, s, "%%multicol new without start") break } if (posy > multicol.maxy) multicol.maxy = posy cfmt.leftmargin = multicol.lm cfmt.rightmargin = multicol.rm cfmt.pagewidth = multicol.w cfmt.scale = multicol.sc posy = multicol.posy img.chg = 1 //true break case "mc_end": // multicol end if (!multicol || multicol.state != parse.state) { error(1, s, "%%multicol end without start") break } if (posy < multicol.maxy) posy = multicol.maxy cfmt.leftmargin = multicol.lm cfmt.rightmargin = multicol.rm cfmt.pagewidth = multicol.w cfmt.scale = multicol.sc multicol = undefined blk_flush() img.chg = 1 //true break case "ml": blk_flush() user.img_out(s.text) break case "newpage": if (!user.page_format) break blk_flush() if (blkdiv < 0) // split the tune user.img_out('') blkdiv = 2 // start the next SVG in a new page break case "sep": set_page(); vskip(s.sk1); output += '\n'; vskip(s.sk2); break case "text": set_font(s.font) use_font(s.font) write_text(s.text, s.opt) break case "title": write_title(s.text, true) break case "vskip": vskip(s.sk); break } } /* -- define the start and end of a piece of tune -- */ /* tsnext becomes the beginning of the next line */ function set_piece() { var s, last, p_voice, st, v, nv, tmp, non_empty, non_empty_gl = [], sy = cur_sy function reset_staff(st) { var p_staff = staff_tb[st], sy_staff = sy.staves[st] if (!p_staff) p_staff = staff_tb[st] = {} p_staff.y = 0; // staff system not computed yet p_staff.stafflines = sy_staff.stafflines; p_staff.staffscale = sy_staff.staffscale; p_staff.ann_top = p_staff.ann_bot = 0 } // reset_staff() // adjust the empty flag of brace systems function set_brace() { var st, i, empty_fl, n = sy.staves.length // if a system brace has empty and non empty staves, keep all staves for (st = 0; st < n; st++) { if (!(sy.staves[st].flags & (OPEN_BRACE | OPEN_BRACE2))) continue empty_fl = 0; i = st while (st < n) { empty_fl |= non_empty[st] ? 1 : 2 if (sy.staves[st].flags & (CLOSE_BRACE | CLOSE_BRACE2)) break st++ } if (empty_fl == 3) { // if both empty and not empty staves while (i <= st) { non_empty[i] = true; non_empty_gl[i++] = true } } } } // set_brace() // set the top and bottom of the staves function set_top_bot() { var st, p_staff, i, j, l for (st = 0; st <= nstaff; st++) { p_staff = staff_tb[st] // ledger lines // index = line number // values = [x symbol, x start, x stop] p_staff.hlu = [] // above the staff p_staff.hld = [] // under the staff l = p_staff.stafflines.length; p_staff.topbar = 6 * (l - 1) for (i = 0; i < l - 1; i++) { switch (p_staff.stafflines[i]) { case '.': case '-': continue } break } p_staff.botline = p_staff.botbar = i * 6 if (i >= l - 2) { // 0, 1 or 2 lines if (p_staff.stafflines[i] != '.') { p_staff.botbar -= 6; p_staff.topbar += 6 } else { // no line: big bar p_staff.botbar -= 12; p_staff.topbar += 12 continue // no helper line } } if (!non_empty_gl[st]) continue // define the helper lines p_staff.hll = 17 + i * 2 // pitch of lowest note // without helper line // ('D' when standard staff) p_staff.hlmap = new Int8Array((l - i + 1) * 2 + 2) // (bug android 4.0) for (j = 1; i < l; i++, j += 2) { switch (p_staff.stafflines[i]) { case '|': case '[': p_staff.hlmap[j - 1] = 1; // no helper line p_staff.hlmap[j] = 1; p_staff.hlmap[j + 1] = 1 break } } } } // set_top_bot() // remove the staff system at start of line if (tsfirst.type == C.STAVES) { s = tsfirst tsfirst = tsfirst.ts_next tsfirst.ts_prev = null if (s.seqst) tsfirst.seqst = true s.p_v.sym = s.next if (s.next) s.next.prev = null } /* reset the staves */ nstaff = sy.nstaff for (st = 0; st <= nstaff; st++) reset_staff(st); non_empty = new Uint8Array(nstaff + 1) /* * search the next end of line, * and mark the empty staves */ for (s = tsfirst; s; s = s.ts_next) { if (s.nl) break switch (s.type) { case C.STAVES: set_brace(); sy.st_print = non_empty sy = s.sy; while (nstaff < sy.nstaff) reset_staff(++nstaff) non_empty = new Uint8Array(nstaff + 1) continue // the block symbols will be treated after music line generation case C.BLOCK: if (!s.play) { blocks.push(s) unlksym(s) } else if (s.ts_next && s.ts_next.shrink) s.ts_next.shrink = 0 continue } st = s.st if (st > nstaff) { switch (s.type) { case C.CLEF: staff_tb[st].clef = s // clef warning/change for new staff break case C.KEY: s.p_v.ckey = s break //useless ? case C.METER: s.p_v.meter = s break } unlksym(s) continue } if (non_empty[st]) continue switch (s.type) { default: continue case C.BAR: if (s.bar_mrep || sy.staves[st].staffnonote > 1) break continue case C.GRACE: break case C.NOTE: case C.REST: case C.SPACE: case C.MREST: if (sy.staves[st].staffnonote > 1) break if (s.invis) continue if (sy.staves[st].staffnonote || s.type == C.NOTE) break continue } non_empty_gl[st] = non_empty[st] = true } tsnext = s; /* set the last empty staves */ set_brace() sy.st_print = non_empty /* define the offsets of the measure bars */ set_top_bot() // if not the end of the tune, set the end of the music line if (tsnext) { s = tsnext; delete s.nl; last = s.ts_prev; last.ts_next = null; // and the end of the voices nv = voice_tb.length for (v = 0; v < nv; v++) { p_voice = voice_tb[v] if (p_voice.sym && p_voice.sym.time <= tsnext.time) { for (s = last; s; s = s.ts_prev) { if (s.v == v) { p_voice.s_next = s.next; s.next = null; break } } if (s) continue } p_voice.s_next = p_voice.sym; p_voice.sym = null } } // initialize the music line init_music_line() // keep the array of the staves to be printed gene.st_print = non_empty_gl } /* -- position the symbols along the staff -- */ // (possible hook) Abc.prototype.set_sym_glue = function(width) { var g, x, some_grace, stretch, cnt = 4, xmin = 0, // sigma shrink = minimum spacing xx = 0, // sigma natural spacing xs = 0, // sigma unexpandable elements with no space xse = 0, // sigma unexpandable elements with space ll = !tsnext || // last line? yes (tsnext.type == C.BLOCK // no, but followed by %%command && !tsnext.play) || blocks.length, // (abcm2ps compatibility) s = tsfirst, spf = 1, // spacing factor xx0 = 0 /* calculate the whole space of the symbols */ for ( ; s; s = s.ts_next) { if (s.type == C.GRACE && !some_grace) some_grace = s if (s.seqst) { xmin += s.shrink if (xmin > width) { error(1, s, "Line too much shrunk $1 $2 $3", xmin.toFixed(1), xx.toFixed(1), width.toFixed(1)) break } if (s.space) { if (s.space < s.shrink) { xse += s.shrink; xx += s.shrink } else { // xx += s.space * spf + s.shrink * (1 - spf) xx += s.space xx0 += s.shrink } } else { xs += s.shrink } } } // can occur when bar alone in a staff system if (!xx) { realwidth = 0 return } // stretch or not? s = tsfirst if (ll) { if ((xx - xx0 + xs) / width > (1 - s.fmt.stretchlast)) stretch = 1 //true } else if (s.fmt.stretchstaff) { stretch = 1 //true } // strong shrink if (xmin >= width) { x = 0 for ( ; s; s = s.ts_next) { if (s.seqst) x += s.shrink; s.x = x } // realwidth = width spf_last = .65 } else { if (stretch) { if (xx == xse) // if no space xx += 10 spf = (width - xs - xse) / (xx - xse) } else { spf = spf_last if (ll && spf < s.fmt.stretchlast) spf = s.fmt.stretchlast if (spf > (width - xs) / xx) spf = (width - xs) / xx } while (--cnt >= 0) { xx = 0; xse = 0; x = 0 for (s = tsfirst; s; s = s.ts_next) { if (s.seqst) { if (s.space) { if (s.space * spf <= s.shrink) { xse += s.shrink; xx += s.shrink; x += s.shrink } else { xx += s.space; x += s.space * spf } } else { x += s.shrink } } s.x = x } if (!stretch && x < width) break if (Math.abs(x - width) < 0.1) break if (xx == xse) // if no space xx += 10 spf = (width - xs - xse) / (xx - xse) } spf_last = spf } realwidth = x /* set the x offsets of the grace notes */ for (s = some_grace; s; s = s.ts_next) { if (s.type != C.GRACE) continue if (s.gr_shift) x = s.prev.x + s.prev.wr else x = s.x - s.wl for (g = s.extra; g; g = g.next) g.x += x } } // set the starting symbols of the voices for the new music line function set_sym_line() { var p_v, s, v = voice_tb.length // set the first symbol of each voice while (--v >= 0) { p_v = voice_tb[v] if (p_v.sym && p_v.s_prev) { p_v.sym.prev = p_v.s_prev p_v.s_prev.next = p_v.sym } s = p_v.s_next // (set in set_piece) p_v.s_next = null p_v.sym = s if (s) { if (s.prev) s.prev.next = s p_v.s_prev = s.prev // (save for play) s.prev = null } else { p_v.s_prev = null } } } // set the left offset the images function set_posx() { posx = img.lm / cfmt.scale } // initialize the start of generation / new music line // and output the inter-staff blocks if any function gen_init() { var s = tsfirst, tim = s.time for ( ; s; s = s.ts_next) { if (s.time != tim) { set_page() return } switch (s.type) { case C.NOTE: case C.REST: case C.MREST: case C.SPACE: set_page() return default: continue case C.STAVES: cur_sy = s.sy // break continue case C.BLOCK: if (s.play) continue // keep for play self.block_gen(s) break } unlksym(s) if (s.p_v.s_next == s) s.p_v.s_next = s.next } tsfirst = null /* no more notes */ } /* -- generate the music -- */ // (possible hook) Abc.prototype.output_music = function() { var v, lwidth, indent, lsh, line_height, ts1st, tslast, p_v, nv = voice_tb.length set_global() if (nv > 1) // if many voices self.set_stem_dir() // set the stems direction in 'multi' for (v = 0; v < nv; v++) set_beams(voice_tb[v].sym); /* decide on beams */ self.set_stems() // set the stem lengths set_acc_shft() // set the horizontal offset of accidentals if (nv > 1) { // if many voices set_rest_offset(); /* set the vertical offset of rests */ set_overlap(); /* shift the notes on voice overlap */ } set_allsymwidth(1) // set the width of all symbols // output the blocks and define the page layout gen_init() if (!tsfirst) return lsh = get_lshift() /* if single line, adjust the page width */ if (cfmt.singleline) { v = get_ck_width(); lwidth = lsh[0] + v[0] + v[1] + get_width(tsfirst, null)[0] v = cfmt.singleline == 2 // if as wide as the page width ? get_lwidth() : lwidth if (v > lwidth) lwidth = v else img.width = lwidth * cfmt.scale + img.lm + img.rm + 2 } else { /* else, split the tune into music lines */ lwidth = get_lwidth(); cut_tune(lwidth, lsh) } // save symbol pointers for play ts1st = tsfirst v = nv while (--v >= 0) voice_tb[v].osym = voice_tb[v].sym spf_last = .65 // last spacing factor while (1) { /* loop per music line */ set_piece(); indent = set_indent(lsh) if (!line_height && cfmt.indent && indent < cfmt.indent) indent = cfmt.indent self.set_sym_glue(lwidth - indent) if (realwidth) { if (img.wx < realwidth) img.wx = realwidth if (indent) { img.wx += indent posx += indent } draw_sym_near(); // delayed output line_height = set_staff(); if (line_height) { // if some music draw_systems(indent); draw_all_sym(); delayed_update(); vskip(line_height) } if (indent) posx -= indent; } blk_flush() while (blocks.length) self.block_gen(blocks.shift()) if (tslast) tslast.ts_next.ts_prev = tslast if (!tsnext) break tsnext.ts_prev.ts_next = // (restore for play) tsfirst = tsnext // next line gen_init() if (!tsfirst) break tslast = tsfirst.ts_prev tsfirst.ts_prev = null; set_sym_line(); lwidth = get_lwidth() // the image size may have changed } // restore for play tsfirst = ts1st v = nv while (--v >= 0) { p_v = voice_tb[v] if (p_v.sym && p_v.s_prev) p_v.sym.prev = p_v.s_prev p_v.sym = p_v.osym } } // abc2svg - parse.js - ABC parse // // Copyright (C) 2014-2024 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 . var a_gch, // array of parsed guitar chords a_dcn = [], // array of parsed decoration names multicol, // multi column object maps = {} // maps object - see set_map() var qplet_tb = new Int8Array([ 0, 1, 3, 2, 3, 0, 2, 0, 3, 0 ]), ntb = "CDEFGABcdefgab" // set the source references of a symbol function set_ref(s) { s.fname = parse.fname; s.istart = parse.istart; s.iend = parse.iend } // -- %% pseudo-comment // clef definition (%%clef, K: and V:) function new_clef(clef_def) { var s = { type: C.CLEF, clef_line: 2, clef_type: "t", v: curvoice.v, p_v: curvoice, time: curvoice.time, dur: 0 }, i = 1 set_ref(s) switch (clef_def[0]) { case '"': i = clef_def.indexOf('"', 1); s.clef_name = clef_def.slice(1, i); i++ break case 'a': if (clef_def[1] == 'u') { // auto s.clef_type = "a"; s.clef_auto = true; i = 4 break } i = 4 // alto case 'C': s.clef_type = "c"; s.clef_line = 3 break case 'b': // bass i = 4 case 'F': s.clef_type = "b"; s.clef_line = 4 break case 'n': // none i = 4 s.invis = true s.clef_none = 1 //true break case 't': if (clef_def[1] == 'e') { // tenor s.clef_type = "c"; s.clef_line = 4 break } i = 6 case 'G': // s.clef_type = "t" // treble break case 'p': i = 4 case 'P': // perc s.clef_type = "p"; s.clef_line = 3; break default: syntax(1, "Unknown clef '$1'", clef_def) return //undefined } if (clef_def[i] >= '1' && clef_def[i] <= '9') { s.clef_line = +clef_def[i] i++ } // handle the octave (+/-8 - ^/_8) delete curvoice.snd_oct if (clef_def[i + 1] != '8' && clef_def[i + 1] != '1') return s switch (clef_def[i]) { // octave case '^': s.clef_oct_transp = true case '+': s.clef_octave = clef_def[i + 1] == '8' ? 7 : 14 if (!s.clef_oct_transp) // MIDI higher octave curvoice.snd_oct = clef_def[i + 1] == 8 ? 12 : 24 break case '_': s.clef_oct_transp = true case '-': s.clef_octave = clef_def[i + 1] == '8' ? -7 : -14 if (!s.clef_oct_transp) // MIDI lower octave curvoice.snd_oct = clef_def[i + 1] == 8 ? -12 : -24 break } return s } // convert an interval to a base-40 interval function get_interval(param, score) { var i, val, tmp, note, pit tmp = new scanBuf; tmp.buffer = param pit = [] for (i = 0; i < 2; i++) { note = tmp.buffer[tmp.index] ? parse_acc_pit(tmp) : null if (!note) { if (i != 1 || !score) { syntax(1, errs.bad_transp) return } pit[i] = 242 // 'c' (C5) } else { if (typeof note.acc == "object") { syntax(1, errs.bad_transp) return } pit[i] = abc2svg.pab40(note.pit, note.acc) } } return pit[1] - pit[0] } // transpose a note for the score // Return the transposed real accidental function nt_trans(nt, a) { // real accidental var ak, an, d, b40, n if (typeof a == "object") { // if microtonal accidental n = a[0] // numerator d = a[1] // denominator a = n > 0 ? 1 : -1 // base accidental for transpose } b40 = abc2svg.pab40(nt.pit, a) + curvoice.tr_sco // base-40 transposition nt.pit = abc2svg.b40p(b40) // new pitch an = abc2svg.b40a(b40) // new accidental if (!d) { // if not a microtonal accidental if (an == -3) // if triple sharp/flat return an a = an if (nt.acc) { // if old accidental if (!a) a = 3 // required natural } else { if (!curvoice.ckey.k_none) // if normal key a = 0 // no accidental } nt.acc = a return an } // set the microtonal accidental after transposition switch (an) { case -2: if (n > 0) n -= d * 2 else n -= d break case -1: if (n > 0) n -= d break case 0: case 3: if (n > 0) n -= d else n += d break case 1: if (n < 0) n += d break case 2: if (n < 0) n += d * 2 else n += d break } nt.acc = [n, d] return an } // nt_trans() // set the linebreak character function set_linebreak(param) { var i, item for (i = 0; i < 128; i++) { if (char_tb[i] == "\n") char_tb[i] = nil // remove old definition } param = param.split(/\s+/) for (i = 0; i < param.length; i++) { item = param[i] switch (item) { case '!': case '$': case '*': case ';': case '?': case '@': break case "": continue case "": item = '\n' break default: syntax(1, "Bad value '$1' in %%linebreak - ignored", item) continue } char_tb[item.charCodeAt(0)] = '\n' } } // set a new user character (U: or %%user) function set_user(parm) { var k, c, v, a = parm.match(/(.)[=\s]*(\[I:.+\]|".+"|!.+!)$/) if (!a) { syntax(1, 'Lack of starting [, ! or " in U: / %%user') return } c = a[1]; v = a[2] if (c[0] == '\\') { if (c[1] == 't') c = '\t' else if (!c[1]) c = ' ' } k = c.charCodeAt(0) if (k >= 128) { syntax(1, errs.not_ascii) return } switch (char_tb[k][0]) { case '0': // nil case 'd': case 'i': case ' ': break case '"': case '!': case '[': if (char_tb[k].length > 1) break // fall thru default: syntax(1, "Bad user character '$1'", c) return } switch (v) { case "!beambreak!": v = " " break case "!ignore!": v = "i" break case "!nil!": case "!none!": v = "d" break } char_tb[k] = v } // get a stafflines value function get_st_lines(param) { if (!param) return if (/^[\]\[|.-]+$/.test(param)) return param.replace(/\]/g, '[') var n = +param switch (n) { case 0: return "..." case 1: return "..|" case 2: return ".||" case 3: return ".|||" } if (isNaN(n) || n < 0 || n > 16) return //undefined return "||||||||||||||||".slice(0, n) } // create a block symbol in the tune body function new_block(subtype) { var c_v, s = { type: C.BLOCK, subtype: subtype, dur: 0 } c_v = curvoice if (subtype.slice(0, 4) != "midi") // if not a play command curvoice = voice_tb[0] // set the block in the first voice sym_link(s) if (c_v) curvoice = c_v return s } // set the voice parameters // (possible hook) Abc.prototype.set_vp = function(a) { var s, item, pos, val, clefpit, tr_p = 0 while (1) { item = a.shift() if (!item) break if (item.slice(-1) == '=' && !a.length) { syntax(1, errs.bad_val, item) break } switch (item) { case "clef=": s = a.shift() // keep last clef break case "clefpitch=": item = a.shift() // () if (item) { val = ntb.indexOf(item[0]) if (val >= 0) { switch (item[1]) { case "'": val += 7 break case ',': val -= 7 if (item[2] == ',') val -= 7 break } clefpit = 4 - val // 4 = 'G' break } } syntax(1, errs.bad_val, item) break case "octave=": val = +a.shift() if (isNaN(val)) syntax(1, errs.bad_val, item) else curvoice.octave = val break case "cue=": curvoice.scale = a.shift() == 'on' ? .7 : 1 break case "instrument=": // instrument=M/N => score=MN and sound=cN // (instrument=M == instrument=M/M) item = a.shift() val = item.indexOf('/') if (val < 0) { val = get_interval('c' + item) if (val == undefined) break curvoice.sound = val tr_p |= 2 val = 0 } else { val = get_interval('c' + item.slice(val + 1)) if (val == undefined) break curvoice.sound = val tr_p |= 2 val = get_interval(item.replace('/', '')) if (val == undefined) break } curvoice.score = cfmt.sound ? curvoice.sound : val tr_p |= 1 break case "map=": // %%voicemap curvoice.map = a.shift() break case "name=": case "nm=": curvoice.nm = a.shift() if (curvoice.nm[0] == '"') curvoice.nm = cnv_escape(curvoice.nm.slice(1, -1)) curvoice.new_name = true break case "stem=": // compatibility case "pos=": // from %%pos only if (item == "pos=") item = a.shift() .slice(1, -1) // always inside dble quotes .split(' ') else item = ["stm", a.shift()]; val = posval[item[1]] if (val == undefined) { syntax(1, errs.bad_val, "%%pos") break } switch (item[2]) { case "align": val |= C.SL_ALIGN; break case "center": val |= C.SL_CENTER; break case "close": val |= C.SL_CLOSE; break } if (!pos) pos = {} pos[item[0]] = val break case "scale=": // %%voicescale val = +a.shift() if (isNaN(val) || val < .5 || val > 2) syntax(1, errs.bad_val, "%%voicescale") else curvoice.scale = val break case "score=": if (cfmt.nedo) { syntax(1, errs.notransp) break } // score=MN // (score=M == score=Mc) item = a.shift() if (cfmt.sound) break val = get_interval(item, true) if (val != undefined) { curvoice.score = val tr_p |= 1 } break case "shift=": if (cfmt.nedo) { syntax(1, errs.notransp) break } val = get_interval(a.shift()) if (val != undefined) { curvoice.shift = val tr_p = 3 } break case "sound=": if (cfmt.nedo) { syntax(1, errs.notransp) break } // concert-score display: apply sound= // sounding-score display: apply sound= only if M != c/C // sound: apply sound= val = get_interval(a.shift()) if (val == undefined) break curvoice.sound = val if (cfmt.sound) curvoice.score = val tr_p |= 2 break case "subname=": case "sname=": case "snm=": curvoice.snm = a.shift() if (curvoice.snm[0] == '"') curvoice.snm = curvoice.snm.slice(1, -1); break case "stafflines=": val = get_st_lines(a.shift()) if (val == undefined) syntax(1, "Bad %%stafflines value") else if (curvoice.st != undefined) par_sy.staves[curvoice.st].stafflines = val else curvoice.stafflines = val break case "staffnonote=": val = +a.shift() if (isNaN(val)) syntax(1, "Bad %%staffnonote value") else curvoice.staffnonote = val break case "staffscale=": val = +a.shift() if (isNaN(val) || val < .3 || val > 2) syntax(1, "Bad %%staffscale value") else curvoice.staffscale = val break case "tacet=": val = a.shift() curvoice.tacet = val || undefined break case "transpose=": // (abcMIDI compatibility) if (cfmt.nedo) { syntax(1, errs.notransp) break } val = get_transp(a.shift()) if (val == undefined) { syntax(1, errs.bad_transp) } else { curvoice.sound = val if (cfmt.sound) curvoice.score = val tr_p = 2 } break default: switch (item.slice(0, 4)) { case "treb": case "bass": case "alto": case "teno": case "perc": s = item break default: if ("GFC".indexOf(item[0]) >= 0) s = item else if (item.slice(-1) == '=') a.shift() break } break } } if (pos) { curvoice.pos = clone(curvoice.pos) for (item in pos) if (pos.hasOwnProperty(item)) curvoice.pos[item] = pos[item] } if (s) { s = new_clef(s) if (s) { if (clefpit) s.clefpit = clefpit get_clef(s) } } // if transposition if (tr_p & 2) { // curvoice.tr_sco is set in key_trans() tr_p = (curvoice.sound | 0) + (curvoice.shift | 0) if (tr_p) curvoice.tr_snd = abc2svg.b40m(tr_p + 122) - 36 // semi-tone interval else if (curvoice.tr_snd) curvoice.tr_snd = 0 } } // set_vp() // set the K: / V: parameters function set_kv_parm(a) { // array of items if (!curvoice.init) { // add the global parameters if not done yet curvoice.init = true if (info.V) { if (info.V[curvoice.id]) a = info.V[curvoice.id].concat(a) if (info.V['*']) a = info.V['*'].concat(a) } } if (a.length) self.set_vp(a) } // set_kv_parm() // memorize the K:/V: parameters function memo_kv_parm(vid, // voice ID (V:) / '*' (K:/V:*) a) { // array of items if (!a.length) return if (!info.V) info.V = {} if (info.V[vid]) Array.prototype.push.apply(info.V[vid], a) else info.V[vid] = a } // K: key signature // return the key and the voice/clef parameters function new_key(param) { var i, key_end, c, tmp, note, sf = "FCGDAEB".indexOf(param[0]) - 1, mode = 0, s = { type: C.KEY, dur: 0 } set_ref(s); // tonic i = 1 if (sf < -1) { switch (param[0]) { case 'H': // bagpipe key_end = true if (param[1].toLowerCase() != 'p') { syntax(1, "Unknown bagpipe-like key") break } s.k_bagpipe = param[1]; sf = param[1] == 'P' ? 0 : 2; i++ // initialize the temperament if not done yet if (!cfmt.temper) // detune in cents for just intonation in A // (from https://patrickmclaurin.com/wordpress/?page_id=2420) // C ^C D _E E F ^F G _A A _B B // 15.3 -14.0 -2.0 -10.0 1.9 13.3 -16.0 -31.8 -12.0 0.0 11.4 3.8 // but 'A' bagpipe = 480Hz => raise Math.log2(480/440)*1200 = 151 cfmt.temper = new Float32Array([ // 1.66, 1.37, 1.49, 1.41, 1.53, 1.63, 1.35, 1.19, 1.39, 1.51, 1.62, 1.55 // C ^C D _E E F ^F G _A A _B B 11.62, 12.55, 1.66, 2.37, 3.49, 0, 1.66, 2.37, 3.49, 4.41, 5.53, 0, 3.49, 4.41, 5.53, 6.63, 7.35, 4.41, 5.53, 6.63, 7.35, 8.19, 0, 6.63, 7.35, 8.19, 9.39, 10.51, 0, 8.19, 9.39, 10.51, 11.62, 12.55, 0, 10.51, 11.62, 12.55, 1.66, 1.66 ]) break case 'P': syntax(1, "K:P is deprecated"); sf = 0; s.k_drum = true; key_end = true break case 'n': // none if (param.indexOf("none") == 0) { sf = 0; s.k_none = true; i = 4 break } // fall thru default: s.k_map = [] s.k_mode = 0 return [s, info_split(param)] } } if (!key_end) { switch (param[i]) { case '#': sf += 7; i++; break case 'b': sf -= 7; i++; break } param = param.slice(i).trim() switch (param.slice(0, 3).toLowerCase()) { default: if (param[0] != 'm' || (param[1] != ' ' && param[1] != '\t' && param[1] != '\n')) { key_end = true break } // fall thru ('m') case "aeo": case "m": case "min": sf -= 3; mode = 5 break case "dor": sf -= 2; mode = 1 break case "ion": case "maj": break case "loc": sf -= 5; mode = 6 break case "lyd": sf += 1; mode = 3 break case "mix": sf -= 1; mode = 4 break case "phr": sf -= 4; mode = 2 break } if (!key_end) param = param.replace(/\w+\s*/, '') // [exp] accidentals if (param.indexOf("exp ") == 0) { param = param.replace(/\w+\s*/, '') if (!param) syntax(1, "No accidental after 'exp'"); s.exp = 1 //true } c = param[0] if (c == '^' || c == '_' || c == '=') { s.k_a_acc = []; tmp = new scanBuf; tmp.buffer = param do { note = parse_acc_pit(tmp) if (!note) break s.k_a_acc.push(note); c = param[tmp.index] while (c == ' ') c = param[++tmp.index] } while (c == '^' || c == '_' || c == '='); param = param.slice(tmp.index) } else if (s.exp && param.indexOf("none") == 0) { sf = 0 param = param.replace(/\w+\s*/, '') } } if (sf < -7 || sf > 7) { syntax(1, "Key with double sharps/flats") if (sf > 7) sf -= 12 else sf += 12 } s.k_sf = sf; // set the map of the notes with accidentals s.k_map = s.k_bagpipe && !sf ? abc2svg.keys[9] // implicit F# and C# : abc2svg.keys[sf + 7] if (s.k_a_acc) { s.k_map = new Int8Array(s.k_map) i = s.k_a_acc.length while (--i >= 0) { note = s.k_a_acc[i] s.k_map[(note.pit + 19) % 7] = note.acc } } s.k_mode = mode // key note as base-40 s.k_b40 = [1,24,7,30,13,36,19, 2 ,25,8,31,14,37,20,3][sf + 7] return [s, info_split(param)] } // M: meter function new_meter(p) { var p_v, s = { type: C.METER, dur: 0, a_meter: [] }, meter = {}, val, v, m1 = 0, m2, i = 0, j, wmeasure, in_parenth; set_ref(s) if (p.indexOf("none") == 0) { i = 4; /* no meter */ wmeasure = 1; // simplify measure numbering and C.MREST conversion } else { wmeasure = 0 while (i < p.length) { if (p[i] == '=') break switch (p[i]) { case 'C': meter.top = p[i++] if (!m1) { m1 = 4; m2 = 4 } break case 'c': case 'o': meter.top = p[i++] if (!m1) { if (p[i - 1] == 'c') { m1 = 2; m2 = 4 // c = 2/4 } else { m1 = 3; m2 = 4 // o = 3/4 } switch (p[i]) { case '|': m2 /= 2 // c| = 2/2, o| = 3/2 break case '.': m1 *= 3; m2 *= 2 // c. = 6/8, o. = 9/8 break } } break case '.': case '|': m1 = 0; meter.top = p[i++] break case '(': if (p[i + 1] == '(') { /* "M:5/4 ((2+3)/4)" */ in_parenth = true; meter.top = p[i++]; s.a_meter.push(meter); meter = {} } j = i + 1 while (j < p.length) { if (p[j] == ')' || p[j] == '/') break j++ } if (p[j] == ')' && p[j + 1] == '/') { /* "M:5/4 (2+3)/4" */ i++ /* remove the parenthesis */ continue } /* "M:5 (2+3)" */ /* fall thru */ case ')': in_parenth = p[i] == '('; meter.top = p[i++]; s.a_meter.push(meter); meter = {} continue default: if (p[i] <= '0' || p[i] > '9') { syntax(1, "Bad char '$1' in M:", p[i]) return } m2 = 2; /* default when no bottom value */ meter.top = p[i++] for (;;) { while (p[i] >= '0' && p[i] <= '9') meter.top += p[i++] if (p[i] == ')') { if (p[i + 1] != '/') break i++ } if (p[i] == '/') { i++; if (p[i] <= '0' || p[i] > '9') { syntax(1, "Bad char '$1' in M:", p[i]) return } meter.bot = p[i++] while (p[i] >= '0' && p[i] <= '9') meter.bot += p[i++] break } if (p[i] != ' ' && p[i] != '+') break if (i >= p.length || p[i + 1] == '(') /* "M:5 (2/4+3/4)" */ break meter.top += p[i++] } m1 = eval(meter.top) break } if (!in_parenth) { if (meter.bot) m2 = +meter.bot wmeasure += m1 * C.BLEN / m2 } s.a_meter.push(meter); meter = {} while (p[i] == ' ') i++ if (p[i] == '+') { meter.top = p[i++]; s.a_meter.push(meter); meter = {} } } } if (p[i] == '=') { val = p.substring(++i).match(/^(\d+)\/(\d+)$/) if (!val) { syntax(1, "Bad duration '$1' in M:", p.substring(i)) return } wmeasure = C.BLEN * val[1] / val[2] } if (!wmeasure) { syntax(1, errs.bad_val, 'M:') return } s.wmeasure = wmeasure if (cfmt.writefields.indexOf('M') < 0) s.a_meter = [] if (parse.state != 3) { info.M = p; glovar.meter = s if (parse.state) { /* in the tune header, change the unit note length */ if (!glovar.ulen) { if (wmeasure <= 1 || wmeasure >= C.BLEN * 3 / 4) glovar.ulen = C.BLEN / 8 else glovar.ulen = C.BLEN / 16 } for (v = 0; v < voice_tb.length; v++) { voice_tb[v].meter = s; voice_tb[v].wmeasure = wmeasure } } } else { curvoice.wmeasure = wmeasure if (is_voice_sig()) curvoice.meter = s else sym_link(s) // set the meter of the overlay voices for (p_v = curvoice.voice_down; p_v; p_v = p_v.voice_down) p_v.wmeasure = wmeasure } } // link P: or Q: function link_pq(s, text) { var p_v, s2 if (curvoice.v == par_sy.top_voice) { sym_link(s) } else if (voice_tb[par_sy.top_voice].time == s.time) { p_v = curvoice curvoice = voice_tb[par_sy.top_voice] sym_link(s) curvoice = p_v } else if (voice_tb[par_sy.top_voice].time > s.time) { p_v = voice_tb[par_sy.top_voice] for (s2 = p_v.sym; ; s2 = s2.next) { if (s2.time >= s.time) { set_ref(s) s.fmt = cfmt s.next = s2 s.prev = s2.prev if (s2.prev) s.prev.next = s else p_v.sym = s s2.prev = s s.v = s2.v s.p_v = p_v s.st = p_v.st break } } } else { set_ref(s) s.fmt = cfmt if (!parse.pq_d) parse.pq_d = [] parse.pq_d.push(s) // delayed insertion } if (!parse.pq) parse.pq = {} parse.pq[text] = s.time } // link_pq() /* Q: tempo */ function new_tempo(text) { var i, c, d, nd, txt = text, // (for info.Q) s = { type: C.TEMPO, dur: 0 } // get a note duration function get_nd(p) { var n, d, nd = p.match(/(\d+)\/(\d+)/) if (nd) { d = +nd[2] if (d && !isNaN(d) && !(d & (d - 1))) { n = +nd[1] if (!isNaN(n)) return C.BLEN * n / d } } syntax(1, "Invalid note duration $1", c) } // get_nd() set_ref(s) if (cfmt.writefields.indexOf('Q') < 0) s.invis = true // don't display /* string before */ if (text[0] == '"') { c = text.match(/"([^"]*)"/) // " if (!c) { syntax(1, "Unterminated string in Q:") return } s.tempo_str1 = c[1] text = text.slice(c[0].length).replace(/^\s+/,'') } // string after if (text.slice(-1) == '"') { i = text.indexOf('"') s.tempo_str2 = text.slice(i + 1, -1) text = text.slice(0, i).replace(/\s+$/,'') } /* beat */ i = text.indexOf('=') if (i > 0) { d = text.slice(0, i).split(/\s+/) text = text.slice(i + 1).replace(/^\s+/,'') while (1) { c = d.shift() if (!c) break nd = get_nd(c) if (!nd) return if (!s.tempo_notes) s.tempo_notes = [] s.tempo_notes.push(nd) } // tempo value if (text.slice(0, 4) == "ca. ") { s.tempo_ca = 'ca. ' text = text.slice(4) } i = text.indexOf('/') if (i > 0) { nd = get_nd(text) if (!nd) return s.new_beat = nd } else { s.tempo = +text if (!s.tempo || isNaN(s.tempo)) { syntax(1, "Bad tempo value") return } } } if (parse.state < 2 // if in tune header || (!curvoice.time && !glovar.tempo)) { info.Q = txt glovar.tempo = s return } if (!glovar.tempo) syntax(0, "No previous tempo") s.time = curvoice.time text = 'Q' + s.time if (parse.pq && parse.pq[text] == s.time) return // already seen link_pq(s, text) } // treat the information fields which may embedded function do_info(info_type, text) { var s, d1, d2, a, vid, tim, v, p_v // skip this line if the current voice is ignored // but keep the time related definitions if (curvoice && curvoice.ignore) { switch (info_type) { default: return case 'P': case 'Q': case 'V': break } } switch (info_type) { // info fields in any state case 'I': self.do_pscom(text) break case 'L': a = text.match(/^1\/(\d+)(=(\d+)\/(\d+))?$/) if (a) { d1 = +a[1] if (!d1 || (d1 & (d1 - 1)) != 0) break d1 = C.BLEN / d1 if (a[2]) { // if '=' d2 = +a[4] d2 = d2 ? +a[3] / d2 * C.BLEN : 0 } else { d2 = d1 } } else if (text == "auto") { d1 = d2 = -1 } if (!d2) { syntax(1, "Bad L: value") break } if (parse.state <= 1) { glovar.ulen = d1 } else { curvoice.ulen = d1; curvoice.dur_fact = d2 / d1 } break case 'M': new_meter(text) break case 'U': set_user(text) break // fields in tune header or tune body case 'P': if (!parse.state) break if (parse.state == 1) { info.P = text break } s = { type: C.PART, text: text, time: curvoice.time } tim = parse.pq && parse.pq[text] // time of previous P: with same text if (tim == s.time) break // already seen if (tim != null) { syntax(1, "Misplaced P:") // different dates break } if (cfmt.writefields.indexOf('P') < 0) s.invis = 1 //true link_pq(s, text) break case 'Q': if (!parse.state) break new_tempo(text) break case 'V': get_voice(text) if (parse.state == 3) curvoice.ignore = !par_sy.voices[curvoice.v] break // key signature at end of tune header or in tune body case 'K': if (!parse.state) // ignore if in file header break get_key(text) break // info in any state case 'N': case 'R': if (!info[info_type]) info[info_type] = text else info[info_type] += '\n' + text break case 'r': if (!user.keep_remark || parse.state != 3) break s = { type: C.REMARK, text: text, dur: 0 } sym_link(s) break default: syntax(0, "'$1:' line ignored", info_type) break } } // music line parsing functions /* -- adjust the duration and time of symbols in a measure when L:auto -- */ function adjust_dur(s) { var s2, time, auto_time, i, fac; /* search the start of the measure */ s2 = curvoice.last_sym if (!s2) return; /* the bar time is correct if there are multi-rests */ if (s2.type == C.MREST || s2.type == C.BAR) /* in second voice */ return while (s2.type != C.BAR && s2.prev) s2 = s2.prev; time = s2.time; auto_time = curvoice.time - time fac = curvoice.wmeasure / auto_time if (fac == 1) return /* already good duration */ for ( ; s2; s2 = s2.next) { s2.time = time if (!s2.dur || s2.grace) continue s2.dur *= fac; s2.dur_orig *= fac; time += s2.dur if (s2.type != C.NOTE && s2.type != C.REST) continue for (i = 0; i <= s2.nhd; i++) s2.notes[i].dur *= fac } curvoice.time = s.time = time } /* -- parse a bar -- */ function new_bar() { var s2, c, bar_type, line = parse.line, s = { type: C.BAR, fname: parse.fname, istart: parse.bol + line.index, dur: 0, multi: 0 // needed for decorations } if (vover && vover.bar) // end of voice overlay get_vover('|') if (glovar.new_nbar) { // %%setbarnb s.bar_num = glovar.new_nbar; glovar.new_nbar = 0 } bar_type = line.char() while (1) { c = line.next_char() switch (c) { case '|': case '[': case ']': case ':': bar_type += c continue } break } if (bar_type[0] == ':') { if (bar_type == ':') { // ":" alone bar_type = '|'; s.bar_dotted = true } else { s.rbstop = 2 // right repeat with end } } // set the annotations and the decorations if (a_gch) csan_add(s) if (a_dcn.length) deco_cnv(s) /* if the last element is '[', it may start * a chord or an embedded header */ if (bar_type.slice(-1) == '[' && !(/[0-9" ]/.test(c))) { // " bar_type = bar_type.slice(0, -1); line.index--; c = '[' } // check if a repeat variant if (c > '0' && c <= '9') { s.text = c while (1) { c = line.next_char() if ("0123456789,.-".indexOf(c) < 0) break s.text += c } } else if (c == '"' && bar_type.slice(-1) == '[') { s.text = "" while (1) { c = line.next_char() if (!c) { syntax(1, "No end of repeat string") 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 case "[|": case "|]": s.rbstop = 2 break } s.bar_type = bar_type if (!curvoice.lyric_restart) curvoice.lyric_restart = s if (!curvoice.sym_restart) curvoice.sym_restart = s sym_link(s); s.st = curvoice.st /* original staff */ // possibly shift the volta bracket if not on the first staff if (s.text && s.st > 0 && !s.norepbra && !(par_sy.staves[s.st - 1].flags & STOP_BAR) && bar_type != '[') s.xsh = 4 // volta shift if (!s.bar_dotted && !s.invis) curvoice.acc = [] // no accidental anymore } // parse %%staves / %%score // return an array of [vid, flags] / null function parse_staves(p) { var v, vid, vids = {}, a_vf = [], err = false, flags = 0, brace = 0, bracket = 0, parenth = 0, flags_st = 0, e, a = p.match(/[^[\]|{}()*+\s]+|[^\s]/g) if (!a) { syntax(1, errs.bad_val, "%%score") return // null } while (1) { e = a.shift() if (!e) break switch (e) { case '[': if (parenth || brace + bracket >= 2) { syntax(1, errs.misplaced, '['); err = true break } flags |= brace + bracket == 0 ? OPEN_BRACKET : OPEN_BRACKET2; bracket++; flags_st <<= 8; flags_st |= OPEN_BRACKET break case '{': if (parenth || brace || bracket >= 2) { syntax(1, errs.misplaced, '{'); err = true break } flags |= !bracket ? OPEN_BRACE : OPEN_BRACE2; brace++; flags_st <<= 8; flags_st |= OPEN_BRACE break case '(': if (parenth) { syntax(1, errs.misplaced, '('); err = true break } flags |= OPEN_PARENTH; parenth++; flags_st <<= 8; flags_st |= OPEN_PARENTH break case '*': if (brace && !parenth && !(flags & (OPEN_BRACE | OPEN_BRACE2))) flags |= FL_VOICE break case '+': flags |= MASTER_VOICE break case ']': case '}': case ')': syntax(1, "Bad voice ID in %%score"); err = true break default: // get / create the voice in the voice table vid = e while (1) { e = a.shift() if (!e) break switch (e) { case ']': if (!(flags_st & OPEN_BRACKET)) { syntax(1, errs.misplaced, ']'); err = true break } bracket--; flags |= brace + bracket == 0 ? CLOSE_BRACKET : CLOSE_BRACKET2; flags_st >>= 8 continue case '}': if (!(flags_st & OPEN_BRACE)) { syntax(1, errs.misplaced, '}'); err = true break } brace--; flags |= !bracket ? CLOSE_BRACE : CLOSE_BRACE2; flags &= ~FL_VOICE; flags_st >>= 8 continue case ')': if (!(flags_st & OPEN_PARENTH)) { syntax(1, errs.misplaced, ')'); err = true break } parenth--; flags |= CLOSE_PARENTH; flags_st >>= 8 continue case '|': flags |= STOP_BAR continue } break } if (vids[vid]) { syntax(1, "Double voice in %%score") err = true } else { vids[vid] = true a_vf.push([vid, flags]) } flags = 0 if (!e) break a.unshift(e) break } } if (flags_st != 0) { syntax(1, "'}', ')' or ']' missing in %%score"); err = true } if (err || !a_vf.length) return //null return a_vf } // split an info string function info_split(text) { if (!text) return [] var a = text.match(/[^\s"=]+=?|"[^"]*"/g) // " if (!a) { //fixme: bad error text syntax(1, "Unterminated string") return [] } return a } // parse a duration and return [numerator, denominator] // 'line' is not always 'parse.line' var reg_dur = /(\d*)(\/*)(\d*)/g /* (stop comment) */ function parse_dur(line) { var res, num, den; reg_dur.lastIndex = line.index; res = reg_dur.exec(line.buffer) if (!res[0]) return [1, 1]; num = res[1] || 1; den = res[3] || 1 if (!res[3]) den *= 1 << res[2].length; line.index = reg_dur.lastIndex return [+num, +den] } // parse the note accidental and pitch function parse_acc_pit(line) { var note, acc, pit, d, nd, c = line.char() // optional accidental switch (c) { case '^': c = line.next_char() if (c == '^') { acc = 2; c = line.next_char() } else { acc = 1 } break case '=': acc = 3; c = line.next_char() break case '_': c = line.next_char() if (c == '_') { acc = -2; c = line.next_char() } else { acc = -1 } break } /* look for microtone value */ if (acc == 1 || acc == -1) { if ((c >= '1' && c <= '9') || c == '/') { // shortcut nd = parse_dur(line); if (acc < 0) nd[0] = -nd[0] if (cfmt.nedo && nd[1] == 1) { nd[0] *= 12 nd[1] *= cfmt.nedo } acc = nd c = line.char() } } /* get the pitch */ pit = ntb.indexOf(c) + 16; c = line.next_char() if (pit < 16) { syntax(1, "'$1' is not a note", line.buffer[line.index - 1]) return //undefined } // octave while (c == "'") { pit += 7; c = line.next_char() } while (c == ',') { pit -= 7; c = line.next_char() } note = { pit: pit, shhd: 0, shac: 0 } if (acc) note.acc = acc return note } // return the mapping of a note // // The global 'maps' object is indexed by the map name. // Its content is an object ('map') indexed from the map type: // - normal = ABC note // - octave = 'o' + ABC note in C..B interval // - key = 'k' + scale index // - all = 'all' // The 'map' is stored in the note. It is an array of // [0] array of heads (glyph names) // [1] print (note) // [2] color // [3] play (note) function set_map(p_v, note, acc, trp) { // transpose done var nn = not2abc(note.pit, acc), map = maps[p_v.map] // never null if (!map[nn]) { nn = 'o' + nn.replace(/[',]+/, '') // ' octave if (!map[nn]) { nn = 'k' + ntb[(note.pit + 75 - p_v.ckey.k_sf * 11) % 7] if (!map[nn]) { nn = 'all' // 'all' if (!map[nn]) return } } } map = map[nn] if (!trp) { // transpose not done yet if (map[1]) { // if note shift note.pit = map[1].pit note.acc = map[1].acc if (map[1].notrp) { note.notrp = 1 //true // no transpose note.noplay = 1 //true // no play } } return } note.map = map if (map[2]) // if color note.color = map[2] nn = map[3] if (nn) // if play map note.midi = pit2mid(nn.pit + 19, nn.acc) } /* -- parse note or rest with pitch and length -- */ // 'line' is not always 'parse.line' function parse_basic_note(line, ulen) { var nd, note = parse_acc_pit(line) if (!note) return //null // duration if (line.char() == '0') { // compatibility parse.stemless = true; line.index++ } nd = parse_dur(line); note.dur = ulen * nd[0] / nd[1] return note } function parse_vpos() { var line = parse.line, ty = 0 if (a_dcn.length && a_dcn[a_dcn.length - 1] == "dot") { ty = C.SL_DOTTED a_dcn.pop() } switch (line.next_char()) { case "'": line.index++ return ty + C.SL_ABOVE case ",": line.index++ return ty + C.SL_BELOW case '?': // slur between staves (like ~) line.index++ return ty + C.SL_CENTER } return ty + C.SL_AUTO } // on end of slur, create the slur function slur_add(s, nt) { // nt = note if slur ending on note var i, s2, sl // go back and find the last start of slur for (i = curvoice.sls.length; --i >= 0; ) { sl = curvoice.sls[i] // the slur must not start and stop on a same symbol if (sl.ss == s) continue curvoice.sls.splice(i, 1) sl.se = s // ending symbol if (nt) sl.nte = nt s2 = sl.ss // start of slur if (!s2.sls) s2.sls = [] s2.sls.push(sl) // set a flag if the slur starts on a grace note if (sl.grace) sl.grace.sl1 = true return } // the lack of a starting slur may be due to a repeat for (s2 = s.prev; s2; s2 = s2.prev) { if (s2.type == C.BAR && s2.bar_type[0] == ':' && s2.text) { if (!s2.sls) s2.sls = []; s2.sls.push({ //fixme: should go back to the bar "|1" and find the slur type... ty: C.SL_AUTO, ss: s2, se: s }) if (nt) s2.sls[s2.sls.length - 1].nte = nt return } } // syntax(1, "End of slur without start") if (!s.sls) s.sls = []; s.sls.push({ ty: C.SL_AUTO, se: s, loc: 'i' // no slur start }) if (nt) s.sls[s.sls.length - 1].nte = nt } // convert a diatonic pitch and accidental to a MIDI pitch with cents function pit2mid(pit, acc) { var p = [0, 2, 4, 5, 7, 9, 11][pit % 7], // chromatic pitch o = ((pit / 7) | 0) * 12, // octave p0, p1, s, b40 if (curvoice.snd_oct) o += curvoice.snd_oct if (acc == 3) // if natural accidental acc = 0 if (acc) { if (typeof acc == "object") { s = acc[0] / acc[1] // microtonal accidental if (acc[1] == 100) // in cents return p + o + s } else { s = acc // simple accidental } } else { if (cfmt.temper) return cfmt.temper[abc2svg.p_b40[pit % 7]] + o return p + o } if (!cfmt.nedo) { // non equal temperament if (!cfmt.temper) { p += o + s // standard temperament return p } } else { // equal temperament if (typeof acc != "object") { // if not a fraction b40 = abc2svg.p_b40[pit % 7] + acc return cfmt.temper[b40] + o } if (acc[1] == cfmt.nedo) { // fraction of the edo divider b40 = abc2svg.p_b40[pit % 7] return cfmt.temper[b40] + o + s } } p0 = cfmt.temper[abc2svg.p_b40[pit % 7]] // main note if (s > 0) { // sharp p1 = cfmt.temper[(abc2svg.p_b40[pit % 7] + 1) % 40] if (p1 < p0) p1 += 12 } else { // flat p1 = cfmt.temper[(abc2svg.p_b40[pit % 7] + 39) % 40] if (p1 > p0) p1 -= 12 s = -s } return p0 + o + (p1 - p0) * s } // pit2mid() // handle the ties // @s = tie ending smbol // @tei_s = tie starting symbol function do_ties(s, tie_s) { var i, m, not1, not2, mid, g, nt = 0, se = (tie_s.time + tie_s.dur) == curvoice.time // 'start-end' flag for (m = 0; m <= s.nhd; m++) { not2 = s.notes[m] mid = not2.midi if (tie_s.type != C.GRACE) { for (i = 0; i <= tie_s.nhd; i++) { not1 = tie_s.notes[i] if (!not1.tie_ty) continue if (not1.midi == mid && (!se || !not1.tie_e)) { // (if unison) not2.tie_s = not1 not2.s = s if (se) { not1.tie_e = not2 not1.s = tie_s } nt++ break } } } else { for (g = tie_s.extra; g; g = g.next) { not1 = g.notes[0] // (fixme: only one note) if (!not1.tie_ty) continue if (not1.midi == mid) { g.ti1 = true not2.tie_s = not1 not2.s = s not1.tie_e = not2 not1.s = g nt++ break } } } } if (!nt) error(1, tie_s, "Bad tie") else s.ti2 = true } // do_ties() // (possible hook) Abc.prototype.new_note = function(grace, sls) { var note, s, in_chord, c, dcn, type, tie_s, acc_tie, i, n, s2, nd, res, num, dur, apit, div, ty, dpit = 0, sl1 = [], line = parse.line, a_dcn_sav = a_dcn // save parsed decoration names a_dcn = [] parse.stemless = false; s = { type: C.NOTE, fname: parse.fname, stem: 0, multi: 0, nhd: 0, xmx: 0 } s.istart = parse.bol + line.index if (curvoice.color) s.color = curvoice.color if (grace) { s.grace = true } else { if (curvoice.tie_s) { // if tie from previous note / grace note tie_s = curvoice.tie_s curvoice.tie_s = null } if (a_gch) csan_add(s) if (parse.repeat_n) { s.repeat_n = parse.repeat_n; s.repeat_k = parse.repeat_k; parse.repeat_n = 0 } } c = line.char() switch (c) { case 'X': s.invis = true case 'Z': s.type = C.MREST; c = line.next_char() s.nmes = (c > '0' && c <= '9') ? line.get_int() : 1; if (curvoice.wmeasure == 1) { error(1, s, "multi-measure rest, but no measure!") return } s.dur = curvoice.wmeasure * s.nmes // ignore if in second voice if (curvoice.second) { delete curvoice.eoln // ignore the end of line curvoice.time += s.dur return //null } // convert 'Z'/'Z1' to a whole measure rest if (s.nmes == 1) { s.type = C.REST; s.dur_orig = s.dur; s.fmr = 1 // full measure rest s.notes = [{ pit: 18, dur: s.dur }] } else { glovar.mrest_p = true if (par_sy.voices.length == 1) { s.tacet = curvoice.tacet delete s.invis // show the 'H' when 'Xn' } } break case 'y': s.type = C.SPACE; s.invis = true; s.dur = 0; c = line.next_char() if (c >= '0' && c <= '9') s.width = line.get_int() else s.width = 10 if (tie_s) { curvoice.tie_s = tie_s tie_s = null } break case 'x': s.invis = true case 'z': s.type = C.REST; line.index++; nd = parse_dur(line); s.dur_orig = ((curvoice.ulen < 0) ? C.BLEN : curvoice.ulen) * nd[0] / nd[1]; s.dur = s.dur_orig * curvoice.dur_fact; if (s.dur == curvoice.wmeasure) s.fmr = 1 // full measure rest s.notes = [{ pit: 18, dur: s.dur_orig }] break case '[': // chord in_chord = true; c = line.next_char() // fall thru default: // accidental, chord, note if (curvoice.acc_tie) { acc_tie = curvoice.acc_tie curvoice.acc_tie = null } s.notes = [] // loop on the chord while (1) { // when in chord, get the slurs and decorations if (in_chord) { while (1) { if (!c) break i = c.charCodeAt(0); if (i >= 128) { syntax(1, errs.not_ascii) return //null } type = char_tb[i] switch (type[0]) { case '(': sl1.push(parse_vpos()); c = line.char() continue case '!': if (type.length > 1) a_dcn.push(type.slice(1, -1)) else get_deco() // line -> a_dcn c = line.next_char() continue } break } } note = parse_basic_note(line, s.grace ? C.BLEN / 4 : curvoice.ulen < 0 ? C.BLEN : curvoice.ulen) if (!note) return //null if (curvoice.octave) note.pit += curvoice.octave * 7 // get the real accidental apit = note.pit + 19 // pitch from C-1 i = note.acc if (!i) { if (cfmt["propagate-accidentals"][0] == 'p') i = curvoice.acc[apit % 7] else i = curvoice.acc[apit] if (!i) i = curvoice.ckey.k_map[apit % 7] || 0 } if (i) { if (cfmt["propagate-accidentals"][0] == 'p') curvoice.acc[apit % 7] = i else if (cfmt["propagate-accidentals"][0] != 'n') curvoice.acc[apit] = i } if (acc_tie && acc_tie[apit]) i = acc_tie[apit] // tied note // set the MIDI pitch if (!note.midi) // if not map play note.midi = pit2mid(apit, i) // transpose if (curvoice.map && maps[curvoice.map]) set_map(curvoice, note, i) // transpose not done if (curvoice.tr_sco) { if (!note.notrp) { i = nt_trans(note, i) if (i == -3) { // if triple sharp/flat error(1, s, "triple sharp/flat") i = note.acc > 0 ? 1 : -1 note.pit += i note.acc = i } dpit = note.pit + 19 - apit } } if (curvoice.tr_snd) note.midi += curvoice.tr_snd //fixme: does not work if transposition if (i) { switch (cfmt["writeout-accidentals"][1]) { case 'd': // added s2 = curvoice.ckey if (!s2.k_a_acc) break for (n = 0; n < s2.k_a_acc.length; n++) { if ((s2.k_a_acc[n].pit - note.pit) % 7 == 0) { note.acc = i break } } break case 'l': // all note.acc = i break } } // starting slurs if (sl1.length) { while (1) { i = sl1.shift() if (!i) break curvoice.sls.push({ ty: i, ss: s, nts: note // starting note }) } } if (a_dcn.length) { s.time = curvoice.time // (needed for !tie)! dh_cnv(s, note) } s.notes.push(note) if (!in_chord) break // in chord: get the ending slurs and the ties c = line.char() while (1) { switch (c) { case ')': slur_add(s, note) c = line.next_char() continue case '-': note.tie_ty = parse_vpos() note.s = s curvoice.tie_s = s s.ti1 = true if (curvoice.acc[apit] || (acc_tie && acc_tie[apit])) { if (!curvoice.acc_tie) curvoice.acc_tie = [] i = curvoice.acc[apit] if (acc_tie && acc_tie[apit]) i = acc_tie[apit] curvoice.acc_tie[apit] = i } c = line.char() continue case '.': c = line.next_char() switch (c) { case '-': case '(': a_dcn.push("dot") continue } syntax(1, "Misplaced dot") break } break } if (c == ']') { line.index++; // adjust the chord duration nd = parse_dur(line); s.nhd = s.notes.length - 1 for (i = 0; i <= s.nhd ; i++) { note = s.notes[i]; note.dur = note.dur * nd[0] / nd[1] } break } } // handle the starting slurs if (sls.length) { while (1) { i = sls.shift() if (!i) break curvoice.sls.push({ ty: i, ss: s // no starting note }) if (grace) curvoice.sls[curvoice.sls.length - 1].grace = grace } } // the duration of the chord is the duration of the 1st note s.dur_orig = s.notes[0].dur; s.dur = s.notes[0].dur * curvoice.dur_fact break } if (s.grace && s.type != C.NOTE) { syntax(1, errs.bad_grace) return //null } if (s.notes) { // if note or rest if (!grace) { switch (curvoice.pos.stm & 0x07) { case C.SL_ABOVE: s.stem = 1; break case C.SL_BELOW: s.stem = -1; break case C.SL_HIDDEN: s.stemless = true; break } // adjust the symbol duration num = curvoice.brk_rhythm if (num) { curvoice.brk_rhythm = 0; s2 = curvoice.last_note if (num > 0) { n = num * 2 - 1; s.dur = s.dur * n / num; s.dur_orig = s.dur_orig * n / num for (i = 0; i <= s.nhd; i++) s.notes[i].dur = s.notes[i].dur * n / num; s2.dur /= num; s2.dur_orig /= num for (i = 0; i <= s2.nhd; i++) s2.notes[i].dur /= num } else { num = -num; n = num * 2 - 1; s.dur /= num; s.dur_orig /= num for (i = 0; i <= s.nhd; i++) s.notes[i].dur /= num; s2.dur = s2.dur * n / num; s2.dur_orig = s2.dur_orig * n / num for (i = 0; i <= s2.nhd; i++) s2.notes[i].dur = s2.notes[i].dur * n / num } curvoice.time = s2.time + s2.dur; // adjust the time of the grace notes, bars... for (s2 = s2.next; s2; s2 = s2.next) s2.time = curvoice.time } } else { /* grace note - adjust its duration */ div = curvoice.ckey.k_bagpipe ? 8 : 4 for (i = 0; i <= s.nhd; i++) s.notes[i].dur /= div; s.dur /= div; s.dur_orig /= div if (grace.stem) s.stem = grace.stem } curvoice.last_note = s // get the possible ties and end of slurs c = line.char() while (1) { switch (c) { case '.': if (line.buffer[line.index + 1] != '-') break a_dcn.push("dot") line.index++ // fall thru case '-': ty = parse_vpos() for (i = 0; i <= s.nhd; i++) { s.notes[i].tie_ty = ty s.notes[i].s = s } curvoice.tie_s = grace || s curvoice.tie_s.ti1 = true for (i = 0; i <= s.nhd; i++) { note = s.notes[i] apit = note.pit + 19 // pitch from C-1 - dpit // (if transposition) if (curvoice.acc[apit] || (acc_tie && acc_tie[apit])) { if (!curvoice.acc_tie) curvoice.acc_tie = [] n = curvoice.acc[apit] if (acc_tie && acc_tie[apit]) n = acc_tie[apit] curvoice.acc_tie[apit] = n } } c = line.char() continue } break } // handle the ties ending on this chord/note if (tie_s) // if tie from previous note / grace note do_ties(s, tie_s) } sym_link(s) if (!grace) { if (!curvoice.lyric_restart) curvoice.lyric_restart = s if (!curvoice.sym_restart) curvoice.sym_restart = s } if (a_dcn_sav.length) { a_dcn = a_dcn_sav deco_cnv(s, s.prev) } if (grace && s.ottava) grace.ottava = s.ottava if (parse.stemless) s.stemless = true s.iend = parse.bol + line.index return s } // adjust the duration of the elements in a tuplet function tp_adj(s, fact) { var d, tim = s.time, to = curvoice.time - tim, // previous delta time tt = to * fact // new delta time curvoice.time = tim + tt while (1) { //fixme: tuplets in grace notes? s.in_tuplet = true if (!s.grace) { s.time = tim if (s.dur) { d = Math.round(s.dur * tt / to) // new duration to -= s.dur // old remaining time s.dur = d tt -= s.dur // new remaining time tim += s.dur } } if (!s.next) { if (s.tpe) s.tpe++ else s.tpe = 1 break } s = s.next } } // tp_adj() // get a decoration function get_deco() { 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 } } return r } // expand() // parse a macro function parse_mac(k, m, b) { var te, ti, curv, s, line_sav = line, istart_sav = parse.istart; parse.line = line = new scanBuf; parse.istart += line_sav.index; // if the macro is not displayed if (cfmt.writefields.indexOf('m') < 0) { // build the display sequence from the original sequence line.buffer = k.replace('n', n2n(b)) s = curvoice.last_sym ti = curvoice.time // start time parse_seq(true) if (!s) s = curvoice.sym for (s = s.next ; s; s = s.next) s.noplay = true te = curvoice.time // end time curv = curvoice // and put the macro sequence in a play specific voice curvoice = clone_voice(curv.id + '-p') if (!par_sy.voices[curvoice.v]) { curvoice.second = true par_sy.voices[curvoice.v] = { st: curv.st, second: true, range: curvoice.v } } curvoice.time = ti s = curvoice.last_sym parse.line = line = new scanBuf parse.istart += line_sav.index line.buffer = expand(m, b) parse_seq(true) if (curvoice.time != te) syntax(1, "Bad length of the macro sequence") if (!s) s = curvoice.sym for ( ; s; s = s.next) s.invis = s.play = true curvoice = curv } else { line.buffer = expand(m, b) parse_seq(true) } 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 } get_vover('(') break } line.index--; sls.push(parse_vpos()) continue case ')': // slur end s = curvoice.last_sym if (s) { switch (s.type) { case C.SPACE: if (!s.notes) { s.notes = [] s.notes[0] = {} } case C.NOTE: case C.REST: break case C.GRACE: // stop the slur on the last grace note for (s = s.extra; s.next; s = s.next) ; break default: s = null break } } if (!s) { syntax(1, errs.bad_char, c) break } slur_add(s) break case '!': // start of decoration if (type.length > 1) // decoration letter a_dcn.push(type.slice(1, -1)) else get_deco() // (line -> a_dcn) break case '"': if (grace) { syntax(1, errs.bad_grace) break } parse_gchord(type) break case '[': if (type.length > 1) { // U: [I:xxx] self.do_pscom(type.slice(3, -1)) break } var c_next = line.buffer[line.index + 1] if ('|[]: "'.indexOf(c_next) >= 0 || (c_next >= '1' && c_next <= '9')) { if (grace) { syntax(1, errs.bar_grace) break } new_bar() continue } if (line.buffer[line.index + 2] == ':') { if (grace) { syntax(1, errs.bad_grace) break } i = line.buffer.indexOf(']', line.index + 1) if (i < 0) { syntax(1, "Lack of ']'") break } text = line.buffer.slice(line.index + 3, i).trim() parse.istart = parse.bol + line.index; parse.iend = parse.bol + ++i; line.index = 0; do_info(c_next, text); line.index = i continue } // fall thru ('[' is start of chord) case 'n': // note/rest s = self.new_note(grace, sls) if (!s) continue // handle the tuplets if (grace || !s.notes) continue if (tpn >= 0) { // new tuplet s.tp = tp.slice(tpn) tpn = -1 if (tps) s.tp[0].s = tps // if nested tps = s } else if (!tps) { continue // no tuplet active } k = tp[tp.length - 1] if (--k.r > 0) continue // not end of tuplet yet while (1) { tp_adj(tps, k.q / k.p) i = k.ro // number of notes of this tuplet if (k.s) tps = k.s // start of upper tuplet tp.pop() // previous level if (!tp.length) { tps = null // done break } k = tp[tp.length - 1] k.r -= i if (k.r > 0) break } continue case '<': /* '<' and '>' */ if (!curvoice.last_note) { syntax(1, "No note before '<'") break } if (grace) { syntax(1, "Cannot have a broken rhythm in grace notes") break } n = c == '<' ? 1 : -1 while (c == '<' || c == '>') { n *= 2; c = line.next_char() } curvoice.brk_rhythm = n continue case 'i': // ignore break case '{': if (grace) { syntax(1, "'{' in grace note") break } last_note_sav = curvoice.last_note; 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-2023 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 . // add font styles var sheet var add_fstyle = typeof document != "undefined" ? function(s) { var e if (cfmt.fullsvg) font_style += "\n" + s if (!sheet) { if (abc2svg.sheet) { // if styles from a previous generation sheet = abc2svg.sheet e = sheet.cssRules.length while (--e >= 0) sheet.deleteRule(e) } else { e = document.createElement('style') document.head.appendChild(e) abc2svg.sheet = sheet = e.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, .556,.722,.667,.556,.611,.722,.722,.944, // 50 .722,.722,.611,.333,.278,.333,.469,.500, .333,.444,.500,.444,.500,.444,.333,.500, // 60 .500,.278,.278,.500,.278,.778,.500,.500, .500,.500,.333,.389,.278,.500,.500,.722, // 70 .500,.500,.444,.480,.200,.480,.541,.500]), // sans-serif ssw_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, .278,.278,.355,.556,.556,.889,.667,.191, // 20 .333,.333,.389,.584,.278,.333,.278,.278, .556,.556,.556,.556,.556,.556,.556,.556, // 30 .556,.556,.278,.278,.584,.584,.584,.556, 1.015,.667,.667,.722,.722,.667,.611,.778, // 40 .722,.278,.500,.667,.556,.833,.722,.778, .667,.778,.722,.667,.611,.722,.667,.944, // 50 .667,.667,.611,.278,.278,.278,.469,.556, .333,.556,.556,.500,.556,.556,.278,.556, // 60 .556,.222,.222,.500,.222,.833,.556,.556, .556,.556,.333,.500,.278,.556,.500,.722, // 70 .500,.500,.500,.334,.260,.334,.584,.512]), // monospace mw_tb = new Float32Array([ .0,.0,.0,.0,.0,.0,.0,.0, // 00 .0,.0,.0,.0,.0,.0,.0,.0, .0,.0,.0,.0,.0,.0,.0,.0, // 10 .0,.0,.0,.0,.0,.0,.0,.0, .52,.52,.52,.52,.52,.52,.52,.52, // 20 .52,.52,.52,.52,.52,.52,.52,.52, .52,.52,.52,.52,.52,.52,.52,.52, // 30 .52,.52,.52,.52,.52,.52,.52,.52, .52,.52,.52,.52,.52,.52,.52,.52, // 40 .52,.52,.52,.52,.52,.52,.52,.52, .52,.52,.52,.52,.52,.52,.52,.52, // 50 .52,.52,.52,.52,.52,.52,.52,.52, .52,.52,.52,.52,.52,.52,.52,.52, // 60 .52,.52,.52,.52,.52,.52,.52,.52, .52,.52,.52,.52,.52,.52,.52,.52, // 70 .52,.52,.52,.52,.52,.52,.52,.52]) /* -- return the character width -- */ function cwid(c, font) { var i = c.charCodeAt(0) // utf-16 if (i >= 0x80) { // if not ASCII if (i >= 0x300 && i < 0x370) return 0; // combining diacritical mark i = 0x61 // 'a' } return (font || gene.curfont).cw_tb[i] } // return the character width with the current font function cwidf(c) { return cwid(c) * gene.curfont.swfac } // estimate the width and height of a string .. var strwh (function() { if (typeof document != "undefined") { // .. by the browser // create a text element if not done yet var el // change the function strwh = function(str) { if (str.wh) return str.wh if (!el) { el = document.createElement('text') el.style.position = 'absolute' el.style.top = '-1000px' el.style.padding = '0' el.style.visibility = "hidden" document.body.appendChild(el) } var c, font = gene.curfont, h = font.size, w = 0, n = str.length, i0 = 0, i = 0 el.className = font_class(font) el.style.lineHeight = 1 if (typeof str == "object") { // if string already converted el.innerHTML = str str.wh = [ el.clientWidth, el.clientHeight ] return str.wh } str = str.replace(/<|>|&[^&\s]*?;|&/g, function(c){ switch (c) { case '<': return "<" case '>': return ">" case '&': return "&" } return c // &xxx; }) while (1) { i = str.indexOf('$', i) if (i >= 0) { c = str[i + 1] if (c == '0') { font = gene.deffont } else if (c >= '1' && c <= '9') { font = get_font("u" + c) } else { i++ continue } } el.innerHTML = str.slice(i0, i >= 0 ? i : undefined) w += el.clientWidth //fixme: bad width if space(s) at end of string if (el.clientHeight > h) h = el.clientHeight if (i < 0) break el.style.font = style_font(font).slice(5); i += 2; i0 = i } return [w, h] } } else { // .. by internal tables strwh = function(str) { var font = gene.curfont, swfac = font.swfac, h = font.size, w = 0, i, j, c, n = str.length for (i = 0; i < n; i++) { c = str[i] switch (c) { case '$': c = str[i + 1] if (c == '0') { font = gene.deffont } else if (c >= '1' && c <= '9') { font = get_font("u" + c) } else { c = '$' break } i++; swfac = font.swfac if (font.size > h) h = font.size continue case '&': if (str[i + 1] == ' ') break // normal '&' j = str.indexOf(';', i) if (j > 0 && j - i < 10) { i = j; c = 'a' // XML character reference } break } w += cwid(c, font) * swfac } return [w, h] } } })() // convert a string to a SVG text, handling the font changes // The string size is memorized into the String. function str2svg(str) { // check if the string is already converted if (typeof str == "object") return str var n_font, wh, o_font = gene.deffont, c_font = gene.curfont, o = "" // start a '' element function tspan(nf, of) { var cl if (nf.class && nf.name == of.name && nf.size == of.size && nf.weight == of.weight && nf.style == of.style) cl = nf.class // change only the class else cl = font_class(nf) return '' } // tspan() if (c_font != o_font) o = tspan(c_font, o_font) o += str.replace(/<|>|&[^&\s]*?;|&|\$./g, function(c){ switch (c) { case '<': return "<" case '>': return ">" case '&': return "&" default: if (c[0] != '$') break if (c[1] == '0') n_font = gene.deffont else if (c[1] >= '1' && c[1] <= '9') n_font = get_font("u" + c[1]) else break c = '' if (n_font == c_font) return c if (c_font != o_font) c = "" c_font = n_font if (c_font == o_font) return c return c + tspan(c_font, o_font) } return c // &xxx; }) if (c_font != o_font) o += "" // convert to String and memorize the string width and height o = new String(o) if (typeof document != "undefined") strwh(o) // browser else o.wh = strwh(str) // CLI gene.curfont = c_font // keep the current font for the next paragraph return o } // str2svg() // set the default and current font function set_font(xxx) { if (typeof xxx == "string") xxx = get_font(xxx) gene.curfont = gene.deffont = xxx } // output a string handling the font changes function out_str(str) { output += str2svg(str) } // output a string, handling the font changes // the action is: // 'c' align center // 'r' align right // 'j' justify - w is the line width // otherwise align left function xy_str(x, y, str, // string or object String with attribute 'wh' action, // default: align left w, // needed for justify wh) { // optional [width, height] if (!wh) wh = str.wh || strwh(str) if (cfmt.trimsvg) { var wx = wh[0] switch (action) { case 'c': wx = wh[0] / 2 break case 'j': wx = w break case 'r': wx = 0 break } if (img.wx < x + wx) img.wx = x + wx } output += '' break case 'j': output += '" textLength="' + w.toFixed(1) + '">' break case 'r': output += '" text-anchor="end">' break default: output += '">' break } out_str(str); output += "\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(); title = trim_title(title, is_subtitle) 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) { case 'c': x = strlw / 2; break case 'r': x = strlw - font.pad; break default: x = font.pad; break } j = 0 while (1) { i = text.indexOf('\n', j) if (i == j) { // new paragraph vskip(parskip); blk_flush() use_font(gene.curfont) while (text[i + 1] == '\n') { vskip(lineskip); i++ } if (i == text.length) break } else { if (i < 0) str = text.slice(j) else str = text.slice(j, i) ww = strwh(str) vskip(ww[1] * cfmt.lineskipfac + font.pad * 2) xy_str(x, font.pad + ww[1] * .2, str, action) if (i < 0) break } j = i + 1 } vskip(parskip); blk_flush() break case 'f': case 'j': j = 0 while (1) { i = text.indexOf('\n\n', j) if (i < 0) words = text.slice(j) else words = text.slice(j, i); words = words.split(/\s+/); w = k = wh = 0 for (j = 0; j < words.length; j++) { ww = strwh(words[j] + ' ') //   w += ww[0] if (w >= strlw) { vskip(wh * cfmt.lineskipfac) xy_str(0, ww[1] * .2, words.slice(k, j).join(' '), action, strlw) k = j; w = ww[0] wh = 0 } if (ww[1] > wh) wh = ww[1] } if (w != 0) { // last line vskip(wh * cfmt.lineskipfac) xy_str(0, ww[1] * .2, words.slice(k).join(' ')) } vskip(parskip); blk_flush() if (i < 0) break while (text[i + 2] == '\n') { vskip(lineskip); i++ } if (i == text.length) break use_font(gene.curfont); j = i + 2 } break } } /* -- output the words after tune -- */ function put_words(words) { var p, i, j, nw, w, lw, x1, x2, i1, i2, do_flush, maxn = 0, // max number of characters per line n = 1 // number of verses // output a line of words after tune function put_wline(p, x) { var i = 0, k = 0 if (p[0] == '$' // if font change && p[1] >= '0' && p[1] <= '9') { gene.curfont = p[1] == '0' ? gene.deffont : get_font("u" + p[1]) p = p.slice(2) } if ((p[i] >= '0' && p[i] <= '9') || p[i + 1] == '.') { while (i < p.length) { i++ if (p[i] == ' ' || p[i - 1] == ':' || p[i - 1] == '.') break } k = i while (p[i] == ' ') i++ } var y = gene.curfont.size * .22 // descent if (k != 0) xy_str(x, y, p.slice(0, k), 'r') if (i < p.length) xy_str(x + 5, y, p.slice(i), 'l') } // put_wline() set_font("words") vskip(cfmt.wordsspace) svg_flush() // estimate the width of the lines words = words.split('\n') nw = words.length for (i = 0; i < nw; i++) { p = words[i] if (!p) { while (i + 1 < nw && !words[i + 1]) i++ n++ } else if (p.length > maxn) { maxn = p.length i1 = i // keep this line } } w = get_lwidth() / 2 // half line width lw = strwh(words[i1])[0] i1 = i2 = 0 if (lw < w) { // if 2 columns j = n >> 1 for (i = 0; i < nw; i++) { p = words[i] if (!p) { if (--j <= 0) i1 = i while (i + 1 < nw && !words[i + 1]) i++ if (j <= 0) { i2 = i + 1 break } } } n >>= 1 } if (i2) { x1 = (w - lw) / 2 + 10 x2 = x1 + w } else { // one column x2 = w - lw / 2 + 10 } do_flush = true for (i = 0; i < i1 || i2 < nw; i++, i2++) { vskip(cfmt.lineskipfac * gene.curfont.size) if (i < i1) { p = words[i] if (p) put_wline(p, x1) else use_font(gene.curfont) } if (i2 < nw) { p = words[i2] if (p) { put_wline(p, x2) } else { if (--n == 0) { if (i < i1) { n++ } else if (i2 < nw - 1) { // center the last verse x2 = w - lw / 2 + 10 svg_flush() } } } } if (!words[i + 1] && !words[i2 + 1]) { if (do_flush) { svg_flush() do_flush = false } } else { do_flush = true } } } /* -- output history -- */ function put_history() { var i, j, c, str, font, h, w, wh, head, names = cfmt.infoname.split("\n"), n = names.length for (i = 0; i < n; i++) { c = names[i][0] if (cfmt.writefields.indexOf(c) < 0) continue str = info[c] if (!str) continue if (!font) { font = true; set_font("history"); vskip(cfmt.textspace); h = gene.curfont.size * cfmt.lineskipfac } head = names[i].slice(2) if (head[0] == '"') head = head.slice(1, -1); vskip(h); wh = strwh(head); xy_str(0, wh[1] * .22, head, null, null, wh); w = wh[0]; str = str.split('\n'); xy_str(w, wh[1] * .22, str[0]) for (j = 1; j < str.length; j++) { if (!str[j]) { // new paragraph vskip(gene.curfont.size * cfmt.parskipfac) continue } vskip(h); xy_str(w, wh[1] * .22, str[j]) } vskip(h * cfmt.parskipfac) use_font(gene.curfont) } } /* -- write heading with format -- */ var info_font_init = { A: "info", C: "composer", O: "composer", P: "parts", Q: "tempo", R: "info", T: "title", X: "title" } function write_headform(lwidth) { var c, font, font_name, align, x, y, sz, w, info_val = {}, info_font = Object.create(info_font_init), info_sz = { A: cfmt.infospace, C: cfmt.composerspace, O: cfmt.composerspace, R: cfmt.infospace }, info_nb = {} // compress the format var fmt = "", p = cfmt.titleformat, j = 0, i = 0 while (1) { while (p[i] == ' ') i++ c = p[i++] if (!c) break if (c < 'A' || c > 'Z') { switch (c) { case '+': align = '+' c = p[i++] break case ',': fmt += '\n' // fall thru default: continue case '<': align = 'l' c = p[i++] break case '>': align = 'r' c = p[i++] break } } else { switch (p[i]) { // old syntax case '-': align = 'l' i++ break case '1': align = 'r' i++ break case '0': i++ // fall thru default: align = 'c' break } } if (!info_val[c]) { if (!info[c]) continue info_val[c] = info[c].split('\n'); info_nb[c] = 1 } else { info_nb[c]++ } fmt += align + c } fmt += '\n' // loop on the blocks var ya = { l: cfmt.titlespace, c: cfmt.titlespace, r: cfmt.titlespace }, xa = { l: 0, c: lwidth * .5, r: lwidth }, yb = {}, str; p = fmt; i = 0 while (1) { // get the y offset of the top text yb.l = yb.c = yb.r = y = 0; j = i while (1) { align = p[j++] if (align == '\n') break c = p[j++] if (align == '+' || yb[align]) continue str = info_val[c] if (!str) continue font_name = info_font[c] if (!font_name) font_name = "history"; font = get_font(font_name); sz = font.size * 1.1 if (info_sz[c]) sz += info_sz[c] if (y < sz) y = sz; yb[align] = sz } ya.l += y - yb.l; ya.c += y - yb.c; ya.r += y - yb.r while (1) { align = p[i++] if (align == '\n') break c = p[i++] if (!info_val[c].length) continue str = info_val[c].shift() if (p[i] == '+') { info_nb[c]--; i++ c = p[i++]; if (info_val[c].length) { if (str) str += ' ' + info_val[c].shift() else str = ' ' + info_val[c].shift() } } font_name = info_font[c] if (!font_name) font_name = "history"; font = get_font(font_name); sz = font.size * 1.1 if (info_sz[c]) sz += info_sz[c]; set_font(font); x = xa[align]; y = ya[align] + sz if (c == 'Q') { /* special case for tempo */ self.set_width(glovar.tempo) if (!glovar.tempo.invis) { if (align != 'l') { tempo_build(glovar.tempo) w = glovar.tempo.tempo_wh[0] if (align == 'c') w *= .5; x -= w } writempo(glovar.tempo, x, -y) } } else if (str) { if (c == 'T') str = trim_title(str, info_font.T[0] == 's') xy_str(x, -y, str, align) } if (c == 'T') { font_name = info_font.T = "subtitle"; info_sz.T = cfmt.subtitlespace } if (info_nb[c] <= 1) { if (c == 'T') { font = get_font(font_name); sz = font.size * 1.1 if (info_sz[c]) sz += info_sz[c]; set_font(font) } while (info_val[c].length > 0) { y += sz; str = info_val[c].shift(); xy_str(x, -y, str, align) } } info_nb[c]--; ya[align] = y } if (ya.c > ya.l) ya.l = ya.c if (ya.r > ya.l) ya.l = ya.r if (i >= p.length) break ya.c = ya.r = ya.l } vskip(ya.l) } /* -- output the tune heading -- */ function write_heading() { var i, j, area, composer, origin, rhythm, down1, down2, lwidth = get_lwidth() vskip(cfmt.topspace) if (cfmt.titleformat) { write_headform(lwidth); vskip(cfmt.musicspace) return } /* titles */ if (info.T && cfmt.writefields.indexOf('T') >= 0) { i = 0 while (1) { j = info.T.indexOf("\n", i) if (j < 0) { write_title(info.T.substring(i), i != 0) break } write_title(info.T.slice(i, j), i != 0); i = j + 1 } } /* rhythm, composer, origin */ down1 = down2 = 0 if (parse.ckey.k_bagpipe && !cfmt.infoline && cfmt.writefields.indexOf('R') >= 0) rhythm = info.R if (rhythm) { set_font("composer"); down1 = cfmt.composerspace + gene.curfont.size + 2 xy_str(0, -down1 + gene.curfont.size *.22, rhythm) } area = info.A if (cfmt.writefields.indexOf('C') >= 0) composer = info.C if (cfmt.writefields.indexOf('O') >= 0) origin = info.O if (composer || origin || cfmt.infoline) { var xcomp, align; set_font("composer"); if (cfmt.aligncomposer < 0) { xcomp = 0; align = ' ' } else if (cfmt.aligncomposer == 0) { xcomp = lwidth * .5; align = 'c' } else { xcomp = lwidth; align = 'r' } if (composer || origin) { down2 = cfmt.composerspace + 2 i = 0 while (1) { down2 += gene.curfont.size if (composer) j = composer.indexOf("\n", i) else j = -1 if (j < 0) { put_inf2r(xcomp, -down2 + gene.curfont.size *.22, composer ? composer.substring(i) : null, origin, align) break } xy_str(xcomp, -down2 + gene.curfont.size *.22, composer.slice(i, j), align); i = j + 1 } } rhythm = rhythm ? null : info.R if ((rhythm || area) && cfmt.infoline) { /* if only one of rhythm or area then do not use ()'s * otherwise output 'rhythm (area)' */ set_font("info"); down2 += cfmt.infospace + gene.curfont.size put_inf2r(lwidth, -down2 + gene.curfont.size *.22, rhythm, area, 'r') } } /* parts */ if (info.P && cfmt.writefields.indexOf('P') >= 0) { set_font("parts"); i = cfmt.partsspace + gene.curfont.size + gene.curfont.pad if (down1 + i > down2) down2 = down1 + i else down2 += i xy_str(0, -down2 + gene.curfont.size *.22, info.P) down2 += gene.curfont.pad } else if (down1 > down2) { down2 = down1 } vskip(down2 + cfmt.musicspace) } // abc2svg - svg.js - svg functions // // Copyright (C) 2014-2023 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 . var output = "", // output buffer style = '\ \n.stroke{stroke:currentColor;fill:none}\ \n.bW{stroke:currentColor;fill:none;stroke-width:1}\ \n.bthW{stroke:currentColor;fill:none;stroke-width:3}\ \n.slW{stroke:currentColor;fill:none;stroke-width:.7}\ \n.slthW{stroke:currentColor;fill:none;stroke-width:1.5}\ \n.sW{stroke:currentColor;fill:none;stroke-width:.7}\ \n.box{outline:1px solid black;outline-offset:1px}', font_style = '', posx = cfmt.leftmargin / cfmt.scale, // default x offset of the images posy = 0, // y offset in the block img = { // image width: cfmt.pagewidth, // width lm: cfmt.leftmargin, // left and right margins rm: cfmt.rightmargin, wx: 0, // used width between the left and right margins chg: 1 //true }, defined_glyph = {}, defs = '', fulldefs = '', // unreferenced defs as stv_g = { /* staff/voice graphic parameters */ scale: 1, dy: 0, st: -1, v: -1, g: 0 // color: undefined }, blkdiv = 0 // block of contiguous SVGs // -1: block started // 0: no block // 1: start a block // 2: start a new page // glyphs in music font var tgls = { "mtr ": {x:0, y:0, c:"\u0020"}, // space brace: {x:0, y:0, c:"\ue000"}, lphr: {x:0, y:23, c:"\ue030"}, mphr: {x:0, y:23, c:"\ue038"}, sphr: {x:0, y:26, c:"\ue039"}, short: {x:0, y:32, c:"\ue038"}, tick: {x:0, y:29, c:"\ue039"}, rdots: {x:-1, y:0, c:"\ue043"}, // repeat dots dsgn: {x:-12, y:0, c:"\ue045"}, // D.S. dcap: {x:-12, y:0, c:"\ue046"}, // D.C. sgno: {x:-5, y:0, c:"\ue047"}, // segno coda: {x:-10, y:0, c:"\ue048"}, tclef: {x:-8, y:0, c:"\ue050"}, cclef: {x:-8, y:0, c:"\ue05c"}, bclef: {x:-8, y:0, c:"\ue062"}, pclef: {x:-6, y:0, c:"\ue069"}, spclef: {x:-6, y:0, c:"\ue069"}, stclef: {x:-8, y:0, c:"\ue07a"}, scclef: {x:-8, y:0, c:"\ue07b"}, sbclef: {x:-7, y:0, c:"\ue07c"}, oct: {x:0, y:2, c:"\ue07d"}, // 8 for clefs oct2: {x:0, y:2, c:"\ue07e"}, // 15 for clefs mtr0: {x:0, y:0, c:"\ue080"}, // meters mtr1: {x:0, y:0, c:"\ue081"}, mtr2: {x:0, y:0, c:"\ue082"}, mtr3: {x:0, y:0, c:"\ue083"}, mtr4: {x:0, y:0, c:"\ue084"}, mtr5: {x:0, y:0, c:"\ue085"}, mtr6: {x:0, y:0, c:"\ue086"}, mtr7: {x:0, y:0, c:"\ue087"}, mtr8: {x:0, y:0, c:"\ue088"}, mtr9: {x:0, y:0, c:"\ue089"}, mtrC: {x:0, y:0, c:"\ue08a"}, // common time (4/4) // "mtrC|": {x:0, y:0, c:"\ue08b"}, // cut time (2/2) (unused) "mtr+": {x:0, y:0, c:"\ue08c"}, "mtr(": {x:0, y:0, c:"\ue094"}, "mtr)": {x:0, y:0, c:"\ue095"}, HDD: {x:-7, y:0, c:"\ue0a0"}, breve: {x:-7, y:0, c:"\ue0a1"}, HD: {x:-5.2, y:0, c:"\ue0a2"}, Hd: {x:-3.8, y:0, c:"\ue0a3"}, hd: {x:-3.7, y:0, c:"\ue0a4"}, ghd: {x:2, y:0, c:"\ue0a4", sc:.66}, // grace note head pshhd: {x:-3.7, y:0, c:"\ue0a9"}, pfthd: {x:-3.7, y:0, c:"\ue0b3"}, x: {x:-3.7, y:0, c:"\ue0a9"}, // 'x' note head "circle-x": {x:-3.7, y:0, c:"\ue0b3"}, // 'circle-x' note head srep: {x:-5, y:0, c:"\ue101"}, "dot+": {x:-5, y:0, sc:.7, c:"\ue101"}, diamond: {x:-4, y:0, c:"\ue1b9"}, triangle: {x:-4, y:0, c:"\ue1bb"}, dot: {x:-1, y:0, c:"\ue1e7"}, flu1: {x:-.3, y:0, c:"\ue240"}, // flags fld1: {x:-.3, y:0, c:"\ue241"}, flu2: {x:-.3, y:0, c:"\ue242"}, fld2: {x:-.3, y:0, c:"\ue243"}, flu3: {x:-.3, y:3.5, c:"\ue244"}, fld3: {x:-.3, y:-4, c:"\ue245"}, flu4: {x:-.3, y:8, c:"\ue246"}, fld4: {x:-.3, y:-9, c:"\ue247"}, flu5: {x:-.3, y:12.5, c:"\ue248"}, fld5: {x:-.3, y:-14, c:"\ue249"}, "acc-1": {x:-1, y:0, c:"\ue260"}, // flat "cacc-1": {x:-18, y:0, c:"\ue26a\ue260\ue26b"}, // courtesy flat (note deco) "sacc-1": {x:-1, y:0, sc:.7, c:"\ue260"}, // small flat (editorial) acc3: {x:-1, y:0, c:"\ue261"}, // natural "cacc3": {x:-18, y:0, c:"\ue26a\ue261\ue26b"}, // courtesy natural (note deco) sacc3: {x:-1, y:0, sc:.7, c:"\ue261"}, // small natural (editorial) acc1: {x:-2, y:0, c:"\ue262"}, // sharp "cacc1": {x:-18, y:0, c:"\ue26a\ue262\ue26b"}, // courtesy sharp (note deco) sacc1: {x:-2, y:0, sc: .7, c:"\ue262"}, // small sharp (editorial) acc2: {x:-3, y:0, c:"\ue263"}, // double sharp "acc-2": {x:-3, y:0, c:"\ue264"}, // double flat "acc-1_2": {x:-2, y:0, c:"\ue280"}, // quarter-tone flat "acc-3_2": {x:-3, y:0, c:"\ue281"}, // three-quarter-tones flat acc1_2: {x:-1, y:0, c:"\ue282"}, // quarter-tone sharp acc3_2: {x:-3, y:0, c:"\ue283"}, // three-quarter-tones sharp accent: {x:-3, y:2, c:"\ue4a0"}, stc: {x:0, y:-2, c:"\ue4a2"}, // staccato emb: {x:0, y:-2, c:"\ue4a4"}, wedge: {x:0, y:0, c:"\ue4a8"}, marcato: {x:-3, y:-2, c:"\ue4ac"}, hld: {x:-7, y:-2, c:"\ue4c0"}, // fermata brth: {x:0, y:0, c:"\ue4ce"}, caes: {x:0, y:8, c:"\ue4d1"}, r00: {x:-1.5, y:0, c:"\ue4e1"}, r0: {x:-1.5, y:0, c:"\ue4e2"}, r1: {x:-3.5, y:-6, c:"\ue4e3"}, r2: {x:-3.2, y:0, c:"\ue4e4"}, r4: {x:-3, y:0, c:"\ue4e5"}, r8: {x:-3, y:0, c:"\ue4e6"}, r16: {x:-4, y:0, c:"\ue4e7"}, r32: {x:-4, y:0, c:"\ue4e8"}, r64: {x:-4, y:0, c:"\ue4e9"}, r128: {x:-4, y:0, c:"\ue4ea"}, // mrest: {x:-10, y:0, c:"\ue4ee"}, mrep: {x:-6, y:0, c:"\ue500"}, mrep2: {x:-9, y:0, c:"\ue501"}, p: {x:-3, y:0, c:"\ue520"}, f: {x:-3, y:0, c:"\ue522"}, pppp: {x:-15, y:0, c:"\ue529"}, ppp: {x:-14, y:0, c:"\ue52a"}, pp: {x:-8, y:0, c:"\ue52b"}, mp: {x:-8, y:0, c:"\ue52c"}, mf: {x:-8, y:0, c:"\ue52d"}, ff: {x:-7, y:0, c:"\ue52f"}, fff: {x:-10, y:0, c:"\ue530"}, ffff: {x:-14, y:0, c:"\ue531"}, sfz: {x:-10, y:0, c:"\ue539"}, trl: {x:-5, y:-2, c:"\ue566"}, // trill turn: {x:-5, y:0, c:"\ue567"}, turnx: {x:-5, y:0, c:"\ue569"}, umrd: {x:-6, y:2, c:"\ue56c"}, lmrd: {x:-6, y:2, c:"\ue56d"}, dplus: {x:-3, y:0, c:"\ue582"}, // plus sld: {x:-8, y:12, c:"\ue5d0"}, // slide grm: {x:-3, y:-2, c:"\ue5e2"}, // grace mark dnb: {x:-3, y:0, c:"\ue610"}, // down bow upb: {x:-2, y:0, c:"\ue612"}, // up bow opend: {x:-2, y:-2, c:"\ue614"}, // harmonic roll: {x:0, y:0, c:"\ue618"}, thumb: {x:-2, y:-2, c:"\ue624"}, snap: {x:-2, y:-2, c:"\ue630"}, ped: {x:-10, y:0, c:"\ue650"}, pedoff: {x:-5, y:0, c:"\ue655"}, // "mtro.": {x:0, y:0, c:"\ue910"}, // (unused) mtro: {x:0, y:0, c:"\ue911"}, // tempus perfectum // "mtro|": {x:0, y:0, c:"\ue912"}, // (unused) // "mtrc.": {x:0, y:0, c:"\ue914"}, // (unused) mtrc: {x:0, y:0, c:"\ue915"}, // tempus imperfectum // "mtrc|": {x:0, y:0, c:"\ue918"}, // (unused) "mtr.": {x:0, y:0, c:"\ue920"}, // prolatione perfecta "mtr|": {x:0, y:0, c:"\ue925"}, // (twice as fast) longa: {x:-4.7, y:0, c:"\ue95d"}, custos: {x:-4, y:3, c:"\uea02"}, ltr: {x:2, y:6, c:"\ueaa4"} // long trill element } // glyphs to put in var glyphs = { } // convert a meter string to a SmuFL encoded string function m_gl(s) { return s.replace(/[Cco]\||[co]\.|./g, function(e) { var m = tgls["mtr" + e] //fixme: !! no m.x nor m.y yet !! // if (!m.x && !m.y) return m ? m.c : 0 // return '' + // m.c + '' }) } // mark a glyph as used and add it in function def_use(gl) { var i, j, g if (defined_glyph[gl]) return defined_glyph[gl] = true; g = glyphs[gl] if (!g) { //throw new Error("unknown glyph: " + gl) error(1, null, "Unknown glyph: '$1'", gl) return // fixme: the xlink is set } j = 0 while (1) { i = g.indexOf('xlink:href="#', j) if (i < 0) break i += 13; j = g.indexOf('"', i); def_use(g.slice(i, j)) } defs += '\n' + g } // add user defs from %%beginsvg function defs_add(text) { var i, j, gl, tag, is, ie = 0 // remove XML comments text = text.replace(//g, '') while (1) { is = text.indexOf('<', ie); if (is < 0) break i = text.indexOf('id="', is) if (i < 0) break i += 4; j = text.indexOf('"', i); if (j < 0) break gl = text.slice(i, j); ie = text.indexOf('>', j); if (ie < 0) break if (text[ie - 1] == '/') { ie++ } else { i = text.indexOf(' ', is); if (i < 0) break tag = text.slice(is + 1, i); ie = text.indexOf('', ie) if (ie < 0) break ie += 3 + tag.length } if (text.substr(is, 7) == '= 0 ? staff_tb[st].staffscale : 1 if (st >= 0 && new_scale != 1) dy = staff_tb[st].y else dy = posy if (new_scale == stv_g.scale && dy == stv_g.dy) return stv_g.scale = new_scale; stv_g.dy = dy; stv_g.st = st; stv_g.v = -1; set_g() } /* -- set the voice or staff scale -- */ function set_scale(s) { var new_dy, new_scale = s.p_v.scale if (new_scale == 1) { set_sscale(s.st) return } new_dy = posy if (staff_tb[s.st].staffscale != 1) { new_scale *= staff_tb[s.st].staffscale; new_dy = staff_tb[s.st].y } if (new_scale == stv_g.scale && stv_g.dy == posy) return stv_g.scale = new_scale; stv_g.dy = new_dy; stv_g.st = staff_tb[s.st].staffscale == 1 ? -1 : s.st; stv_g.v = s.v; set_g() } // -- set the staff output buffer and scale when delayed output function set_dscale(st, no_scale) { if (output) { if (stv_g.started) { // close the previous sequence stv_g.started = false glout() output += "\n" } if (stv_g.st < 0) { staff_tb[0].output += output } else if (stv_g.scale == 1) { staff_tb[stv_g.st].output += output } else { staff_tb[stv_g.st].sc_out += output } output = "" } if (st < 0) stv_g.scale = 1 else stv_g.scale = no_scale ? 1 : staff_tb[st].staffscale; stv_g.st = st; stv_g.dy = 0 } // update the y offsets of delayed output function delayed_update() { var st, new_out, text for (st = 0; st <= nstaff; st++) { if (staff_tb[st].sc_out) { output += '\n' + staff_tb[st].sc_out + '\n'; staff_tb[st].sc_out = "" } if (!staff_tb[st].output) continue output += '\n' + staff_tb[st].output + '\n'; staff_tb[st].output = "" } } // output the annotations function anno_out(s, t, f) { if (s.istart == undefined) return var type = s.type, h = s.ymx - s.ymn + 4, wl = s.wl || 2, wr = s.wr || 2 if (s.grace) type = C.GRACE f(t || abc2svg.sym_name[type], s.istart, s.iend, s.x - wl - 2, staff_tb[s.st].y + s.ymn + h - 2, wl + wr + 4, h, s); } function a_start(s, t) { anno_out(s, t, user.anno_start) } function a_stop(s, t) { anno_out(s, t, user.anno_stop) } function empty_function() { } // the values are updated on generate() var anno_start = empty_function, anno_stop = empty_function // output the stop user annotations function anno_put() { var s while (1) { s = anno_a.shift() if (!s) break switch (s.type) { case C.CLEF: case C.METER: case C.KEY: case C.REST: if (s.type != C.REST || s.rep_nb) { set_sscale(s.st) break } // fall thru case C.GRACE: case C.NOTE: case C.MREST: set_scale(s) break // default: // continue } anno_stop(s) } } // anno_put() // output a string with x, y, a and b // In the string, // X and Y are replaced by scaled x and y // A and B are replaced by a and b as string // F and G are replaced by a and b as float function out_XYAB(str, x, y, a, b) { x = sx(x); y = sy(y); output += str.replace(/X|Y|A|B|F|G/g, function(c) { switch (c) { case 'X': return x.toFixed(1) case 'Y': return y.toFixed(1) case 'A': return a case 'B': return b case 'F': return a.toFixed(1) // case 'G': default: return b.toFixed(1) } }) } // open / close containers function g_open(x, y, rot, sx, sy) { glout() out_XYAB('\n'; stv_g.g++ } function g_close() { glout() stv_g.g--; output += '\n' } // external SVG string Abc.prototype.out_svg = function(str) { output += str } // exported functions for the annotation function sx(x) { if (stv_g.g) return x return (x + posx) / stv_g.scale } Abc.prototype.sx = sx function sy(y) { if (stv_g.g) return -y if (stv_g.scale == 1) return posy - y if (stv_g.v >= 0) return (stv_g.dy - y) / voice_tb[stv_g.v].scale return stv_g.dy - y // staff scale only } Abc.prototype.sy = sy; Abc.prototype.sh = function(h) { if (stv_g.st < 0) return h / stv_g.scale return h } // for absolute X,Y coordinates Abc.prototype.ax = function(x) { return x + posx } Abc.prototype.ay = function(y) { if (stv_g.st < 0) return posy - y return posy + (stv_g.dy - y) * stv_g.scale - stv_g.dy } Abc.prototype.ah = function(h) { if (stv_g.st < 0) return h return h * stv_g.scale } // output scaled (x + + y) function out_sxsy(x, sep, y) { x = sx(x); y = sy(y); output += x.toFixed(1) + sep + y.toFixed(1) } Abc.prototype.out_sxsy = out_sxsy // define the start of a path function xypath(x, y, fill) { if (fill) out_XYAB('\n' } } // hlud() for (st = 0; st <= nstaff; st++) { p_st = staff_tb[st] if (!p_st.hlu) continue // (staff not yet displayed) set_sscale(st) hlud(p_st.hlu, 6) hlud(p_st.hld, -6) } } // draw_all_hl() // output the list of glyphs and the stems // [0] = x glyph // [1] = y glyph // [2] = glyph code // [3] = x, y, h of stem (3 values per stem) var gla = [[], [], "", [], [], []] function glout() { var e, v = [] // glyphs (notes, accidentals...) if (gla[0].length) { while (1) { e = gla[0].shift() if (e == undefined) break v.push(e.toFixed(1)) } output += '' + gla[2] + '\n' gla[2] = "" } // stems if (!gla[3].length) return output += '\n' } // glout() // output a glyph function xygl(x, y, gl) { // (avoid ps<->js loop) // if (psxygl(x, y, gl)) // return if (glyphs[gl]) { def_use(gl) out_XYAB('\n', x, y, gl) } else { var tgl = tgls[gl] if (tgl) { x += tgl.x * stv_g.scale; y -= tgl.y if (tgl.sc) { out_XYAB('B\n', x, y, tgl.sc, tgl.c); } else { // out_XYAB('A\n', x, y, tgl.c) gla[0].push(sx(x)) gla[1].push(sy(y)) gla[2] += tgl.c } } else { error(1, null, 'no definition of $1', gl) } } } // - specific functions - // gua gda (acciaccatura) function out_acciac(x, y, dx, dy, up) { if (up) { x -= 1; y += 4 } else { x -= 5; y -= 4 } out_XYAB('\n', x, y, dx, -dy) } // staff system brace function out_brace(x, y, h) { //fixme: '-6' depends on the scale x += posx - 6; y = posy - y; h /= 24; output += '' + tgls.brace.c + '\n' } // staff system bracket function out_bracket(x, y, h) { x += posx - 5; y = posy - y - 3; h += 2; output += '\n' } // hyphen function out_hyph(x, y, w) { var n, a_y, d = 25 + ((w / 20) | 0) * 3 if (w > 15.) n = ((w - 15) / d) | 0 else n = 0; x += (w - d * n - 5) / 2; out_XYAB('\n', x, y + 4, // set the line a bit upper Math.round((d - 5) / stv_g.scale), d * n + 5) } // stem [and flags] function out_stem(x, y, h, grace, nflags, straight) { // optional //fixme: dx KO with half note or longa var dx = grace ? GSTEM_XOFF : 3.5, slen = -h if (h < 0) dx = -dx; // down x += dx * stv_g.scale if (stv_g.v >= 0) slen /= voice_tb[stv_g.v].scale; gla[3].push(sx(x)) gla[3].push(sy(y)) gla[3].push(slen) if (!nflags) return y += h if (h > 0) { // up if (!straight) { if (!grace) { xygl(x, y, "flu" + nflags) return } else { // grace output += '= 0) { out_XYAB('MX Yl7 3.2 0 3.2 -7 -3.2z\n', x, y); y -= 5.4 } } else { // grace while (--nflags >= 0) { out_XYAB('MX Yl3 1.5 0 2 -3 -1.5z\n', x, y); y -= 3 } } } } else { // down if (!straight) { if (!grace) { xygl(x, y, "fld" + nflags) return } else { // grace output += '= 0) { out_XYAB('MX Yl7 -3.2 0 -3.2 -7 3.2z\n', x, y); y += 5.4 } // } else { // grace //--fixme: error? } } } output += '"/>\n' } // tremolo function out_trem(x, y, ntrem) { out_XYAB('\n' } // tuplet bracket - the staves are not defined function out_tubr(x, y, dx, dy, up) { var h = up ? -3 : 3; y += h; dx /= stv_g.scale; output += '\n' } // tuplet bracket with number - the staves are not defined function out_tubrn(x, y, dx, dy, up, str) { var dxx, sw = str.length * 10, h = up ? -3 : 3; set_font("tuplet") xy_str(x + dx / 2, y + dy / 2 - gene.curfont.size * .1, str, 'c') dx /= stv_g.scale if (!up) y += 6; output += '\n' + '\n' } // underscore line function out_wln(x, y, w) { out_XYAB('\n', x, y + 1, w) } // decorations with string var deco_str_style = { crdc: { // cresc., decresc., dim., ... dx: 0, dy: 5, style: 'font:italic 14px text,serif', anchor: ' text-anchor="middle"' }, dacs: { // long repeats (da capo, fine...) dx: 0, dy: 3, style: 'font:bold 15px text,serif', anchor: ' text-anchor="middle"' }, pf: { dx: 0, dy: 5, style: 'font:italic bold 16px text,serif', anchor: ' text-anchor="middle"' } } deco_str_style.at = deco_str_style.crdc function out_deco_str(x, y, de) { var name = de.dd.glyph // class if (name == 'fng') { out_XYAB('\ A\n', x - 2, y, m_gl(de.dd.str)) return } if (name == '@') { // compatibility name = 'at' } else if (!/^[A-Za-z][A-Za-z\-_]*$/.test(name)) { error(1, de.s, "No function for decoration '$1'", de.dd.name) return } var f, a_deco = deco_str_style[name] if (!a_deco) a_deco = deco_str_style.crdc // default style else if (a_deco.style) style += "\n." + name + "{" + a_deco.style + "}", delete a_deco.style x += a_deco.dx; y += a_deco.dy; out_XYAB('', x, y, name, a_deco.anchor || ""); set_font("annotation"); out_str(de.dd.str) output += '\n' } function out_arp(x, y, val) { g_open(x, y, 270); x = 0; val = Math.ceil(val / 6) while (--val >= 0) { xygl(x, 6, "ltr"); x += 6 } g_close() } function out_cresc(x, y, val, defl) { x += val * stv_g.scale val = -val; out_XYAB('\n' else output += '-4l' + (-val).toFixed(1) + ' -4"/>\n' } function out_dim(x, y, val, defl) { out_XYAB('\n' else output += '-4l' + (-val).toFixed(1) + ' -4"/>\n' } function out_ltr(x, y, val) { y += 4; val = Math.ceil(val / 6) while (--val >= 0) { xygl(x, y, "ltr"); x += 6 } } Abc.prototype.out_lped = function(x, y, val, defl) { if (!defl.nost) xygl(x, y, "ped"); if (!defl.noen) xygl(x + val + 6, y, "pedoff") } function out_8va(x, y, val, defl) { if (val < 18) { val = 18 x -= 4 } if (!defl.nost) { out_XYAB('8\ va\n', x - 8, y); x += 12; val -= 12 } y += 6; out_XYAB('\n', x, y, val) if (!defl.noen) out_XYAB('\n', x + val, y) } function out_8vb(x, y, val, defl) { if (val < 18) { val = 18 x -= 4 } if (!defl.nost) { out_XYAB('8\ vb\n', x - 8, y); x += 10 val -= 10 } // y -= 2; out_XYAB('\n', x, y, val) if (!defl.noen) out_XYAB('\n', x + val, y) } function out_15ma(x, y, val, defl) { if (val < 25) { val = 25 x -= 6 } if (!defl.nost) { out_XYAB('15\ ma\n', x - 10, y); x += 20; val -= 20 } y += 6; out_XYAB('\n', x, y, val) if (!defl.noen) out_XYAB('\n', x + val, y) } function out_15mb(x, y, val, defl) { if (val < 24) { val = 24 x -= 5 } if (!defl.nost) { out_XYAB('15\ mb\n', x - 10, y); x += 18 val -= 18 } // y -= 2; out_XYAB('\n', x, y, val) if (!defl.noen) out_XYAB('\n', x + val, y) } var deco_val_tb = { arp: out_arp, cresc: out_cresc, dim: out_dim, ltr: out_ltr, lped: function(x, y, val, defl) { self.out_lped(x, y, val, defl) }, "8va": out_8va, "8vb": out_8vb, "15ma": out_15ma, "15mb": out_15mb } function out_deco_val(x, y, name, val, defl) { if (deco_val_tb[name]) deco_val_tb[name](x, y, val, defl) else error(1, null, "No function for decoration '$1'", name) } function out_glisq(x2, y2, de) { var ar, a, len, de1 = de.start, x1 = de1.x, y1 = de1.y + staff_tb[de1.st].y, dx = x2 - x1, dy = self.sh(y1 - y2) if (!stv_g.g) dx /= stv_g.scale ar = Math.atan2(dy, dx) a = ar / Math.PI * 180 len = (dx - (de1.s.dots ? 13 + de1.s.xmx : 8) - 8 - (de.s.notes[0].shac || 0)) / Math.cos(ar) g_open(x1, y1, a); x1 = de1.s.dots ? 13 + de1.s.xmx : 8; len = len / 6 | 0 if (len < 1) len = 1 while (--len >= 0) { xygl(x1, 0, "ltr"); x1 += 6 } g_close() } function out_gliss(x2, y2, de) { var ar, a, len, de1 = de.start, x1 = de1.x, y1 = de1.y + staff_tb[de1.st].y, dx = x2 - x1, dy = self.sh(y1 - y2) if (!stv_g.g) dx /= stv_g.scale ar = Math.atan2(dy, dx) a = ar / Math.PI * 180 len = (dx - (de1.s.dots ? 13 + de1.s.xmx : 8) - 8 - (de.s.notes[0].shac || 0)) / Math.cos(ar) g_open(x1, y1, a); xypath(de1.s.dots ? 13 + de1.s.xmx : 8, 0) output += 'h' + len.toFixed(1) + '" stroke-width="1"/>\n'; g_close() } var deco_l_tb = { glisq: out_glisq, gliss: out_gliss } function out_deco_long(x, y, de) { var s, p_v, m, nt, i, name = de.dd.glyph, de1 = de.start if (!deco_l_tb[name]) { error(1, null, "No function for decoration '$1'", name) return } // if no start or no end, get the y offset of the other end p_v = de.s.p_v // voice if (de.defl.noen) { // if no end s = p_v.s_next // start of the next music line while (s && !s.dur) s = s.next if (s) { for (m = 0; m <= s.nhd; m++) { nt = s.notes[m] if (!nt.a_dd) continue for (i = 0; i < nt.a_dd.length; i++) { if (nt.a_dd[i].name == de.dd.name) { y = 3 * (nt.pit - 18) + staff_tb[de.s.st].y break } } } } x += 8 // (there is no note width) } else if (de.defl.nost) { // no start s = p_v.s_prev // end of the previous music line while (s && !s.dur) s = s.prev if (s) { for (m = 0; m <= s.nhd; m++) { nt = s.notes[m] if (!nt.a_dd) continue for (i = 0; i < nt.a_dd.length; i++) { if (nt.a_dd[i].name == de1.dd.name) { de1.y = 3 * (nt.pit - 18) break } } } } de1.x -= 8 // (there is no note width) } deco_l_tb[name](x, y, de) } // return a tempo note function tempo_note(s, dur) { var p, elts = identify_note(s, dur) switch (elts[0]) { // head case C.OVAL: p = "\ueca2" break case C.EMPTY: p = "\ueca3" break default: switch (elts[2]) { // flags case 2: p = "\ueca9" break case 1: p = "\ueca7" break default: p = "\ueca5" break } break } if (elts[1]) // dot p += '\uecb7' return p } // tempo_note() // build the tempo string function tempo_build(s) { var i, j, bx, p, wh, dy, w = 0, str = [] if (s.tempo_str) // already done return // the music font must be defined if (!cfmt.musicfont.used) get_font("music") set_font("tempo") if (s.tempo_str1) { str.push(s.tempo_str1) w += strwh(s.tempo_str1)[0] } if (s.tempo_notes) { dy = ' dy="-1"' // notes a bit higher for (i = 0; i < s.tempo_notes.length; i++) { p = tempo_note(s, s.tempo_notes[i]) str.push('' + p + '') j = p.length > 1 ? 2 : 1 // (note and optional dot) w += j * gene.curfont.swfac dy = '' } str.push('=') w += cwidf('=') if (s.tempo_ca) { str.push(s.tempo_ca) w += strwh(s.tempo_ca)[0] j = s.tempo_ca.length + 1 } if (s.tempo) { // with a number of beats per minute str.push(s.tempo) w += strwh(s.tempo.toString())[0] } else { // with a beat as a note p = tempo_note(s, s.new_beat) str.push('' + p + '') j = p.length > 1 ? 2 : 1 w += j * gene.curfont.swfac dy = 'y' } } if (s.tempo_str2) { if (dy) str.push('' + s.tempo_str2 + '') else str.push(s.tempo_str2) w += strwh(s.tempo_str2)[0] } // build the string s.tempo_str = str.join(' ') w += cwidf(' ') * (str.length - 1) s.tempo_wh = [w, 13.0] // (the height is not used) } // tempo_build() // output a tempo function writempo(s, x, y) { var bh set_font("tempo") if (gene.curfont.box) { gene.curfont.box = false bh = gene.curfont.size + 4 } //fixme: xy_str() cannot be used because in s.tempo_str //fixme: then there cannot be font changes by "$n" in the Q: texts output += '' + s.tempo_str + '\n' if (bh) { gene.curfont.box = true output += '\n' } // don't display anymore s.invis = true } // writempo() // update the vertical offset function vskip(h) { posy += h } // create the SVG image of the block function svg_flush() { if (multicol || !output || !user.img_out || posy == 0) return var i, font, fmt = tsnext ? tsnext.fmt : cfmt, w = Math.ceil((fmt.trimsvg || fmt.singleline == 1) ? (cfmt.leftmargin + img.wx * cfmt.scale + cfmt.rightmargin + 2) : img.width), head = '\n' if (defs) head += '' + defs + '\n\n' // if %%pagescale != 1, do a global scale // (with a container: transform scale in does not work // the same in all browsers) // the class is used to know that the container is global if (cfmt.scale != 1) { head += '\n'; g = '\n' } if (psvg) // if PostScript support psvg.ps_flush(true); // + setg(0) // start a block if needed if (blkdiv > 0) { user.img_out(blkdiv == 1 ? '
' : '
') blkdiv = -1 // block started } user.img_out(head + output + g + ""); output = "" font_style = '' if (cfmt.fullsvg) { defined_glyph = {} for (i = 0; i < abc2svg.font_tb.length; i++) abc2svg.font_tb[i].used = false } else { style = ''; fulldefs = '' } defs = ''; posy = 0 img.wx = 0 // space used between the margins } // mark the end of a
block function blk_flush() { svg_flush() if (blkdiv < 0 && (!parse.state || cfmt.splittune)) { user.img_out('
') blkdiv = 0 } } Abc.prototype.blk_flush = blk_flush // abc2svg - tune.js - tune generation // // Copyright (C) 2014-2024 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 . var par_sy, // current staff system for parse cur_sy, // current staff system for generation voice_tb, curvoice, staves_found, vover, // voice overlay tsfirst /* apply the %%voice options of the current voice */ 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) 0, // grace (must be null) 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, ir = 0 while (1) { v = vn[ir++] if (v == undefined) break s = vtb[v] if (!s || !s.bar_type || s.invis || s.time != time) continue if (!bt) { bt = s.bar_type if (s.text && bt == '|') t = s.text continue } if (s.bar_type != bt) break if (s.text && !t && bt == '|') { t = s.text break } } // if the previous symbol is a grace note at the same offset as the bar // remove the grace notes from the previous time sequence if (!fl) { while (prev.type == C.GRACE && vtb[prev.v] && !vtb[prev.v].bar_type) { vtb[prev.v] = prev prev = prev.ts_prev fl = 1 //true } } if (v == undefined) return // no problem // change "::" to ":| |:" // and "|1" to "| [1" if (bt == "::" || bt == ":|" || t) { ir = 0 bt = t ? '|' : "::" while (1) { v = vn[ir++] if (v == undefined) break s = vtb[v] if (!s || s.invis || s.bar_type != bt || (bt == '|' && !s.text)) continue s2 = clone(s) if (bt == "::") { s.bar_type = ":|" s2.bar_type = "|:" } else { // s.bar_type = '|' delete s.text delete s.rbstart s2.bar_type = '[' s2.invis = 1 //true s2.xsh = 0 } s2.next = s.next if (s2.next) s2.next.prev = s2 s2.prev = s s.next = s2 } } else { error(1, s, "Different bars $1 and $2", (bt + (t || '')), (s.bar_type + (s.text || ''))) } } // b_chk() // set the first symbol of each voice for (v = 0; v < nv; v++) { s = voice_tb[v].sym vtb[v] = s if (sy.voices[v]) { vn[sy.voices[v].range] = v if (!prev && s) { fmt = s.fmt p_voice = voice_tb[v] prev = { // symbol defining the first staff system type: C.STAVES, fname: parse.fname, dur: 0, v: v, p_v: p_voice, time: 0, st: 0, sy: sy, next: s, fmt: fmt, seqst: true } } } } if (!prev) return // no symbol yet // insert the first staff system in the first voice p_voice.sym = tsfirst = s = prev if (s.next) s.next.prev = s else p_voice.last_sym = s // if Q: from tune header, put it at start of the music // (after the staff system) s = glovar.tempo if (s) { s.v = v = p_voice.v s.p_v = p_voice s.st = 0 s.time = 0 s.prev = prev s.next = prev.next if (s.next) s.next.prev = s else p_voice.last_sym = s s.prev.next = s s.fmt = fmt glovar.tempo = null vtb[v] = s } // if only one voice, quickly create the time links if (nv == 1) { s = tsfirst s.ts_next = s.next while (1) { s = s.next if (!s) return if (s.time != s.prev.time || w_tb[s.prev.type] || s.type == C.GRACE && s.prev.type == C.GRACE) s.seqst = 1 //true if (s.type == C.PART) { // move the part s.prev.next = s.prev.ts_next = s.next if (s.next) { s.next.part = s // to the next symbol s.next.prev = s.prev if (s.soln) s.next.soln = 1 //true if (s.seqst) s.next.seqst = 1 //true } continue } s.ts_prev = s.prev s.ts_next = s.next } // not reached } // loop on the symbols of all voices while (1) { if (new_sy) { sy = new_sy; new_sy = null; vn.length = 0 for (v = 0; v < nv; v++) { if (!sy.voices[v]) continue vn[sy.voices[v].range] = v } } /* search the min time and symbol weight */ wmin = time = 10000000 // big int ir = 0 while (1) { v = vn[ir++] if (v == undefined) break s = vtb[v] if (!s || s.time > time) continue w = w_tb[s.type] if (s.type == C.GRACE && s.next && s.next.type == C.GRACE) w-- if (s.time < time) { time = s.time; wmin = w } else if (w < wmin) { wmin = w } } if (wmin > 127) break // done // check the type of the measure bars if (wmin == 6) // !! weight of bars b_chk() /* link the vertical sequence */ ir = 0 while (1) { v = vn[ir++] if (v == undefined) break s = vtb[v] if (!s || s.time != time) continue w = w_tb[s.type] if (!w && s.type == C.GRACE && s.next && s.next.type == C.GRACE) w-- if (w != wmin) continue if (!w && s.type == C.PART) { // move the part if (s.prev) s.prev.next = s.next else s.p_v.sym = s.next vtb[v] = s.next if (s.next) { s.next.part = s // to the next symbol s.next.prev = s.prev if (s.soln) s.next.soln = 1 //true // } else { // ignored } continue } if (s.type == C.STAVES) new_sy = s.sy if (fl) { fl = 0; s.seqst = true } s.ts_prev = prev prev.ts_next = s prev = s vtb[v] = s.next } if (wmin) // if some width fl = 1 //true // start a new sequence } } // adjust some voice elements // (possible hook) Abc.prototype.voice_adj = function (sys_chg) { var p_voice, s, s2, v, sl // insert the delayed P: and Q: in the top_voice function ins_pq() { var s, s2, p_v = voice_tb[par_sy.top_voice] while (1) { s = parse.pq_d.shift() if (!s) break for (s2 = p_v.sym; ; s2 = s2.next) { 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() // do the note mapping stuff function do_map(s) { var m, nt for (m = 0; m <= s.nhd; m++) { nt = s.notes[m] set_map(s.p_v, nt, nt.acc, 1) // transpose done } } // do_map() // 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 } break } continue case C.NOTE: if (s.feathered_beam) set_feathered_beam(s) if (p_voice.map && maps[p_voice.map]) do_map(s) break } } } } /* -- create a new staff system -- */ function new_syst(init) { var st, v, sy_staff, p_voice, sy_new = { voices: [], staves: [], top_voice: 0 } if (init) { /* first staff system */ cur_sy = par_sy = sy_new return } // update the previous system for (v = 0; v < voice_tb.length; v++) { if (par_sy.voices[v]) { st = par_sy.voices[v].st sy_staff = par_sy.staves[st] p_voice = voice_tb[v] sy_staff.staffnonote = p_voice.staffnonote if (p_voice.staffscale) sy_staff.staffscale = p_voice.staffscale; } } for (st = 0; st < par_sy.staves.length; st++) { sy_new.staves[st] = clone(par_sy.staves[st]); sy_new.staves[st].flags = 0 } par_sy.next = sy_new; par_sy = sy_new } /* -- set the bar numbers -- */ // (possible hook) Abc.prototype.set_bar_num = function() { var s, s2, rep_tim, k, n, nu, txt, tim = 0, // time of the previous bar bar_num = gene.nbar, bar_tim = 0, // time of previous repeat variant ptim = 0, // time of previous bar wmeasure = voice_tb[cur_sy.top_voice].meter.wmeasure // check the measure duration function check_meas() { var s3 if (tim > ptim + wmeasure && s.prev.type != C.MREST) return 1 //true // the measure is too short, // check if there is a bar a bit further for (s3 = s.next; s3 && s3.time == s.time; s3 = s3.next) ; for ( ; s3 && !s3.bar_type; s3 = s3.next) ; return s3 && (s3.time - bar_tim) % wmeasure } // don't count a bar at start of tune for (s = tsfirst; ; s = s.ts_next) { if (!s) return switch (s.type) { case C.METER: wmeasure = s.wmeasure // fall thru case C.CLEF: case C.KEY: case C.STBRK: continue case C.BAR: if (s.bar_num) bar_num = s.bar_num // %%setbarnb) break } break } // at start of tune, check for an anacrusis for (s2 = s.ts_next; s2; s2 = s2.ts_next) { if (s2.type == C.BAR && s2.time && !s2.invis && !s2.bar_dotted) { if (s2.time < wmeasure) { // if anacrusis s = s2 bar_tim = s.time } break } } // set the measure number on the top bars for ( ; s; s = s.ts_next) { switch (s.type) { case C.METER: if (wmeasure != 1) // if not M:none bar_num += (s.time - bar_tim) / wmeasure bar_tim = s.time wmeasure = s.wmeasure while (s.ts_next && s.ts_next.wmeasure) s = s.ts_next break case C.BAR: if (s.time <= tim) break // already seen tim = s.time nu = 1 //true // no num update txt = "" for (s2 = s; s2; s2 = s2.next) { if (s2.time > tim) break if (!s2.bar_type) continue if (s2.bar_type != '[') nu = 0 //false // do update if (s2.text) txt = s2.text } if (s.bar_num) { bar_num = s.bar_num // (%%setbarnb) ptim = bar_tim = tim break } if (wmeasure == 1) { // if M:none if (s.bar_dotted) break if (txt) { if (!cfmt.contbarnb) { if (txt[0] == '1') rep_tim = bar_num else bar_num = rep_tim } } if (!nu) s.bar_num = ++bar_num break } n = bar_num + (tim - bar_tim) / wmeasure k = n - (n | 0) if (cfmt.checkbars && k && check_meas()) error(0, s, "Bad measure duration") if (tim > ptim + wmeasure) { // if more than one measure n |= 0 k = 0 bar_tim = tim // re-synchronize bar_num = n } if (txt) { if (txt[0] == '1') { if (!cfmt.contbarnb) rep_tim = tim - bar_tim if (!nu) s.bar_num = n } else { if (!cfmt.contbarnb) bar_tim = tim - rep_tim n = bar_num + (tim - bar_tim) / wmeasure if (n == (n | 0)) s.bar_num = n } } else if (n == (n | 0)) { s.bar_num = n } if (!k) ptim = tim break } } } // convert a note to ABC function not2abc(pit, acc) { var i, nn = '' if (acc && acc != 3) { if (typeof acc != "object") { nn = ['__', '_', '', '^', '^^'][acc + 2] } else { i = acc[0] if (i > 0) { nn += '^' } else { nn += '_' i = -i } nn += i + '/' + acc[1] } } nn += ntb[(pit + 75) % 7] for (i = pit; i >= 23; i -= 7) nn += "'" for (i = pit; i < 16; i += 7) nn += "," return nn } // not2abc() // note mapping // %%map map_name note [print [note_head]] [param]* function get_map(text) { if (!text) return var i, note, notes, map, tmp, ns, ty = '', a = text.split(/\s+/) if (a.length < 3) { syntax(1, errs.not_enough_p) return } ns = a[1] if (ns[0] == '*' || ns.indexOf("all") == 0) { ns = 'all' } else { if (ns.indexOf("octave,") == 0 // remove the octave part || ns.indexOf("key,") == 0) { ty = ns[0] ns = ns.split(',')[1] ns = ns.replace(/[,']+/, '').toUpperCase() //' if (ns.indexOf("key,") == 0) ns = ns.replace(/[=^_]+/, '') } tmp = new scanBuf tmp.buffer = ns note = parse_acc_pit(tmp) if (!note) { syntax(1, "Bad note in %%map") return } ns = ty + not2abc(note.pit, note.acc) } notes = maps[a[0]] if (!notes) maps[a[0]] = notes = {} map = notes[ns] if (!map) notes[ns] = map = [] // try the optional 'print' and 'heads' parameters a.shift() a.shift() if (!a.length) return a = info_split(a.join(' ')) i = 0 if (a[0].indexOf('=') < 0) { if (a[0][0] != '*') { tmp = new scanBuf; // print tmp.buffer = a[0]; map[1] = parse_acc_pit(tmp) } if (!a[1]) return i++ if (a[1].indexOf('=') < 0) { map[0] = a[1].split(',') // heads i++ } } for (; i < a.length; i++) { switch (a[i]) { case "heads=": if (!a[++i]) { syntax(1, errs.not_enough_p) break } map[0] = a[i].split(',') break case "print=": case "play=": case "print_notrp=": if (!a[++i]) { syntax(1, errs.not_enough_p) break } tmp = new scanBuf; tmp.buffer = a[i]; note = parse_acc_pit(tmp) if (a[i - 1][5] == '_') // if print no transpose note.notrp = 1 //true if (a[i - 1][1] == 'r') map[1] = note else map[3] = note break case "color=": if (!a[++i]) { syntax(1, errs.not_enough_p) break } map[2] = a[i] break } } } // get a abcm2ps/abcMIDI compatible transposition value as a base-40 interval // The value may be // - [+|-][s|f] // - [] % default is 'c' function get_transp(param) { if (param[0] == '0') return 0 if ("123456789-+".indexOf(param[0]) >= 0) { // by semi-tone var val = parseInt(param) if (isNaN(val) || val < -36 || val > 36) { //fixme: no source reference... syntax(1, errs.bad_transp) return } val += 36 val = ((val / 12 | 0) - 3) * 40 + abc2svg.isb40[val % 12] if (param.slice(-1) == 'b') val += 4 return val } // return undefined } // get_transp() /* -- process a pseudo-comment (%% or I:) -- */ // (possible hook) Abc.prototype.do_pscom = function(text) { var h1, val, s, cmd, param, n, k, b cmd = text.match(/[^\s]+/) if (!cmd) return cmd = cmd[0]; // ignore the command if the voice is ignored, // but not if %%score/%%staves! if (curvoice && curvoice.ignore) { switch (cmd) { case "staves": case "score": break default: return } } 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) { 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 return case "sep": var h2, len, values, lwidth; set_page(); lwidth = img.width - img.lm - img.rm; h1 = h2 = len = 0 if (param) { values = param.split(/\s+/); h1 = get_unit(values[0]) if (values[1]) { h2 = get_unit(values[1]) if (values[2]) len = get_unit(values[2]) } if (isNaN(h1) || isNaN(h2) || isNaN(len)) { syntax(1, errs.bad_val, "%%sep") return } } if (h1 < 1) h1 = 14 if (h2 < 1) h2 = h1 if (len < 1) len = 90 if (parse.state >= 2) { s = new_block(cmd); s.x = (lwidth - len) / 2 / cfmt.scale; s.l = len / cfmt.scale; s.sk1 = h1; s.sk2 = h2 return } vskip(h1); output += '\n'; vskip(h2); blk_flush() return case "setbarnb": val = parseInt(param) if (isNaN(val) || val < 1) { syntax(1, "Bad %%setbarnb value") break } glovar.new_nbar = val return case "staff": if (parse.state != 3) return val = parseInt(param) if (isNaN(val)) { syntax(1, "Bad %%staff value '$1'", param) return } var st if (param[0] == '+' || param[0] == '-') st = curvoice.cst + val else st = val - 1 if (st < 0 || st > nstaff) { syntax(1, "Bad %%staff number $1 (cur $2, max $3)", st, curvoice.cst, nstaff) return } delete curvoice.floating; curvoice.cst = st return case "staffbreak": if (parse.state != 3) return s = { type: C.STBRK, dur:0 } if (param.slice(-1) == 'f') { s.stbrk_forced = true param = param.replace(/\sf$/, '') } if (param) { val = get_unit(param) if (isNaN(val)) { syntax(1, errs.bad_val, "%%staffbreak") return } s.xmx = val } else { s.xmx = 14 } sym_link(s) return case "tacet": if (param[0] == '"') param = param.slice(1, -1) // fall thru case "stafflines": case "staffscale": case "staffnonote": set_v_param(cmd, param) return case "staves": case "score": if (!parse.state) return if (parse.scores && parse.scores.length > 0) { text = parse.scores.shift(); cmd = text.match(/([^\s]+)\s*(.*)/); param = cmd[2] cmd = cmd[1] } get_staves(cmd, param) return case "center": case "text": k = cmd[0] == 'c' ? 'c' : cfmt.textoption set_font("text") if (parse.state >= 2) { s = new_block("text") s.text = param s.opt = k s.font = cfmt.textfont return } write_text(param, k) return case "transpose": // (abcm2ps compatibility) if (cfmt.sound) return val = get_transp(param) if (val == undefined) { // accept note interval val = get_interval(param) if (val == undefined) return } switch (parse.state) { case 0: cfmt.transp = 0 // fall thru case 1: cfmt.transp = (cfmt.transp || 0) + val return } curvoice.shift = val key_trans() return case "tune": //fixme: to do return case "user": set_user(param) return case "voicecolor": if (curvoice) curvoice.color = param return case "vskip": val = get_unit(param) if (isNaN(val)) { syntax(1, errs.bad_val, "%%vskip") return } if (val < 0) { syntax(1, "%%vskip cannot be negative") return } if (parse.state >= 2) { s = new_block(cmd); s.sk = val return } vskip(val); return case "newpage": case "leftmargin": case "rightmargin": case "pagescale": case "pagewidth": case "printmargin": case "scale": case "staffwidth": if (parse.state >= 2) { s = new_block(cmd); s.param = param return } if (cmd == "newpage") { blk_flush() if (user.page_format) blkdiv = 2 // start the next SVG in a new page return } break } self.set_format(cmd, param) } // treat the %%beginxxx / %%endxxx sequences // (possible hook) Abc.prototype.do_begin_end = function(type, opt, text) { var i, j, action, s switch (type) { case "js": js_inject(text) break case "ml": if (cfmt.pageheight) { syntax(1, "Cannot have %%beginml with %%pageheight") break } if (parse.state >= 2) { s = new_block(type); s.text = text } else { blk_flush() if (user.img_out) user.img_out(text) } break case "svg": j = 0 while (1) { i = text.indexOf('', i) j = text.indexOf('', i) if (j < 0) { syntax(1, "No in %%beginsvg sequence") break } style += text.slice(i + 1, j).replace(/\s+$/, '') } j = 0 while (1) { i = text.indexOf('\n', j) if (i < 0) break j = text.indexOf('', i) if (j < 0) { syntax(1, "No in %%beginsvg sequence") break } defs_add(text.slice(i + 6, j)) } break case "text": action = get_textopt(opt); if (!action) action = cfmt.textoption set_font("text") if (text.indexOf('\\') >= 0) text = cnv_escape(text) if (parse.state > 1) { s = new_block(type); s.text = text s.opt = action s.font = cfmt.textfont break } write_text(text, action) break } } /* -- generate a piece of tune -- */ function generate() { var s, v, p_voice; if (a_dcn.length) { syntax(1, "Decoration(s) without symbol: $1", a_dcn) a_dcn = [] } if (parse.tp) { syntax(1, "No end of tuplet") s = parse.tps if (s) delete s.tp delete parse.tp } if (vover) { syntax(1, "No end of voice overlay"); get_vover(vover.bar ? '|' : ')') } self.voice_adj() sort_all() /* define the time / vertical sequences */ if (tsfirst) { for (v = 0; v < voice_tb.length; v++) { if (!voice_tb[v].key) voice_tb[v].key = parse.ckey // set the starting key } if (user.anno_start) anno_start = a_start if (user.anno_stop) anno_stop = a_stop self.set_bar_num() if (info.P) tsfirst.parts = info.P // for play // give the parser result to the application if (user.get_abcmodel) user.get_abcmodel(tsfirst, voice_tb, abc2svg.sym_name, info) if (user.img_out) // if SVG generation self.output_music() } // (tsfirst) // finish the generation set_page() // the page layout may have changed if (info.W) put_words(info.W) put_history() parse.state = 0 // file header blk_flush() // (force end of block) if (tsfirst) { // if non void, keep tune data for upper layers tunes.push([tsfirst, voice_tb, info, cfmt]) tsfirst = null } } // transpose the current key of the voice (called on K: or V:) function key_trans() { var i, n, a_acc, b40, d, s = curvoice.ckey, // current key ti = s.time || 0 if (s.k_bagpipe || s.k_drum) return // no transposition // set the score transposition n = (curvoice.score | 0) // new transposition + (curvoice.shift | 0) + (cfmt.transp | 0) if ((curvoice.tr_sco | 0) == n) { // if same transposition s.k_sf = curvoice.ckey.k_sf return } // get the current key or create a new one if (is_voice_sig()) { // if no symbol yet curvoice.key = s // new root key of the voice } else if (curvoice.time != ti) { // if no K: at this time s = clone(s.orig || s) // new key if (!curvoice.new) s.k_old_sf = curvoice.ckey.k_sf sym_link(s) } curvoice.ckey = s // current key if (cfmt.transp && curvoice.shift) // if %%transpose and shift= syntax(0, "Mix of old and new transposition syntaxes"); // define the new key curvoice.tr_sco = n // b40 interval n = abc2svg.b40l5[(n + 202) % 40] // transpose in the line of fifth + s.orig.k_sf // + old = new sf if (n < -7) { n += 12 curvoice.tr_sco -= 4 } else if (n > 7) { n -= 12 curvoice.tr_sco += 4 } if (!s.k_none) s.k_sf = n for (b40 = 0; b40 < 40; b40++) { if (abc2svg.b40l5[b40] == n) break } s.k_b40 = b40 // transpose the accidental list if (!s.k_a_acc) return d = b40 - s.orig.k_b40 a_acc = [] for (i = 0; i < s.k_a_acc.length; i++) { b40 = abc2svg.pab40(s.k_a_acc[i].pit, s.k_a_acc[i].acc) + d a_acc[i] = { pit: abc2svg.b40p(b40), 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, 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.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) /* * create a new staff system and * link the 'staves' symbol in a voice which is seen from * the previous system - see sort_all */ for (v = 0; v < par_sy.voices.length; v++) { if (par_sy.voices[v]) { curvoice = voice_tb[v] break } } curvoice.time = maxtime; 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 // and do a voice re-synchronization if (!parm) { s.sy = clone(par_sy, 1) par_sy.next = s.sy par_sy = s.sy staves_found = maxtime for (v = 0; v < nv; v++) voice_tb[v].time = 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); p_voice.time = maxtime; 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) par_sy.one_v = 1 //true // one voice /* change the behavior from %%staves to %%score */ if (cmd[1] == 't') { /* if %%staves */ for (i = 0; i < a_vf.length; i++) { flags = a_vf[i][1] if (!(flags & (OPEN_BRACE | OPEN_BRACE2))) continue if ((flags & (OPEN_BRACE | CLOSE_BRACE)) == (OPEN_BRACE | CLOSE_BRACE) || (flags & (OPEN_BRACE2 | CLOSE_BRACE2)) == (OPEN_BRACE2 | CLOSE_BRACE2)) continue if (a_vf[i + 1][1] != 0) continue if ((flags & OPEN_PARENTH) || (a_vf[i + 2][1] & OPEN_PARENTH)) continue /* {a b c} -> {a *b c} */ if (a_vf[i + 2][1] & (CLOSE_BRACE | CLOSE_BRACE2)) { a_vf[i + 1][1] |= FL_VOICE /* {a b c d} -> {(a b) (c d)} */ } else if (a_vf[i + 2][1] == 0 && (a_vf[i + 3][1] & (CLOSE_BRACE | CLOSE_BRACE2))) { a_vf[i][1] |= OPEN_PARENTH; a_vf[i + 1][1] |= CLOSE_PARENTH; a_vf[i + 2][1] |= OPEN_PARENTH; a_vf[i + 3][1] |= CLOSE_PARENTH } } } /* set the staff system */ st = -1 for (i = 0; i < a_vf.length; i++) { flags = a_vf[i][1] if ((flags & (OPEN_PARENTH | CLOSE_PARENTH)) == (OPEN_PARENTH | CLOSE_PARENTH)) { flags &= ~(OPEN_PARENTH | CLOSE_PARENTH); a_vf[i][1] = flags } p_voice = a_vf[i][0] if (flags & FL_VOICE) { p_voice.floating = true; p_voice.second = true } else { st++; if (!par_sy.staves[st]) { par_sy.staves[st] = { stafflines: p_voice.stafflines || "|||||", staffscale: 1 } } par_sy.staves[st].flags = 0 } v = p_voice.v; p_voice.st = p_voice.cst = par_sy.voices[v].st = st; par_sy.staves[st].flags |= flags if (flags & OPEN_PARENTH) { p_voice2 = p_voice while (i < a_vf.length - 1) { p_voice = a_vf[++i][0]; v = p_voice.v if (a_vf[i][1] & MASTER_VOICE) { p_voice2.second = true p_voice2 = p_voice } else { p_voice.second = true; } p_voice.st = p_voice.cst = par_sy.voices[v].st = st if (a_vf[i][1] & CLOSE_PARENTH) break } par_sy.staves[st].flags |= a_vf[i][1] } } if (st < 0) st = 0 par_sy.nstaff = nstaff = st /* change the behaviour of '|' in %%score */ if (cmd[1] == 'c') { /* if %%score */ for (st = 0; st < nstaff; st++) par_sy.staves[st].flags ^= STOP_BAR } nv = voice_tb.length st = 0 for (v = 0; v < nv; v++) { p_voice = voice_tb[v] if (par_sy.voices[v]) st = p_voice.st else p_voice.st = st // (this avoids later crashes) // if first %%staves // update the staff of the symbols with no time if (!maxtime) { for (s = p_voice.sym; s; s = s.next) s.st = st } if (!par_sy.voices[v]) continue // set the staff of the overlay voices p_voice2 = p_voice.voice_down while (p_voice2) { p_voice2.second = 1 //true i = p_voice2.v p_voice2.st = p_voice2.cst = par_sy.voices[i].st = st p_voice2 = p_voice2.voice_down } par_sy.voices[v].second = p_voice.second; st = p_voice.st if (st > 0 && p_voice.norepbra == undefined && !(par_sy.staves[st - 1].flags & STOP_BAR)) p_voice.norepbra = true } curvoice = parse.state >= 2 ? voice_tb[par_sy.top_voice] : null } // get a voice or create a clone of the current voice function clone_voice(id) { var v, p_voice for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v] if (p_voice.id == id) return p_voice // found } p_voice = clone(curvoice); p_voice.v = voice_tb.length; p_voice.id = id; p_voice.sym = p_voice.last_sym = null; p_voice.key = clone(curvoice.key) p_voice.sls = [] delete p_voice.nm delete p_voice.snm delete p_voice.new_name delete p_voice.lyric_restart delete p_voice.lyric_cont delete p_voice.sym_restart delete p_voice.sym_cont delete p_voice.have_ly delete p_voice.tie_s voice_tb.push(p_voice) return p_voice } // clone_voice() /* -- get a voice overlay -- */ function get_vover(type) { var p_voice2, p_voice3, range, s, time, v, v2, v3, s2 /* treat the end of overlay */ if (type == '|' || type == ')') { if (!curvoice.last_note) { syntax(1, errs.nonote_vo) if (vover) { curvoice = vover.p_voice vover = null } return } curvoice.last_note.beam_end = true if (!vover) { syntax(1, "Erroneous end of voice overlay") return } if (curvoice.time != vover.p_voice.time) { if (!curvoice.ignore) syntax(1, "Wrong duration in voice overlay"); if (curvoice.time > vover.p_voice.time) vover.p_voice.time = curvoice.time } curvoice.acc = [] // no accidental anymore // if the last symbols are spaces, move them to the main voice p_voice2 = vover.p_voice // main voice s = curvoice.last_sym if (s.type == C.SPACE && p_voice2.last_sym.type != C.SPACE) { s.p_v = p_voice2 s.v = s.p_v.v while (s.prev.type == C.SPACE) { s = s.prev s.p_v = p_voice2 s.v = s.p_v.v } s2 = s.prev s2.next = null s.prev = p_voice2.last_sym s.prev.next = s p_voice2.last_sym = curvoice.last_sym curvoice.last_sym = s2 } curvoice = p_voice2 vover = null return } /* treat the full overlay start */ if (type == '(') { if (vover) { syntax(1, "Voice overlay already started") return } vover = { p_voice: curvoice, time: curvoice.time } return } /* (here is treated a new overlay - '&') */ /* create the extra voice if not done yet */ if (!curvoice.last_note) { syntax(1, errs.nonote_vo) return } curvoice.last_note.beam_end = true; p_voice2 = curvoice.voice_down if (!p_voice2) { p_voice2 = clone_voice(curvoice.id + 'o'); curvoice.voice_down = p_voice2; p_voice2.time = 0; p_voice2.second = true; p_voice2.last_note = null v2 = p_voice2.v; if (par_sy.voices[curvoice.v]) { // if voice in the staff system par_sy.voices[v2] = { st: curvoice.st, second: true } range = par_sy.voices[curvoice.v].range for (v = 0; v < par_sy.voices.length; v++) { if (par_sy.voices[v] && par_sy.voices[v].range > range) par_sy.voices[v].range++ } par_sy.voices[v2].range = range + 1 } } p_voice2.ulen = curvoice.ulen p_voice2.dur_fact = curvoice.dur_fact p_voice2.acc = [] // no accidental if (!vover) { /* first '&' in a measure */ time = p_voice2.time if (curvoice.ignore) s = curvoice.last_bar else for (s = curvoice.last_sym; /*s*/; s = s.prev) { if (s.type == C.BAR || s.time <= time) /* (if start of tune) */ break } vover = { bar: (s && s.bar_type) ? s.bar_type : '|', p_voice: curvoice, time: s ? s.time : curvoice.time } } else { if (curvoice != vover.p_voice && curvoice.time != vover.p_voice.time) { syntax(1, "Wrong duration in voice overlay") if (curvoice.time > vover.p_voice.time) vover.p_voice.time = curvoice.time } } p_voice2.time = vover.time; curvoice = p_voice2 } // check if a clef, key or time signature may go at start of the current voice function is_voice_sig() { var s if (curvoice.time) return false 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) } if (s.prev) // if not the first clef of the voice s.clef_small = 1 //true // have a small clef } // 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) curvoice.clone = null // don't stop the multi-voice sequence get_voice(curvoice.id + ' ' + a.join(' ')) if (p_voice) curvoice.clone = p_voice if (def) curvoice.default = 1 //true } // get / create a new voice function new_voice(id) { var v, p_v_sav, p_voice = voice_tb[0], n = voice_tb.length // if first explicit voice and no music, replace the default V:1 if (n == 1 && p_voice.default) { delete p_voice.default if (!p_voice.time) { // if no symbol yet p_voice.id = id p_voice.init = 0 // set back the global voice parameters return p_voice // default voice } } for (v = 0; v < n; v++) { p_voice = voice_tb[v] if (p_voice.id == id) return p_voice // old voice } p_voice = { v: v, id: id, time: 0, new: true, pos: { // dyn: 0, // gch: 0, // gst: 0, // orn: 0, // stm: 0, // tup: 0, // voc: 0, // vol: 0 }, 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.eol, 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 if (curvoice.new) { // if new voice delete curvoice.new if (staves_found < 0) { // if no %%score/%%staves curvoice.st = curvoice.cst = ++nstaff; par_sy.nstaff = nstaff; par_sy.voices[v] = { st: nstaff, range: v } par_sy.staves[nstaff] = { stafflines: curvoice.stafflines || "|||||", staffscale: 1 } } else if (!par_sy.voices[v]) { curvoice.ignore = 1 // voice not declared in %%staves return } } if (!curvoice.filtered && par_sy.voices[v] && (parse.voice_opts || parse.tune_v_opts)) { curvoice.filtered = true; voice_filter() } } // change state from 'tune header' to 'in tune body' // curvoice is defined when called from get_voice() function goto_tune() { var v, p_voice set_page(); write_heading(); if (glovar.new_nbar) { gene.nbar = glovar.new_nbar // measure numbering glovar.new_nbar = 0 } else { gene.nbar = 1 } parse.state = 3 // in tune body // update some voice parameters for (v = 0; v < voice_tb.length; v++) { p_voice = voice_tb[v]; p_voice.ulen = glovar.ulen if (parse.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 } } // initialize the voices when no %%staves/score if (staves_found < 0) { v = voice_tb.length par_sy.nstaff = nstaff = v - 1 while (--v >= 0) { p_voice = voice_tb[v]; delete p_voice.new; // old voice p_voice.st = p_voice.cst = v; par_sy.voices[v] = { st: v, range: v } par_sy.staves[v] = { stafflines: p_voice.stafflines || "|||||", staffscale: 1 } } } } // abc2svg - lyrics.js - lyrics // // Copyright (C) 2014-2023 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 . // parse a symbol line (s:) function get_sym(p, cont) { var s, c, i, j, d if (curvoice.ignore) return // get the starting symbol of the lyrics if (cont) { // +: s = curvoice.sym_cont if (!s) { syntax(1, "+: symbol line without music") return } } else { if (curvoice.sym_restart) { // new music curvoice.sym_start = curvoice.sym_restart; curvoice.sym_restart = null } s = curvoice.sym_start if (!s) s = curvoice.sym if (!s) { syntax(1, "s: without music") return } } /* scan the symbol line */ i = 0 while (1) { while (p[i] == ' ' || p[i] == '\t') i++; c = p[i] if (!c) break switch (c) { case '|': while (s && s.type != C.BAR) s = s.next if (!s) { syntax(1, "Not enough measure bars for symbol line") return } s = s.next; i++ continue case '!': case '"': j = ++i i = p.indexOf(c, j) if (i < 0) { syntax(1, c == '!' ? "No end of decoration" : "No end of chord symbol/annotation"); i = p.length continue } d = p.slice(j - 1, i + 1) break case '*': break default: d = c.charCodeAt(0) if (d < 128) { d = char_tb[d] if (d.length > 1 && (d[0] == '!' || d[0] == '"')) { c = d[0] break } } syntax(1, errs.bad_char, c) break } /* store the element in the next note */ while (s && s.type != C.NOTE) s = s.next if (!s) { syntax(1, "Too many elements in symbol line") return } switch (c) { default: // case '*': break case '!': a_dcn.push(d.slice(1, -1)) deco_cnv(s, s.prev) break case '"': parse_gchord(d) if (a_gch) // if no error csan_add(s) break } s = s.next; i++ } curvoice.sym_cont = s } /* -- parse a lyric (vocal) line (w:) -- */ function get_lyrics(text, cont) { var s, word, p, i, j, ly, dfnt, ln, c, cf if (curvoice.ignore) return if ((curvoice.pos.voc & 0x07) != C.SL_HIDDEN) curvoice.have_ly = true // get the starting symbol of the lyrics if (cont) { // +: s = curvoice.lyric_cont if (!s) { syntax(1, "+: lyric without music") return } dfnt = get_font("vocal") if (gene.deffont != dfnt) { // if vocalfont change if (gene.curfont == gene.deffont) gene.curfont = dfnt gene.deffont = dfnt } } else { set_font("vocal") if (curvoice.lyric_restart) { // new music curvoice.lyric_start = s = curvoice.lyric_restart; curvoice.lyric_restart = null; curvoice.lyric_line = 0 } else { curvoice.lyric_line++; s = curvoice.lyric_start } if (!s) s = curvoice.sym if (!s) { syntax(1, "w: without music") return } } /* scan the lyric line */ p = text; i = 0 cf = gene.curfont while (1) { while (p[i] == ' ' || p[i] == '\t') i++ if (!p[i]) break ln = 0 j = parse.istart + i + 2 // start index switch (p[i]) { case '|': while (s && s.type != C.BAR) s = s.next if (!s) { syntax(1, "Not enough measure bars for lyric line") return } s = s.next; i++ continue case '-': case '_': word = p[i] ln = p[i] == '-' ? 2 : 3 // line continuation break case '*': word = "" break default: word = ""; while (1) { if (!p[i]) break switch (p[i]) { case '_': case '*': case '|': i-- case ' ': case '\t': break case '~': word += ' ' i++ continue case '-': ln = 1 // start of line break case '\\': if (!p[++i]) continue word += p[i++] continue case '$': word += p[i++] c = p[i] if (c == '0') gene.curfont = gene.deffont else if (c >= '1' && c <= '9') gene.curfont = get_font("u" + c) // fall thru default: word += p[i++] continue } break } break } /* store the word in the next note */ while (s && s.type != C.NOTE) s = s.next if (!s) { syntax(1, "Too many words in lyric line") return } if (word && (s.pos.voc & 0x07) != C.SL_HIDDEN) { ly = { t: word, font: cf, istart: j, iend: j + word.length } if (ln) ly.ln = ln if (!s.a_ly) s.a_ly = [] s.a_ly[curvoice.lyric_line] = ly cf = gene.curfont } s = s.next; i++ } curvoice.lyric_cont = s } // install the words under a note // (this function is called during the generation) function ly_set(s) { var i, j, ly, d, s1, s2, p, w, spw, xx, sz, shift, dw, s3 = s, // start of the current time sequence wx = 0, wl = 0, n = 0, dx = 0, a_ly = s.a_ly, align = 0 // get the available horizontal space before the next lyric words for (s2 = s.ts_next; s2; s2 = s2.ts_next) { if (s2.shrink) { dx += s2.shrink n++ // number of symbols without word } if (s2.bar_type) { // stop on a bar dx += 3 // and take some of its spacing break } if (!s2.a_ly) continue i = s2.a_ly.length while (--i >= 0) { ly = s2.a_ly[i] if (!ly) continue if (!ly.ln || ly.ln < 2) break } if (i >= 0) break } // define the offset of the words for (i = 0; i < a_ly.length; i++) { ly = a_ly[i] if (!ly) continue gene.curfont = ly.font ly.t = str2svg(ly.t) p = ly.t.replace(/<[^>]*>/g, '') // remove the XML tags if (ly.ln >= 2) { ly.shift = 0 continue } spw = cwid(' ') * ly.font.swfac w = ly.t.wh[0] if (s.type == C.GRACE) { // %%graceword shift = s.wl } else if ((p[0] >= '0' && p[0] <= '9' && p.length > 2) || p[1] == ':' || p[0] == '(' || p[0] == ')') { if (p[0] == '(') { sz = spw } else { j = p.indexOf(' ') set_font(ly.font) if (j > 0) sz = strwh(p.slice(0, j))[0] else sz = w * .2 } shift = (w - sz) * .4 if (shift > 14) shift = 14 shift += sz if (p[0] >= '0' && p[0] <= '9') { if (shift > align) align = shift } } else { shift = w * .4 if (shift > 14) shift = 14 } ly.shift = shift if (shift > wl) wl = shift // max left space w += spw * 1.5 // space after the syllable w -= shift // right width if (w > wx) wx = w // max width } // set the left space while (!s3.seqst) s3 = s3.ts_prev if (s3.ts_prev && s3.ts_prev.bar_type) wl -= 4 // don't move too much the measure bar if (s3.wl < wl) { s3.shrink += wl - s3.wl s3.wl = wl } // if not room enough, shift the following notes to the right dx -= 6 if (dx < wx) { dx = (wx - dx) / n s1 = s.ts_next while (1) { if (s1.shrink) { s1.shrink += dx s3.wr += dx // (needed for end of line) s3 = s1 } if (s1 == s2) break s1 = s1.ts_next } } if (align > 0) { for (i = 0; i < a_ly.length; i++) { ly = a_ly[i] if (ly && ly.t[0] >= '0' && ly.t[0] <= '9') ly.shift = align } } } // ly_set() /* -- draw the lyrics under (or above) notes -- */ /* (the staves are not yet defined) */ function draw_lyric_line(p_voice, j, y) { var p, lastx, w, s, s2, ly, lyl, ln, hyflag, lflag, x0, shift if (p_voice.hy_st & (1 << j)) { hyflag = true; p_voice.hy_st &= ~(1 << j) } for (s = p_voice.sym; /*s*/; s = s.next) if (s.type != C.CLEF && s.type != C.KEY && s.type != C.METER) break lastx = s.prev ? s.prev.x : tsfirst.x; x0 = 0 for ( ; s; s = s.next) { if (s.a_ly) ly = s.a_ly[j] else ly = null if (!ly) { switch (s.type) { case C.REST: case C.MREST: if (lflag) { out_wln(lastx + 3, y, x0 - lastx); lflag = false; lastx = s.x + s.wr } } continue } if (ly.font != gene.curfont) /* font change */ gene.curfont = ly.font p = ly.t; ln = ly.ln || 0 w = p.wh[0] shift = ly.shift if (hyflag) { if (ln == 3) { // '_' ln = 2 } else if (ln < 2) { // not '-' out_hyph(lastx, y, s.x - shift - lastx); hyflag = false; lastx = s.x + s.wr } } if (lflag && ln != 3) { // not '_' out_wln(lastx + 3, y, x0 - lastx + 3); lflag = false; lastx = s.x + s.wr } if (ln >= 2) { // '-' or '_' if (x0 == 0 && lastx > s.x - 18) lastx = s.x - 18 if (ln == 2) // '-' hyflag = true else lflag = true; x0 = s.x - shift continue } x0 = s.x - shift; if (ln) // '-' at end hyflag = true if (user.anno_start || user.anno_stop) { s2 = { p_v: s.p_v, st: s.st, istart: ly.istart, iend: ly.iend, ts_prev: s, ts_next: s.ts_next, x: x0, y: y, ymn: y, ymx: y + gene.curfont.size, wl: 0, wr: w } anno_start(s2, 'lyrics') } xy_str(x0, y, p) anno_stop(s2, 'lyrics') lastx = x0 + w } if (hyflag) { hyflag = false; x0 = realwidth - 10 if (x0 < lastx + 10) x0 = lastx + 10; out_hyph(lastx, y, x0 - lastx) if (p_voice.s_next && p_voice.s_next.fmt.hyphencont) p_voice.hy_st |= (1 << j) } /* see if any underscore in the next line */ for (p_voice.s_next; s; s = s.next) { if (s.type == C.NOTE) { if (!s.a_ly) break ly = s.a_ly[j] if (ly && ly.ln == 3) { // '_' lflag = true; x0 = realwidth - 15 if (x0 < lastx + 12) x0 = lastx + 12 } break } } if (lflag) { out_wln(lastx + 3, y, x0 - lastx + 3); lflag = false } } function draw_lyrics(p_voice, nly, a_h, y, incr) { /* 1: below, -1: above */ var j, top, sc = staff_tb[p_voice.st].staffscale; set_font("vocal") if (incr > 0) { /* under the staff */ if (y > -tsfirst.fmt.vocalspace) y = -tsfirst.fmt.vocalspace; y *= sc for (j = 0; j < nly; j++) { y -= a_h[j] * 1.1; draw_lyric_line(p_voice, j, y + a_h[j] * .22) // (descent) } return y / sc } /* above the staff */ top = staff_tb[p_voice.st].topbar + tsfirst.fmt.vocalspace if (y < top) y = top; y *= sc for (j = nly; --j >= 0;) { draw_lyric_line(p_voice, j, y + a_h[j] * .22) y += a_h[j] * 1.1 } return y / sc } // -- draw all the lyrics -- /* (the staves are not yet defined) */ function draw_all_lyrics() { var p_voice, s, v, nly, i, x, y, w, a_ly, ly, lyst_tb = new Array(nstaff + 1), nv = voice_tb.length, h_tb = new Array(nv), nly_tb = new Array(nv), above_tb = new Array(nv), rv_tb = new Array(nv), top = 0, bot = 0, st = -1 /* compute the number of lyrics per voice - staff * and their y offset on the staff */ for (v = 0; v < nv; v++) { p_voice = voice_tb[v] if (!p_voice.sym) continue if (p_voice.st != st) { top = 0; bot = 0; st = p_voice.st } nly = 0 if (p_voice.have_ly) { if (!h_tb[v]) h_tb[v] = [] for (s = p_voice.sym; s; s = s.next) { a_ly = s.a_ly if (!a_ly) continue /*fixme:should get the real width*/ x = s.x; w = 10 for (i = 0; i < a_ly.length; i++) { ly = a_ly[i] if (ly) { x -= ly.shift; w = ly.t.wh[0] break } } y = y_get(p_voice.st, 1, x, w) if (top < y) top = y; y = y_get(p_voice.st, 0, x, w) if (bot > y) bot = y while (nly < a_ly.length) h_tb[v][nly++] = 0 for (i = 0; i < a_ly.length; i++) { ly = a_ly[i] if (!ly) continue if (!h_tb[v][i] || ly.t.wh[1] > h_tb[v][i]) h_tb[v][i] = ly.t.wh[1] } } } else { y = y_get(p_voice.st, 1, 0, realwidth) if (top < y) top = y; y = y_get(p_voice.st, 0, 0, realwidth) if (bot > y) bot = y } if (!lyst_tb[st]) lyst_tb[st] = {} lyst_tb[st].top = top; lyst_tb[st].bot = bot; nly_tb[v] = nly if (nly == 0) continue if (p_voice.pos.voc) above_tb[v] = (p_voice.pos.voc & 0x07) == C.SL_ABOVE else if (voice_tb[v + 1] /*fixme:%%staves:KO - find an other way..*/ && voice_tb[v + 1].st == st && voice_tb[v + 1].have_ly) above_tb[v] = true else above_tb[v] = false if (above_tb[v]) lyst_tb[st].a = true else lyst_tb[st].b = true } /* draw the lyrics under the staves */ i = 0 for (v = 0; v < nv; v++) { p_voice = voice_tb[v] if (!p_voice.sym) continue if (!p_voice.have_ly) continue if (above_tb[v]) { rv_tb[i++] = v continue } st = p_voice.st; // don't scale the lyrics set_dscale(st, true) if (nly_tb[v] > 0) lyst_tb[st].bot = draw_lyrics(p_voice, nly_tb[v], h_tb[v], lyst_tb[st].bot, 1) } /* draw the lyrics above the staff */ while (--i >= 0) { v = rv_tb[i]; p_voice = voice_tb[v]; st = p_voice.st; set_dscale(st, true); lyst_tb[st].top = draw_lyrics(p_voice, nly_tb[v], h_tb[v], lyst_tb[st].top, -1) } /* set the max y offsets of all symbols */ for (v = 0; v < nv; v++) { p_voice = voice_tb[v] if (!p_voice.sym) continue st = p_voice.st; if (lyst_tb[st].a) { top = lyst_tb[st].top + 2 for (s = p_voice.sym; s; s = s.next) { /*fixme: may have lyrics crossing a next symbol*/ if (s.a_ly) { /*fixme:should set the real width*/ y_set(st, 1, s.x - 2, 10, top) } } } if (lyst_tb[st].b) { bot = lyst_tb[st].bot - 2 if (nly_tb[p_voice.v] > 0) { for (s = p_voice.sym; s; s = s.next) { if (s.a_ly) { /*fixme:should set the real width*/ y_set(st, 0, s.x - 2, 10, bot) } } } else { y_set(st, 0, 0, realwidth, bot) } } } } // abc2svg - gchord.js - chord symbols // // Copyright (C) 2014-2023 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 . // -- parse a chord symbol / annotation -- // the result is added in the global variable a_gch // 'type' may be a single '"' or a string '"xxx"' created by U: function parse_gchord(type) { var c, text, gch, x_abs, y_abs, i, j, istart, iend, ann_font = get_font("annotation"), h_ann = ann_font.size, line = parse.line function get_float() { var txt = '' while (1) { c = text[i++] if ("1234567890.-".indexOf(c) < 0) return parseFloat(txt) txt += c } } // get_float() istart = parse.bol + line.index if (type.length > 1) { // U: text = type.slice(1, -1); iend = istart + 1 } else { i = ++line.index // search the ending double quote while (1) { j = line.buffer.indexOf('"', i) if (j < 0) { syntax(1, "No end of chord symbol/annotation") return } if (line.buffer[j - 1] != '\\' || line.buffer[j - 2] == '\\') // (string ending with \\") break i = j + 1 } text = cnv_escape(line.buffer.slice(line.index, j)) line.index = j iend = parse.bol + line.index + 1 } if (ann_font.pad) h_ann += ann_font.pad i = 0; type = 'g' while (1) { c = text[i] if (!c) break gch = { text: "", istart: istart, iend: iend, font: ann_font } switch (c) { case '@': type = c; i++; x_abs = get_float() if (c != ',') { syntax(1, "',' lacking in annotation '@x,y'"); y_abs = 0 } else { y_abs = get_float() if (c != ' ') i-- } gch.x = x_abs; gch.y = y_abs break case '^': gch.pos = C.SL_ABOVE // fall thru case '_': if (c == '_') gch.pos = C.SL_BELOW // fall thru case '<': case '>': i++; type = c break default: switch (type) { case 'g': gch.font = get_font("gchord") gch.pos = curvoice.pos.gch || C.SL_ABOVE break case '^': gch.pos = C.SL_ABOVE break case '_': gch.pos = C.SL_BELOW break case '@': gch.x = x_abs; y_abs -= h_ann; gch.y = y_abs break } break } gch.type = type while (1) { c = text[i] if (!c) break switch (c) { default: gch.text += c; i++ continue case '&': /* skip "&xxx;" */ while (1) { gch.text += c; c = text[++i] switch (c) { default: continue case ';': case undefined: case '\\': break } break } if (c == ';') { i++; gch.text += c continue } break case '\n': // abcm2ps compatibility case ';': break } i++ break } gch.otext = gch.text // save for play accompaniment if (!a_gch) a_gch = [] a_gch.push(gch) } } // transpose a chord symbol function gch_tr1(p, tr) { var i, o, n, ip, csa = p.split('/') tr = abc2svg.b40l5[(tr + 202) % 40] // transpose in the line of fifth for (i = 0; i < csa.length; i++) { // main and optional bass p = csa[i]; o = p.search(/[A-G]/) if (o < 0) continue // strange chord symbol! ip = o + 1 // bbb fb cb gb db ab eb bb f c g d a e b f# c# g# d# a# e# b# f## // -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 n = "FCGDAEB".indexOf(p[o]) - 1 if (p[ip] == '#' || p[ip] == '\u266f') { n += 7 ip++ } else if (p[ip] == 'b' || p[ip] == '\u266d') { n -= 7 ip++ } n += tr // transpose // // remove chords with double sharps/flats // if ((!i && n > 7) // main chord // || (i && n > 12)) // bass // n -= 12 // else if (i < -7) // n += 12 csa[i] = p.slice(0, o) + "FCGDAEB"[(n + 22) % 7] + (n >= 13 ? '##' : n >= 6 ? '#' : n <= -9 ? 'bb' : n <= -2 ? 'b' : '') + p.slice(ip) } return csa.join('/') } // gch_tr1 // parser: add the parsed list of chord symbols and annotations // to the symbol (note, rest or bar) // and transpose the chord symbols function csan_add(s) { var i, gch // there cannot be chord symbols on measure bars if (s.type == C.BAR) { for (i = 0; i < a_gch.length; i++) { if (a_gch[i].type == 'g') { error(1, s, "There cannot be chord symbols on measure bars") a_gch.splice(i) } } } if (curvoice.tr_sco) { for (i = 0; i < a_gch.length; i++) { gch = a_gch[i] if (gch.type == 'g') gch.text = gch_tr1(gch.text, curvoice.tr_sco) } } if (s.a_gch) s.a_gch = s.a_gch.concat(a_gch) else s.a_gch = a_gch a_gch = null } // csan_add // generator: build the chord symbols / annotations // (possible hook) Abc.prototype.gch_build = function(s) { /* split the chord symbols / annotations * and initialize their vertical offsets */ var gch, wh, xspc, ix, y_left = 0, y_right = 0, GCHPRE = .4; // portion of chord before note // change the accidentals in the chord symbols, // convert the escape sequences in annotations, and // set the offsets for (ix = 0; ix < s.a_gch.length; ix++) { gch = s.a_gch[ix] if (gch.type == 'g') { gch.text = gch.text.replace(/##|#|=|bb|b/g, function(x) { switch (x) { case '##': return "𝄪" case '#': return "\u266f" case '=': return "\u266e" case 'b': return "\u266d" } return "𝄫" }); } else { if (gch.type == '@' && !user.anno_start && !user.anno_stop) { set_font(gch.font) gch.text = str2svg(gch.text) continue /* no width */ } } /* set the offsets and widths */ set_font(gch.font); gch.text = str2svg(gch.text) wh = gch.text.wh switch (gch.type) { case '@': break default: // case 'g': // chord symbol // case '^': /* above */ // case '_': /* below */ xspc = wh[0] * GCHPRE if (xspc > 8) xspc = 8; gch.x = -xspc; break case '<': /* left */ gch.x = -(wh[0] + 6); y_left -= wh[1]; gch.y = y_left + wh[1] / 2 break case '>': /* right */ gch.x = 6; y_right -= wh[1]; gch.y = y_right + wh[1] / 2 break } } /* move upwards the top and middle texts */ y_left /= 2; y_right /= 2 for (ix = 0; ix < s.a_gch.length; ix++) { gch = s.a_gch[ix] switch (gch.type) { case '<': /* left */ gch.y -= y_left break case '>': /* right */ gch.y -= y_right break } } } // -- draw the chord symbols and annotations // (the staves are not yet defined) // (unscaled delayed output) // (possible hook) Abc.prototype.draw_gchord = function(i, s, x, y) { if (s.invis && s.play) // play sequence: no chord nor annotation return var y2, an = s.a_gch[i], h = an.text.wh[1], pad = an.font.pad, w = an.text.wh[0] + pad * 2, dy = h * .22 // descent if (an.font.figb) { h *= 2.4 dy += an.font.size * 1.3 } switch (an.type) { case '_': // below y -= h + pad break case '^': // above y += pad break case '<': // left case '>': // right if (an.type == '<') { /*fixme: what symbol space?*/ if (s.notes[0].acc) x -= s.notes[0].shac x -= pad } else { if (s.xmx) x += s.xmx if (s.dots) x += 1.5 + 3.5 * s.dots x += pad } y += (s.type == C.NOTE ? (((s.notes[s.nhd].pit + s.notes[0].pit) >> 1) - 18) * 3 : 12) // fixed offset on rests and bars - h / 2 break default: // chord symbol if (y >= 0) y += pad else y -= h + pad break case '@': // absolute y += (s.type == C.NOTE ? (((s.notes[s.nhd].pit + s.notes[0].pit) >> 1) - 18) * 3 : 12) // fixed offset on rests and bars - h / 2 if (y > 0) { y2 = y + h + pad + 2 if (y2 > staff_tb[s.st].ann_top) staff_tb[s.st].ann_top = y2 } else { y2 = y - 2 if (y2 < staff_tb[s.st].ann_bot) staff_tb[s.st].ann_bot = y2 } break } if (an.type != '@') { if (y >= 0) y_set(s.st, 1, x, w, y + h + pad + 2) else y_set(s.st, 0, x, w, y - pad) } use_font(an.font) set_font(an.font) set_dscale(s.st) if (user.anno_start) user.anno_start("annot", an.istart, an.iend, x - 2, y + h + 2, w + 4, h + 4, s) xy_str(x, y + dy, an.text) if (user.anno_stop) user.anno_stop("annot", an.istart, an.iend, x - 2, y + h + 2, w + 4, h + 4, s) } // draw_gchord() // draw all chord symbols function draw_all_chsy() { var s, san1, an, i, x, y, w, n_an = 0, // max number of annotations minmax = new Array(nstaff + 1) // set a vertical offset to all the chord symbols/annotations function set_an_yu(j) { var an, i, s, x, y, w for (s = san1 ; s; s = s.ts_next) { an = s.a_gch if (!an) continue i = an.length - j - 1 an = an[i] if (!an) continue if (an.pos == C.SL_ABOVE) { x = s.x + an.x w = an.text.wh[0] if (w && x + w > realwidth) x = realwidth - w // let the text in the page y = y_get(s.st, 1, x, w) // y / staff if (an.type == 'g' && y < minmax[s.st].yup) y = minmax[s.st].yup } else if (an.pos == C.SL_BELOW || an.pos == C.SL_HIDDEN) { continue } else { x = s.x + an.x y = an.y } self.draw_gchord(i, s, x, y) } } // set_an_yu() function set_an_yl(i) { var an, x, y, w for (var s = san1 ; s; s = s.ts_next) { an = s.a_gch if (!an) continue an = an[i] if (!an || an.pos != C.SL_BELOW) continue x = s.x + an.x w = an.text.wh[0] if (w && x + w > realwidth) // let the text inside the page x = realwidth - w y = y_get(s.st, 0, x, w) - 2 // y / staff if (an.type == 'g' && y > minmax[s.st].ydn) y = minmax[s.st].ydn self.draw_gchord(i, s, x, y) } } // set_an_yl() // get the number of chord symbols / annotations // and the vertical offset for the chord symbols for (i = 0; i <= nstaff; i++) minmax[i] = { ydn: staff_tb[i].botbar - 3, yup: staff_tb[i].topbar + 4 } for (s = tsfirst; s; s = s.ts_next) { an = s.a_gch if (!an) continue if (!san1) san1 = s // first chord symbol / annotation i = an.length if (i > n_an) n_an = i while (--i >= 0) { if (an[i].type == 'g') { an = an[i] x = s.x + an.x w = an.text.wh[0] if (w && x + w > realwidth) x = realwidth - w if (an.pos == C.SL_ABOVE) { y = y_get(s.st, true, x, w) if (y > minmax[s.st].yup) minmax[s.st].yup = y } else if (an.pos == C.SL_BELOW) { y = y_get(s.st, false, x, w) - 2 if (y < minmax[s.st].ydn) minmax[s.st].ydn = y } break } } } if (!san1) return // no chord symbol nor annotation // draw the elements set_dscale(-1) // restore the scale parameters for (i = 0; i < n_an; i++) { set_an_yu(i) // upper offsets set_an_yl(i) // lower offsets } } // draw_all_chsy() // abc2svg - tail.js // // Copyright (C) 2014-2024 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 . // initialize init_tune() // Abc functions used by the modules Abc.prototype.a_de = function() { return a_de } Abc.prototype.add_style = function(s) { style += s }; Abc.prototype.anno_a = anno_a Abc.prototype.cfmt = function() { return cfmt }; Abc.prototype.clone = clone; Abc.prototype.deco_put = function(nm, s) { a_dcn.push(nm) deco_cnv(s) } Abc.prototype.defs_add = defs_add Abc.prototype.dh_put = function(nm, s, nt) { a_dcn.push(nm) dh_cnv(s, nt) } Abc.prototype.draw_meter = draw_meter Abc.prototype.draw_note = draw_note; Abc.prototype.errs = errs; Abc.prototype.font_class = font_class; Abc.prototype.gch_tr1 = gch_tr1; Abc.prototype.get_bool = get_bool; Abc.prototype.get_cur_sy = function() { return cur_sy }; Abc.prototype.get_curvoice = function() { return curvoice }; Abc.prototype.get_delta_tb = function() { return delta_tb }; Abc.prototype.get_decos = function() { return decos }; Abc.prototype.get_font = get_font; Abc.prototype.get_font_style = function() { return font_style }; Abc.prototype.get_glyphs = function() { return glyphs }; Abc.prototype.get_img = function() { return img }; Abc.prototype.get_maps = function() { return maps }; Abc.prototype.get_multi = function() { return multicol }; Abc.prototype.get_newpage = function() { if (block.newpage) { block.newpage = false; return true } }; Abc.prototype.get_posy = function() { return posy } Abc.prototype.get_staff_tb = function() { return staff_tb }; Abc.prototype.get_top_v = function() { return par_sy.top_voice }; Abc.prototype.get_tsfirst = function() { return tsfirst }; Abc.prototype.get_unit = get_unit; Abc.prototype.get_voice_tb = function() { return voice_tb }; Abc.prototype.glout = glout Abc.prototype.info = function() { return info }; Abc.prototype.new_block = new_block; Abc.prototype.out_arp = out_arp; Abc.prototype.out_deco_str = out_deco_str; Abc.prototype.out_deco_val = out_deco_val; Abc.prototype.out_ltr = out_ltr; Abc.prototype.param_set_font = param_set_font; Abc.prototype.parse = parse; Abc.prototype.psdeco = empty_function; Abc.prototype.psxygl = empty_function; Abc.prototype.set_cur_sy = function(sy) { cur_sy = sy }; Abc.prototype.set_dscale = set_dscale; Abc.prototype.set_font = set_font; Abc.prototype.set_a_gch = function(s, a) { a_gch = a; csan_add(s) } Abc.prototype.set_hl = set_hl Abc.prototype.set_page = set_page Abc.prototype.set_pagef = function() { blkdiv = 1 } Abc.prototype.set_realwidth = function(v) { realwidth = v } Abc.prototype.set_scale = set_scale; Abc.prototype.set_sscale = set_sscale Abc.prototype.set_tsfirst = function(s) { tsfirst = s }; Abc.prototype.set_v_param = set_v_param; Abc.prototype.strwh = strwh; Abc.prototype.stv_g = function() { return stv_g }; Abc.prototype.svg_flush = svg_flush; Abc.prototype.syntax = syntax; Abc.prototype.tunes = tunes Abc.prototype.unlksym = unlksym; Abc.prototype.use_font = use_font; Abc.prototype.vskip = vskip Abc.prototype.xy_str = xy_str; Abc.prototype.xygl = xygl; Abc.prototype.y_get = y_get Abc.prototype.y_set = y_set } // end of Abc() // compatibility var Abc = abc2svg.Abc // nodejs if (typeof module == 'object' && typeof exports == 'object') { exports.abc2svg = abc2svg; exports.Abc = Abc } // abc2svg - modules.js - module handling // // Copyright (C) 2018-2022 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 . // empty function if (!abc2svg.loadjs) { abc2svg.loadjs = function(fn, onsuccess, onerror) { if (onerror) onerror(fn) } } abc2svg.modules = { ambitus: {}, begingrid: { fn: 'grid3' }, beginps: { fn: 'psvg' }, break: {}, capo: {}, chordnames: {}, clip: {}, clairnote: { fn: 'clair' }, voicecombine: { fn: 'combine' }, diagram: { fn: 'diag' }, equalbars: {}, gamelan: {}, grid: {}, grid2: {}, jazzchord: {}, jianpu: {}, mdnn: {}, MIDI: {}, nns: {}, pageheight: { fn: 'page' }, pedline: {}, percmap: { fn: 'perc' }, roman: {}, soloffs: {}, sth: {}, strtab: {}, temperament: { fn: 'temper' }, tropt: {}, nreq: 0, // scan the file and find the required modules // @file: ABC file // @relay: (optional) callback function for continuing the treatment // @errmsg: (optional) function to display an error message if any // This function gets one argument: the message // return true when all modules are loaded load: function(file, relay, errmsg) { function get_errmsg() { if (typeof user == 'object' && user.errmsg) return user.errmsg if (typeof abc2svg.printErr == 'function') return abc2svg.printErr if (typeof alert == 'function') return function(m) { alert(m) } if (typeof console == 'object') return console.log return function(){} } // get_errmsg() // call back functions for loadjs() function load_end() { if (--abc2svg.modules.nreq == 0) abc2svg.modules.cbf() } // test if some keyword in the file var m, i, fn, nreq_i = this.nreq, ls = file.match(/(^|\n)(%%|I:).+?\b/g) if (!ls) return true this.cbf = relay || // (only one callback function) function(){} this.errmsg = errmsg || get_errmsg() for (i = 0; i < ls.length; i++) { fn = ls[i].replace(/\n?(%%|I:)/, '') m = abc2svg.modules[fn] if (!m || m.loaded) continue m.loaded = true // load the module if (m.fn) fn = m.fn this.nreq++ abc2svg.loadjs(fn + "-1.js", load_end, function () { abc2svg.modules.errmsg( 'Error loading the module ' + fn) load_end() }) } return this.nreq == nreq_i } } // modules abc2svg.version="1.22.13";abc2svg.vdate="2024-01-30" App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/COPYING.LESSER0000644000400000040000000001674314565315274022214 0ustar jvjv GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/temper-1.js0000644000400000040000000000473314565315274022151 0ustar jvjv// temper.js - module to define the temperament // // Copyright (C) 2018-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 . // // This module is loaded when "%%temperament" appears in a ABC source. // // Parameters // %%temperament // The must contain 12 integer values that are the detune values in cents // of the 12 notes of the equal scale. // Examples: // // % pythagore (~500 B.C) // %%temperament +00 +14 +04 -06 +08 -02 +12 +02 +16 +06 -04 +10 // // % just intonation // %%temperament +00 -08 -18 -06 -14 -02 -10 +02 -08 -16 -04 -12 // // % meantone (Pietro Aaron 1523) // %%temperament +00 -24 -07 +10 -14 +03 -21 -03 -27 +10 +07 -17 // // % Andreas Werckmeister III (1681) // %%temperament +00 -04 +04 +00 -04 +04 +00 +02 -08 +00 +02 -02 // // % well temperament (F.A. Vallotti 1754) // %%temperament +00 -06 -04 -02 -08 +02 -08 -02 -04 -06 +00 -10 if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.temper = { // get the temperament set_fmt: function(of, cmd, param) { if (cmd == "temperament") { var i, tb, tb40 = new Float32Array(40), ls = new Float32Array(param.split(/ +/)) for (i = 0; i < ls.length; i++) { if (isNaN(ls[i])) break ls[i] = i + ls[i] / 100 // delta -> MIDI/octave } switch (i) { case 12: tb = [ 10,11,0,1,2,0, // C 0,1,2,3,4,0, // D 2,3,4,5,6, // E 3,4,5,6,7,0, // F 5,6,7,8,9,0, // G 7,8,9,10,11,0, // A 9,10,11,0,1] // B break default: this.syntax(1, this.errs.bad_val, "%%temperament") return } for (i = 0; i < 40; i++) tb40[i] = ls[tb[i]] this.cfmt().temper = tb40 return } of(cmd, param) }, set_hooks: function(abc) { abc.set_format = abc2svg.temper.set_fmt.bind(abc, abc.set_format) } } // temper if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.temper = abc2svg.temper.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/chordnames-1.js0000644000400000040000000000506414565315274022776 0ustar jvjv// abc2svg - chordnames.js - change the names of the chord symbols // // Copyright (C) 2020-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 . // // This module is loaded by %%chordnames. // // Parameters // 1st syntax: // %%chordnames // Each name replace one chord. The order is: // CDEFGAB // 2nd syntax: // %%chordnames // The key may be a chord letter ('A') with/or an accidental // Example: // %%chordnames Bb:B,B:H,b:s % German chords if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.chordnames = { gch_build: function(of, s) { var gch, ix, t, cfmt = this.cfmt() if (s.a_gch && cfmt.chordnames) { for (ix = 0; ix < s.a_gch.length; ix++) { gch = s.a_gch[ix] t = gch.text if (gch.type != 'g' || !t) continue if (t[0] == 'n' || t[0] == 'N') t = 'N' gch.text = t.replace(cfmt.chordnames.re, function(c){return cfmt.chordnames.o[c]}) } } of(s) }, // gch_build() gimpl: 'CDEFGABN', set_fmt: function(of, cmd, parm) { var i, v, re = [], o = {}, cfmt = this.cfmt() if (cmd == "chordnames") { parm = parm.split(',') if (parm[0].indexOf(':') > 0) { // by object for (i = 0; i < parm.length; i++) { v = parm[i].split(':') if (!v[1]) // (no ':') continue o[v[0]] = v[1] re.push(v[0]) } } else { // implicit for (i = 0; i < parm.length; i++) { v = abc2svg.chordnames.gimpl[i] o[v] = parm[i] re.push(v) } } cfmt.chordnames = {re: new RegExp(re.join('|'), 'g'), o: o} return } of(cmd, parm) }, // set_fmt() set_hooks: function(abc) { abc.gch_build = abc2svg.chordnames.gch_build.bind(abc, abc.gch_build) abc.set_format = abc2svg.chordnames.set_fmt.bind(abc, abc.set_format) } // set_hooks() } // chordnames if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.chordnames = abc2svg.chordnames.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/capo-1.js0000644000400000040000000000514314565315274021573 0ustar jvjv// capo.js - module to add a capo chord line // // Copyright (C) 2018-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 . // // This module is loaded when "%%capo" appears in a ABC source. // // Parameters // %%capo n 'n' is the capo fret number if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.capo = { // b40 intervals of capo icb40: [0, 5, 6,11,16,17,22,23,28,33,34,39], // e f ^f g _a a _b b c _d d _e gch_build: function(of, s) { var t, i, gch, gch2, i2, abc = this, p_v = abc.get_curvoice(), a_gch = s.a_gch if (p_v.capo && a_gch) { t = p_v.capo i = 0 while (1) { gch = a_gch[i++] if (!gch) { of(s) return } if (gch.type == 'g') break } gch2 = Object.create(gch) gch2.capo = false // (would be erased when setting gch) gch2.text = abc.gch_tr1(gch2.text, -abc2svg.capo.icb40[t % 12]) if (!p_v.capo_first) { // if new voice p_v.capo_first = true gch2.text += " (capo: " + t.toString() + ")" } gch2.font = abc.get_font(abc.cfmt().capofont ? "capo" : "annotation") a_gch.splice(i, 0, gch2) // set a mark in the first chord symbol for %%diagram gch.capo = true } of(s) }, set_fmt: function(of, cmd, param) { if (cmd == "capo") { this.set_v_param("capo_", param) return } of(cmd, param) }, // get the parameters of the current voice set_vp: function(of, a) { var i, v, p_v = this.get_curvoice() for (i = 0; i < a.length; i++) { if (a[i] == "capo_=") { v = Number(a[++i]) if (isNaN(v) || v <= 0) this.syntax(1, "Bad fret number in %%capo") else p_v.capo = v break } } of(a) }, // set_vp() set_hooks: function(abc) { abc.gch_build = abc2svg.capo.gch_build.bind(abc, abc.gch_build); abc.set_format = abc2svg.capo.set_fmt.bind(abc, abc.set_format) abc.set_vp = abc2svg.capo.set_vp.bind(abc, abc.set_vp) } } // capo if (!abc2svg.mhooks) abc2svg.mhooks = {} abc2svg.mhooks.capo = abc2svg.capo.set_hooks App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/page-1.js0000644000400000040000000003555414565315274021576 0ustar jvjv// page.js - module to generate pages // // Copyright (C) 2018-2024 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 . // // This module is loaded when "%%pageheight" appears in a ABC source. // // Parameters // %%pageheight if (typeof abc2svg == "undefined") var abc2svg = {} abc2svg.page = { // function called at end of generation abc_end: function(of) { if (this.cfmt().pageheight) { var page = this.page if (page && page.in_page) abc2svg.page.close_page(page) if (abc2svg.page.user_out) { user.img_out = abc2svg.page.user_out abc2svg.page.user_out = null } } of() }, // abc_end() // output the SVG tag svg_tag: function(w, h, ty) { w = Math.ceil(w) h = Math.ceil(h) abc2svg.page.user_out( '' ) }, // svg_tag() // generate a header or a footer in page.hf and return its height gen_hf: function(page, ty) { var a, i, j, k, x, y, y0, s, str, font = page.abc.get_font(ty.substr(0, 6)), cfmt = page.abc.cfmt(), fh = font.size * 1.1, pos = [ '">', '" text-anchor="middle">', '" text-anchor="end">' ] // replace <>& by XML character references function clean_txt(txt) { return txt.replace(/<|>|&.*?;|&/g, function(c) { switch (c) { case '<': return "<" case '>': return ">" case '&': return "&" } return c }) } // clean_txt() // clear a field if $x said so function clr(str) { return str.indexOf('\u00ff') >= 0 ? '' : str } //clr() // create the text of a header or a footer function header_footer(o_font, str) { var c, d, i, k, t, n_font, s, c_font = o_font, nl = 1, j = 0, r = ["", "", ""] if (str[0] == '"') str = str.slice(1, -1) while (1) { i = str.indexOf('$', j) if (i < 0) break c = str[++i] s = '$' + c // string to replace switch (c) { case 'd': if (!abc2svg.get_mtime) break // cannot know the change time of the file d = abc2svg.get_mtime(abc.parse.fname) // fall thru case 'D': if (c == 'D') d = new Date() if (cfmt.dateformat[0] == '"') cfmt.dateformat = cfmt.dateformat.slice(1, -1) d = strftime(cfmt.dateformat, d) break case 'F': d = typeof document != "undefined" ? window.location.href : page.abc.parse.fname break case 'I': c = str[++i] s += c // fall thru case 'T': t = page.abc.info()[c] d = t ? t.split('\n', 1)[0] : '' break case 'P': // current page number case 'Q': // absolute page number t = c == 'P' ? page.pn : page.pna switch (str[i + 1]) { case '0': s += '0' d = (t & 1) ? '\u00ff' : t break case '1': s += '1' d = (t & 1) ? t : '\u00ff' break default: d = t break } break case 'V': d = "abc2svg-" + abc2svg.version break default: d = '' if (c == '0') n_font = o_font else if (c >= '1' && c < '9') n_font = page.abc.get_font("u" + c) else break // handle the font changes if (n_font == c_font) break if (c_font != o_font) d += "" c_font = n_font if (c_font == o_font) break d += '' break } str = str.replace(s, d) j = i } if (c_font != o_font) str += ""; str = str.split('\n') r[4] = str.length // number of lines for (j = 0; j < str.length; j++) { if (j != 0) for (i = 0; i < 3; i++) r[i] += '\n' t = str[j].split('\t') if (t.length == 1) { r[1] += clr(t[0]) } else { for (i = 0; i < 3; i++) { if (t[i]) r[i] += clr(t[i]) } } } return r } // header_footer() function font_class(font) { if (font.class) return 'f' + font.fid + cfmt.fullsvg + ' ' + font.class return 'f' + font.fid + cfmt.fullsvg } // gen_hf if (!(page.pn & 1)) str = page[ty + '2'] || page[ty] else str = page[ty] if (str[0] == '-') { // not on 1st page if (page.pn == 1) return 0 str = str.slice(1) } a = header_footer(font, clean_txt(str)) y0 = font.size * .8 for (i = 0; i < 3; i++) { str = a[i] if (!str) continue if (i == 0) x = cfmt.leftmargin else if (i == 1) x = cfmt.pagewidth / 2 else x = cfmt.pagewidth - cfmt.rightmargin y = y0 k = 0 while (1) { j = str.indexOf('\n', k) if (j >= 0) s = str.slice(k, j) else s = str.slice(k) if (s) page.hf += '') page.in_page = true ht += page.topmargin page.hmax = cfmt.pageheight - page.botmargin - ht // define the header/footer page.hf = '' if (page.header) { l = abc.get_font_style().length h = abc2svg.page.gen_hf(page, "header") if (!h && page.pn == 1 && page.header1) h = abc2svg.page.gen_hf(page, "header1") sty = abc.get_font_style().slice(l) // new style(s) if (cfmt.fullsvg || sty != page.hsty) { page.hsty = sty sty = '\n' } else { sty = '' } abc2svg.page.svg_tag(cfmt.pagewidth, ht + h, "header") abc2svg.page.user_out(sty + '\n' + page.hf + '\n') page.hmax -= h; page.hf = '' } else { abc2svg.page.svg_tag(cfmt.pagewidth, ht, "header") abc2svg.page.user_out('\n') } if (page.footer) { l = abc.get_font_style().length page.fh = abc2svg.page.gen_hf(page, "footer") sty = abc.get_font_style().slice(l) // new style(s) if (cfmt.fullsvg || sty != page.fsty) { page.fsty = sty page.ffsty = '\n' } else { page.ffsty = '' } page.hmax -= page.fh } page.h = 0 }, // open_page() close_page: function(page) { var h, cfmt = page.abc.cfmt() page.in_page = false if (page.footer) { h = page.hmax + page.fh - page.h abc2svg.page.svg_tag(cfmt.pagewidth, h, "footer") abc2svg.page.user_out(page.ffsty + '\n' + page.hf + '\n') } abc2svg.page.user_out('
') page.h = 0 }, // close_page() // handle the output flow of the abc2svg generator img_in: function(p) { var h, ht, nh, page = this.page // copy a block function blkcpy(page) { while (page.blk.length) abc2svg.page.user_out(page.blk.shift()) page.blk = null // direct output } // blkcpy() // img_in() switch (p.slice(0, 4)) { case " 0 || (page.oneperpage && this.info().X) || !page.h) { // empty page if (page.in_page) abc2svg.page.close_page(page) abc2svg.page.open_page(page, 0) } page.blk = [] // in block page.hb = page.h // keep the offset of the start of tune break case "= page.hmax) { // if (still) page overflow ht = page.blk ? 0 : this.cfmt().topspace // tune continuation if (page.blk) { if (!page.hb) { // overflow on the first page blkcpy(page) nh = 0 } else { nh = page.h - page.hb page.h = page.hb } } abc2svg.page.close_page(page) abc2svg.page.open_page(page, ht) if (page.blk) { // if inside a block blkcpy(page) // output the beginning of the tune page.h = nh } if (h > page.hmax) break // error } // if no overflow yet, keep the block if (page.blk) page.blk.push(p) else abc2svg.page.user_out(p) page.h += h break case "1 && isLeapYear()) ? 1 : 0), 3), '%k': nHour, '%l': (nHour+11)%12 + 1, '%m': zeroPad(nMonth + 1, 2), '%n': nMonth + 1, '%M': zeroPad(date.getMinutes(), 2), '%p': (nHour<12) ? 'AM' : 'PM', '%P': (nHour<12) ? 'am' : 'pm', '%s': Math.round(date.getTime()/1000), '%S': zeroPad(date.getSeconds(), 2), '%u': nDay || 7, '%V': (function() { var target = getThursday(), n1stThu = target.valueOf(); target.setMonth(0, 1); var nJan1 = target.getDay(); if (nJan1!==4) target.setMonth(0, 1 + ((4-nJan1)+7)%7); return zeroPad(1 + Math.ceil((n1stThu-target)/604800000), 2); })(), '%w': nDay, '%x': date.toLocaleDateString(), '%X': date.toLocaleTimeString(), '%y': (nYear + '').slice(2), '%Y': nYear, '%z': date.toTimeString().replace(/.+GMT([+-]\d+).+/, '$1'), '%Z': date.toTimeString().replace(/.+\((.+?)\)$/, '$1') }[sMatch] || '') + '') || sMatch; }); } App-Music-ChordPro-6.050.7/lib/ChordPro/res/abc/abc2svg/diag-1.js0000644000400000040000000001617614565315274021565 0ustar jvjv// diag.js - module to insert guitar chord diagrams // // Copyright (C) 2018-2024 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 . // // This module is loaded when "%%diagram" or "%%setdiag" appear in a ABC source. // // The command %%diagram draws a chord diagram above the chord symbols. // // Parameters // %%diagram [ <#diagram> | 0 ] // with // <#diagram> identifier of the diagram type (number) // ('1' is for the 6 strings guitar) // // The command %%setdiag defines the chord diagram of a chord symbol // for the current diagram type. // // Parameters // %%setdiag [barre=-] // with // = chord symbol // = list of diagram offset of dots on the strings - '0 or 'x': no dot //
;; $whitespace_tokens++; } # Ignore empty tokens/whitespace tokens. foreach my $token ( grep { length } split( /(<[^>]+>)/, $data ) ) { next if $token =~ /^\s+$/s && !$whitespace_tokens; next if $token =~ /<\?$re_name.*?\?>/is || $token =~ /^!i ) { # close tag croak( "SVG Parser: Not well-formed (at \"$token\")" ) if $elem->{name} ne $1; $elem = delete $elem->{parent}; } elsif ( $token =~ /^<$re_name(\s[^>]*)*(\s*\/)?>/is ) { # open tag my ( $tagname, $attribs_raw ) = ( $token =~ m!<(\S*)(.*?)(\s*/)?>!s ); # First make attribs into a list so we can spot duplicate keys. my $attrib = [ # Do double- and single- quoted attribs seperately. $attribs_raw =~ /\s($re_name)\s*=\s*"([^"]*?)"/gi, $attribs_raw =~ /\s($re_name)\s*=\s*'([^']*?)'/gi ]; if ( @{$attrib} == 2 * keys %{{@{$attrib}}} ) { $attrib = { @{$attrib} } } else { croak( "SVG Parser: Not well-formed (duplicate attribute)" ); } # Now trash any attribs that we *did* manage to parse and see # if there's anything left. $attribs_raw =~ s/\s($re_name)\s*=\s*"([^"]*?)"//gi; $attribs_raw =~ s/\s($re_name)\s*=\s*'([^']*?)'//gi; croak( "SVG Parser: Not well-formed ($attribs_raw)" ) if $attribs_raw =~ /\S/ || grep { /{$key} = $fixent->($attrib->{$key})) =~ s/\x00//g; # get rid of CDATA marker } } # We have an element. Push it. $elem = { content => [], name => $tagname, type => 'e', attrib => $attrib, parent => $elem }; push( @{ $elem->{parent}->{content} }, $elem ); # Handle self-closing tags. if ( $token =~ /\s*\/>$/ ) { $elem->{name} =~ s/\/$//; $elem = delete( $elem->{parent} ); } } elsif ( $token =~ /^($token); } push( @{$elem->{content}}, { content => $token, type => 't' } ); } } croak( "SVG Parser: Not well-formed (", $elem->{name}, " duplicated parent)" ) if exists($elem->{parent}); if ( $whitespace_tokens ) { while ( @{$elem->{content}} > 1 && $elem->{content}->[0]->{type} eq 't' && $elem->{content}->[0]->{content} !~ /\S/ ) { shift( @{$elem->{content}} ); } while ( @{$elem->{content}} > 1 && $elem->{content}->[-1]->{type} eq 't' && $elem->{content}->[-1]->{content} !~ /\S/ ) { pop( @{$elem->{content}} ); } } croak( "SVG Parser: Junk after end of document" ) if @{$elem->{content}} > 1; croak( "SVG Parser: No elements?" ) if @{$elem->{content}} == 0 || $elem->{content}->[0]->{type} ne 'e'; return $elem->{content}; } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Rect.pm0000644000400000040000000000300614544755676020311 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; use Carp; class SVGPDF::Rect :isa(SVGPDF::Element); method process () { my $atts = $self->atts; my $xo = $self->xo; return if $atts->{omit}; # for testing/debugging. my ( $x, $y, $w, $h, $rx, $ry, $tf ) = $self->get_params( $atts, qw( x:H y:V width:H height:V rx:U ry:U transform:s ) ); $self->_dbg( $self->name, " x=$x y=$y w=$w h=$h" ); $self->_dbg( "+ xo save" ); $xo->save; $self->set_graphics; $self->set_transform($tf) if $tf; unless ( $rx || $ry ) { $xo->rectangle( $x, $y, $x+$w, $y+$h ); } else { # https://svgwg.org/svg2-draft/shapes.html#RectElement # Resolve percentages. if ( $rx ) { $rx = $1 * $w if $rx =~ /^([-+,\d]+)\%$/; } if ( $ry ) { $ry = $1 * $h if $ry =~ /^([-+,\d]+)\%$/; } # Default one to the other. $rx ||= $ry; $ry ||= $rx; # Maximize to half of the width/height. if ( $rx > $w/2 ) { $rx = $w/2; } if ( $ry > $h/2 ) { $ry = $h/2; } $self->_dbg( $self->name, "(rounded) rx=$rx ry=$ry" ); $xo->move( $x+$rx, $y ); $xo->hline( $x + $w - $rx ); $xo->arc( $x+$w-$rx, $y+$ry, $rx, $ry, -90, 0 ); $xo->vline( $y + $h - $ry ); $xo->arc( $x+$w-$rx, $y+$h-$ry, $rx, $ry, 0, 90 ); $xo->hline( $x + $rx ); $xo->arc( $x+$rx, $y+$h-$ry, $rx, $ry, 90, 180 ); $xo->vline( $y + $ry ); $xo->arc( $x+$rx, $y+$ry, $rx, $ry, 180, 270 ); $xo->close; } $self->_paintsub->(); $self->_dbg( "- xo restore" ); $xo->restore; $self->css_pop; } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Use.pm0000644000400000040000000000164514467636437020154 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; use Carp; use Storable; class SVGPDF::Use :isa(SVGPDF::Element); method process () { my $atts = $self->atts; my $xo = $self->xo; return if $atts->{omit}; # for testing/debugging. my ( $x, $y, $hr, $tf ) = $self->get_params( $atts, qw( x:H y:V href:! transform:s ) ); my $r = $self->root->defs->{$hr}; unless ( $r ) { warn("SVG: Missing def for use \"$hr\" (skipped)\n"); $self->css_pop; return; } # Update its xo. $r->xo = $self->xo; $self->_dbg( $self->name, " \"$hr\" (", $r->name, "), x=$x, y=$y" ); $self->_dbg("+ xo save"); $xo->save; if ( $x || $y ) { $self->_dbg( "translate( %.2f %.2f )", $x, $y ); $xo->transform( translate => [ $x, $y ] ); } $self->set_transform($tf) if $tf; $self->set_graphics; $r->process; $self->_dbg("- xo restore"); $xo->restore; $self->css_pop; } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Circle.pm0000644000400000040000000000117514467630421020602 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; use Carp; class SVGPDF::Circle :isa(SVGPDF::Element); method process () { my $atts = $self->atts; my $xo = $self->xo; return if $atts->{omit}; # for testing/debugging. my ( $cx, $cy, $r, $tf ) = $self->get_params( $atts, qw( cx:H cy:V r:U transform:s ) ); $self->_dbg( $self->name, " cx=$cx cy=$cy r=$r" ); $self->_dbg( "+ xo save" ); $xo->save; $self->set_graphics; $self->set_transform($tf) if $tf; $xo->circle( $cx, $cy, $r ); $self->_paintsub->(); $self->_dbg( "- xo restore" ); $xo->restore; $self->css_pop; } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Polyline.pm0000644000400000040000000000154714467631231021177 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; use Carp; class SVGPDF::Polyline :isa(SVGPDF::Element); method process () { $self->process_polyline(0); } method process_polyline ( $close ) { my $atts = $self->atts; my $xo = $self->xo; return if $atts->{omit}; # for testing/debugging. my ( $points, $tf ) = $self->get_params( $atts, qw(points:s transform:s) ); my @d = $self-> getargs($points); my $t = $points; $t = substr($t,0,20) . "..." if length($t) > 20; $self->_dbg( $self->name, " points=\"$t\"" ); $self->_dbg( "+ xo save" ); $xo->save; $self->set_graphics; $self->set_transform($tf) if $tf; if ( @d ) { $xo->move( $d[0], $d[1] ); $xo->polyline( @d[2 .. $#d] ); $xo->close if $close; $self->_paintsub->(); } $self->_dbg( "- xo restore" ); $xo->restore; $self->css_pop; } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Defs.pm0000644000400000040000000000123614472671021020255 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; use Carp; class SVGPDF::Defs :isa(SVGPDF::Element); method process () { my $atts = $self->atts; my $xo = $self->xo; return if $atts->{omit}; # for testing/debugging. $self->_dbg( "+", $self->name, " ====" ); for ( $self->get_children ) { next if ref($_) eq 'SVGPDF::TextElement'; my $id = $_->atts->{id}; unless ( defined($id) ) { warn("SVG: Missing id for ", $_->name, " in defs (skipped)\n") if $self->root->verbose && $_->name ne "style"; next; } $self->_dbg( "defs: \"$id\" (", $_->name, ")" ); $self->root->defs->{ "#$id" } = $_; } $self->_dbg( "-" ); } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Element.pm0000644000400000040000000003263314555670316021002 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; class SVGPDF::Element; use Carp; field $xo :mutator; field $style :accessor; field $name :param :accessor; field $atts :param :accessor; field $css :accessor; field $content :param :accessor; # array of children field $root :param :accessor; # top module BUILD { $css = $root->css; $xo = $root->xoforms->[-1]->{xo}; }; method _dbg (@args) { $root->_dbg(@args); } method css_push ( $updated_atts = undef ) { $style = $css->push( element => $name, %{$updated_atts // $atts} ); } method css_pop () { $css->pop; } method set_transform ( $tf ) { return unless $tf; my $nooptimize = 1; $tf =~ s/\s+/ /g; # The parts of the transform need to be executed in order. while ( $tf =~ /\S/ ) { if ( $tf =~ /^\s*translate\s*\((.*?)\)(.*)/ ) { $tf = $2; my ( $x, $y ) = $self->getargs($1); $y ||= 0; if ( $nooptimize || $x || $y ) { $xo->transform( translate => [ $x, $y ] ); $self->_dbg( "transform translate(%.2f,%.2f)", $x, $y ); } } elsif ( $tf =~ /^\s*rotate\s*\((.*?)\)(.*)/ ) { $tf = $2; my ( $r, $x, $y ) = $self->getargs($1); if ( $nooptimize || $r ) { if ( $x || $y ) { $xo->transform( translate => [ $x, $y ] ); $self->_dbg( "transform translate(%.2f,%.2f)", $x, $y ); } $self->_dbg( "transform rotate(%.2f)", $r ); $xo->transform( rotate => $r ); if ( $x || $y ) { $xo->transform( translate => [ -$x, -$y ] ); $self->_dbg( "transform translate(%.2f,%.2f)", -$x, -$y ); } } } elsif ( $tf =~ /^\s*scale\s*\((.*?)\)(.*)/ ) { $tf = $2; my ( $x, $y ) = $self->getargs($1); $y ||= $x; if ( $nooptimize || $x != 1 && $y != 1 ) { $self->_dbg( "transform scale(%.2f,%.2f)", $x, $y ); $xo->transform( scale => [ $x, $y ] ); } } elsif ( $tf =~ /^\s*matrix\s*\((.*?)\)(.*)/ ) { $tf = $2; my ( @m ) = $self->getargs($1); # 1 0 0 1 dx dy translate # sx 0 0 sy 0 0 scale # c s -s c 0 0 rotate (s = sin, c = cos) # 1 a b 1 0 0 skew (a = tan a, b = tan b) $self->_dbg( "transform matrix(%.2f,%.2f %.2f,%.2f %.2f,%.2f)", @m); $xo->matrix(@m); } elsif ( $tf =~ /^\s*skew([XY])\s*\((.*?)\)(.*)/i ) { $tf = $3; my ( $x ) = $self->getargs($2); my $y = 0; if ( $1 eq "X" ) { $y = $x; $x = 0; } $self->_dbg( "transform skew(%.2f %.2f)", $x, $y ); $xo->transform( skew => [ $x, $y ] ); } else { warn("Ignoring transform: $tf") if $self->root->verbose; $self->_dbg("Ignoring transform: \"$tf\""); $tf = ""; } # %rel = ( relative => 1 ); } } # # # # result = multiply_matrices( $m1, $m2, $m3 ); method multiply_matrices :common (@m) { my $i = @m; my $m2 = pop(@m); die("Matrix$i must have 6 elements\n") unless @$m2 == 6; while ( --$i > 0 ) { my $m1 = pop(@m); die("Matrix$i must have 6 elements\n") unless @$m1 == 6; $m2 = [ $m1->[0] * $m2->[0] + $m1->[2] * $m2->[1], $m1->[1] * $m2->[0] + $m1->[3] * $m2->[1], $m1->[0] * $m2->[2] + $m1->[2] * $m2->[3], $m1->[1] * $m2->[2] + $m1->[3] * $m2->[3], $m1->[0] * $m2->[4] + $m1->[2] * $m2->[5] + $m1->[4], $m1->[1] * $m2->[4] + $m1->[3] * $m2->[5] + $m1->[5] ]; } $m2; } method set_graphics () { my $msg = $name; if ( defined( my $lw = $style->{'stroke-width'} ) ) { my $w = $self->u( $lw, fontsize => $style->{'font-size'}, width => $self->root->xoforms->[-1]->{diag}); $msg .= sprintf(" stroke-width=%.2f", $w); if ( $lw =~ /e[mx]/ ) { $msg .= sprintf("(%s\@%.2f)", $lw, $self->u( $style->{'font-size'}|| $self->root->fontsize, fontsize => $style->{'font-size'}, width => $self->root->xoforms->[-1]->{diag})); } if ( $lw =~ /\%/ ) { $msg .= sprintf("(%s\@%.2f)", $lw, $self->u( $self->root->xoforms->[-1]->{diag}, fontsize => $style->{'font-size'}, width => $self->root->xoforms->[-1]->{diag})); } $xo->line_width($w); } if ( defined( my $linecap = $style->{'stroke-linecap'} ) ) { $linecap = lc($linecap); if ( $linecap eq "round" ) { $linecap = 1 } elsif ( $linecap eq "r" ) { $linecap = 1 } elsif ( $linecap eq "square" ) { $linecap = 2 } elsif ( $linecap eq "s" ) { $linecap = 2 } else { $linecap = 0 } # b butt $msg .= " linecap=$linecap"; $xo->line_cap($linecap); } if ( defined( my $linejoin = $style->{'stroke-linejoin'} ) ) { $linejoin = lc($linejoin); if ( $linejoin eq "round" ) { $linejoin = 1 } elsif ( $linejoin eq "r" ) { $linejoin = 1 } elsif ( $linejoin eq "bevel" ) { $linejoin = 2 } elsif ( $linejoin eq "b" ) { $linejoin = 2 } else { $linejoin = 0 } # m miter $msg .= " linejoin=$linejoin"; $xo->line_join($linejoin); } my $color = $style->{color}; my $stroke = $style->{stroke}; if ( lc($stroke) eq "currentcolor" ) { # Nothing. Use current. $msg .= " stroke=(current)"; $stroke = $color; } if ( $stroke ne "none" ) { $stroke =~ s/\s+//g; if ( $stroke =~ /rgb\(([\d.]+)%,([\d.]+)%,([\d.]+)%\)/ ) { $stroke = sprintf("#%02X%02X%02X", map { $_*2.55 } $1, $2, $3); } elsif ( $stroke =~ /rgb\(([\d.]+),([\d.]+),([\d.]+)\)/ ) { $stroke = sprintf("#%02X%02X%02X", $1, $2, $3); } $xo->stroke_color($stroke); $msg .= " stroke=$stroke"; } else { $msg .= " stroke=none"; } my $fill = $style->{fill}; if ( lc($fill) eq "currentcolor" ) { # Nothing. Use current. $msg .= " fill=(current)"; $fill = $color; } if ( lc($fill) ne "none" && $fill ne "transparent" ) { $fill =~ s/\s+//g; if ( $fill =~ /rgb\(([\d.]+)%,([\d.]+)%,([\d.]+)%\)/ ) { $fill = sprintf("#%02X%02X%02X", map { $_*2.55 } $1, $2, $3); } elsif ( $fill =~ /rgb\(([\d.]+),([\d.]+),([\d.]+)\)/ ) { $fill = sprintf("#%02X%02X%02X", $1, $2, $3); } $xo->fill_color($fill); $msg .= " fill=$fill"; } else { $msg .= " fill=none"; } if ( my $sda = $style->{'stroke-dasharray'} ) { my @sda; if ( $sda && $sda ne "none" ) { $sda =~ s/,/ /g; @sda = split( ' ', $sda ); } $msg .= " sda=@sda"; $xo->line_dash_pattern(@sda); } $self->_dbg( "%s", $msg ); return $style; } # Return a stroke/fill/paint sub depending on the fill stroke styles. method _paintsub () { if ( $style->{stroke} && $style->{stroke} ne 'none' && $style->{stroke} ne 'transparent' # Hmm. Saw a note somewhere that it defaults to 0 but other notes # say that it should be 1px... && $style->{'stroke-width'}//1 != 0 ) { if ( $style->{fill} && $style->{fill} ne 'none' && $style->{fill} ne 'transparent' ) { return sub { $self->_dbg("xo paint (", join(" ", $style->{stroke}, $style->{fill} ), ")"); $xo->paint; }; } else { return sub { $self->_dbg("xo stroke (", $style->{stroke}, ")"); $xo->stroke; }; } } elsif ( $style->{fill} && $style->{fill} ne 'none' && $style->{fill} ne 'transparent' ) { return sub { $self->_dbg("xo fill (", $style->{stroke}, ")"); $xo->fill; }; } else { return sub { $self->_dbg("xo end"); $xo->end; } } } method process () { # Unless overridden in a subclass there's not much we can do. state $warned = { desc => 1, title => 1, metadata => 1 }; warn("SVG: Skipping element \"$name\" (not implemented)\n") unless $warned->{$name}++ || !$self->root->verbose; $self->_dbg("skipping $name (not implemented)"); # $self->traverse; } method get_children () { # Note: This is the only place where these objects are created. my @res; for my $e ( @{$self->content} ) { if ( $e->{type} eq 'e' ) { my $pkg = "SVGPDF::" . ucfirst(lc $e->{name}); $pkg = "SVGPDF::Element" unless $pkg->can("process"); push( @res, $pkg->new ( name => $e->{name}, atts => { map { lc($_) => $e->{attrib}->{$_} } keys %{$e->{attrib}} }, content => $e->{content}, root => $self->root, ) ); } elsif ( $e->{type} eq 't' ) { push( @res, SVGPDF::TextElement->new ( content => $e->{content}, ) ); } else { # Basically a 'cannot happen', croak("Unhandled node type ", $e->{type}); } } return @res; } method traverse () { for my $c ( $self->get_children ) { next if ref($c) eq "SVGPDF::TextElement"; $self->_dbg("+ start handling ", $c->name, " (", ref($c), ")"); $c->process; $self->_dbg("- end handling ", $c->name); } } method u ( $a, %args ) { confess("Undef in units") unless defined $a; # Pixels per point. Usually 96/72. my $pxpt = $self->root->pxpi / $self->root->ptpi; return undef unless $a =~ /^([-+]?[\d.]+)(.*)$/; return $1*$pxpt if $2 eq "pt"; # default is px return $1 if $2 eq "" || $2 eq "px"; # 1 inch = pxpi px. return $1/2.54 * $self->root->pxpi if $2 eq "cm"; return $1/25.4 * $self->root->pxpi if $2 eq "mm"; return $1 * $self->root->pxpi if $2 eq "in"; if ( $2 eq '%' ) { my $w = $args{width} || $self->root->xoforms->[-1]->{diag}; return $1/100 * $w * $pxpt; } # Font dependent. # CSS defines em to be the font size. if ( $2 eq "em" ) { return $1 * ( $args{fontsize} || $style->{'font-size'} || $self->root->fontsize ); } # CSS defines ex to be half the font size. if ( $2 eq "ex" ) { return $1 * 0.5 * ( $args{fontsize} || $style->{'font-size'} || $self->root->fontsize ); } confess("Unhandled units in \"$a\""); return $a; # will hopefully crash somewhere... } method getargs ( $a ) { confess("Null attr?") unless defined $a; $a =~ s/^\s+//; $a =~ s/\s+$//; map { $self->u($_) } split( /\s*[,\s]\s*/, $a ); } # Initial fiddling with entity attributes. method get_params ( @desc ) { my $atts = shift(@desc) if ref($desc[0]) eq 'HASH'; my @res; my %atts = %{ $atts // $self->atts }; # copy # xlink:href is obsoleted in favour of href. $atts{href} //= delete $atts{"xlink:href"} if exists $atts{"xlink:href"}; my @todo; for my $param ( @desc ) { # Attribute may be followed by ':' and flags. # 0 undef -> 0 # h process units, % is viewBox height # s undef -> "" # u process units # v process units, % is viewBox width # U undef -> 0, process units # ! barf if undef my $flags = ""; ( $param, $flags ) = ( $1, $2 ) if $param =~ /^(.*):(.*)$/; $param = lc($param); # Get and remove the attribute. my $p = delete( $atts{$param} ); # Queue. push( @todo, [ $param, $flags, $p ] ); } # CSS push with updated attributes. $self->css_push( \%atts ); # Now we can process the values. for ( @todo ) { my ( $param, $flags, $p ) = @$_; unless ( defined $p ) { if ( $flags =~ /s/ ) { $p = ""; } elsif ( $flags =~ /[0HUV]/ ) { $p = 0; } else { croak("Undefined mandatory attribute: $param") if $flags =~ /\!/; push( @res, $p ); next; } } $flags = lc($flags); # Convert units if 'u' flag. if ( $flags =~ /([huv])/ ) { my $flag = $1; if ( $p =~ /^([\d.]+)\%$/ ) { $p = $1/100; if ( $flags eq "w" || $param =~ /^(?:w(?:idth)|x)?$/i ) { # Percentage of viewBox width. $p *= $root->xoforms->[-1]->{width}; } elsif ( $flag eq "h" || $param =~ /^(?:h(?:eight)?|y)$/i ) { # Percentage of viewBox height. $p *= $root->xoforms->[-1]->{height}; } else { # Percentage of viewBox diagonal. $p *= $root->xoforms->[-1]->{diag}; } } else { $p = $self->u($p); } } push( @res, $p ); } # Return param values. return @res; } method get_cdata () { my $res = ""; for ( $self->get_children ) { $res .= "\n" . $_->content if ref($_) eq "SVGPDF::TextElement"; } $res; } method nfi ( $tag ) { state $aw = {}; warn("SVG: $tag - not fully implemented, expect strange results.\n") unless !$self->root->verbose || $aw->{$tag}++; } method set_font ( $xo, $style ) { my $msg =""; my $ret; { local $SIG{__WARN__} = sub { $msg .= "@_" }; $ret = $self->root->fontmanager->set_font( $xo, $style ); } if ( $msg && $self->root->verbose ) { warn($msg); } $ret; } ################ Bounding Box ################ # method bb ( $x, $y, $t = 0 ) { # my $bb = $self->root->xoforms->[-1]->{bb}; # # $t = $self->u($t) unless $t =~ /^[-+]?\d*(?:\.\d*)$/; # $t /= 2; # $bb->[0] = $x-$t if $bb->[0] > $x-$t; # $bb->[1] = $y-$t if $bb->[1] > $y-$t; # $bb->[2] = $x+$t if $bb->[2] < $x+$t; # $bb->[3] = $y+$t if $bb->[3] < $y+$t; # # return $bb; # } # ################ Utility ################ method data_inline($src) { my %info; unless ( $src =~ m! ^ data: (? [^/]+ ) / (? [^;]+ ) ; (? [^,]+ ) , (? . + ) $ !sx ) { return { error => "Malformed inline data" }; } if ( $+{encoding} eq "base64" ) { require MIME::Base64; $info{data} = MIME::Base64::decode($+{data}); } else { return { error => "Unhandled encoding \"$+{encoding}\" in inline data" }; } $info{mimetype} = $+{mimetype}; $info{subtype} = $+{subtype}; return \%info; } ################ TextElement ################ class SVGPDF::TextElement; field $content :param :accessor; # Actually, we should take style->{white-space} into account... BUILD { # Reduce whitespace. $content =~ s/\s+/ /g; } method process () { # Nothing to process. } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Image.pm0000644000400000040000000000534314545261604020424 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; use Carp; class SVGPDF::Image :isa(SVGPDF::Element); method process () { my $atts = $self->atts; my $xo = $self->xo; return if $atts->{omit}; # for testing/debugging. my ( $x, $y, $w, $h, $link, $tf ) = $self->get_params( $atts, qw( x:H y:V width:H height:V href:! transform:s ) ); $x ||= 0; $y ||= 0; $w ||= 0; $h ||= 0; unless ( $w && $h ) { $self->_dbg( $self->name, " x=$x y=$y w=$w h=$h **skipped**" ); return; } $self->_dbg( $self->name, " x=$x y=$y w=$w h=$h" ); my $img; if ( $link =~ /^data:/ ) { # In-line image asset. my $info = $self->data_inline($link); if ( $info->{error} ) { warn( "SVG: ", $info->{error}, "\n" ); $self->css_pop, return; } my $mimetype = $info->{mimetype}; my $subtype = $info->{subtype}; unless ( $mimetype eq "image" ) { warn("SVG: Unhandled mime type \"mimetype/$subtype\" in inline image\n"); $self->css_pop, return; } my $data = $info->{data}; # Get info. require Image::Info; $info = Image::Info::image_info(\$data); if ( $info->{error} ) { warn($info->{error}); $self->css_pop, return; } my $format = $info->{file_ext}; $format = "jpeg" if $format eq "jpg"; $format = "pnm" if $format =~ /^p[bgp]m$/; $format = "pnm" if $format =~ /^x[bp]m$/; # bonus $format = "tiff" if $format eq "tif"; # Make the image. Silence missing library warnings. my $fh; # Also, do not use the fast IPL module, it cannot read from scalar. if ( $format eq "tiff" ) { # TIFF can't read from scalar file handle. use File::Temp; ( my $fd, $fh ) = File::Temp::tempfile( UNLINK => 1 ); binmode $fd => ':raw'; print $fd $data; close($fd); # Yes, trickery... $fh is now a file name, not a handle. } else { open( $fh, '<:raw', \$data ); } $img = $self->root->pdf->image( $fh, format => $format, silent => 1, nouseIPL => 1 ); } elsif ( $link =~ m!^.+\.(png|jpe?g|gif)$!i && -s $link ) { # Autodetected. Make the image. $img = $self->root->pdf->image( $link, silent => 1 ); } elsif ( $link =~ m!^.+\.(tiff?|p[bgp]m|x[bp]m)$!i && -s $link ) { # Not autodetected, need format. # Note that xbm and xpm are bonus. my $format = $1 =~ /tif/i ? "tiff" : "pnm"; # Make the image. $img = $self->root->pdf->image( $link, format => $format, silent => 1 ); } else { warn("SVG: Unhandled or missing image link: ", "\"$link\""//"", "\n"); return; } $self->_dbg( "xo save" ); $xo->save; # Place the image. $self->set_transform($tf) if $tf; $xo->transform( translate => [ $x, $y+$h ] ); $xo->image( $img, 0, 0, $w, -$h ); $self->_dbg( "xo restore" ); $xo->restore; $self->css_pop; } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Ellipse.pm0000644000400000040000000000123214467630526020776 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; use Carp; class SVGPDF::Ellipse :isa(SVGPDF::Element); method process () { my $atts = $self->atts; my $xo = $self->xo; return if $atts->{omit}; # for testing/debugging. my ( $cx, $cy, $rx, $ry, $tf ) = $self->get_params( $atts, qw( cx:H cy:V rx:H ry:V transform:s ) ); $self->_dbg( $self->name, " cx=$cx cy=$cy rx=$rx ry=$ry" ); $self->_dbg( "+ xo save" ); $xo->save; $self->set_graphics; $self->set_transform($tf) if $tf; $xo->ellipse( $cx, $cy, $rx, $ry ); $self->_paintsub->(); $self->_dbg( "- xo restore" ); $xo->restore; $self->css_pop; } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Path.pm0000644000400000040000000001446014542320174020271 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; use Carp; class SVGPDF::Path :isa(SVGPDF::Element); use SVGPDF::Contrib::PathExtract qw( extract_path_info ); method process () { my $atts = $self->atts; my $xo = $self->xo; return if $atts->{omit}; # for testing/debugging. if ( defined $atts->{id} ) { $self->root->defs->{ "#" . $atts->{id} } = $self; # MathJax uses curves to draw glyphs. These glyphs are filles # *and* stroked with a very small stroke-width. According to # the PDF specs, this should yield a 1-pixel (device pixel) # stroke, which results in fat glyphs on screen. # To avoid this, disable stroke when drawing MathJax glyphs. if ( $atts->{id} =~ /^MJX-/ ) { $atts->{stroke} = 'none'; } } my ( $d, $tf ) = $self->get_params( $atts, "d:!", "transform:s" ); ( my $t = $d ) =~ s/\s+/ /g; $t = substr($t,0,20) . "..." if length($t) > 20; $self->_dbg( $self->name, " d=\"$t\"", $tf ? " tf=\"$tf\"" : "" ); return unless $d; $self->_dbg( "+ xo save" ); $xo->save; $self->set_transform($tf); $self->set_graphics; # Get path info, turning relative coordinates into absolute # and eliminate S and T curves. my @d = extract_path_info( $d, { absolute => 1, no_smooth => 1 } ); my $open; # path is open my $paint = $self->_paintsub; # Initial x,y for path, if open. See 'z'. my ( $ix, $iy ); # Current point. Starting point of this path. # Since we're always use absolute coordinates, this is the # starting point for subpaths as well. my ( $cx, $cy ) = ( 0, 0 ); # For debugging: collect control points. my @cp; my $id = -1; # for diagnostics while ( @d ) { my $d = shift(@d); my $op = $d->{svg_key}; $id++; # Reset starting point for the subpath. my ( $x, $y ) = ( 0, 0 ); # Remember initial point of path. $ix = $cx, $iy = $cy unless $open++ || $op eq "Z"; warn(sprintf("%s[%d] x=%.2f,y=%.2f cx=%.2f,cy=%.2f ix=%.2f,iy=%.2f\n", $op, $id, $x, $y, $cx, $cy, $ix, $iy)) if 0 & ($x || $y || $ix || $iy); # MoveTo if ( $op eq "M" ) { $x += $d->{point}->[0]; $y += $d->{point}->[1]; $self->_dbg( "xo move(%.2f,%.2f)", $x, $y ); $xo->move( $x, $y ); } # Horizontal LineTo. elsif ( $op eq "H" ) { $x += $d->{x}; $y = $cy; $self->_dbg( "xo hline(%.2f)", $x ); $xo->hline($x); } # Vertical LineTo. elsif ( $op eq "V" ) { $x = $cx; $y += $d->{y}; $self->_dbg( "xo vline(%.2f)", $y ); $xo->vline($y); } # Generic LineTo. elsif ( $op eq "L" ) { $x += $d->{point}->[0]; $y += $d->{point}->[1]; $self->_dbg( "xo line(%.2f,%.2f)", $x, $y ); $xo->line( $x, $y ); } # Cubic Bézier curves. elsif ( $op eq "C" ) { my @c = ( # control point 1 $x + $d->{control1}->[0], $y + $d->{control1}->[1], # control point 2 $x + $d->{control2}->[0], $y + $d->{control2}->[1], # end point $x + $d->{end}->[0], $y + $d->{end}->[1], ); $self->_dbg( "xo curve(%.2f,%.2f %.2f,%.2f %.2f,%.2f)", @c ); $xo->curve(@c); push( @cp, [ $cx, $cy, $c[0], $c[1] ] ); push( @cp, [ $c[4], $c[5], $c[2], $c[3] ] ); $x = $c[4]; $y = $c[5]; # end point } # Quadratic Bézier curves. elsif ( $op eq "Q" ) { my @c = ( # control point 1 $x + $d->{control}->[0], $y + $d->{control}->[1], # end point $x + $d->{end}->[0], $y + $d->{end}->[1], ); $self->_dbg( "xo spline(%.2f,%.2f %.2f,%.2f)", @c ); $xo->spline(@c); push( @cp, [ $cx, $cy, $c[0], $c[1] ] ); push( @cp, [ $c[2], $c[3], $c[0], $c[1] ] ); $x = $c[2]; $y = $c[3]; # end point } # Arcs. elsif ( $op eq "A" ) { my $rx = $d->{rx}; # radius 1 my $ry = $d->{ry}; # radius 2 my $rot = $d->{x_axis_rotation}; # rotation my $large = $d->{large_arc_flag}; # select larger arc my $sweep = $d->{sweep_flag}; # clockwise my $ex = $x + $d->{x}; # end point my $ey = $y + $d->{y}; $self->_dbg( "xo arc(%.2f,%.2f %.2f %d,%d %.2f,%.2f)", $rx, $ry, $rot, $large, $sweep, $ex, $ey ); # for circular arcs. if ( $rx == $ry ) { $self->_dbg( "circular_arc(%.2f,%.2f %.2f,%.2f %.2f ". "move=%d large=%d dir=%d rot=%.2f)", $cx, $cy, $ex, $ey, $rx, 0, $large, $sweep, $rot ); $self->circular_arc( $cx, $cy, $ex, $ey, $rx, move => 0, large => $large, rotate => $rot, dir => $sweep ); } else { $self->_dbg( "elliptic_arc(%.2f,%.2f %.2f,%.2f %.2f,%.2f ". "move=%d large=%d dir=%d rot=%.2f)", $cx, $cy, $ex, $ey, $rx, $ry, 0, $large, $sweep, $rot ); $self->elliptic_arc( $cx, $cy, $ex, $ey, $rx, $ry, move => 0, large => $large, rotate => $rot, dir => $sweep ); } ( $x, $y ) = ( $ex, $ey ); # end point } # Close path and paint. elsif ( $op eq "Z" ) { $self->_dbg( "xo z" ); if ( $open ) { $xo->close; $open = 0; # currentpoint becomes the initial point. $x = $ix; $y = $iy; } if ( @d && $d[0]->{svg_key} eq 'M' ) { # Close is followed by a move -> do not paint yet. } else { $paint->(); } } # Unidenfied subpath element. else { croak("Unidenfied subpath element[$id] $op"); } ( $cx, $cy ) = ( $x, $y ) unless $op eq "Z"; } $paint->() if $open; $self->_dbg( "- xo restore" ); $xo->restore; # Show collected control points. if ( 0 && $self->root->debug && @cp ) { $xo->save; $xo->stroke_color('lime'); $xo->line_width(0.5); for ( @cp ) { $self->_dbg( "xo line(%.2f %.2f %.2f %.2f)", @$_ ); $xo->move( $_->[0], $_->[1] ); $xo->line( $_->[2], $_->[3] ); } $xo->stroke; $xo->restore; } $self->css_pop; } method curve ( @points ) { $self->_dbg( "+ xo curve( %.2f,%.2f %.2f,%.2f %.2f,%.2f )", @points ); $self->xo->curve(@points); $self->_dbg( "-" ); } method elliptic_arc( $x1,$y1, $x2,$y2, $rx,$ry, %opts) { require SVGPDF::Contrib::Bogen; SVGPDF::Contrib::Bogen::bogen_ellip ( $self, $x1,$y1, $x2,$y2, $rx,$ry, %opts ); } method circular_arc( $x1,$y1, $x2,$y2, $r, %opts) { require SVGPDF::Contrib::Bogen; SVGPDF::Contrib::Bogen::bogen ( $self, $x1,$y1, $x2,$y2, $r, $opts{move}, $opts{large}, $opts{dir} ); } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/FontManager.pm0000644000400000040000000001530514563365555021614 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; class SVGPDF::FontManager; use Carp; use List::Util qw( any none uniq ); use Text::ParseWords qw( quotewords ); field $svg :mutator :param; field $fc :mutator; field $td :accessor; # The 14 corefonts. my @corefonts = qw( Courier Courier-Bold Courier-BoldOblique Courier-Oblique Helvetica Helvetica-Bold Helvetica-BoldOblique Helvetica-Oblique Symbol Times-Bold Times-BoldItalic Times-Italic Times-Roman ZapfDingbats ); # Set a font according to the style. # # Strategy: First see if there was a @font-face defined. If so, use it. # Then dispatch to user callback, if specified. # Otherwise, try builtin fonts. method set_font ( $xo, $style ) { my ( $font, $size, $src ) = $self->find_font($style); $xo->font( $font, $size ); } method find_font ( $style ) { my $stl = lc( $style->{'font-style'} // "normal" ); my $weight = lc( $style->{'font-weight'} // "normal" ); ####TODO: normalize styles and weights. # Process the font-families, if any. for ( ffsplit( $style->{'font-family'} // [] ) ) { my $family = lc($_); # Check against @font-faces, if any. for ( @{ $style->{'@font-face'}//[] } ) { my $fam = $_->{'font-family'}; next unless $fam && lc($fam) eq $family; next unless $_->{src}; next if $_->{'font-style'} && $style->{'font-style'} && $_->{'font-style'} ne $style->{'font-style'}; next if $_->{'font-weight'} && $style->{'font-weight'} && $_->{'font-weight'} ne $style->{'font-weight'}; $fam = lc($fam); my $key = join( "|", $fam, $stl, $weight ); # Font in cache? if ( my $f = $fc->{$key} ) { return ( $f->{font}, $style->{'font-size'} || 12, $f->{src} ); } # Fetch font from source. my $src = $_->{src}; ####TODO: Multiple sources if ( $src =~ /^\s*url\s*\((["'])((?:data|https?|file):.*?)\1\s*\)/is ) { use File::LoadLines 1.044; my $opts = {}; my $data = File::LoadLines::loadblob( $2, $opts ); # To load font data from net and data urls. use File::Temp qw( tempfile tempdir ); use MIME::Base64 qw( decode_base64 ); $td //= tempdir( CLEANUP => 1 ); my $sfx; # suffix for font file name if ( $src =~ /\bformat\((["'])(.*?)\1\)/ ) { $sfx = lc($2) eq "truetype" ? ".ttf" : lc($2) eq "opentype" ? ".otf" : ''; } elsif ( $opts->{_filesource} =~ /\.(\w+)$/ ) { $sfx = $2; } # No (or unknown) format, skip. next unless $sfx; my ($fh,$fn) = tempfile( "${td}SVGXXXX", SUFFIX => $sfx ); binmode( $fh => ':raw' ); print $fh $data; close($fh); my $font = eval { $svg->pdf->font($fn) }; croak($@) if $@; my $f = $fc->{$key} = { font => $font, src => $opts->{_filesource} }; #warn("SRC: ", $opts->{_filesource}, "\n"); return ( $f->{font}, $style->{'font-size'} || 12, $f->{src} ); } # Temp. elsif ( $src =~ /^\s*url\s*\((["'])data:application\/octet-stream;base64,(.*?)\1\s*\)/is ) { my $data = $2; # To load font data from net and data urls. use File::Temp qw( tempfile tempdir ); use MIME::Base64 qw( decode_base64 ); $td //= tempdir( CLEANUP => 1 ); my $sfx; # suffix for font file name if ( $src =~ /\bformat\((["'])(.*?)\1\)/ ) { $sfx = lc($2) eq "truetype" ? ".ttf" : lc($2) eq "opentype" ? ".otf" : ''; } # No (or unknown) format, skip. next unless $sfx; my ($fh,$fn) = tempfile( "${td}SVGXXXX", SUFFIX => $sfx ); binmode( $fh => ':raw' ); print $fh decode_base64($data); close($fh); my $font = eval { $svg->pdf->font($fn) }; croak($@) if $@; my $f = $fc->{$key} = { font => $font, src => 'data' }; return ( $f->{font}, $style->{'font-size'} || 12, $f->{src} ); } elsif ( $src =~ /^\s*url\s*\((["'])(.*?\.[ot]tf)\1\s*\)/is ) { my $fn = $2; my $font = eval { $svg->pdf->font($fn) }; croak($@) if $@; my $f = $fc->{$key} = { font => $font, src => $fn }; return ( $f->{font}, $style->{'font-size'} || 12, $f->{src} ); } else { croak("\@font-face: Unhandled src \"", substr($src,0,50), "...\""); } } } my $key = join( "|", $style->{'font-family'}, $stl, $weight ); # Font in cache? if ( my $f = $fc->{$key} ) { return ( $f->{font}, $style->{'font-size'} || 12, $f->{src} ); } if ( my $cb = $svg->fc ) { my $font; unless ( ref($cb) eq 'ARRAY' ) { $cb = [ $cb ]; } # Run callbacks. my %args = ( pdf => $svg->pdf, style => $style ); for ( @$cb ) { eval { $font = $_->( $svg, %args ) }; croak($@) if $@; last if $font; } if ( $font ) { my $src = "Callback($key)"; $fc->{$key} = { font => $font, src => $src }; return ( $font, $style->{'font-size'} || 12, $src ); } } # No @font-face, no (or failed) callbacks, we're on our own. my $fn = $style->{'font-family'} // "Times-Roman"; my $sz = $style->{'font-size'} || 12; my $em = $style->{'font-style'} && $style->{'font-style'} =~ /^(italic|oblique)$/ || ''; my $bd = $style->{'font-weight'} && $style->{'font-weight'} =~ /^(bold|black)$/ || ''; for ( ffsplit($fn) ) { $fn = lc($_); # helvetica sans sans-serif text,sans-serif if ( $fn =~ /^(sans|helvetica|(?:text,)?sans-serif)$/ ) { $fn = $bd ? $em ? "Helvetica-BoldOblique" : "Helvetica-Bold" : $em ? "Helvetica-Oblique" : "Helvetica"; } # courier mono monospace mono-space text elsif ( $fn =~ /^(text|courier|mono(?:-?space)?)$/ ) { $fn = $bd ? $em ? "Courier-BoldOblique" : "Courier-Bold" : $em ? "Courier-Oblique" : "Courier"; } # times serif text,serif elsif ( $fn =~ /^(serif|times|(?:text,)?serif)$/ ) { $fn = $bd ? $em ? "Times-BoldItalic" : "Times-Bold" : $em ? "Times-Italic" : "Times-Roman"; } # Any of the corefonts, case insensitive. elsif ( none { $fn eq lc($_) } @corefonts ) { undef $fn; } last if $fn; # Retry other families, if any. } unless ( $fn ) { # Nothing found... $fn = $bd ? $em ? "Times-BoldItalic" : "Times-Bold" : $em ? "Times-Italic" : "Times-Roman"; } my $font = $fc->{$fn} //= do { unless ( $fn =~ /\.\w+$/ ) { my $t = ""; $t .= "italic, " if $em; $t .= "bold, " if $bd; $t = " (" . substr($t, 0, length($t)-2) . ")" if $t; warn("SVG: Font ", $style->{'font-family'}//"", "$t - falling back to built-in font $fn with limited glyphs!\n") } { font => $svg->pdf->font($fn), src => $fn }; }; return ( $font->{font}, $sz, $font->{src} ); } sub ffsplit ( $family ) { # I hope this traps most (ab)uses of quotes and commas. $family =~ s/^\s+//; $family =~ s/\s+$//; map { s/,+$//r } uniq quotewords( qr/(\s+|\s*,\s*)/, 0, $family); } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF/Svg.pm0000644000400000040000000001003214474442610020127 0ustar jvjv#! perl use v5.26; use Object::Pad; use utf8; use Carp; class SVGPDF::Svg :isa(SVGPDF::Element); method process () { my $atts = $self->atts; return if $atts->{omit}; # for testing/debugging. my $xo = $self->xo; my $xoforms = $self->root->xoforms; delete $atts->{$_} for qw( xmlns:xlink xmlns:svg xmlns version ); my ( $x, $y, $vwidth, $vheight, $vbox, $par, $tf ) = $self->get_params( $atts, qw( x:U y:U width:s height:s viewBox preserveAspectRatio:s transform:s ) ); $self->nfi("nested svg transform") if $tf; $self->nfi("preserveAspectRatio") if $par; my $style = $self->style; my $parent; for ( @{ $self->root->xoforms } ) { next unless $_->{xo} eq $xo; $parent = $_; last; } croak("I feel like a motherless child") unless $parent; my $pwidth = $parent->{width}; my $pheight = $parent->{height}; for ( $vwidth ) { $_ = $self->u( $_ || $pwidth, width => $pwidth ); } for ( $vheight ) { $_ = $self->u( $_ || $pheight, width => $pheight ); } $self->_dbg("pp w=$pwidth h=$pheight vw=$vwidth vh=$vheight"); my @vb; # viewBox: llx lly width height my @bb; # bbox: llx lly urx ury my $width; # width of the vbox my $height; # height of the vbox if ( $vbox ) { @vb = $self->getargs($vbox); $width = $self->u( $vb[2], width => $pwidth ); $height = $self->u( $vb[3], width => $height ); } else { $width = $vwidth; $height = $vheight; @vb = ( 0, 0, $width, $height ); $vbox = "@vb"; } # Get llx lly urx ury bounding box rectangle. @bb = $self->root->vb2bb_noflip(@vb); $self->_dbg( "vb $vbox => bb %.2f %.2f %.2f %.2f", @bb ); warn( sprintf("vb $vbox => bb %.2f %.2f %.2f %.2f\n", @bb )) if $self->root->verbose && !$self->root->debug; my $new_xo = $self->root->pdf->xo_form; $new_xo->bbox(@bb); # Set up result forms. push( @$xoforms, { xo => $new_xo, # Design (desired) width and height. vwidth => $vwidth || $vb[2], vheight => $vheight || $vb[3], # viewBox (SVG coordinates) vbox => [ @vb ], width => $vb[2], height => $vb[3], diag => sqrt( $vb[2]**2 + $vb[3]**2 ) / sqrt(2), # bbox (PDF coordinates) bbox => [ @bb ], yflip => 0, } ); $self->_dbg("XObject #", scalar(@$xoforms) ); $self->traverse; my $scalex = 1; my $scaley = 1; my $dx = 0; my $dy = 0; if ( $vbox ) { my @pbb = $xo->bbox; # parent if ( $vwidth ) { $scalex = $vwidth / $vb[2]; } if ( $vheight ) { $scaley = $vheight / $vb[3]; } # warn("pbbx @pbb\n"); # warn("bbox @bb scale=$scalex/$scaley\n"); if ( $par =~ /none/i ) { $par = ""; } else { # Uniform scale. # $scalex = $scaley = min( $scalex, $scaley ); } if ( $par =~ /xM(ax|id|in)/i && $scalex > $scaley ) { if ( $1 eq "ax" ) { $dx = max($pbb[0],$pbb[2]) - max($bb[0],$bb[2]); } elsif ( $1 eq "id" ) { $dx = (($pbb[2]-$pbb[0])/2) - (($bb[2]-$bb[0])/2); } else { $dx = min($pbb[0],$pbb[2]) - min($bb[0],$bb[2]); } } if ( $par =~ /yM(in|id|ax)/i && $scaley > $scalex ) { if ( $1 eq "ax" ) { $dy = max($pbb[1],$pbb[3]) - max($bb[1],$bb[3]); } elsif ( $1 eq "id" ) { $dy = ((max($pbb[1],$pbb[3])-min($pbb[1],$pbb[3]))/2) - ((max($bb[1],$bb[3])-min($bb[1],$bb[3]))/2) } else { $dy = min($pbb[1],$pbb[3]) - min($bb[1],$bb[3]); } } if ( $par ) { $scalex = $scaley = min( $scalex, $scaley ); $dx *= $scalex; $dy *= $scaley; warn("disp dx=$dx, dy=$dy\n"); } } $self->_dbg( "xo object( %.2f%+.2f %.2f%+.2f %.3f %.3f ) %s", $x, $dx, $y, $dy, $scalex, $scaley, $par//"" ); warn(sprintf("xo object( %.2f%+.2f %.2f%+.2f %.3f %.3f ) %s\n", $x, $dx, $y, $dy, $scalex, $scaley, $par//"" )) if $self->root->verbose && !$self->root->debug; $xo->object( $new_xo, $x+$dx, $y+$dy, $scalex, $scaley ); pop( @$xoforms ); $self->css_pop; } sub min ( $x, $y ) { $x < $y ? $x : $y } sub max ( $x, $y ) { $x > $y ? $x : $y } 1; App-Music-ChordPro-6.050.7/lib/ChordPro/lib/SVGPDF.pm0000644000400000040000000006001214566040177017377 0ustar jvjv#! perl use v5.26; use Object::Pad; use Carp; use utf8; class SVGPDF; our $VERSION = '0.086.2'; =head1 NAME SVGPDF - Create PDF XObject from SVG data =head1 SYNOPSIS my $pdf = PDF::API2->new; my $svg = SVGPDF->new($pdf); my $xof = $svg->process("demo.svg"); # If all goes well, $xof is an array of hashes, each representing an # XObject corresponding to the elements in the file. # Get a page and graphics context. my $page = $pdf->page; $page->bbox( 0, 0, 595, 842 ); my $gfx = $pdf->gfx; # Place the objects. my $y = 832; foreach my $xo ( @$xof ) { my @bb = @{$xo->{vbox}}; my $h = $bb[3]; $gfx->object( $xo->{xo}, 10, $y-$h, 1 ); $y -= $h; } $pdf->save("demo.pdf"); =head1 DESCRIPTION This module processes SVG data and produces one or more PDF XObjects to be placed in a PDF document. This module is intended to be used with L, L and compatible PDF packages. The main method is process(). It takes the SVG from an input source, see L. =head1 COORDINATES & UNITS SVG coordinates run from top-left to bottom-right. Dimensions without units are B, at 96 pixels / inch. E.g., C means 96px (pixels) and is equal to 72pt (points) or 1in (inch). For font sizes, CSS defines C to be equal to the font size, and C is half of the font size. =head1 CONSTRUCTORS =head2 SVGPDF->new($pdf) In its most simple form, a new SVGPDF object can be created with a single argument, the PDF document. There are a few optional arguments, these can be specified as key/value pairs. =over 8 =item C A reference to a callback routine to handle fonts. See L. It may also be an array of routines which will be called in sequence until one of them succeeds (returns a 'true' result). =item C The font size to be used for dimensions in 'ex' and 'em' units. Note that CSS defines 'em' to be the font size, and 'ex' half of the font size. =item C An array reference containing the maximum width and height of the resultant image. There is no widely accepted default for this, so we use C<[595,842]> which corresponds to an ISO A4 page. =item C If not zero, a grid will be added to the image. This is mostly for developing and debugging. The value determines the grid spacing. =item C Verbosity of informational messages. Set to zero to silence all but fatal errors. =item C Internal use only. =back For convenience, the mandatory PDF argument can also be specified with a key/value pair: $svg = SVGPDF->new( pdf => $pdf, grid => 1, fc => \&fonthandler ); =cut field $pdf :accessor :param; field $atts :accessor :param = undef; # Callback for font and text handling. field $fc :accessor :param = undef; field $tc :accessor :param = undef; # If an SVG file contains more than a single SVG, the CSS applies to all. field $css :accessor; # Font manager. field $fontmanager :accessor; field $xoforms :accessor; field $defs :accessor; # Defaults for rendering. field $pagesize :accessor; field $fontsize :accessor; field $pxpi :mutator = 96; # pixels per inch field $ptpi :accessor = 72; # points per inch # For debugging/development. field $verbose :accessor; field $debug :accessor; field $grid :accessor; field $prog :accessor; field $debug_styles :accessor; field $trace :accessor; field $wstokens :accessor; our $indent = ""; use SVGPDF::Parser; use SVGPDF::Element; use SVGPDF::CSS; use SVGPDF::FontManager; use SVGPDF::PAST; # The SVG elements. use SVGPDF::Circle; use SVGPDF::Defs; use SVGPDF::Ellipse; use SVGPDF::G; use SVGPDF::Image; use SVGPDF::Line; use SVGPDF::Path; use SVGPDF::Polygon; use SVGPDF::Polyline; use SVGPDF::Rect; use SVGPDF::Style; use SVGPDF::Svg; use SVGPDF::Text; use SVGPDF::Tspan; use SVGPDF::Use; ################ General methods ################ =head1 METHODS =cut # $pdf [ , fc => $callback ] [, atts => { ... } ] [, foo => bar ] # pdf => $pdf [ , fc => $callback ] [, atts => { ... } ] [, foo => bar ] sub BUILDARGS ( @args ) { my $cls = shift(@args); # Assume first is pdf if uneven. unshift( @args, "pdf" ) if @args % 2; my %args = @args; @args = (); push( @args, $_, delete $args{$_} ) for qw( pdf fc tc ); # Flatten everything else into %atts. my %x = %{ delete($args{atts}) // {} }; $x{$_} = $args{$_} for keys(%args); # And store as ref. push( @args, "atts", \%x ); # Return new argument list. @args; } BUILD { $verbose = $atts->{verbose} // 1; $debug = $atts->{debug} || 0; $grid = $atts->{grid} || 0; $prog = $atts->{prog} || 0; $debug_styles = $atts->{debug_styles} || $debug > 1; $trace = $atts->{trace} || 0; $pagesize = $atts->{pagesize} || [ 595, 842 ]; $fontsize = $atts->{fontsize} || 12; $wstokens = $atts->{wstokens} || 0; $indent = ""; $xoforms = []; $defs = {}; $fontmanager = SVGPDF::FontManager->new( svg => $self ); $self; } =head2 process $xof = $svg->process( $data, %options ) This methods gets SVG data from C<$data> and returns an array reference with rendered images. See L for details. The input is read using File::LoadLines. See L for details. Recognized attributes in C<%options> are: =over 4 =item fontsize The font size to be used for dimensions in 'ex' and 'em' units. This value overrides the value set in the constructor. =item combine An SVG can produce multiple XObjects, but sometimes these should be kept as a single image. There are two ways to combine the image objects. This can be selected by setting $opts{combine} to either C<"stacked"> or C<"bbox">. Type C<"stacked"> (default) stacks the images on top of each other, left sides aligned. The bounding box of each object is only used to obtain the width and height. Type C<"bbox"> stacks the images using the bounding box details. The origins of the images are vertically aligned and images may protrude other images when the image extends below the origin. =item sep When combining images, add additional vertical space between the individual images. =back =cut method process ( $data, %options ) { if ( $options{reset} ) { # for testing, mostly $xoforms = []; } my $save_fontsize = $fontsize; $fontsize = $options{fontsize} if $options{fontsize}; # TODO: Page size # Load the SVG data. my $svg = SVGPDF::Parser->new; my $tree = $svg->parse_file ( $data, whitespace_tokens => $wstokens||$options{whitespace_tokens} ); return unless $tree; # CSS persists over svgs, but not over files. $css = SVGPDF::CSS->new; # Search for svg elements and process them. $self->search($tree); # Restore. $fontsize = $save_fontsize; my $combine = $options{combine} // "none"; if ( $combine ne "none" && @$xoforms > 1 ) { my $sep = $options{sep} || 0; $xoforms = $self->combine_svg( $xoforms, type => $combine, sep => $sep ); } # Return (hopefully a stack of XObjects). return $xoforms; } method _dbg ( @args ) { return unless $debug; my $msg; if ( $args[0] =~ /\%/ ) { $msg = sprintf( $args[0], @args[1..$#args] ); } else { $msg = join( "", @args ); } if ( $msg =~ /^\+\s*(.*)/ ) { $indent = $indent . " "; warn( $indent, $1, "\n") if $1; } elsif ( $msg =~ /^\-\s*(.*)/ ) { warn( $indent, $1, "\n") if $1; confess("oeps") if length($indent) < 2; $indent = substr( $indent, 2 ); } elsif ( $msg =~ /^\^\s*(.*)/ ) { $indent = ""; warn( $indent, $1, "\n") if $1; } else { warn( $indent, $msg, "\n") if $msg; } } method search ( $content ) { # In general, we'll have an XHTML tree with one or more # elements. for ( @$content ) { next if $_->{type} eq 't'; my $name = $_->{name}; if ( $name eq "svg" ) { $indent = ""; $self->handle_svg($_); # Adds results to $self->{xoforms}. } else { # Skip recursively. $self->_dbg( "$name (ignored)" ) unless $name eq "<>"; # top $self->search($_->{content}); } } } method handle_svg ( $e ) { $self->_dbg( "^ ==== start ", $e->{name}, " ====" ); my $xo; if ( $prog ) { $xo = SVGPDF::PAST->new( pdf => $pdf ); } else { $xo = $pdf->xo_form; } push( @$xoforms, { xo => $xo } ); $self->_dbg("XObject #", scalar(@$xoforms) ); my $svg = SVGPDF::Element->new ( name => $e->{name}, atts => { map { lc($_) => $e->{attrib}->{$_} } keys %{$e->{attrib}} }, content => $e->{content}, root => $self, ); # If there are
Swing Low Sweet Chariot
Traditional
D G D
Swing low, sweet chariot,
A7
Comin’ for to carry me home.
D7 G D
Swing low, sweet chariot,
A7 D
Comin’ for to carry me home.
D G D
I looked over Jordan, and what did I see,
A7
“Comin’ for to carry me home.”
D7 G D
A band of angels comin’ after me,
A7 D G D G
Comin’ for to carry me home.
Chorus
If you get there before I do,
Comin’ for to carry me home.
Just tell my friends that I’m a comin’ too.
Comin’ for to carry me home.
Chorus
I’m sometimes up and sometimes down,
Comin’ for to carry me home.
But still my soul feels heavenly bound.
Comin’ for to carry me home.
Chorus
And a final chorus
App-Music-ChordPro-6.050.7/t/ref/40_html_1.html0000644000400000040000000000725414324567577016433 0ustar jvjv
Swing Low Sweet Chariot
Traditional
D G D
Swing low, sweet chariot,
A7
Comin’ for to carry me home.
D7 G D
Swing low, sweet chariot,
A7 D
Comin’ for to carry me home.
D G D
I looked over Jordan, and what did I see,
A7
“Comin’ for to carry me home.”
D7 G D
A band of angels comin’ after me,
A7 D G D G
Comin’ for to carry me home.
Chorus
If you get there before I do,
Comin’ for to carry me home.
Just tell my friends that I’m a comin’ too.
Comin’ for to carry me home.
Chorus
I’m sometimes up and sometimes down,
Comin’ for to carry me home.
But still my soul feels heavenly bound.
Comin’ for to carry me home.
Chorus
And a final chorus
App-Music-ChordPro-6.050.7/t/ref/30_cho_2.cho0000644000400000040000000000216114544300524016012 0ustar jvjv{title: Swing Low Sweet Chariot} {subtitle: Traditional} {columns: 2} {titles: center} {start_of_chorus} Swing [D]low, sweet [G]chari[D]ot, Comin’ for to carry me [A7]home. Swing [D7]low, sweet [G]chari[D]ot, Comin’ for to [A7]carry me [D]home. {end_of_chorus} I [D]looked over Jordan, and [G]what did I [D]see, “Comin’ for to carry me [A7]home.” A [D7]band of angels [G]comin’ after [D]me, Comin’ for to [A7]carry me [D]home. [G]     [D] [G] {new_page} {comment: Chorus} If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. {column_break} {comment: Chorus} # Note the ' here will be changed into a decent apostrophe. I’m sometimes up and sometimes down, Comin’ for to carry me home. But still my soul feels heavenly bound. Comin’ for to carry me home. {comment: Chorus} {comment_italic: And a final chorus} #{chord: D base-fret 1 frets N N 0 2 3 2} #{chord: G base-fret 1 frets 3 2 0 0 0 3} #{chord: A7 base-fret 1 frets N 0 2 0 2 0} #{chord: D7 base-fret 1 frets N N 0 2 1 2} App-Music-ChordPro-6.050.7/t/ref/31_cho_3.cho0000644000400000040000000000166614544300524016025 0ustar jvjv{title: Swing Low Sweet Chariot} {subtitle: Traditional} {columns: 2} {titles: center} {start_of_chorus} Swing low, sweet chariot, Comin’ for to carry me home. Swing low, sweet chariot, Comin’ for to carry me home. {end_of_chorus} I looked over Jordan, and what did I see, “Comin’ for to carry me home.” A band of angels comin’ after me, Comin’ for to carry me home.       {start_of_tab} I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. {end_of_tab} {comment: Chorus} {start_of_tab} I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. {end_of_tab} #{chord: D base-fret 1 frets N N 0 2 3 2} #{chord: G base-fret 1 frets 3 2 0 0 0 3} #{chord: A7 base-fret 1 frets N 0 2 0 2 0} #{chord: D7 base-fret 1 frets N N 0 2 1 2} App-Music-ChordPro-6.050.7/t/ref/31_cho_2.cho0000644000400000040000000000177314544300524016023 0ustar jvjv{title: Swing Low Sweet Chariot} {subtitle: Traditional} {columns: 2} {titles: center} {start_of_chorus} Swing [D]low, sweet [G]chari[D]ot, Comin’ for to carry me [A7]home. Swing [D7]low, sweet [G]chari[D]ot, Comin’ for to [A7]carry me [D]home. {end_of_chorus} I [D]looked over Jordan, and [G]what did I [D]see, “Comin’ for to carry me [A7]home.” A [D7]band of angels [G]comin’ after [D]me, Comin’ for to [A7]carry me [D]home. [G]     [D] [G] {start_of_tab} I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. {end_of_tab} {comment: Chorus} {start_of_tab} I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. {end_of_tab} #{chord: D base-fret 1 frets N N 0 2 3 2} #{chord: G base-fret 1 frets 3 2 0 0 0 3} #{chord: A7 base-fret 1 frets N 0 2 0 2 0} #{chord: D7 base-fret 1 frets N N 0 2 1 2} App-Music-ChordPro-6.050.7/t/ref/30_cho_3.cho0000644000400000040000000000205414544300524016014 0ustar jvjv{title: Swing Low Sweet Chariot} {subtitle: Traditional} {columns: 2} {titles: center} {start_of_chorus} Swing low, sweet chariot, Comin’ for to carry me home. Swing low, sweet chariot, Comin’ for to carry me home. {end_of_chorus} I looked over Jordan, and what did I see, “Comin’ for to carry me home.” A band of angels comin’ after me, Comin’ for to carry me home.       {new_page} {comment: Chorus} If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. {column_break} {comment: Chorus} # Note the ' here will be changed into a decent apostrophe. I’m sometimes up and sometimes down, Comin’ for to carry me home. But still my soul feels heavenly bound. Comin’ for to carry me home. {comment: Chorus} {comment_italic: And a final chorus} #{chord: D base-fret 1 frets N N 0 2 3 2} #{chord: G base-fret 1 frets 3 2 0 0 0 3} #{chord: A7 base-fret 1 frets N 0 2 0 2 0} #{chord: D7 base-fret 1 frets N N 0 2 1 2} App-Music-ChordPro-6.050.7/t/ref/40_html_3.html0000644000400000040000000000472314324567577016433 0ustar jvjv
Swing Low Sweet Chariot
Traditional
Swing low, sweet chariot,
Comin’ for to carry me home.
Swing low, sweet chariot,
Comin’ for to carry me home.
I looked over Jordan, and what did I see,
“Comin’ for to carry me home.”
A band of angels comin’ after me,
Comin’ for to carry me home.
Chorus
If you get there before I do,
Comin’ for to carry me home.
Just tell my friends that I’m a comin’ too.
Comin’ for to carry me home.
Chorus
I’m sometimes up and sometimes down,
Comin’ for to carry me home.
But still my soul feels heavenly bound.
Comin’ for to carry me home.
Chorus
And a final chorus
App-Music-ChordPro-6.050.7/t/ref/85_pages.csv0000644000400000040000000000046014324567577016176 0ustar jvjvtitle;pages;sort title;artists;composers;collections;keys;years __front_matter__;1;Front Matter;ChordPro;;;; __table_of_contents__;3-4;Table of Contents;ChordPro;;;; Song1;7;;Artist1;Composer1;;A; Song2;9;;Artist2;Composer2;;B; Song3;11;;Artist3;Composer3;;C; __back_matter__;12;Back Matter;ChordPro;;;; App-Music-ChordPro-6.050.7/t/ref/20_crd_2.crd0000644000400000040000000000200412757037534016017 0ustar jvjv-- Title: Swing Low Sweet Chariot -- Subtitle: Traditional -- Start of chorus D G D Swing low, sweet chariot, A7 Comin’ for to carry me home. D7 G D Swing low, sweet chariot, A7 D Comin’ for to carry me home. -- End of chorus D G D I looked over Jordan, and what did I see, A7 “Comin’ for to carry me home.” D7 G D A band of angels comin’ after me, A7 D G D G Comin’ for to carry me home.       -- -- New page -- Chorus If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. -- Column break -- Chorus I’m sometimes up and sometimes down, Comin’ for to carry me home. But still my soul feels heavenly bound. Comin’ for to carry me home. -- Chorus -- And a final chorus App-Music-ChordPro-6.050.7/t/ref/21_crd_3.crd0000644000400000040000000000133114120712212015776 0ustar jvjv-- Title: Swing Low Sweet Chariot -- Subtitle: Traditional -- Start of chorus Swing low, sweet chariot, Comin’ for to carry me home. Swing low, sweet chariot, Comin’ for to carry me home. -- End of chorus I looked over Jordan, and what did I see, “Comin’ for to carry me home.” A band of angels comin’ after me, Comin’ for to carry me home. -- Start of tab I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. -- End of tab -- Chorus -- Start of tab I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. -- End of tab App-Music-ChordPro-6.050.7/t/ref/21_crd_2.crd0000644000400000040000000000175314120712212016005 0ustar jvjv-- Title: Swing Low Sweet Chariot -- Subtitle: Traditional -- Start of chorus D G D Swing low, sweet chariot, A7 Comin’ for to carry me home. D7 G D Swing low, sweet chariot, A7 D Comin’ for to carry me home. -- End of chorus D G D I looked over Jordan, and what did I see, A7 “Comin’ for to carry me home.” D7 G D A band of angels comin’ after me, A7 D G D G Comin’ for to carry me home.       -- -- Start of tab I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. -- End of tab -- Chorus -- Start of tab I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. -- End of tab App-Music-ChordPro-6.050.7/t/ref/20_crd_3.crd0000644000400000040000000000136212757037535016027 0ustar jvjv-- Title: Swing Low Sweet Chariot -- Subtitle: Traditional -- Start of chorus Swing low, sweet chariot, Comin’ for to carry me home. Swing low, sweet chariot, Comin’ for to carry me home. -- End of chorus I looked over Jordan, and what did I see, “Comin’ for to carry me home.” A band of angels comin’ after me, Comin’ for to carry me home. -- New page -- Chorus If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. -- Column break -- Chorus I’m sometimes up and sometimes down, Comin’ for to carry me home. But still my soul feels heavenly bound. Comin’ for to carry me home. -- Chorus -- And a final chorus App-Music-ChordPro-6.050.7/t/ref/84_pages.csv0000644000400000040000000000023514324567577016175 0ustar jvjvtitle;pages;sort title;artists;composers;collections;keys;years Song1;5;;Artist1;Composer1;;A; Song2;7;;Artist2;Composer2;;B; Song3;9;;Artist3;Composer3;;C; App-Music-ChordPro-6.050.7/t/ref/83_pages.csv0000644000400000040000000000023514324567577016174 0ustar jvjvtitle;pages;sort title;artists;composers;collections;keys;years Song1;3;;Artist1;Composer1;;A; Song2;5;;Artist2;Composer2;;B; Song3;7;;Artist3;Composer3;;C; App-Music-ChordPro-6.050.7/t/ref/80_pages.csv0000644000400000040000000000023514324567577016171 0ustar jvjvtitle;pages;sort title;artists;composers;collections;keys;years Song1;5;;Artist1;Composer1;;A; Song2;7;;Artist2;Composer2;;B; Song3;9;;Artist3;Composer3;;C; App-Music-ChordPro-6.050.7/t/ref/81_pages.csv0000644000400000040000000000023514324567577016172 0ustar jvjvtitle;pages;sort title;artists;composers;collections;keys;years Song1;5;;Artist1;Composer1;;A; Song2;6;;Artist2;Composer2;;B; Song3;7;;Artist3;Composer3;;C; App-Music-ChordPro-6.050.7/t/ref/60_crd_3.crd0000644000400000040000000000136614236032144016020 0ustar jvjv-- Title: Transpositions -- Key is D, actual D -- Start of chorus Swing low, sweet chariot, -- End of chorus I looked over Jordan, and what did I see, -- Start of chorus Swing low, sweet chariot, -- End of chorus -- Key is D, actual E, from D I looked over Jordan, and what did I see, -- Start of chorus Swing low, sweet chariot, -- End of chorus -- Key is D, actual F#, from E I looked over Jordan, and what did I see, -- Start of chorus Swing low, sweet chariot, -- End of chorus -- Key is D, actual E, from F# I looked over Jordan, and what did I see, -- Start of chorus Swing low, sweet chariot, -- End of chorus -- Key is D, actual D I looked over Jordan, and what did I see, -- Start of chorus Swing low, sweet chariot, -- End of chorus App-Music-ChordPro-6.050.7/t/ref/60_crd_2.crd0000644000400000040000000000211314236032144016006 0ustar jvjv-- Title: Transpositions -- Key is D, actual D -- Start of chorus D G D Swing low, sweet chariot, -- End of chorus D G D I looked over Jordan, and what did I see, -- Start of chorus D G D Swing low, sweet chariot, -- End of chorus -- Key is D, actual E, from D E A E I looked over Jordan, and what did I see, -- Start of chorus E A E Swing low, sweet chariot, -- End of chorus -- Key is D, actual F#, from E F# B F# I looked over Jordan, and what did I see, -- Start of chorus F# B F# Swing low, sweet chariot, -- End of chorus -- Key is D, actual E, from F# E A E I looked over Jordan, and what did I see, -- Start of chorus E A E Swing low, sweet chariot, -- End of chorus -- Key is D, actual D D G D I looked over Jordan, and what did I see, -- Start of chorus D G D Swing low, sweet chariot, -- End of chorus App-Music-ChordPro-6.050.7/t/ref/82_pages.csv0000644000400000040000000000023514324567577016173 0ustar jvjvtitle;pages;sort title;artists;composers;collections;keys;years Song1;3;;Artist1;Composer1;;A; Song2;4;;Artist2;Composer2;;B; Song3;5;;Artist3;Composer3;;C; App-Music-ChordPro-6.050.7/t/ref/60_crd_1.crd0000644000400000040000000000211314236032144016005 0ustar jvjv-- Title: Transpositions -- Key is D, actual D -- Start of chorus D G D Swing low, sweet chariot, -- End of chorus D G D I looked over Jordan, and what did I see, -- Start of chorus D G D Swing low, sweet chariot, -- End of chorus -- Key is D, actual E, from D E A E I looked over Jordan, and what did I see, -- Start of chorus E A E Swing low, sweet chariot, -- End of chorus -- Key is D, actual F#, from E F# B F# I looked over Jordan, and what did I see, -- Start of chorus F# B F# Swing low, sweet chariot, -- End of chorus -- Key is D, actual E, from F# E A E I looked over Jordan, and what did I see, -- Start of chorus E A E Swing low, sweet chariot, -- End of chorus -- Key is D, actual D D G D I looked over Jordan, and what did I see, -- Start of chorus D G D Swing low, sweet chariot, -- End of chorus App-Music-ChordPro-6.050.7/t/ref/87_pages.csv0000644000400000040000000000046514324567577016205 0ustar jvjvtitle;pages;sort title;artists;composers;collections;keys;years __front_matter__;1;Front Matter;ChordPro;;;; __table_of_contents__;3-4;Table of Contents;ChordPro;;;; Song1;7-8;;Artist1;Composer1;;A; Song2;9-10;;Artist2;Composer2;;B; Song3;11;;Artist3;Composer3;;C; __back_matter__;12;Back Matter;ChordPro;;;; App-Music-ChordPro-6.050.7/t/110_chords_roman.t0000644000400000040000000033425014544300524016502 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Chords; my %tbl; #=for testing while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; $tbl{$c} = $info; } #=cut plan tests => 0 + keys(%tbl); ChordPro::Chords::set_parser("roman"); =for generating for my $r ( 'I', '#I', 'bII', 'II', '#II', 'bIII', 'III', 'IV', '#IV', 'bV', 'V', '#V', 'bVI', 'VI', '#VI', 'bVII', 'VII' ) { for my $q ( '', '0', '+' ) { for my $e ( '', '7', '^', 'h', 'h7', '^7' ) { my $chord = "$r$q$e"; my $res = ChordPro::Chords::parse_chord($chord); unless ( $res ) { print( "$chord\tFAIL\n"); next; } print("$chord\t", reformat($res), "\n"); $chord = lc($r)."$q$e"; $res = ChordPro::Chords::parse_chord($chord); unless ( $res ) { print( "$chord\tFAIL\n"); next; } print("$chord\t", reformat($res), "\n"); } } } exit; =cut while ( my ( $c, $info ) = each %tbl ) { my $res = ChordPro::Chords::parse_chord($c); $res //= "FAIL"; if ( UNIVERSAL::isa( $res, 'HASH' ) ) { delete($res->{parser}); $res = reformat($res); } is( $res, $info, "parsing chord $c"); } sub reformat { my ( $res ) = @_; delete($res->{parser}); $res = {%$res}; # unbless use Data::Dumper qw(); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Deparse = 1; local $Data::Dumper::Terse = 1; local $Data::Dumper::Trailingcomma = 1; local $Data::Dumper::Useperl = 1; local $Data::Dumper::Useqq = 0; # I want unicode visible my $s = Data::Dumper::Dumper($res); $s =~ s/\s+/ /gs; $s =~ s/, \}/ }/gs; $s =~ s/\s+$//; return $s; } __DATA__ I { bass => '', ext => '', ext_canon => '', name => 'I', qual => '', qual_canon => '', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i { bass => '', ext => '', ext_canon => '', name => 'i', qual => '', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I7 { bass => '', ext => 7, ext_canon => 7, name => 'I7', qual => '', qual_canon => '', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i7 { bass => '', ext => 7, ext_canon => 7, name => 'i7', qual => '', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I^ { bass => '', ext => '^', ext_canon => '^', name => 'I^', qual => '', qual_canon => '', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i^ { bass => '', ext => '^', ext_canon => '^', name => 'i^', qual => '', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } Ih { bass => '', ext => '', ext_canon => '', name => 'Ih', qual => 'h', qual_canon => 'h', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } ih { bass => '', ext => '', ext_canon => '', name => 'ih', qual => 'h', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } Ih7 { bass => '', ext => 7, ext_canon => 7, name => 'Ih7', qual => 'h', qual_canon => 'h', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } ih7 { bass => '', ext => 7, ext_canon => 7, name => 'ih7', qual => 'h', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'I^7', qual => '', qual_canon => '', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'i^7', qual => '', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I0 { bass => '', ext => '', ext_canon => '', name => 'I0', qual => 0, qual_canon => 0, root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i0 { bass => '', ext => '', ext_canon => '', name => 'i0', qual => 0, qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I07 { bass => '', ext => 7, ext_canon => 7, name => 'I07', qual => 0, qual_canon => 0, root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i07 { bass => '', ext => 7, ext_canon => 7, name => 'i07', qual => 0, qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I0^ { bass => '', ext => '^', ext_canon => '^', name => 'I0^', qual => 0, qual_canon => 0, root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i0^ { bass => '', ext => '^', ext_canon => '^', name => 'i0^', qual => 0, qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I0h { bass => '', ext => 'h', ext_canon => 'h', name => 'I0h', qual => 0, qual_canon => 0, root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i0h { bass => '', ext => 'h', ext_canon => 'h', name => 'i0h', qual => 0, qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'I0h7', qual => 0, qual_canon => 0, root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'i0h7', qual => 0, qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'I0^7', qual => 0, qual_canon => 0, root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'i0^7', qual => 0, qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I+ { bass => '', ext => '', ext_canon => '', name => 'I+', qual => '+', qual_canon => '+', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i+ { bass => '', ext => '', ext_canon => '', name => 'i+', qual => '+', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I+7 { bass => '', ext => 7, ext_canon => 7, name => 'I+7', qual => '+', qual_canon => '+', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i+7 { bass => '', ext => 7, ext_canon => 7, name => 'i+7', qual => '+', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I+^ { bass => '', ext => '^', ext_canon => '^', name => 'I+^', qual => '+', qual_canon => '+', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i+^ { bass => '', ext => '^', ext_canon => '^', name => 'i+^', qual => '+', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I+h { bass => '', ext => 'h', ext_canon => 'h', name => 'I+h', qual => '+', qual_canon => '+', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i+h { bass => '', ext => 'h', ext_canon => 'h', name => 'i+h', qual => '+', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'I+h7', qual => '+', qual_canon => '+', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'i+h7', qual => '+', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } I+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'I+^7', qual => '+', qual_canon => '+', root => 'I', root_canon => 'I', root_mod => 0, root_ord => 0, system => 'roman' } i+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'i+^7', qual => '+', qual_canon => '-', root => 'i', root_canon => 'i', root_mod => 0, root_ord => 0, system => 'roman' } #I { bass => '', ext => '', ext_canon => '', name => '#I', qual => '', qual_canon => '', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i { bass => '', ext => '', ext_canon => '', name => '#i', qual => '', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I7 { bass => '', ext => 7, ext_canon => 7, name => '#I7', qual => '', qual_canon => '', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i7 { bass => '', ext => 7, ext_canon => 7, name => '#i7', qual => '', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I^ { bass => '', ext => '^', ext_canon => '^', name => '#I^', qual => '', qual_canon => '', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i^ { bass => '', ext => '^', ext_canon => '^', name => '#i^', qual => '', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #Ih { bass => '', ext => '', ext_canon => '', name => '#Ih', qual => 'h', qual_canon => 'h', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #ih { bass => '', ext => '', ext_canon => '', name => '#ih', qual => 'h', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #Ih7 { bass => '', ext => 7, ext_canon => 7, name => '#Ih7', qual => 'h', qual_canon => 'h', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #ih7 { bass => '', ext => 7, ext_canon => 7, name => '#ih7', qual => 'h', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#I^7', qual => '', qual_canon => '', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#i^7', qual => '', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I0 { bass => '', ext => '', ext_canon => '', name => '#I0', qual => 0, qual_canon => 0, root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i0 { bass => '', ext => '', ext_canon => '', name => '#i0', qual => 0, qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I07 { bass => '', ext => 7, ext_canon => 7, name => '#I07', qual => 0, qual_canon => 0, root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i07 { bass => '', ext => 7, ext_canon => 7, name => '#i07', qual => 0, qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I0^ { bass => '', ext => '^', ext_canon => '^', name => '#I0^', qual => 0, qual_canon => 0, root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i0^ { bass => '', ext => '^', ext_canon => '^', name => '#i0^', qual => 0, qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I0h { bass => '', ext => 'h', ext_canon => 'h', name => '#I0h', qual => 0, qual_canon => 0, root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i0h { bass => '', ext => 'h', ext_canon => 'h', name => '#i0h', qual => 0, qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#I0h7', qual => 0, qual_canon => 0, root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#i0h7', qual => 0, qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#I0^7', qual => 0, qual_canon => 0, root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#i0^7', qual => 0, qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I+ { bass => '', ext => '', ext_canon => '', name => '#I+', qual => '+', qual_canon => '+', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i+ { bass => '', ext => '', ext_canon => '', name => '#i+', qual => '+', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I+7 { bass => '', ext => 7, ext_canon => 7, name => '#I+7', qual => '+', qual_canon => '+', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i+7 { bass => '', ext => 7, ext_canon => 7, name => '#i+7', qual => '+', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I+^ { bass => '', ext => '^', ext_canon => '^', name => '#I+^', qual => '+', qual_canon => '+', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i+^ { bass => '', ext => '^', ext_canon => '^', name => '#i+^', qual => '+', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I+h { bass => '', ext => 'h', ext_canon => 'h', name => '#I+h', qual => '+', qual_canon => '+', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i+h { bass => '', ext => 'h', ext_canon => 'h', name => '#i+h', qual => '+', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#I+h7', qual => '+', qual_canon => '+', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#i+h7', qual => '+', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } #I+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#I+^7', qual => '+', qual_canon => '+', root => '#I', root_canon => '#I', root_mod => 1, root_ord => 1, system => 'roman' } #i+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#i+^7', qual => '+', qual_canon => '-', root => '#i', root_canon => '#i', root_mod => 1, root_ord => 1, system => 'roman' } bII { bass => '', ext => '', ext_canon => '', name => 'bII', qual => '', qual_canon => '', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii { bass => '', ext => '', ext_canon => '', name => 'bii', qual => '', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII7 { bass => '', ext => 7, ext_canon => 7, name => 'bII7', qual => '', qual_canon => '', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii7 { bass => '', ext => 7, ext_canon => 7, name => 'bii7', qual => '', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII^ { bass => '', ext => '^', ext_canon => '^', name => 'bII^', qual => '', qual_canon => '', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii^ { bass => '', ext => '^', ext_canon => '^', name => 'bii^', qual => '', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bIIh { bass => '', ext => '', ext_canon => '', name => 'bIIh', qual => 'h', qual_canon => 'h', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } biih { bass => '', ext => '', ext_canon => '', name => 'biih', qual => 'h', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bIIh7 { bass => '', ext => 7, ext_canon => 7, name => 'bIIh7', qual => 'h', qual_canon => 'h', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } biih7 { bass => '', ext => 7, ext_canon => 7, name => 'biih7', qual => 'h', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bII^7', qual => '', qual_canon => '', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bii^7', qual => '', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII0 { bass => '', ext => '', ext_canon => '', name => 'bII0', qual => 0, qual_canon => 0, root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii0 { bass => '', ext => '', ext_canon => '', name => 'bii0', qual => 0, qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII07 { bass => '', ext => 7, ext_canon => 7, name => 'bII07', qual => 0, qual_canon => 0, root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii07 { bass => '', ext => 7, ext_canon => 7, name => 'bii07', qual => 0, qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII0^ { bass => '', ext => '^', ext_canon => '^', name => 'bII0^', qual => 0, qual_canon => 0, root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii0^ { bass => '', ext => '^', ext_canon => '^', name => 'bii0^', qual => 0, qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII0h { bass => '', ext => 'h', ext_canon => 'h', name => 'bII0h', qual => 0, qual_canon => 0, root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii0h { bass => '', ext => 'h', ext_canon => 'h', name => 'bii0h', qual => 0, qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bII0h7', qual => 0, qual_canon => 0, root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bii0h7', qual => 0, qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bII0^7', qual => 0, qual_canon => 0, root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bii0^7', qual => 0, qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII+ { bass => '', ext => '', ext_canon => '', name => 'bII+', qual => '+', qual_canon => '+', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii+ { bass => '', ext => '', ext_canon => '', name => 'bii+', qual => '+', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII+7 { bass => '', ext => 7, ext_canon => 7, name => 'bII+7', qual => '+', qual_canon => '+', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii+7 { bass => '', ext => 7, ext_canon => 7, name => 'bii+7', qual => '+', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII+^ { bass => '', ext => '^', ext_canon => '^', name => 'bII+^', qual => '+', qual_canon => '+', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii+^ { bass => '', ext => '^', ext_canon => '^', name => 'bii+^', qual => '+', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII+h { bass => '', ext => 'h', ext_canon => 'h', name => 'bII+h', qual => '+', qual_canon => '+', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii+h { bass => '', ext => 'h', ext_canon => 'h', name => 'bii+h', qual => '+', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bII+h7', qual => '+', qual_canon => '+', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bii+h7', qual => '+', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } bII+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bII+^7', qual => '+', qual_canon => '+', root => 'bII', root_canon => 'bII', root_mod => -1, root_ord => 11, system => 'roman' } bii+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bii+^7', qual => '+', qual_canon => '-', root => 'bii', root_canon => 'bii', root_mod => -1, root_ord => 11, system => 'roman' } II { bass => '', ext => '', ext_canon => '', name => 'II', qual => '', qual_canon => '', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii { bass => '', ext => '', ext_canon => '', name => 'ii', qual => '', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II7 { bass => '', ext => 7, ext_canon => 7, name => 'II7', qual => '', qual_canon => '', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii7 { bass => '', ext => 7, ext_canon => 7, name => 'ii7', qual => '', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II^ { bass => '', ext => '^', ext_canon => '^', name => 'II^', qual => '', qual_canon => '', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii^ { bass => '', ext => '^', ext_canon => '^', name => 'ii^', qual => '', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } IIh { bass => '', ext => '', ext_canon => '', name => 'IIh', qual => 'h', qual_canon => 'h', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } iih { bass => '', ext => '', ext_canon => '', name => 'iih', qual => 'h', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } IIh7 { bass => '', ext => 7, ext_canon => 7, name => 'IIh7', qual => 'h', qual_canon => 'h', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } iih7 { bass => '', ext => 7, ext_canon => 7, name => 'iih7', qual => 'h', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'II^7', qual => '', qual_canon => '', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'ii^7', qual => '', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II0 { bass => '', ext => '', ext_canon => '', name => 'II0', qual => 0, qual_canon => 0, root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii0 { bass => '', ext => '', ext_canon => '', name => 'ii0', qual => 0, qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II07 { bass => '', ext => 7, ext_canon => 7, name => 'II07', qual => 0, qual_canon => 0, root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii07 { bass => '', ext => 7, ext_canon => 7, name => 'ii07', qual => 0, qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II0^ { bass => '', ext => '^', ext_canon => '^', name => 'II0^', qual => 0, qual_canon => 0, root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii0^ { bass => '', ext => '^', ext_canon => '^', name => 'ii0^', qual => 0, qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II0h { bass => '', ext => 'h', ext_canon => 'h', name => 'II0h', qual => 0, qual_canon => 0, root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii0h { bass => '', ext => 'h', ext_canon => 'h', name => 'ii0h', qual => 0, qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'II0h7', qual => 0, qual_canon => 0, root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'ii0h7', qual => 0, qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'II0^7', qual => 0, qual_canon => 0, root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'ii0^7', qual => 0, qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II+ { bass => '', ext => '', ext_canon => '', name => 'II+', qual => '+', qual_canon => '+', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii+ { bass => '', ext => '', ext_canon => '', name => 'ii+', qual => '+', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II+7 { bass => '', ext => 7, ext_canon => 7, name => 'II+7', qual => '+', qual_canon => '+', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii+7 { bass => '', ext => 7, ext_canon => 7, name => 'ii+7', qual => '+', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II+^ { bass => '', ext => '^', ext_canon => '^', name => 'II+^', qual => '+', qual_canon => '+', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii+^ { bass => '', ext => '^', ext_canon => '^', name => 'ii+^', qual => '+', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II+h { bass => '', ext => 'h', ext_canon => 'h', name => 'II+h', qual => '+', qual_canon => '+', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii+h { bass => '', ext => 'h', ext_canon => 'h', name => 'ii+h', qual => '+', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'II+h7', qual => '+', qual_canon => '+', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'ii+h7', qual => '+', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } II+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'II+^7', qual => '+', qual_canon => '+', root => 'II', root_canon => 'II', root_mod => 0, root_ord => 2, system => 'roman' } ii+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'ii+^7', qual => '+', qual_canon => '-', root => 'ii', root_canon => 'ii', root_mod => 0, root_ord => 2, system => 'roman' } #II { bass => '', ext => '', ext_canon => '', name => '#II', qual => '', qual_canon => '', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii { bass => '', ext => '', ext_canon => '', name => '#ii', qual => '', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II7 { bass => '', ext => 7, ext_canon => 7, name => '#II7', qual => '', qual_canon => '', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii7 { bass => '', ext => 7, ext_canon => 7, name => '#ii7', qual => '', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II^ { bass => '', ext => '^', ext_canon => '^', name => '#II^', qual => '', qual_canon => '', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii^ { bass => '', ext => '^', ext_canon => '^', name => '#ii^', qual => '', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #IIh { bass => '', ext => '', ext_canon => '', name => '#IIh', qual => 'h', qual_canon => 'h', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #iih { bass => '', ext => '', ext_canon => '', name => '#iih', qual => 'h', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #IIh7 { bass => '', ext => 7, ext_canon => 7, name => '#IIh7', qual => 'h', qual_canon => 'h', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #iih7 { bass => '', ext => 7, ext_canon => 7, name => '#iih7', qual => 'h', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#II^7', qual => '', qual_canon => '', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#ii^7', qual => '', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II0 { bass => '', ext => '', ext_canon => '', name => '#II0', qual => 0, qual_canon => 0, root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii0 { bass => '', ext => '', ext_canon => '', name => '#ii0', qual => 0, qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II07 { bass => '', ext => 7, ext_canon => 7, name => '#II07', qual => 0, qual_canon => 0, root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii07 { bass => '', ext => 7, ext_canon => 7, name => '#ii07', qual => 0, qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II0^ { bass => '', ext => '^', ext_canon => '^', name => '#II0^', qual => 0, qual_canon => 0, root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii0^ { bass => '', ext => '^', ext_canon => '^', name => '#ii0^', qual => 0, qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II0h { bass => '', ext => 'h', ext_canon => 'h', name => '#II0h', qual => 0, qual_canon => 0, root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii0h { bass => '', ext => 'h', ext_canon => 'h', name => '#ii0h', qual => 0, qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#II0h7', qual => 0, qual_canon => 0, root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#ii0h7', qual => 0, qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#II0^7', qual => 0, qual_canon => 0, root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#ii0^7', qual => 0, qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II+ { bass => '', ext => '', ext_canon => '', name => '#II+', qual => '+', qual_canon => '+', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii+ { bass => '', ext => '', ext_canon => '', name => '#ii+', qual => '+', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II+7 { bass => '', ext => 7, ext_canon => 7, name => '#II+7', qual => '+', qual_canon => '+', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii+7 { bass => '', ext => 7, ext_canon => 7, name => '#ii+7', qual => '+', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II+^ { bass => '', ext => '^', ext_canon => '^', name => '#II+^', qual => '+', qual_canon => '+', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii+^ { bass => '', ext => '^', ext_canon => '^', name => '#ii+^', qual => '+', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II+h { bass => '', ext => 'h', ext_canon => 'h', name => '#II+h', qual => '+', qual_canon => '+', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii+h { bass => '', ext => 'h', ext_canon => 'h', name => '#ii+h', qual => '+', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#II+h7', qual => '+', qual_canon => '+', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#ii+h7', qual => '+', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } #II+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#II+^7', qual => '+', qual_canon => '+', root => '#II', root_canon => '#II', root_mod => 1, root_ord => 1, system => 'roman' } #ii+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#ii+^7', qual => '+', qual_canon => '-', root => '#ii', root_canon => '#ii', root_mod => 1, root_ord => 1, system => 'roman' } bIII { bass => '', ext => '', ext_canon => '', name => 'bIII', qual => '', qual_canon => '', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii { bass => '', ext => '', ext_canon => '', name => 'biii', qual => '', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII7 { bass => '', ext => 7, ext_canon => 7, name => 'bIII7', qual => '', qual_canon => '', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii7 { bass => '', ext => 7, ext_canon => 7, name => 'biii7', qual => '', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII^ { bass => '', ext => '^', ext_canon => '^', name => 'bIII^', qual => '', qual_canon => '', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii^ { bass => '', ext => '^', ext_canon => '^', name => 'biii^', qual => '', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIIIh { bass => '', ext => '', ext_canon => '', name => 'bIIIh', qual => 'h', qual_canon => 'h', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biiih { bass => '', ext => '', ext_canon => '', name => 'biiih', qual => 'h', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIIIh7 { bass => '', ext => 7, ext_canon => 7, name => 'bIIIh7', qual => 'h', qual_canon => 'h', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biiih7 { bass => '', ext => 7, ext_canon => 7, name => 'biiih7', qual => 'h', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bIII^7', qual => '', qual_canon => '', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'biii^7', qual => '', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII0 { bass => '', ext => '', ext_canon => '', name => 'bIII0', qual => 0, qual_canon => 0, root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii0 { bass => '', ext => '', ext_canon => '', name => 'biii0', qual => 0, qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII07 { bass => '', ext => 7, ext_canon => 7, name => 'bIII07', qual => 0, qual_canon => 0, root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii07 { bass => '', ext => 7, ext_canon => 7, name => 'biii07', qual => 0, qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII0^ { bass => '', ext => '^', ext_canon => '^', name => 'bIII0^', qual => 0, qual_canon => 0, root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii0^ { bass => '', ext => '^', ext_canon => '^', name => 'biii0^', qual => 0, qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII0h { bass => '', ext => 'h', ext_canon => 'h', name => 'bIII0h', qual => 0, qual_canon => 0, root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii0h { bass => '', ext => 'h', ext_canon => 'h', name => 'biii0h', qual => 0, qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bIII0h7', qual => 0, qual_canon => 0, root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'biii0h7', qual => 0, qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bIII0^7', qual => 0, qual_canon => 0, root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'biii0^7', qual => 0, qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII+ { bass => '', ext => '', ext_canon => '', name => 'bIII+', qual => '+', qual_canon => '+', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii+ { bass => '', ext => '', ext_canon => '', name => 'biii+', qual => '+', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII+7 { bass => '', ext => 7, ext_canon => 7, name => 'bIII+7', qual => '+', qual_canon => '+', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii+7 { bass => '', ext => 7, ext_canon => 7, name => 'biii+7', qual => '+', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII+^ { bass => '', ext => '^', ext_canon => '^', name => 'bIII+^', qual => '+', qual_canon => '+', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii+^ { bass => '', ext => '^', ext_canon => '^', name => 'biii+^', qual => '+', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII+h { bass => '', ext => 'h', ext_canon => 'h', name => 'bIII+h', qual => '+', qual_canon => '+', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii+h { bass => '', ext => 'h', ext_canon => 'h', name => 'biii+h', qual => '+', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bIII+h7', qual => '+', qual_canon => '+', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'biii+h7', qual => '+', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } bIII+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bIII+^7', qual => '+', qual_canon => '+', root => 'bIII', root_canon => 'bIII', root_mod => -1, root_ord => 11, system => 'roman' } biii+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'biii+^7', qual => '+', qual_canon => '-', root => 'biii', root_canon => 'biii', root_mod => -1, root_ord => 11, system => 'roman' } III { bass => '', ext => '', ext_canon => '', name => 'III', qual => '', qual_canon => '', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii { bass => '', ext => '', ext_canon => '', name => 'iii', qual => '', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III7 { bass => '', ext => 7, ext_canon => 7, name => 'III7', qual => '', qual_canon => '', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii7 { bass => '', ext => 7, ext_canon => 7, name => 'iii7', qual => '', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III^ { bass => '', ext => '^', ext_canon => '^', name => 'III^', qual => '', qual_canon => '', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii^ { bass => '', ext => '^', ext_canon => '^', name => 'iii^', qual => '', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } IIIh { bass => '', ext => '', ext_canon => '', name => 'IIIh', qual => 'h', qual_canon => 'h', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iiih { bass => '', ext => '', ext_canon => '', name => 'iiih', qual => 'h', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } IIIh7 { bass => '', ext => 7, ext_canon => 7, name => 'IIIh7', qual => 'h', qual_canon => 'h', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iiih7 { bass => '', ext => 7, ext_canon => 7, name => 'iiih7', qual => 'h', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'III^7', qual => '', qual_canon => '', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'iii^7', qual => '', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III0 { bass => '', ext => '', ext_canon => '', name => 'III0', qual => 0, qual_canon => 0, root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii0 { bass => '', ext => '', ext_canon => '', name => 'iii0', qual => 0, qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III07 { bass => '', ext => 7, ext_canon => 7, name => 'III07', qual => 0, qual_canon => 0, root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii07 { bass => '', ext => 7, ext_canon => 7, name => 'iii07', qual => 0, qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III0^ { bass => '', ext => '^', ext_canon => '^', name => 'III0^', qual => 0, qual_canon => 0, root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii0^ { bass => '', ext => '^', ext_canon => '^', name => 'iii0^', qual => 0, qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III0h { bass => '', ext => 'h', ext_canon => 'h', name => 'III0h', qual => 0, qual_canon => 0, root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii0h { bass => '', ext => 'h', ext_canon => 'h', name => 'iii0h', qual => 0, qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'III0h7', qual => 0, qual_canon => 0, root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'iii0h7', qual => 0, qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'III0^7', qual => 0, qual_canon => 0, root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'iii0^7', qual => 0, qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III+ { bass => '', ext => '', ext_canon => '', name => 'III+', qual => '+', qual_canon => '+', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii+ { bass => '', ext => '', ext_canon => '', name => 'iii+', qual => '+', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III+7 { bass => '', ext => 7, ext_canon => 7, name => 'III+7', qual => '+', qual_canon => '+', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii+7 { bass => '', ext => 7, ext_canon => 7, name => 'iii+7', qual => '+', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III+^ { bass => '', ext => '^', ext_canon => '^', name => 'III+^', qual => '+', qual_canon => '+', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii+^ { bass => '', ext => '^', ext_canon => '^', name => 'iii+^', qual => '+', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III+h { bass => '', ext => 'h', ext_canon => 'h', name => 'III+h', qual => '+', qual_canon => '+', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii+h { bass => '', ext => 'h', ext_canon => 'h', name => 'iii+h', qual => '+', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'III+h7', qual => '+', qual_canon => '+', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'iii+h7', qual => '+', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } III+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'III+^7', qual => '+', qual_canon => '+', root => 'III', root_canon => 'III', root_mod => 0, root_ord => 4, system => 'roman' } iii+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'iii+^7', qual => '+', qual_canon => '-', root => 'iii', root_canon => 'iii', root_mod => 0, root_ord => 4, system => 'roman' } IV { bass => '', ext => '', ext_canon => '', name => 'IV', qual => '', qual_canon => '', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv { bass => '', ext => '', ext_canon => '', name => 'iv', qual => '', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV7 { bass => '', ext => 7, ext_canon => 7, name => 'IV7', qual => '', qual_canon => '', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv7 { bass => '', ext => 7, ext_canon => 7, name => 'iv7', qual => '', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV^ { bass => '', ext => '^', ext_canon => '^', name => 'IV^', qual => '', qual_canon => '', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv^ { bass => '', ext => '^', ext_canon => '^', name => 'iv^', qual => '', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IVh { bass => '', ext => '', ext_canon => '', name => 'IVh', qual => 'h', qual_canon => 'h', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } ivh { bass => '', ext => '', ext_canon => '', name => 'ivh', qual => 'h', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IVh7 { bass => '', ext => 7, ext_canon => 7, name => 'IVh7', qual => 'h', qual_canon => 'h', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } ivh7 { bass => '', ext => 7, ext_canon => 7, name => 'ivh7', qual => 'h', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'IV^7', qual => '', qual_canon => '', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'iv^7', qual => '', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV0 { bass => '', ext => '', ext_canon => '', name => 'IV0', qual => 0, qual_canon => 0, root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv0 { bass => '', ext => '', ext_canon => '', name => 'iv0', qual => 0, qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV07 { bass => '', ext => 7, ext_canon => 7, name => 'IV07', qual => 0, qual_canon => 0, root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv07 { bass => '', ext => 7, ext_canon => 7, name => 'iv07', qual => 0, qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV0^ { bass => '', ext => '^', ext_canon => '^', name => 'IV0^', qual => 0, qual_canon => 0, root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv0^ { bass => '', ext => '^', ext_canon => '^', name => 'iv0^', qual => 0, qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV0h { bass => '', ext => 'h', ext_canon => 'h', name => 'IV0h', qual => 0, qual_canon => 0, root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv0h { bass => '', ext => 'h', ext_canon => 'h', name => 'iv0h', qual => 0, qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'IV0h7', qual => 0, qual_canon => 0, root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'iv0h7', qual => 0, qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'IV0^7', qual => 0, qual_canon => 0, root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'iv0^7', qual => 0, qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV+ { bass => '', ext => '', ext_canon => '', name => 'IV+', qual => '+', qual_canon => '+', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv+ { bass => '', ext => '', ext_canon => '', name => 'iv+', qual => '+', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV+7 { bass => '', ext => 7, ext_canon => 7, name => 'IV+7', qual => '+', qual_canon => '+', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv+7 { bass => '', ext => 7, ext_canon => 7, name => 'iv+7', qual => '+', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV+^ { bass => '', ext => '^', ext_canon => '^', name => 'IV+^', qual => '+', qual_canon => '+', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv+^ { bass => '', ext => '^', ext_canon => '^', name => 'iv+^', qual => '+', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV+h { bass => '', ext => 'h', ext_canon => 'h', name => 'IV+h', qual => '+', qual_canon => '+', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv+h { bass => '', ext => 'h', ext_canon => 'h', name => 'iv+h', qual => '+', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'IV+h7', qual => '+', qual_canon => '+', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'iv+h7', qual => '+', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } IV+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'IV+^7', qual => '+', qual_canon => '+', root => 'IV', root_canon => 'IV', root_mod => 0, root_ord => 5, system => 'roman' } iv+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'iv+^7', qual => '+', qual_canon => '-', root => 'iv', root_canon => 'iv', root_mod => 0, root_ord => 5, system => 'roman' } #IV { bass => '', ext => '', ext_canon => '', name => '#IV', qual => '', qual_canon => '', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv { bass => '', ext => '', ext_canon => '', name => '#iv', qual => '', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV7 { bass => '', ext => 7, ext_canon => 7, name => '#IV7', qual => '', qual_canon => '', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv7 { bass => '', ext => 7, ext_canon => 7, name => '#iv7', qual => '', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV^ { bass => '', ext => '^', ext_canon => '^', name => '#IV^', qual => '', qual_canon => '', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv^ { bass => '', ext => '^', ext_canon => '^', name => '#iv^', qual => '', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IVh { bass => '', ext => '', ext_canon => '', name => '#IVh', qual => 'h', qual_canon => 'h', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #ivh { bass => '', ext => '', ext_canon => '', name => '#ivh', qual => 'h', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IVh7 { bass => '', ext => 7, ext_canon => 7, name => '#IVh7', qual => 'h', qual_canon => 'h', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #ivh7 { bass => '', ext => 7, ext_canon => 7, name => '#ivh7', qual => 'h', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#IV^7', qual => '', qual_canon => '', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#iv^7', qual => '', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV0 { bass => '', ext => '', ext_canon => '', name => '#IV0', qual => 0, qual_canon => 0, root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv0 { bass => '', ext => '', ext_canon => '', name => '#iv0', qual => 0, qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV07 { bass => '', ext => 7, ext_canon => 7, name => '#IV07', qual => 0, qual_canon => 0, root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv07 { bass => '', ext => 7, ext_canon => 7, name => '#iv07', qual => 0, qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV0^ { bass => '', ext => '^', ext_canon => '^', name => '#IV0^', qual => 0, qual_canon => 0, root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv0^ { bass => '', ext => '^', ext_canon => '^', name => '#iv0^', qual => 0, qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV0h { bass => '', ext => 'h', ext_canon => 'h', name => '#IV0h', qual => 0, qual_canon => 0, root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv0h { bass => '', ext => 'h', ext_canon => 'h', name => '#iv0h', qual => 0, qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#IV0h7', qual => 0, qual_canon => 0, root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#iv0h7', qual => 0, qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#IV0^7', qual => 0, qual_canon => 0, root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#iv0^7', qual => 0, qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV+ { bass => '', ext => '', ext_canon => '', name => '#IV+', qual => '+', qual_canon => '+', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv+ { bass => '', ext => '', ext_canon => '', name => '#iv+', qual => '+', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV+7 { bass => '', ext => 7, ext_canon => 7, name => '#IV+7', qual => '+', qual_canon => '+', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv+7 { bass => '', ext => 7, ext_canon => 7, name => '#iv+7', qual => '+', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV+^ { bass => '', ext => '^', ext_canon => '^', name => '#IV+^', qual => '+', qual_canon => '+', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv+^ { bass => '', ext => '^', ext_canon => '^', name => '#iv+^', qual => '+', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV+h { bass => '', ext => 'h', ext_canon => 'h', name => '#IV+h', qual => '+', qual_canon => '+', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv+h { bass => '', ext => 'h', ext_canon => 'h', name => '#iv+h', qual => '+', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#IV+h7', qual => '+', qual_canon => '+', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#iv+h7', qual => '+', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } #IV+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#IV+^7', qual => '+', qual_canon => '+', root => '#IV', root_canon => '#IV', root_mod => 1, root_ord => 1, system => 'roman' } #iv+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#iv+^7', qual => '+', qual_canon => '-', root => '#iv', root_canon => '#iv', root_mod => 1, root_ord => 1, system => 'roman' } bV { bass => '', ext => '', ext_canon => '', name => 'bV', qual => '', qual_canon => '', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv { bass => '', ext => '', ext_canon => '', name => 'bv', qual => '', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV7 { bass => '', ext => 7, ext_canon => 7, name => 'bV7', qual => '', qual_canon => '', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv7 { bass => '', ext => 7, ext_canon => 7, name => 'bv7', qual => '', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV^ { bass => '', ext => '^', ext_canon => '^', name => 'bV^', qual => '', qual_canon => '', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv^ { bass => '', ext => '^', ext_canon => '^', name => 'bv^', qual => '', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bVh { bass => '', ext => '', ext_canon => '', name => 'bVh', qual => 'h', qual_canon => 'h', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bvh { bass => '', ext => '', ext_canon => '', name => 'bvh', qual => 'h', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bVh7 { bass => '', ext => 7, ext_canon => 7, name => 'bVh7', qual => 'h', qual_canon => 'h', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bvh7 { bass => '', ext => 7, ext_canon => 7, name => 'bvh7', qual => 'h', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bV^7', qual => '', qual_canon => '', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bv^7', qual => '', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV0 { bass => '', ext => '', ext_canon => '', name => 'bV0', qual => 0, qual_canon => 0, root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv0 { bass => '', ext => '', ext_canon => '', name => 'bv0', qual => 0, qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV07 { bass => '', ext => 7, ext_canon => 7, name => 'bV07', qual => 0, qual_canon => 0, root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv07 { bass => '', ext => 7, ext_canon => 7, name => 'bv07', qual => 0, qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV0^ { bass => '', ext => '^', ext_canon => '^', name => 'bV0^', qual => 0, qual_canon => 0, root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv0^ { bass => '', ext => '^', ext_canon => '^', name => 'bv0^', qual => 0, qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV0h { bass => '', ext => 'h', ext_canon => 'h', name => 'bV0h', qual => 0, qual_canon => 0, root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv0h { bass => '', ext => 'h', ext_canon => 'h', name => 'bv0h', qual => 0, qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bV0h7', qual => 0, qual_canon => 0, root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bv0h7', qual => 0, qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bV0^7', qual => 0, qual_canon => 0, root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bv0^7', qual => 0, qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV+ { bass => '', ext => '', ext_canon => '', name => 'bV+', qual => '+', qual_canon => '+', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv+ { bass => '', ext => '', ext_canon => '', name => 'bv+', qual => '+', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV+7 { bass => '', ext => 7, ext_canon => 7, name => 'bV+7', qual => '+', qual_canon => '+', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv+7 { bass => '', ext => 7, ext_canon => 7, name => 'bv+7', qual => '+', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV+^ { bass => '', ext => '^', ext_canon => '^', name => 'bV+^', qual => '+', qual_canon => '+', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv+^ { bass => '', ext => '^', ext_canon => '^', name => 'bv+^', qual => '+', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV+h { bass => '', ext => 'h', ext_canon => 'h', name => 'bV+h', qual => '+', qual_canon => '+', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv+h { bass => '', ext => 'h', ext_canon => 'h', name => 'bv+h', qual => '+', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bV+h7', qual => '+', qual_canon => '+', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bv+h7', qual => '+', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } bV+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bV+^7', qual => '+', qual_canon => '+', root => 'bV', root_canon => 'bV', root_mod => -1, root_ord => 11, system => 'roman' } bv+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bv+^7', qual => '+', qual_canon => '-', root => 'bv', root_canon => 'bv', root_mod => -1, root_ord => 11, system => 'roman' } V { bass => '', ext => '', ext_canon => '', name => 'V', qual => '', qual_canon => '', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v { bass => '', ext => '', ext_canon => '', name => 'v', qual => '', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V7 { bass => '', ext => 7, ext_canon => 7, name => 'V7', qual => '', qual_canon => '', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v7 { bass => '', ext => 7, ext_canon => 7, name => 'v7', qual => '', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V^ { bass => '', ext => '^', ext_canon => '^', name => 'V^', qual => '', qual_canon => '', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v^ { bass => '', ext => '^', ext_canon => '^', name => 'v^', qual => '', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } Vh { bass => '', ext => '', ext_canon => '', name => 'Vh', qual => 'h', qual_canon => 'h', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } vh { bass => '', ext => '', ext_canon => '', name => 'vh', qual => 'h', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } Vh7 { bass => '', ext => 7, ext_canon => 7, name => 'Vh7', qual => 'h', qual_canon => 'h', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } vh7 { bass => '', ext => 7, ext_canon => 7, name => 'vh7', qual => 'h', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'V^7', qual => '', qual_canon => '', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'v^7', qual => '', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V0 { bass => '', ext => '', ext_canon => '', name => 'V0', qual => 0, qual_canon => 0, root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v0 { bass => '', ext => '', ext_canon => '', name => 'v0', qual => 0, qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V07 { bass => '', ext => 7, ext_canon => 7, name => 'V07', qual => 0, qual_canon => 0, root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v07 { bass => '', ext => 7, ext_canon => 7, name => 'v07', qual => 0, qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V0^ { bass => '', ext => '^', ext_canon => '^', name => 'V0^', qual => 0, qual_canon => 0, root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v0^ { bass => '', ext => '^', ext_canon => '^', name => 'v0^', qual => 0, qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V0h { bass => '', ext => 'h', ext_canon => 'h', name => 'V0h', qual => 0, qual_canon => 0, root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v0h { bass => '', ext => 'h', ext_canon => 'h', name => 'v0h', qual => 0, qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'V0h7', qual => 0, qual_canon => 0, root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'v0h7', qual => 0, qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'V0^7', qual => 0, qual_canon => 0, root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'v0^7', qual => 0, qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V+ { bass => '', ext => '', ext_canon => '', name => 'V+', qual => '+', qual_canon => '+', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v+ { bass => '', ext => '', ext_canon => '', name => 'v+', qual => '+', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V+7 { bass => '', ext => 7, ext_canon => 7, name => 'V+7', qual => '+', qual_canon => '+', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v+7 { bass => '', ext => 7, ext_canon => 7, name => 'v+7', qual => '+', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V+^ { bass => '', ext => '^', ext_canon => '^', name => 'V+^', qual => '+', qual_canon => '+', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v+^ { bass => '', ext => '^', ext_canon => '^', name => 'v+^', qual => '+', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V+h { bass => '', ext => 'h', ext_canon => 'h', name => 'V+h', qual => '+', qual_canon => '+', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v+h { bass => '', ext => 'h', ext_canon => 'h', name => 'v+h', qual => '+', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'V+h7', qual => '+', qual_canon => '+', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'v+h7', qual => '+', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } V+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'V+^7', qual => '+', qual_canon => '+', root => 'V', root_canon => 'V', root_mod => 0, root_ord => 7, system => 'roman' } v+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'v+^7', qual => '+', qual_canon => '-', root => 'v', root_canon => 'v', root_mod => 0, root_ord => 7, system => 'roman' } #V { bass => '', ext => '', ext_canon => '', name => '#V', qual => '', qual_canon => '', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v { bass => '', ext => '', ext_canon => '', name => '#v', qual => '', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V7 { bass => '', ext => 7, ext_canon => 7, name => '#V7', qual => '', qual_canon => '', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v7 { bass => '', ext => 7, ext_canon => 7, name => '#v7', qual => '', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V^ { bass => '', ext => '^', ext_canon => '^', name => '#V^', qual => '', qual_canon => '', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v^ { bass => '', ext => '^', ext_canon => '^', name => '#v^', qual => '', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #Vh { bass => '', ext => '', ext_canon => '', name => '#Vh', qual => 'h', qual_canon => 'h', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #vh { bass => '', ext => '', ext_canon => '', name => '#vh', qual => 'h', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #Vh7 { bass => '', ext => 7, ext_canon => 7, name => '#Vh7', qual => 'h', qual_canon => 'h', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #vh7 { bass => '', ext => 7, ext_canon => 7, name => '#vh7', qual => 'h', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#V^7', qual => '', qual_canon => '', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#v^7', qual => '', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V0 { bass => '', ext => '', ext_canon => '', name => '#V0', qual => 0, qual_canon => 0, root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v0 { bass => '', ext => '', ext_canon => '', name => '#v0', qual => 0, qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V07 { bass => '', ext => 7, ext_canon => 7, name => '#V07', qual => 0, qual_canon => 0, root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v07 { bass => '', ext => 7, ext_canon => 7, name => '#v07', qual => 0, qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V0^ { bass => '', ext => '^', ext_canon => '^', name => '#V0^', qual => 0, qual_canon => 0, root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v0^ { bass => '', ext => '^', ext_canon => '^', name => '#v0^', qual => 0, qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V0h { bass => '', ext => 'h', ext_canon => 'h', name => '#V0h', qual => 0, qual_canon => 0, root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v0h { bass => '', ext => 'h', ext_canon => 'h', name => '#v0h', qual => 0, qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#V0h7', qual => 0, qual_canon => 0, root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#v0h7', qual => 0, qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#V0^7', qual => 0, qual_canon => 0, root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#v0^7', qual => 0, qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V+ { bass => '', ext => '', ext_canon => '', name => '#V+', qual => '+', qual_canon => '+', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v+ { bass => '', ext => '', ext_canon => '', name => '#v+', qual => '+', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V+7 { bass => '', ext => 7, ext_canon => 7, name => '#V+7', qual => '+', qual_canon => '+', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v+7 { bass => '', ext => 7, ext_canon => 7, name => '#v+7', qual => '+', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V+^ { bass => '', ext => '^', ext_canon => '^', name => '#V+^', qual => '+', qual_canon => '+', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v+^ { bass => '', ext => '^', ext_canon => '^', name => '#v+^', qual => '+', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V+h { bass => '', ext => 'h', ext_canon => 'h', name => '#V+h', qual => '+', qual_canon => '+', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v+h { bass => '', ext => 'h', ext_canon => 'h', name => '#v+h', qual => '+', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#V+h7', qual => '+', qual_canon => '+', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#v+h7', qual => '+', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } #V+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#V+^7', qual => '+', qual_canon => '+', root => '#V', root_canon => '#V', root_mod => 1, root_ord => 1, system => 'roman' } #v+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#v+^7', qual => '+', qual_canon => '-', root => '#v', root_canon => '#v', root_mod => 1, root_ord => 1, system => 'roman' } bVI { bass => '', ext => '', ext_canon => '', name => 'bVI', qual => '', qual_canon => '', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi { bass => '', ext => '', ext_canon => '', name => 'bvi', qual => '', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI7 { bass => '', ext => 7, ext_canon => 7, name => 'bVI7', qual => '', qual_canon => '', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi7 { bass => '', ext => 7, ext_canon => 7, name => 'bvi7', qual => '', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI^ { bass => '', ext => '^', ext_canon => '^', name => 'bVI^', qual => '', qual_canon => '', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi^ { bass => '', ext => '^', ext_canon => '^', name => 'bvi^', qual => '', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVIh { bass => '', ext => '', ext_canon => '', name => 'bVIh', qual => 'h', qual_canon => 'h', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvih { bass => '', ext => '', ext_canon => '', name => 'bvih', qual => 'h', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVIh7 { bass => '', ext => 7, ext_canon => 7, name => 'bVIh7', qual => 'h', qual_canon => 'h', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvih7 { bass => '', ext => 7, ext_canon => 7, name => 'bvih7', qual => 'h', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bVI^7', qual => '', qual_canon => '', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bvi^7', qual => '', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI0 { bass => '', ext => '', ext_canon => '', name => 'bVI0', qual => 0, qual_canon => 0, root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi0 { bass => '', ext => '', ext_canon => '', name => 'bvi0', qual => 0, qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI07 { bass => '', ext => 7, ext_canon => 7, name => 'bVI07', qual => 0, qual_canon => 0, root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi07 { bass => '', ext => 7, ext_canon => 7, name => 'bvi07', qual => 0, qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI0^ { bass => '', ext => '^', ext_canon => '^', name => 'bVI0^', qual => 0, qual_canon => 0, root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi0^ { bass => '', ext => '^', ext_canon => '^', name => 'bvi0^', qual => 0, qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI0h { bass => '', ext => 'h', ext_canon => 'h', name => 'bVI0h', qual => 0, qual_canon => 0, root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi0h { bass => '', ext => 'h', ext_canon => 'h', name => 'bvi0h', qual => 0, qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bVI0h7', qual => 0, qual_canon => 0, root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bvi0h7', qual => 0, qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bVI0^7', qual => 0, qual_canon => 0, root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bvi0^7', qual => 0, qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI+ { bass => '', ext => '', ext_canon => '', name => 'bVI+', qual => '+', qual_canon => '+', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi+ { bass => '', ext => '', ext_canon => '', name => 'bvi+', qual => '+', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI+7 { bass => '', ext => 7, ext_canon => 7, name => 'bVI+7', qual => '+', qual_canon => '+', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi+7 { bass => '', ext => 7, ext_canon => 7, name => 'bvi+7', qual => '+', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI+^ { bass => '', ext => '^', ext_canon => '^', name => 'bVI+^', qual => '+', qual_canon => '+', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi+^ { bass => '', ext => '^', ext_canon => '^', name => 'bvi+^', qual => '+', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI+h { bass => '', ext => 'h', ext_canon => 'h', name => 'bVI+h', qual => '+', qual_canon => '+', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi+h { bass => '', ext => 'h', ext_canon => 'h', name => 'bvi+h', qual => '+', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bVI+h7', qual => '+', qual_canon => '+', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bvi+h7', qual => '+', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } bVI+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bVI+^7', qual => '+', qual_canon => '+', root => 'bVI', root_canon => 'bVI', root_mod => -1, root_ord => 11, system => 'roman' } bvi+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bvi+^7', qual => '+', qual_canon => '-', root => 'bvi', root_canon => 'bvi', root_mod => -1, root_ord => 11, system => 'roman' } VI { bass => '', ext => '', ext_canon => '', name => 'VI', qual => '', qual_canon => '', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi { bass => '', ext => '', ext_canon => '', name => 'vi', qual => '', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI7 { bass => '', ext => 7, ext_canon => 7, name => 'VI7', qual => '', qual_canon => '', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi7 { bass => '', ext => 7, ext_canon => 7, name => 'vi7', qual => '', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI^ { bass => '', ext => '^', ext_canon => '^', name => 'VI^', qual => '', qual_canon => '', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi^ { bass => '', ext => '^', ext_canon => '^', name => 'vi^', qual => '', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VIh { bass => '', ext => '', ext_canon => '', name => 'VIh', qual => 'h', qual_canon => 'h', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vih { bass => '', ext => '', ext_canon => '', name => 'vih', qual => 'h', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VIh7 { bass => '', ext => 7, ext_canon => 7, name => 'VIh7', qual => 'h', qual_canon => 'h', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vih7 { bass => '', ext => 7, ext_canon => 7, name => 'vih7', qual => 'h', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'VI^7', qual => '', qual_canon => '', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'vi^7', qual => '', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI0 { bass => '', ext => '', ext_canon => '', name => 'VI0', qual => 0, qual_canon => 0, root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi0 { bass => '', ext => '', ext_canon => '', name => 'vi0', qual => 0, qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI07 { bass => '', ext => 7, ext_canon => 7, name => 'VI07', qual => 0, qual_canon => 0, root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi07 { bass => '', ext => 7, ext_canon => 7, name => 'vi07', qual => 0, qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI0^ { bass => '', ext => '^', ext_canon => '^', name => 'VI0^', qual => 0, qual_canon => 0, root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi0^ { bass => '', ext => '^', ext_canon => '^', name => 'vi0^', qual => 0, qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI0h { bass => '', ext => 'h', ext_canon => 'h', name => 'VI0h', qual => 0, qual_canon => 0, root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi0h { bass => '', ext => 'h', ext_canon => 'h', name => 'vi0h', qual => 0, qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'VI0h7', qual => 0, qual_canon => 0, root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'vi0h7', qual => 0, qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'VI0^7', qual => 0, qual_canon => 0, root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'vi0^7', qual => 0, qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI+ { bass => '', ext => '', ext_canon => '', name => 'VI+', qual => '+', qual_canon => '+', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi+ { bass => '', ext => '', ext_canon => '', name => 'vi+', qual => '+', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI+7 { bass => '', ext => 7, ext_canon => 7, name => 'VI+7', qual => '+', qual_canon => '+', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi+7 { bass => '', ext => 7, ext_canon => 7, name => 'vi+7', qual => '+', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI+^ { bass => '', ext => '^', ext_canon => '^', name => 'VI+^', qual => '+', qual_canon => '+', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi+^ { bass => '', ext => '^', ext_canon => '^', name => 'vi+^', qual => '+', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI+h { bass => '', ext => 'h', ext_canon => 'h', name => 'VI+h', qual => '+', qual_canon => '+', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi+h { bass => '', ext => 'h', ext_canon => 'h', name => 'vi+h', qual => '+', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'VI+h7', qual => '+', qual_canon => '+', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'vi+h7', qual => '+', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } VI+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'VI+^7', qual => '+', qual_canon => '+', root => 'VI', root_canon => 'VI', root_mod => 0, root_ord => 9, system => 'roman' } vi+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'vi+^7', qual => '+', qual_canon => '-', root => 'vi', root_canon => 'vi', root_mod => 0, root_ord => 9, system => 'roman' } #VI { bass => '', ext => '', ext_canon => '', name => '#VI', qual => '', qual_canon => '', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi { bass => '', ext => '', ext_canon => '', name => '#vi', qual => '', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI7 { bass => '', ext => 7, ext_canon => 7, name => '#VI7', qual => '', qual_canon => '', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi7 { bass => '', ext => 7, ext_canon => 7, name => '#vi7', qual => '', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI^ { bass => '', ext => '^', ext_canon => '^', name => '#VI^', qual => '', qual_canon => '', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi^ { bass => '', ext => '^', ext_canon => '^', name => '#vi^', qual => '', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VIh { bass => '', ext => '', ext_canon => '', name => '#VIh', qual => 'h', qual_canon => 'h', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vih { bass => '', ext => '', ext_canon => '', name => '#vih', qual => 'h', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VIh7 { bass => '', ext => 7, ext_canon => 7, name => '#VIh7', qual => 'h', qual_canon => 'h', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vih7 { bass => '', ext => 7, ext_canon => 7, name => '#vih7', qual => 'h', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#VI^7', qual => '', qual_canon => '', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#vi^7', qual => '', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI0 { bass => '', ext => '', ext_canon => '', name => '#VI0', qual => 0, qual_canon => 0, root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi0 { bass => '', ext => '', ext_canon => '', name => '#vi0', qual => 0, qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI07 { bass => '', ext => 7, ext_canon => 7, name => '#VI07', qual => 0, qual_canon => 0, root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi07 { bass => '', ext => 7, ext_canon => 7, name => '#vi07', qual => 0, qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI0^ { bass => '', ext => '^', ext_canon => '^', name => '#VI0^', qual => 0, qual_canon => 0, root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi0^ { bass => '', ext => '^', ext_canon => '^', name => '#vi0^', qual => 0, qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI0h { bass => '', ext => 'h', ext_canon => 'h', name => '#VI0h', qual => 0, qual_canon => 0, root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi0h { bass => '', ext => 'h', ext_canon => 'h', name => '#vi0h', qual => 0, qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#VI0h7', qual => 0, qual_canon => 0, root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#vi0h7', qual => 0, qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#VI0^7', qual => 0, qual_canon => 0, root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi0^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#vi0^7', qual => 0, qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI+ { bass => '', ext => '', ext_canon => '', name => '#VI+', qual => '+', qual_canon => '+', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi+ { bass => '', ext => '', ext_canon => '', name => '#vi+', qual => '+', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI+7 { bass => '', ext => 7, ext_canon => 7, name => '#VI+7', qual => '+', qual_canon => '+', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi+7 { bass => '', ext => 7, ext_canon => 7, name => '#vi+7', qual => '+', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI+^ { bass => '', ext => '^', ext_canon => '^', name => '#VI+^', qual => '+', qual_canon => '+', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi+^ { bass => '', ext => '^', ext_canon => '^', name => '#vi+^', qual => '+', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI+h { bass => '', ext => 'h', ext_canon => 'h', name => '#VI+h', qual => '+', qual_canon => '+', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi+h { bass => '', ext => 'h', ext_canon => 'h', name => '#vi+h', qual => '+', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#VI+h7', qual => '+', qual_canon => '+', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#vi+h7', qual => '+', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } #VI+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#VI+^7', qual => '+', qual_canon => '+', root => '#VI', root_canon => '#VI', root_mod => 1, root_ord => 1, system => 'roman' } #vi+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#vi+^7', qual => '+', qual_canon => '-', root => '#vi', root_canon => '#vi', root_mod => 1, root_ord => 1, system => 'roman' } bVII { bass => '', ext => '', ext_canon => '', name => 'bVII', qual => '', qual_canon => '', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii { bass => '', ext => '', ext_canon => '', name => 'bvii', qual => '', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII7 { bass => '', ext => 7, ext_canon => 7, name => 'bVII7', qual => '', qual_canon => '', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii7 { bass => '', ext => 7, ext_canon => 7, name => 'bvii7', qual => '', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII^ { bass => '', ext => '^', ext_canon => '^', name => 'bVII^', qual => '', qual_canon => '', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii^ { bass => '', ext => '^', ext_canon => '^', name => 'bvii^', qual => '', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVIIh { bass => '', ext => '', ext_canon => '', name => 'bVIIh', qual => 'h', qual_canon => 'h', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bviih { bass => '', ext => '', ext_canon => '', name => 'bviih', qual => 'h', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVIIh7 { bass => '', ext => 7, ext_canon => 7, name => 'bVIIh7', qual => 'h', qual_canon => 'h', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bviih7 { bass => '', ext => 7, ext_canon => 7, name => 'bviih7', qual => 'h', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bVII^7', qual => '', qual_canon => '', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bvii^7', qual => '', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII0 { bass => '', ext => '', ext_canon => '', name => 'bVII0', qual => 0, qual_canon => 0, root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii0 { bass => '', ext => '', ext_canon => '', name => 'bvii0', qual => 0, qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII07 { bass => '', ext => 7, ext_canon => 7, name => 'bVII07', qual => 0, qual_canon => 0, root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii07 { bass => '', ext => 7, ext_canon => 7, name => 'bvii07', qual => 0, qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII0^ { bass => '', ext => '^', ext_canon => '^', name => 'bVII0^', qual => 0, qual_canon => 0, root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii0^ { bass => '', ext => '^', ext_canon => '^', name => 'bvii0^', qual => 0, qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII0h { bass => '', ext => 'h', ext_canon => 'h', name => 'bVII0h', qual => 0, qual_canon => 0, root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii0h { bass => '', ext => 'h', ext_canon => 'h', name => 'bvii0h', qual => 0, qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bVII0h7', qual => 0, qual_canon => 0, root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bvii0h7', qual => 0, qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bVII0^7', qual => 0, qual_canon => 0, root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bvii0^7', qual => 0, qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII+ { bass => '', ext => '', ext_canon => '', name => 'bVII+', qual => '+', qual_canon => '+', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii+ { bass => '', ext => '', ext_canon => '', name => 'bvii+', qual => '+', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII+7 { bass => '', ext => 7, ext_canon => 7, name => 'bVII+7', qual => '+', qual_canon => '+', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii+7 { bass => '', ext => 7, ext_canon => 7, name => 'bvii+7', qual => '+', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII+^ { bass => '', ext => '^', ext_canon => '^', name => 'bVII+^', qual => '+', qual_canon => '+', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii+^ { bass => '', ext => '^', ext_canon => '^', name => 'bvii+^', qual => '+', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII+h { bass => '', ext => 'h', ext_canon => 'h', name => 'bVII+h', qual => '+', qual_canon => '+', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii+h { bass => '', ext => 'h', ext_canon => 'h', name => 'bvii+h', qual => '+', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bVII+h7', qual => '+', qual_canon => '+', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'bvii+h7', qual => '+', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } bVII+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bVII+^7', qual => '+', qual_canon => '+', root => 'bVII', root_canon => 'bVII', root_mod => -1, root_ord => 11, system => 'roman' } bvii+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'bvii+^7', qual => '+', qual_canon => '-', root => 'bvii', root_canon => 'bvii', root_mod => -1, root_ord => 11, system => 'roman' } VII { bass => '', ext => '', ext_canon => '', name => 'VII', qual => '', qual_canon => '', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii { bass => '', ext => '', ext_canon => '', name => 'vii', qual => '', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII7 { bass => '', ext => 7, ext_canon => 7, name => 'VII7', qual => '', qual_canon => '', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii7 { bass => '', ext => 7, ext_canon => 7, name => 'vii7', qual => '', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII^ { bass => '', ext => '^', ext_canon => '^', name => 'VII^', qual => '', qual_canon => '', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii^ { bass => '', ext => '^', ext_canon => '^', name => 'vii^', qual => '', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VIIh { bass => '', ext => '', ext_canon => '', name => 'VIIh', qual => 'h', qual_canon => 'h', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } viih { bass => '', ext => '', ext_canon => '', name => 'viih', qual => 'h', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VIIh7 { bass => '', ext => 7, ext_canon => 7, name => 'VIIh7', qual => 'h', qual_canon => 'h', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } viih7 { bass => '', ext => 7, ext_canon => 7, name => 'viih7', qual => 'h', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'VII^7', qual => '', qual_canon => '', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'vii^7', qual => '', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII0 { bass => '', ext => '', ext_canon => '', name => 'VII0', qual => 0, qual_canon => 0, root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii0 { bass => '', ext => '', ext_canon => '', name => 'vii0', qual => 0, qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII07 { bass => '', ext => 7, ext_canon => 7, name => 'VII07', qual => 0, qual_canon => 0, root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii07 { bass => '', ext => 7, ext_canon => 7, name => 'vii07', qual => 0, qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII0^ { bass => '', ext => '^', ext_canon => '^', name => 'VII0^', qual => 0, qual_canon => 0, root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii0^ { bass => '', ext => '^', ext_canon => '^', name => 'vii0^', qual => 0, qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII0h { bass => '', ext => 'h', ext_canon => 'h', name => 'VII0h', qual => 0, qual_canon => 0, root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii0h { bass => '', ext => 'h', ext_canon => 'h', name => 'vii0h', qual => 0, qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'VII0h7', qual => 0, qual_canon => 0, root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii0h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'vii0h7', qual => 0, qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'VII0^7', qual => 0, qual_canon => 0, root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii0^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'vii0^7', qual => 0, qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII+ { bass => '', ext => '', ext_canon => '', name => 'VII+', qual => '+', qual_canon => '+', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii+ { bass => '', ext => '', ext_canon => '', name => 'vii+', qual => '+', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII+7 { bass => '', ext => 7, ext_canon => 7, name => 'VII+7', qual => '+', qual_canon => '+', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii+7 { bass => '', ext => 7, ext_canon => 7, name => 'vii+7', qual => '+', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII+^ { bass => '', ext => '^', ext_canon => '^', name => 'VII+^', qual => '+', qual_canon => '+', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii+^ { bass => '', ext => '^', ext_canon => '^', name => 'vii+^', qual => '+', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII+h { bass => '', ext => 'h', ext_canon => 'h', name => 'VII+h', qual => '+', qual_canon => '+', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii+h { bass => '', ext => 'h', ext_canon => 'h', name => 'vii+h', qual => '+', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'VII+h7', qual => '+', qual_canon => '+', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'vii+h7', qual => '+', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } VII+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'VII+^7', qual => '+', qual_canon => '+', root => 'VII', root_canon => 'VII', root_mod => 0, root_ord => 11, system => 'roman' } vii+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'vii+^7', qual => '+', qual_canon => '-', root => 'vii', root_canon => 'vii', root_mod => 0, root_ord => 11, system => 'roman' } App-Music-ChordPro-6.050.7/t/md/0000755000400000040000000000000014567360037013661 5ustar jvjvApp-Music-ChordPro-6.050.7/t/md/30_cho_2.md0000644000400000040000000000244314240671432015472 0ustar jvjv# Swing Low Sweet Chariot ## Traditional ![D](https://chordgenerator.net/D.png?p=xx0232&s=2) ![G](https://chordgenerator.net/G.png?p=320003&s=2) ![A7](https://chordgenerator.net/A7.png?p=x02020&s=2) ![D7](https://chordgenerator.net/D7.png?p=xx0212&s=2) **Chorus** D G D Swing low, sweet chariot, A7 Comin’ for to carry me home. D7 G D Swing low, sweet chariot, A7 D Comin’ for to carry me home. --------------- D G D I looked over Jordan, and what did I see, A7 “Comin’ for to carry me home.” D7 G D A band of angels comin’ after me, A7 D G D G Comin’ for to carry me home.       -- --------------- > Chorus If you get there before I do,      Comin’ for to carry me home. Just tell my friends that I’m a comin’ too.      Comin’ for to carry me home. > Chorus I’m sometimes up and sometimes down,      Comin’ for to carry me home. But still my soul feels heavenly bound.      Comin’ for to carry me home. > Chorus > *And a final chorus* --------------- App-Music-ChordPro-6.050.7/t/md/cho002.md0000644000400000040000000000044214240671432015166 0ustar jvjv# Keys ![D](https://chordgenerator.net/D.png?p=xx0232&s=2) ![E](https://chordgenerator.net/E.png?p=022100&s=2) > Key: , Actual: , From: > Key: D, Actual: D, From: D Hi > Key: D, Actual: E, From: D E Hi > Key: D, Actual: D, From: D Hi --------------- App-Music-ChordPro-6.050.7/t/md/30_cho_1.cho0000644000400000040000000000236214240671432015642 0ustar jvjv{title: Swing Low Sweet Chariot} {subtitle: Traditional} {columns: 2} {titles: center} {define: D base-fret 1 frets X X 0 2 3 2 } {define: G base-fret 1 frets 3 2 0 0 0 3 } {define: A7 base-fret 1 frets X 0 2 0 2 0 } {define: D7 base-fret 1 frets X X 0 2 1 2 } {start_of_chorus} Swing [D]low, sweet [G]chari[D]ot, Comin’ for to carry me [A7]home. Swing [D7]low, sweet [G]chari[D]ot, Comin’ for to [A7]carry me [D]home. {end_of_chorus} I [D]looked over Jordan, and [G]what did I [D]see, “Comin’ for to carry me [A7]home.” A [D7]band of angels [G]comin’ after [D]me, Comin’ for to [A7]carry me [D]home. [G]     [D] [G] {new_page} {comment: Chorus} If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. {column_break} {comment: Chorus} # Note the ' here will be changed into a decent apostrophe. I’m sometimes up and sometimes down, Comin’ for to carry me home. But still my soul feels heavenly bound. Comin’ for to carry me home. {comment: Chorus} {comment_italic: And a final chorus} App-Music-ChordPro-6.050.7/t/md/30_cho_3.md0000644000400000040000000000144614240671432015475 0ustar jvjv# Swing Low Sweet Chariot ## Traditional **Chorus** Swing low, sweet chariot, Comin’ for to carry me home. Swing low, sweet chariot, Comin’ for to carry me home. --------------- I looked over Jordan, and what did I see,      “Comin’ for to carry me home.” A band of angels comin’ after me,      Comin’ for to carry me home. --------------- > Chorus If you get there before I do,      Comin’ for to carry me home. Just tell my friends that I’m a comin’ too.      Comin’ for to carry me home. > Chorus I’m sometimes up and sometimes down,      Comin’ for to carry me home. But still my soul feels heavenly bound.      Comin’ for to carry me home. > Chorus > *And a final chorus* --------------- App-Music-ChordPro-6.050.7/t/md/cho004.md0000644000400000040000000000177314544302032015172 0ustar jvjv# Tranpose ![D](https://chordgenerator.net/D.png?p=xx0232&s=2) ![G](https://chordgenerator.net/G.png?p=320003&s=2) ![F#](https://chordgenerator.net/F#.png?p=244322&s=2) ![B](https://chordgenerator.net/B.png?p=x24442&s=2) ![G#](https://chordgenerator.net/G#.png?p=133211&s=2) ![C#](https://chordgenerator.net/C#.png?p=xx3121&s=2) ![Gb](https://chordgenerator.net/Gb.png?p=244322&s=2) D G D I looked over Jordan, and what did I see, F# B F# I looked over Jordan, and what did I see, G# C# G# I looked over Jordan, and what did I see, Gb B Gb I looked over Jordan, and what did I see, G# C# G# I looked over Jordan, and what did I see, F# B F# I looked over Jordan, and what did I see, D G D I looked over Jordan, and what did I see, --------------- App-Music-ChordPro-6.050.7/t/md/cho002.cho0000644000400000040000000000045714240671432015345 0ustar jvjv{title: Keys} {c: Key: %{key}, Actual: %{key_actual}, From: %{key_from}} {key: D} {c: Key: %{key}, Actual: %{key_actual}, From: %{key_from}} [D]Hi {transpose 2} {c: Key: %{key}, Actual: %{key_actual}, From: %{key_from}} [D]Hi {transpose} {c: Key: %{key}, Actual: %{key_actual}, From: %{key_from}} [D]Hi App-Music-ChordPro-6.050.7/t/md/cho004.cho0000644000400000040000000000076714240671432015353 0ustar jvjv{title Tranpose} I [D]looked over Jordan, and [G]what did I [D]see, {transpose +4} I [D]looked over Jordan, and [G]what did I [D]see, {transpose +2} I [D]looked over Jordan, and [G]what did I [D]see, {transpose -2} I [D]looked over Jordan, and [G]what did I [D]see, {transpose} # Back to +4 +2 I [D]looked over Jordan, and [G]what did I [D]see, {transpose} # Back to +4 I [D]looked over Jordan, and [G]what did I [D]see, {transpose} # Back to default I [D]looked over Jordan, and [G]what did I [D]see, App-Music-ChordPro-6.050.7/t/md/30_cho_3.cho0000644000400000040000000000225514240671432015645 0ustar jvjv{title: Swing Low Sweet Chariot} {subtitle: Traditional} {columns: 2} {titles: center} {define: D base-fret 1 frets X X 0 2 3 2 } {define: G base-fret 1 frets 3 2 0 0 0 3 } {define: A7 base-fret 1 frets X 0 2 0 2 0 } {define: D7 base-fret 1 frets X X 0 2 1 2 } {start_of_chorus} Swing low, sweet chariot, Comin’ for to carry me home. Swing low, sweet chariot, Comin’ for to carry me home. {end_of_chorus} I looked over Jordan, and what did I see, “Comin’ for to carry me home.” A band of angels comin’ after me, Comin’ for to carry me home.       {new_page} {comment: Chorus} If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. {column_break} {comment: Chorus} # Note the ' here will be changed into a decent apostrophe. I’m sometimes up and sometimes down, Comin’ for to carry me home. But still my soul feels heavenly bound. Comin’ for to carry me home. {comment: Chorus} {comment_italic: And a final chorus} App-Music-ChordPro-6.050.7/t/md/a44.cho0000644000400000040000000000035114240671432014733 0ustar jvjv{title: a44} {key: C} {time: 4/4} {tempo: 100} {duration: 21} {comment: A} {start_of_grid 4x8} | C . . . | F . . . | G . . . | C . . . | | . . . . | F . . . | G . . . | {end_of_grid} {start_of_grid 4x7} | C . Am . | {end_of_grid} App-Music-ChordPro-6.050.7/t/md/30_cho_1.md0000644000400000040000000000244314240671432015471 0ustar jvjv# Swing Low Sweet Chariot ## Traditional ![D](https://chordgenerator.net/D.png?p=xx0232&s=2) ![G](https://chordgenerator.net/G.png?p=320003&s=2) ![A7](https://chordgenerator.net/A7.png?p=x02020&s=2) ![D7](https://chordgenerator.net/D7.png?p=xx0212&s=2) **Chorus** D G D Swing low, sweet chariot, A7 Comin’ for to carry me home. D7 G D Swing low, sweet chariot, A7 D Comin’ for to carry me home. --------------- D G D I looked over Jordan, and what did I see, A7 “Comin’ for to carry me home.” D7 G D A band of angels comin’ after me, A7 D G D G Comin’ for to carry me home.       -- --------------- > Chorus If you get there before I do,      Comin’ for to carry me home. Just tell my friends that I’m a comin’ too.      Comin’ for to carry me home. > Chorus I’m sometimes up and sometimes down,      Comin’ for to carry me home. But still my soul feels heavenly bound.      Comin’ for to carry me home. > Chorus > *And a final chorus* --------------- App-Music-ChordPro-6.050.7/t/md/a34.md0000644000400000040000000000045314240671432014564 0ustar jvjv# a34 ![C](https://chordgenerator.net/C.png?p=x32010&s=2) ![F](https://chordgenerator.net/F.png?p=133211&s=2) ![G](https://chordgenerator.net/G.png?p=320003&s=2) ![Am](https://chordgenerator.net/Am.png?p=x02210&s=2) > A **Grid** |C..|F..|G..|C..| |...|F..|G..| |C.Am| --------------- App-Music-ChordPro-6.050.7/t/md/cho005.md0000644000400000040000000000042014240671432015165 0ustar jvjv# Keys and Capo ![D](https://chordgenerator.net/D.png?p=xx0232&s=2) ![F](https://chordgenerator.net/F.png?p=133211&s=2) > Key: D, Actual: E, From: D D Hi > Key: D, Actual: G, From: E F Hi > Key: D, Actual: D, From: D Hi --------------- App-Music-ChordPro-6.050.7/t/md/cho001.cho0000644000400000040000000000035314240671432015337 0ustar jvjv{title: t1} {artist: Foo} {artist: Bar} {key: D} {c: Key: %{key}} [D]Hello[*Bis] {chord B frets 1 2 3 4 5 6} Air [(E)]Hello[E] {chord B} S[NC]ure #{start_of_grid 1+4x4+2} #A | D . . . | E . . . | (D) . . . | E . . . | #{end_of_grid} App-Music-ChordPro-6.050.7/t/md/30_cho_2.cho0000644000400000040000000000236214240671432015643 0ustar jvjv{title: Swing Low Sweet Chariot} {subtitle: Traditional} {columns: 2} {titles: center} {define: D base-fret 1 frets X X 0 2 3 2 } {define: G base-fret 1 frets 3 2 0 0 0 3 } {define: A7 base-fret 1 frets X 0 2 0 2 0 } {define: D7 base-fret 1 frets X X 0 2 1 2 } {start_of_chorus} Swing [D]low, sweet [G]chari[D]ot, Comin’ for to carry me [A7]home. Swing [D7]low, sweet [G]chari[D]ot, Comin’ for to [A7]carry me [D]home. {end_of_chorus} I [D]looked over Jordan, and [G]what did I [D]see, “Comin’ for to carry me [A7]home.” A [D7]band of angels [G]comin’ after [D]me, Comin’ for to [A7]carry me [D]home. [G]     [D] [G] {new_page} {comment: Chorus} If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. {column_break} {comment: Chorus} # Note the ' here will be changed into a decent apostrophe. I’m sometimes up and sometimes down, Comin’ for to carry me home. But still my soul feels heavenly bound. Comin’ for to carry me home. {comment: Chorus} {comment_italic: And a final chorus} App-Music-ChordPro-6.050.7/t/md/cho003.cho0000644000400000040000000000017114240671432015337 0ustar jvjv{title: https://groups.io/g/ChordPro/message/1092} [Bb6] comes out as Bb6 [Bb7] comes out as A#7 [Bb] comes out as A# App-Music-ChordPro-6.050.7/t/md/a34.cho0000644000400000040000000000033014240671432014727 0ustar jvjv{title: a34} {key: C} {time: 3/4} {tempo: 100} {duration: 16} {comment: A} {start_of_grid 4x6} | C . . | F . . | G . . | C . . | | . . . | F . . | G . . | {end_of_grid} {start_of_grid 4x5} | C . Am | {end_of_grid} App-Music-ChordPro-6.050.7/t/md/cho005.cho0000644000400000040000000000040714240671432015343 0ustar jvjv{title: Keys and Capo} {key: D} {capo: 2} {c: Key: %{key}, Actual: %{key_actual}, From: %{key_from}} [D]Hi {transpose 3} {c: Key: %{key}, Actual: %{key_actual}, From: %{key_from}} [D]Hi {transpose} {c: Key: %{key}, Actual: %{key_actual}, From: %{key_from}} [D]Hi App-Music-ChordPro-6.050.7/t/md/a44.md0000644000400000040000000000046314240671432014566 0ustar jvjv# a44 ![C](https://chordgenerator.net/C.png?p=x32010&s=2) ![F](https://chordgenerator.net/F.png?p=133211&s=2) ![G](https://chordgenerator.net/G.png?p=320003&s=2) ![Am](https://chordgenerator.net/Am.png?p=x02210&s=2) > A **Grid** |C...|F...|G...|C...| |....|F...|G...| |C.Am.| --------------- App-Music-ChordPro-6.050.7/t/md/cho003.md0000644000400000040000000000047314240671432015173 0ustar jvjv# https://groups.io/g/ChordPro/message/1092 ![Bb6](https://chordgenerator.net/Bb6.png?p=xx3333&s=2) ![Bb7](https://chordgenerator.net/Bb7.png?p=xx1112&s=2) ![Bb](https://chordgenerator.net/Bb.png?p=x13331&s=2) Bb6 comes out as Bb6 Bb7 comes out as A#7 Bb comes out as A# --------------- App-Music-ChordPro-6.050.7/t/md/cho001.md0000644000400000040000000000033014324567577015202 0ustar jvjv# t1 ![D](https://chordgenerator.net/D.png?p=xx0232&s=2) ![E](https://chordgenerator.net/E.png?p=022100&s=2) > Key: D D *Bis Hello----- (E) E Air Hello-- NC Sure --------------- App-Music-ChordPro-6.050.7/t/108_chords_solfege.t0000644000400000040000000017450314544300524017024 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Chords; use ChordPro::Chords::Parser; my %tbl; our $config = ChordPro::Config::configurator ( { nosysconfig => 1, nolegacyconfig => 1, nouserconfig => 1, config => getresource("config/notes/solfege.json") } ); =begin regenerate # Enable this section to generate new reference data. my $p = ChordPro::Chords::Parser->get_parser("solfege"); open( my $fd, '<', "t/105_chords.t" ); my $skip = 1; while ( <$fd> ) { chomp; if ( $skip && /__DATA__/ ) { $skip = 0; next; } next if $skip; my ( $chord, $info ) = split( /\t/, $_ ); for ( $chord ) { s!^C#!Di! or s!^C!Do! or s!^Db!Ra! or s!^D#!Ri! or s!^D!Re! or s!^Eb!Me! or s!^E!Mi! or s!^F#!Fi! or s!^F!Fa! or s!^Gb!Se! or s!^G#!Si! or s!^G!So! or s!^Ab!Le! or s!^A#!Li! or s!^A!La! or s!^Bb!Te! or s!^B!Ti!; s!/C#!/Di! or s!/C!/Do! or s!/Db!/Ra! or s!/D#!/Ri! or s!/D!/Re! or s!/Eb!/Me! or s!/E!/Mi! or s!/F#!/Fi! or s!/F!/Fa! or s!/Gb!/Se! or s!/G#!/Si! or s!/G!/So! or s!/Ab!/Le! or s!/A#!/Li! or s!/A!/La! or s!/Bb!/Te! or s!/B!/Ti!; } my $c = $chord; $c =~ s/[()]//g; my $res = $p->parse($c); unless ( $res ) { print( "$chord\tFAIL\n"); next; } $res = {%$res}; delete($res->{parser}); print("$chord\t", reformat($res), "\n"); } exit; =cut while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; $tbl{$c} = $info; } plan tests => 0 + keys(%tbl); while ( my ( $c, $info ) = each %tbl ) { my $res = ChordPro::Chords::parse_chord($c); $res //= "FAIL"; if ( UNIVERSAL::isa( $res, 'HASH' ) ) { $res = reformat($res); } is( $res, $info, "parsing chord $c"); } sub reformat { my ( $res ) = @_; $res = {%$res}; delete($res->{parser}); use Data::Dumper qw(); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Deparse = 1; local $Data::Dumper::Terse = 1; local $Data::Dumper::Trailingcomma = 1; local $Data::Dumper::Useperl = 1; local $Data::Dumper::Useqq = 0; # I want unicode visible my $s = Data::Dumper::Dumper($res); $s =~ s/\s+/ /gs; $s =~ s/, \}/ }/gs; $s =~ s/\s+$//; return $s; } __DATA__ Do { bass => '', ext => '', ext_canon => '', name => 'Do', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Do+ { bass => '', ext => '', ext_canon => '', name => 'Do+', qual => '+', qual_canon => '+', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Do4 { bass => '', ext => 4, ext_canon => 4, name => 'Do4', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Do6 { bass => '', ext => 6, ext_canon => 6, name => 'Do6', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Do7 { bass => '', ext => 7, ext_canon => 7, name => 'Do7', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Do9 { bass => '', ext => 9, ext_canon => 9, name => 'Do9', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Do9(11) { bass => '', ext => 911, ext_canon => 911, name => 'Do911', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Do11 { bass => '', ext => 11, ext_canon => 11, name => 'Do11', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Dosus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Dosus', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Dosus2 { bass => '', ext => 'sus2', ext_canon => 'sus2', name => 'Dosus2', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Dosus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Dosus4', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Dosus9 { bass => '', ext => 'sus9', ext_canon => 'sus9', name => 'Dosus9', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Domaj { bass => '', ext => '', ext_canon => '', name => 'Domaj', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Domaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Domaj7', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Dom { bass => '', ext => '', ext_canon => '', name => 'Dom', qual => 'm', qual_canon => '-', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Domin { bass => '', ext => '', ext_canon => '', name => 'Domin', qual => 'min', qual_canon => '-', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Dodim { bass => '', ext => '', ext_canon => '', name => 'Dodim', qual => 'dim', qual_canon => 0, root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Do/Ti { bass => 'Ti', bass_canon => 'Ti', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Do/Ti', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Doadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Doadd9', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Do3 { bass => '', ext => 3, ext_canon => 3, name => 'Do3', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Dom7 { bass => '', ext => 7, ext_canon => 7, name => 'Dom7', qual => 'm', qual_canon => '-', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Dom11 { bass => '', ext => 11, ext_canon => 11, name => 'Dom11', qual => 'm', qual_canon => '-', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Di { bass => '', ext => '', ext_canon => '', name => 'Di', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Di+ { bass => '', ext => '', ext_canon => '', name => 'Di+', qual => '+', qual_canon => '+', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Di4 { bass => '', ext => 4, ext_canon => 4, name => 'Di4', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Di7 { bass => '', ext => 7, ext_canon => 7, name => 'Di7', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Di7(b5) { bass => '', ext => '7b5', ext_canon => '7b5', name => 'Di7b5', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Disus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Disus', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Disus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Disus4', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Dimaj { bass => '', ext => '', ext_canon => '', name => 'Dimaj', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Dimaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Dimaj7', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Didim { bass => '', ext => '', ext_canon => '', name => 'Didim', qual => 'dim', qual_canon => 0, root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Dim { bass => '', ext => '', ext_canon => '', name => 'Dim', qual => 'm', qual_canon => '-', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Dimin { bass => '', ext => '', ext_canon => '', name => 'Dimin', qual => 'min', qual_canon => '-', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Diadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Diadd9', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Di(add9) { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Diadd9', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Dim7 { bass => '', ext => 7, ext_canon => 7, name => 'Dim7', qual => 'm', qual_canon => '-', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Ra { bass => '', ext => '', ext_canon => '', name => 'Ra', qual => '', qual_canon => '', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Ra+ { bass => '', ext => '', ext_canon => '', name => 'Ra+', qual => '+', qual_canon => '+', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Ra7 { bass => '', ext => 7, ext_canon => 7, name => 'Ra7', qual => '', qual_canon => '', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Rasus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Rasus', qual => '', qual_canon => '', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Rasus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Rasus4', qual => '', qual_canon => '', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Ramaj { bass => '', ext => '', ext_canon => '', name => 'Ramaj', qual => '', qual_canon => '', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Ramaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Ramaj7', qual => '', qual_canon => '', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Radim { bass => '', ext => '', ext_canon => '', name => 'Radim', qual => 'dim', qual_canon => 0, root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Ram { bass => '', ext => '', ext_canon => '', name => 'Ram', qual => 'm', qual_canon => '-', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Ramin { bass => '', ext => '', ext_canon => '', name => 'Ramin', qual => 'min', qual_canon => '-', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Ram7 { bass => '', ext => 7, ext_canon => 7, name => 'Ram7', qual => 'm', qual_canon => '-', root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Re { bass => '', ext => '', ext_canon => '', name => 'Re', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re+ { bass => '', ext => '', ext_canon => '', name => 'Re+', qual => '+', qual_canon => '+', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re4 { bass => '', ext => 4, ext_canon => 4, name => 'Re4', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re6 { bass => '', ext => 6, ext_canon => 6, name => 'Re6', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re7 { bass => '', ext => 7, ext_canon => 7, name => 'Re7', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Re7#9', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re7(#9) { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Re7#9', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re9 { bass => '', ext => 9, ext_canon => 9, name => 'Re9', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re11 { bass => '', ext => 11, ext_canon => 11, name => 'Re11', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Resus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Resus', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Resus2 { bass => '', ext => 'sus2', ext_canon => 'sus2', name => 'Resus2', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Resus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Resus4', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re7sus2 { bass => '', ext => '7sus2', ext_canon => '7sus2', name => 'Re7sus2', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'Re7sus4', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Remaj { bass => '', ext => '', ext_canon => '', name => 'Remaj', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Remaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Remaj7', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Redim { bass => '', ext => '', ext_canon => '', name => 'Redim', qual => 'dim', qual_canon => 0, root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rem { bass => '', ext => '', ext_canon => '', name => 'Rem', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Remin { bass => '', ext => '', ext_canon => '', name => 'Remin', qual => 'min', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'Re/La', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re/Ti { bass => 'Ti', bass_canon => 'Ti', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Re/Ti', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'Re/Do', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re/Di { bass => 'Di', bass_canon => 'Di', bass_mod => 1, bass_ord => 1, ext => '', ext_canon => '', name => 'Re/Di', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re/Mi { bass => 'Mi', bass_canon => 'Mi', bass_mod => 0, bass_ord => 4, ext => '', ext_canon => '', name => 'Re/Mi', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re/So { bass => 'So', bass_canon => 'So', bass_mod => 0, bass_ord => 7, ext => '', ext_canon => '', name => 'Re/So', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re5/Mi { bass => 'Mi', bass_canon => 'Mi', bass_mod => 0, bass_ord => 4, ext => 5, ext_canon => 5, name => 'Re5/Mi', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Readd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Readd9', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Re9add6 { bass => '', ext => '9add6', ext_canon => '9add6', name => 'Re9add6', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rem7 { bass => '', ext => 7, ext_canon => 7, name => 'Rem7', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rem#5 { bass => '', ext => '#5', ext_canon => '#5', name => 'Rem#5', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rem#7 { bass => '', ext => '#7', ext_canon => '#7', name => 'Rem#7', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rem/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'Rem/La', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rem/Ti { bass => 'Ti', bass_canon => 'Ti', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Rem/Ti', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rem/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'Rem/Do', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rem/Di { bass => 'Di', bass_canon => 'Di', bass_mod => 1, bass_ord => 1, ext => '', ext_canon => '', name => 'Rem/Di', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rem9 { bass => '', ext => 9, ext_canon => 9, name => 'Rem9', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Ri { bass => '', ext => '', ext_canon => '', name => 'Ri', qual => '', qual_canon => '', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Ri+ { bass => '', ext => '', ext_canon => '', name => 'Ri+', qual => '+', qual_canon => '+', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Ri4 { bass => '', ext => 4, ext_canon => 4, name => 'Ri4', qual => '', qual_canon => '', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Ri7 { bass => '', ext => 7, ext_canon => 7, name => 'Ri7', qual => '', qual_canon => '', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Risus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Risus', qual => '', qual_canon => '', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Risus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Risus4', qual => '', qual_canon => '', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Rimaj { bass => '', ext => '', ext_canon => '', name => 'Rimaj', qual => '', qual_canon => '', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Rimaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Rimaj7', qual => '', qual_canon => '', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Ridim { bass => '', ext => '', ext_canon => '', name => 'Ridim', qual => 'dim', qual_canon => 0, root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Rim { bass => '', ext => '', ext_canon => '', name => 'Rim', qual => 'm', qual_canon => '-', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Rimin { bass => '', ext => '', ext_canon => '', name => 'Rimin', qual => 'min', qual_canon => '-', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Rim7 { bass => '', ext => 7, ext_canon => 7, name => 'Rim7', qual => 'm', qual_canon => '-', root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Me { bass => '', ext => '', ext_canon => '', name => 'Me', qual => '', qual_canon => '', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Me+ { bass => '', ext => '', ext_canon => '', name => 'Me+', qual => '+', qual_canon => '+', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Me4 { bass => '', ext => 4, ext_canon => 4, name => 'Me4', qual => '', qual_canon => '', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Me7 { bass => '', ext => 7, ext_canon => 7, name => 'Me7', qual => '', qual_canon => '', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Mesus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Mesus', qual => '', qual_canon => '', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Mesus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Mesus4', qual => '', qual_canon => '', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Memaj { bass => '', ext => '', ext_canon => '', name => 'Memaj', qual => '', qual_canon => '', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Memaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Memaj7', qual => '', qual_canon => '', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Medim { bass => '', ext => '', ext_canon => '', name => 'Medim', qual => 'dim', qual_canon => 0, root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Meadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Meadd9', qual => '', qual_canon => '', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Me(add9) { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Meadd9', qual => '', qual_canon => '', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Mem { bass => '', ext => '', ext_canon => '', name => 'Mem', qual => 'm', qual_canon => '-', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Memin { bass => '', ext => '', ext_canon => '', name => 'Memin', qual => 'min', qual_canon => '-', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Mem7 { bass => '', ext => 7, ext_canon => 7, name => 'Mem7', qual => 'm', qual_canon => '-', root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Mi { bass => '', ext => '', ext_canon => '', name => 'Mi', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi+ { bass => '', ext => '', ext_canon => '', name => 'Mi+', qual => '+', qual_canon => '+', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi5 { bass => '', ext => 5, ext_canon => 5, name => 'Mi5', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi6 { bass => '', ext => 6, ext_canon => 6, name => 'Mi6', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi7 { bass => '', ext => 7, ext_canon => 7, name => 'Mi7', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Mi7#9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi7(#9) { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Mi7#9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi7(b5) { bass => '', ext => '7b5', ext_canon => '7b5', name => 'Mi7b5', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi7b9 { bass => '', ext => '7b9', ext_canon => '7b9', name => 'Mi7b9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi7(11) { bass => '', ext => 711, ext_canon => 711, name => 'Mi711', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi9 { bass => '', ext => 9, ext_canon => 9, name => 'Mi9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi11 { bass => '', ext => 11, ext_canon => 11, name => 'Mi11', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Misus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Misus', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mimaj { bass => '', ext => '', ext_canon => '', name => 'Mimaj', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mimaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Mimaj7', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Midim { bass => '', ext => '', ext_canon => '', name => 'Midim', qual => 'dim', qual_canon => 0, root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mim { bass => '', ext => '', ext_canon => '', name => 'Mim', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mimin { bass => '', ext => '', ext_canon => '', name => 'Mimin', qual => 'min', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mim6 { bass => '', ext => 6, ext_canon => 6, name => 'Mim6', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mim7 { bass => '', ext => 7, ext_canon => 7, name => 'Mim7', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mim/Ti { bass => 'Ti', bass_canon => 'Ti', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Mim/Ti', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mim/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'Mim/Re', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mim7/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => 7, ext_canon => 7, name => 'Mim7/Re', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mimsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Mimsus4', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mimadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Mimadd9', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Fa { bass => '', ext => '', ext_canon => '', name => 'Fa', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa+ { bass => '', ext => '', ext_canon => '', name => 'Fa+', qual => '+', qual_canon => '+', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa4 { bass => '', ext => 4, ext_canon => 4, name => 'Fa4', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa6 { bass => '', ext => 6, ext_canon => 6, name => 'Fa6', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa7 { bass => '', ext => 7, ext_canon => 7, name => 'Fa7', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa9 { bass => '', ext => 9, ext_canon => 9, name => 'Fa9', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa11 { bass => '', ext => 11, ext_canon => 11, name => 'Fa11', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fasus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Fasus', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fasus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Fasus4', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Famaj { bass => '', ext => '', ext_canon => '', name => 'Famaj', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Famaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Famaj7', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fadim { bass => '', ext => '', ext_canon => '', name => 'Fadim', qual => 'dim', qual_canon => 0, root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fam { bass => '', ext => '', ext_canon => '', name => 'Fam', qual => 'm', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Famin { bass => '', ext => '', ext_canon => '', name => 'Famin', qual => 'min', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'Fa/La', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'Fa/Do', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'Fa/Re', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa/So { bass => 'So', bass_canon => 'So', bass_mod => 0, bass_ord => 7, ext => '', ext_canon => '', name => 'Fa/So', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fa7/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => 7, ext_canon => 7, name => 'Fa7/La', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Famaj7/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => 'maj7', ext_canon => 'maj7', name => 'Famaj7/La', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Famaj7/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => 'maj7', ext_canon => 'maj7', name => 'Famaj7/Do', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Faadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Faadd9', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fam6 { bass => '', ext => 6, ext_canon => 6, name => 'Fam6', qual => 'm', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fam7 { bass => '', ext => 7, ext_canon => 7, name => 'Fam7', qual => 'm', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fammaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Fammaj7', qual => 'm', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fi { bass => '', ext => '', ext_canon => '', name => 'Fi', qual => '', qual_canon => '', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fi+ { bass => '', ext => '', ext_canon => '', name => 'Fi+', qual => '+', qual_canon => '+', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fi7 { bass => '', ext => 7, ext_canon => 7, name => 'Fi7', qual => '', qual_canon => '', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fi9 { bass => '', ext => 9, ext_canon => 9, name => 'Fi9', qual => '', qual_canon => '', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fi11 { bass => '', ext => 11, ext_canon => 11, name => 'Fi11', qual => '', qual_canon => '', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fisus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Fisus', qual => '', qual_canon => '', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fisus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Fisus4', qual => '', qual_canon => '', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fimaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Fimaj7', qual => '', qual_canon => '', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fidim { bass => '', ext => '', ext_canon => '', name => 'Fidim', qual => 'dim', qual_canon => 0, root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fim { bass => '', ext => '', ext_canon => '', name => 'Fim', qual => 'm', qual_canon => '-', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fimin { bass => '', ext => '', ext_canon => '', name => 'Fimin', qual => 'min', qual_canon => '-', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fi/Mi { bass => 'Mi', bass_canon => 'Mi', bass_mod => 0, bass_ord => 4, ext => '', ext_canon => '', name => 'Fi/Mi', qual => '', qual_canon => '', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fi4 { bass => '', ext => 4, ext_canon => 4, name => 'Fi4', qual => '', qual_canon => '', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fim6 { bass => '', ext => 6, ext_canon => 6, name => 'Fim6', qual => 'm', qual_canon => '-', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fim7 { bass => '', ext => 7, ext_canon => 7, name => 'Fim7', qual => 'm', qual_canon => '-', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fim7b5 { bass => '', ext => '7b5', ext_canon => '7b5', name => 'Fim7b5', qual => 'm', qual_canon => '-', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Fim/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'Fim/Do', qual => 'm', qual_canon => '-', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Se { bass => '', ext => '', ext_canon => '', name => 'Se', qual => '', qual_canon => '', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Se+ { bass => '', ext => '', ext_canon => '', name => 'Se+', qual => '+', qual_canon => '+', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Se7 { bass => '', ext => 7, ext_canon => 7, name => 'Se7', qual => '', qual_canon => '', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Se9 { bass => '', ext => 9, ext_canon => 9, name => 'Se9', qual => '', qual_canon => '', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Sesus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Sesus', qual => '', qual_canon => '', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Sesus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Sesus4', qual => '', qual_canon => '', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Semaj { bass => '', ext => '', ext_canon => '', name => 'Semaj', qual => '', qual_canon => '', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Semaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Semaj7', qual => '', qual_canon => '', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Sedim { bass => '', ext => '', ext_canon => '', name => 'Sedim', qual => 'dim', qual_canon => 0, root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Sem { bass => '', ext => '', ext_canon => '', name => 'Sem', qual => 'm', qual_canon => '-', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Semin { bass => '', ext => '', ext_canon => '', name => 'Semin', qual => 'min', qual_canon => '-', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Sem7 { bass => '', ext => 7, ext_canon => 7, name => 'Sem7', qual => 'm', qual_canon => '-', root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } So { bass => '', ext => '', ext_canon => '', name => 'So', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So+ { bass => '', ext => '', ext_canon => '', name => 'So+', qual => '+', qual_canon => '+', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So4 { bass => '', ext => 4, ext_canon => 4, name => 'So4', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So6 { bass => '', ext => 6, ext_canon => 6, name => 'So6', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So7 { bass => '', ext => 7, ext_canon => 7, name => 'So7', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So7b9 { bass => '', ext => '7b9', ext_canon => '7b9', name => 'So7b9', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'So7#9', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So9 { bass => '', ext => 9, ext_canon => 9, name => 'So9', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So9(11) { bass => '', ext => 911, ext_canon => 911, name => 'So911', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So11 { bass => '', ext => 11, ext_canon => 11, name => 'So11', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Sosus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Sosus', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Sosus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Sosus4', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So6sus4 { bass => '', ext => '6sus4', ext_canon => '6sus4', name => 'So6sus4', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'So7sus4', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Somaj { bass => '', ext => '', ext_canon => '', name => 'Somaj', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Somaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Somaj7', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Somaj7sus4 { bass => '', ext => 'maj7sus4', ext_canon => 'maj7sus4', name => 'Somaj7sus4', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Somaj9 { bass => '', ext => 'maj9', ext_canon => 'maj9', name => 'Somaj9', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Som { bass => '', ext => '', ext_canon => '', name => 'Som', qual => 'm', qual_canon => '-', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Somin { bass => '', ext => '', ext_canon => '', name => 'Somin', qual => 'min', qual_canon => '-', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Sodim { bass => '', ext => '', ext_canon => '', name => 'Sodim', qual => 'dim', qual_canon => 0, root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Soadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Soadd9', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So(add9) { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Soadd9', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'So/La', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So/Ti { bass => 'Ti', bass_canon => 'Ti', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'So/Ti', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'So/Re', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } So/Fi { bass => 'Fi', bass_canon => 'Fi', bass_mod => 1, bass_ord => 6, ext => '', ext_canon => '', name => 'So/Fi', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Som6 { bass => '', ext => 6, ext_canon => 6, name => 'Som6', qual => 'm', qual_canon => '-', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Som7 { bass => '', ext => 7, ext_canon => 7, name => 'Som7', qual => 'm', qual_canon => '-', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Som/Te { bass => 'Te', bass_canon => 'Te', bass_mod => -1, bass_ord => 10, ext => '', ext_canon => '', name => 'Som/Te', qual => 'm', qual_canon => '-', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Si { bass => '', ext => '', ext_canon => '', name => 'Si', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Si+ { bass => '', ext => '', ext_canon => '', name => 'Si+', qual => '+', qual_canon => '+', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Si4 { bass => '', ext => 4, ext_canon => 4, name => 'Si4', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Si7 { bass => '', ext => 7, ext_canon => 7, name => 'Si7', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Sisus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Sisus', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Sisus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Sisus4', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Simaj { bass => '', ext => '', ext_canon => '', name => 'Simaj', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Simaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Simaj7', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Sidim { bass => '', ext => '', ext_canon => '', name => 'Sidim', qual => 'dim', qual_canon => 0, root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Sim { bass => '', ext => '', ext_canon => '', name => 'Sim', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Simin { bass => '', ext => '', ext_canon => '', name => 'Simin', qual => 'min', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Sim6 { bass => '', ext => 6, ext_canon => 6, name => 'Sim6', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Sim7 { bass => '', ext => 7, ext_canon => 7, name => 'Sim7', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Sim9maj7 { bass => '', ext => '9maj7', ext_canon => '9maj7', name => 'Sim9maj7', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Le { bass => '', ext => '', ext_canon => '', name => 'Le', qual => '', qual_canon => '', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Le+ { bass => '', ext => '', ext_canon => '', name => 'Le+', qual => '+', qual_canon => '+', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Le4 { bass => '', ext => 4, ext_canon => 4, name => 'Le4', qual => '', qual_canon => '', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Le7 { bass => '', ext => 7, ext_canon => 7, name => 'Le7', qual => '', qual_canon => '', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Le11 { bass => '', ext => 11, ext_canon => 11, name => 'Le11', qual => '', qual_canon => '', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Lesus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Lesus', qual => '', qual_canon => '', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Lesus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Lesus4', qual => '', qual_canon => '', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Ledim { bass => '', ext => '', ext_canon => '', name => 'Ledim', qual => 'dim', qual_canon => 0, root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Lemaj { bass => '', ext => '', ext_canon => '', name => 'Lemaj', qual => '', qual_canon => '', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Lemaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Lemaj7', qual => '', qual_canon => '', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Lem { bass => '', ext => '', ext_canon => '', name => 'Lem', qual => 'm', qual_canon => '-', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Lemin { bass => '', ext => '', ext_canon => '', name => 'Lemin', qual => 'min', qual_canon => '-', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Lem7 { bass => '', ext => 7, ext_canon => 7, name => 'Lem7', qual => 'm', qual_canon => '-', root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } La { bass => '', ext => '', ext_canon => '', name => 'La', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La+ { bass => '', ext => '', ext_canon => '', name => 'La+', qual => '+', qual_canon => '+', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La4 { bass => '', ext => 4, ext_canon => 4, name => 'La4', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La6 { bass => '', ext => 6, ext_canon => 6, name => 'La6', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La7 { bass => '', ext => 7, ext_canon => 7, name => 'La7', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La9 { bass => '', ext => 9, ext_canon => 9, name => 'La9', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La11 { bass => '', ext => 11, ext_canon => 11, name => 'La11', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La13 { bass => '', ext => 13, ext_canon => 13, name => 'La13', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'La7sus4', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La9sus { bass => '', ext => '9sus', ext_canon => '9sus', name => 'La9sus', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lasus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Lasus', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lasus2 { bass => '', ext => 'sus2', ext_canon => 'sus2', name => 'Lasus2', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lasus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Lasus4', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Ladim { bass => '', ext => '', ext_canon => '', name => 'Ladim', qual => 'dim', qual_canon => 0, root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lamaj { bass => '', ext => '', ext_canon => '', name => 'Lamaj', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lamaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Lamaj7', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lam { bass => '', ext => '', ext_canon => '', name => 'Lam', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lamin { bass => '', ext => '', ext_canon => '', name => 'Lamin', qual => 'min', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La/Di { bass => 'Di', bass_canon => 'Di', bass_mod => 1, bass_ord => 1, ext => '', ext_canon => '', name => 'La/Di', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'La/Re', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La/Mi { bass => 'Mi', bass_canon => 'Mi', bass_mod => 0, bass_ord => 4, ext => '', ext_canon => '', name => 'La/Mi', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La/Fi { bass => 'Fi', bass_canon => 'Fi', bass_mod => 1, bass_ord => 6, ext => '', ext_canon => '', name => 'La/Fi', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } La/Si { bass => 'Si', bass_canon => 'Si', bass_mod => 1, bass_ord => 8, ext => '', ext_canon => '', name => 'La/Si', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lam#7 { bass => '', ext => '#7', ext_canon => '#7', name => 'Lam#7', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lam6 { bass => '', ext => 6, ext_canon => 6, name => 'Lam6', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lam7 { bass => '', ext => 7, ext_canon => 7, name => 'Lam7', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lam7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'Lam7sus4', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lam9 { bass => '', ext => 9, ext_canon => 9, name => 'Lam9', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lam/So { bass => 'So', bass_canon => 'So', bass_mod => 0, bass_ord => 7, ext => '', ext_canon => '', name => 'Lam/So', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lamadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Lamadd9', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Li { bass => '', ext => '', ext_canon => '', name => 'Li', qual => '', qual_canon => '', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Li+ { bass => '', ext => '', ext_canon => '', name => 'Li+', qual => '+', qual_canon => '+', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Li4 { bass => '', ext => 4, ext_canon => 4, name => 'Li4', qual => '', qual_canon => '', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Li7 { bass => '', ext => 7, ext_canon => 7, name => 'Li7', qual => '', qual_canon => '', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Lisus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Lisus', qual => '', qual_canon => '', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Lisus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Lisus4', qual => '', qual_canon => '', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Limaj { bass => '', ext => '', ext_canon => '', name => 'Limaj', qual => '', qual_canon => '', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Limaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Limaj7', qual => '', qual_canon => '', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Lidim { bass => '', ext => '', ext_canon => '', name => 'Lidim', qual => 'dim', qual_canon => 0, root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Lim { bass => '', ext => '', ext_canon => '', name => 'Lim', qual => 'm', qual_canon => '-', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Limin { bass => '', ext => '', ext_canon => '', name => 'Limin', qual => 'min', qual_canon => '-', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Lim7 { bass => '', ext => 7, ext_canon => 7, name => 'Lim7', qual => 'm', qual_canon => '-', root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Te { bass => '', ext => '', ext_canon => '', name => 'Te', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Te+ { bass => '', ext => '', ext_canon => '', name => 'Te+', qual => '+', qual_canon => '+', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Te4 { bass => '', ext => 4, ext_canon => 4, name => 'Te4', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Te6 { bass => '', ext => 6, ext_canon => 6, name => 'Te6', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Te7 { bass => '', ext => 7, ext_canon => 7, name => 'Te7', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Te9 { bass => '', ext => 9, ext_canon => 9, name => 'Te9', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Te11 { bass => '', ext => 11, ext_canon => 11, name => 'Te11', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Tesus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Tesus', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Tesus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Tesus4', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Temaj { bass => '', ext => '', ext_canon => '', name => 'Temaj', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Temaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Temaj7', qual => '', qual_canon => '', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Tedim { bass => '', ext => '', ext_canon => '', name => 'Tedim', qual => 'dim', qual_canon => 0, root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Tem { bass => '', ext => '', ext_canon => '', name => 'Tem', qual => 'm', qual_canon => '-', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Temin { bass => '', ext => '', ext_canon => '', name => 'Temin', qual => 'min', qual_canon => '-', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Tem7 { bass => '', ext => 7, ext_canon => 7, name => 'Tem7', qual => 'm', qual_canon => '-', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Tem9 { bass => '', ext => 9, ext_canon => 9, name => 'Tem9', qual => 'm', qual_canon => '-', root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Ti { bass => '', ext => '', ext_canon => '', name => 'Ti', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Ti+ { bass => '', ext => '', ext_canon => '', name => 'Ti+', qual => '+', qual_canon => '+', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Ti4 { bass => '', ext => 4, ext_canon => 4, name => 'Ti4', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Ti7 { bass => '', ext => 7, ext_canon => 7, name => 'Ti7', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Ti7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Ti7#9', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Ti9 { bass => '', ext => 9, ext_canon => 9, name => 'Ti9', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Ti11 { bass => '', ext => 11, ext_canon => 11, name => 'Ti11', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Ti13 { bass => '', ext => 13, ext_canon => 13, name => 'Ti13', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Tisus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Tisus', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Tisus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Tisus4', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Timaj { bass => '', ext => '', ext_canon => '', name => 'Timaj', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Timaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Timaj7', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Tidim { bass => '', ext => '', ext_canon => '', name => 'Tidim', qual => 'dim', qual_canon => 0, root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Tim { bass => '', ext => '', ext_canon => '', name => 'Tim', qual => 'm', qual_canon => '-', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Timin { bass => '', ext => '', ext_canon => '', name => 'Timin', qual => 'min', qual_canon => '-', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Ti/Fi { bass => 'Fi', bass_canon => 'Fi', bass_mod => 1, bass_ord => 6, ext => '', ext_canon => '', name => 'Ti/Fi', qual => '', qual_canon => '', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Tim6 { bass => '', ext => 6, ext_canon => 6, name => 'Tim6', qual => 'm', qual_canon => '-', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Tim7 { bass => '', ext => 7, ext_canon => 7, name => 'Tim7', qual => 'm', qual_canon => '-', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Timmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Timmaj7', qual => 'm', qual_canon => '-', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Timsus9 { bass => '', ext => 'sus9', ext_canon => 'sus9', name => 'Timsus9', qual => 'm', qual_canon => '-', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Tim7b5 { bass => '', ext => '7b5', ext_canon => '7b5', name => 'Tim7b5', qual => 'm', qual_canon => '-', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } App-Music-ChordPro-6.050.7/t/109_chords_nashville.t0000644000400000040000000021761214544300524017365 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Chords; my %tbl; while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; $tbl{$c} = $info; } plan tests => 0 + keys(%tbl); ChordPro::Chords::set_parser("nashville"); =for generating for my $r ( '1', '#1', 'b2', '2', '#2', 'b3', '3', '4', '#4', 'b5', '5', '#5', 'b6', '6', '#6', 'b7', '7' ) { for my $q ( '', '-', '0', '+' ) { for my $e ( '', '7', '^', 'h', 'h7', '^7' ) { my $chord = "$r$q$e"; my $res = ChordPro::Chords::parse_chord($chord); unless ( $res ) { print( "$chord\tFAIL\n"); next; } print("$chord\t", reformat($res), "\n"); } } } exit; =cut while ( my ( $c, $info ) = each %tbl ) { my $res = ChordPro::Chords::parse_chord($c); $res //= "FAIL"; if ( UNIVERSAL::isa( $res, 'HASH' ) ) { $res = reformat($res); } is( $res, $info, "parsing chord $c"); } sub reformat { my ( $res ) = @_; $res = {%$res}; # unbless delete($res->{parser}); use Data::Dumper qw(); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Deparse = 1; local $Data::Dumper::Terse = 1; local $Data::Dumper::Trailingcomma = 1; local $Data::Dumper::Useperl = 1; local $Data::Dumper::Useqq = 0; # I want unicode visible my $s = Data::Dumper::Dumper($res); $s =~ s/\s+/ /gs; $s =~ s/, \}/ }/gs; $s =~ s/\s+$//; return $s; } __DATA__ 1 { bass => '', ext => '', ext_canon => '', name => 1, qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 17 { bass => '', ext => 7, ext_canon => 7, name => 17, qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1^ { bass => '', ext => '^', ext_canon => '^', name => '1^', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1h { bass => '', ext => 'h', ext_canon => 'h', name => '1h', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '1h7', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1^7 { bass => '', ext => '^7', ext_canon => '^7', name => '1^7', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1- { bass => '', ext => '', ext_canon => '', name => '1-', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1-7 { bass => '', ext => 7, ext_canon => 7, name => '1-7', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1-^ { bass => '', ext => '^', ext_canon => '^', name => '1-^', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1-h { bass => '', ext => 'h', ext_canon => 'h', name => '1-h', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '1-h7', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '1-^7', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 10 { bass => '', ext => '', ext_canon => '', name => 10, qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 107 { bass => '', ext => 7, ext_canon => 7, name => 107, qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 10^ { bass => '', ext => '^', ext_canon => '^', name => '10^', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 10h { bass => '', ext => 'h', ext_canon => 'h', name => '10h', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 10h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '10h7', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 10^7 { bass => '', ext => '^7', ext_canon => '^7', name => '10^7', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1+ { bass => '', ext => '', ext_canon => '', name => '1+', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1+7 { bass => '', ext => 7, ext_canon => 7, name => '1+7', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1+^ { bass => '', ext => '^', ext_canon => '^', name => '1+^', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1+h { bass => '', ext => 'h', ext_canon => 'h', name => '1+h', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '1+h7', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } 1+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '1+^7', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 0, root_ord => 0, system => 'nashville' } #1 { bass => '', ext => '', ext_canon => '', name => '#1', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #17 { bass => '', ext => 7, ext_canon => 7, name => '#17', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1^ { bass => '', ext => '^', ext_canon => '^', name => '#1^', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1h { bass => '', ext => 'h', ext_canon => 'h', name => '#1h', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#1h7', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#1^7', qual => '', qual_canon => '', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1- { bass => '', ext => '', ext_canon => '', name => '#1-', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1-7 { bass => '', ext => 7, ext_canon => 7, name => '#1-7', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1-^ { bass => '', ext => '^', ext_canon => '^', name => '#1-^', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1-h { bass => '', ext => 'h', ext_canon => 'h', name => '#1-h', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#1-h7', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#1-^7', qual => '-', qual_canon => '-', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #10 { bass => '', ext => '', ext_canon => '', name => '#10', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #107 { bass => '', ext => 7, ext_canon => 7, name => '#107', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #10^ { bass => '', ext => '^', ext_canon => '^', name => '#10^', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #10h { bass => '', ext => 'h', ext_canon => 'h', name => '#10h', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #10h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#10h7', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #10^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#10^7', qual => 0, qual_canon => 0, root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1+ { bass => '', ext => '', ext_canon => '', name => '#1+', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1+7 { bass => '', ext => 7, ext_canon => 7, name => '#1+7', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1+^ { bass => '', ext => '^', ext_canon => '^', name => '#1+^', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1+h { bass => '', ext => 'h', ext_canon => 'h', name => '#1+h', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#1+h7', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } #1+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#1+^7', qual => '+', qual_canon => '+', root => 1, root_canon => 1, root_mod => 1, root_ord => 1, system => 'nashville' } b2 { bass => '', ext => '', ext_canon => '', name => 'b2', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b27 { bass => '', ext => 7, ext_canon => 7, name => 'b27', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2^ { bass => '', ext => '^', ext_canon => '^', name => 'b2^', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2h { bass => '', ext => 'h', ext_canon => 'h', name => 'b2h', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b2h7', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b2^7', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2- { bass => '', ext => '', ext_canon => '', name => 'b2-', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2-7 { bass => '', ext => 7, ext_canon => 7, name => 'b2-7', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2-^ { bass => '', ext => '^', ext_canon => '^', name => 'b2-^', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2-h { bass => '', ext => 'h', ext_canon => 'h', name => 'b2-h', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b2-h7', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2-^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b2-^7', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b20 { bass => '', ext => '', ext_canon => '', name => 'b20', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b207 { bass => '', ext => 7, ext_canon => 7, name => 'b207', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b20^ { bass => '', ext => '^', ext_canon => '^', name => 'b20^', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b20h { bass => '', ext => 'h', ext_canon => 'h', name => 'b20h', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b20h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b20h7', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b20^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b20^7', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2+ { bass => '', ext => '', ext_canon => '', name => 'b2+', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2+7 { bass => '', ext => 7, ext_canon => 7, name => 'b2+7', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2+^ { bass => '', ext => '^', ext_canon => '^', name => 'b2+^', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2+h { bass => '', ext => 'h', ext_canon => 'h', name => 'b2+h', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b2+h7', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } b2+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b2+^7', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => -1, root_ord => 1, system => 'nashville' } 2 { bass => '', ext => '', ext_canon => '', name => 2, qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 27 { bass => '', ext => 7, ext_canon => 7, name => 27, qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2^ { bass => '', ext => '^', ext_canon => '^', name => '2^', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2h { bass => '', ext => 'h', ext_canon => 'h', name => '2h', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '2h7', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2^7 { bass => '', ext => '^7', ext_canon => '^7', name => '2^7', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2- { bass => '', ext => '', ext_canon => '', name => '2-', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2-7 { bass => '', ext => 7, ext_canon => 7, name => '2-7', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2-^ { bass => '', ext => '^', ext_canon => '^', name => '2-^', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2-h { bass => '', ext => 'h', ext_canon => 'h', name => '2-h', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '2-h7', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '2-^7', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 20 { bass => '', ext => '', ext_canon => '', name => 20, qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 207 { bass => '', ext => 7, ext_canon => 7, name => 207, qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 20^ { bass => '', ext => '^', ext_canon => '^', name => '20^', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 20h { bass => '', ext => 'h', ext_canon => 'h', name => '20h', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 20h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '20h7', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 20^7 { bass => '', ext => '^7', ext_canon => '^7', name => '20^7', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2+ { bass => '', ext => '', ext_canon => '', name => '2+', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2+7 { bass => '', ext => 7, ext_canon => 7, name => '2+7', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2+^ { bass => '', ext => '^', ext_canon => '^', name => '2+^', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2+h { bass => '', ext => 'h', ext_canon => 'h', name => '2+h', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '2+h7', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } 2+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '2+^7', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 0, root_ord => 2, system => 'nashville' } #2 { bass => '', ext => '', ext_canon => '', name => '#2', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #27 { bass => '', ext => 7, ext_canon => 7, name => '#27', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2^ { bass => '', ext => '^', ext_canon => '^', name => '#2^', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2h { bass => '', ext => 'h', ext_canon => 'h', name => '#2h', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#2h7', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#2^7', qual => '', qual_canon => '', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2- { bass => '', ext => '', ext_canon => '', name => '#2-', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2-7 { bass => '', ext => 7, ext_canon => 7, name => '#2-7', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2-^ { bass => '', ext => '^', ext_canon => '^', name => '#2-^', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2-h { bass => '', ext => 'h', ext_canon => 'h', name => '#2-h', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#2-h7', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#2-^7', qual => '-', qual_canon => '-', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #20 { bass => '', ext => '', ext_canon => '', name => '#20', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #207 { bass => '', ext => 7, ext_canon => 7, name => '#207', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #20^ { bass => '', ext => '^', ext_canon => '^', name => '#20^', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #20h { bass => '', ext => 'h', ext_canon => 'h', name => '#20h', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #20h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#20h7', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #20^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#20^7', qual => 0, qual_canon => 0, root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2+ { bass => '', ext => '', ext_canon => '', name => '#2+', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2+7 { bass => '', ext => 7, ext_canon => 7, name => '#2+7', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2+^ { bass => '', ext => '^', ext_canon => '^', name => '#2+^', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2+h { bass => '', ext => 'h', ext_canon => 'h', name => '#2+h', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#2+h7', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } #2+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#2+^7', qual => '+', qual_canon => '+', root => 2, root_canon => 2, root_mod => 1, root_ord => 3, system => 'nashville' } b3 { bass => '', ext => '', ext_canon => '', name => 'b3', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b37 { bass => '', ext => 7, ext_canon => 7, name => 'b37', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3^ { bass => '', ext => '^', ext_canon => '^', name => 'b3^', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3h { bass => '', ext => 'h', ext_canon => 'h', name => 'b3h', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b3h7', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b3^7', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3- { bass => '', ext => '', ext_canon => '', name => 'b3-', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3-7 { bass => '', ext => 7, ext_canon => 7, name => 'b3-7', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3-^ { bass => '', ext => '^', ext_canon => '^', name => 'b3-^', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3-h { bass => '', ext => 'h', ext_canon => 'h', name => 'b3-h', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b3-h7', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3-^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b3-^7', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b30 { bass => '', ext => '', ext_canon => '', name => 'b30', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b307 { bass => '', ext => 7, ext_canon => 7, name => 'b307', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b30^ { bass => '', ext => '^', ext_canon => '^', name => 'b30^', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b30h { bass => '', ext => 'h', ext_canon => 'h', name => 'b30h', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b30h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b30h7', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b30^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b30^7', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3+ { bass => '', ext => '', ext_canon => '', name => 'b3+', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3+7 { bass => '', ext => 7, ext_canon => 7, name => 'b3+7', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3+^ { bass => '', ext => '^', ext_canon => '^', name => 'b3+^', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3+h { bass => '', ext => 'h', ext_canon => 'h', name => 'b3+h', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b3+h7', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } b3+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b3+^7', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => -1, root_ord => 3, system => 'nashville' } 3 { bass => '', ext => '', ext_canon => '', name => 3, qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 37 { bass => '', ext => 7, ext_canon => 7, name => 37, qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3^ { bass => '', ext => '^', ext_canon => '^', name => '3^', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3h { bass => '', ext => 'h', ext_canon => 'h', name => '3h', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '3h7', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3^7 { bass => '', ext => '^7', ext_canon => '^7', name => '3^7', qual => '', qual_canon => '', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3- { bass => '', ext => '', ext_canon => '', name => '3-', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3-7 { bass => '', ext => 7, ext_canon => 7, name => '3-7', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3-^ { bass => '', ext => '^', ext_canon => '^', name => '3-^', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3-h { bass => '', ext => 'h', ext_canon => 'h', name => '3-h', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '3-h7', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '3-^7', qual => '-', qual_canon => '-', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 30 { bass => '', ext => '', ext_canon => '', name => 30, qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 307 { bass => '', ext => 7, ext_canon => 7, name => 307, qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 30^ { bass => '', ext => '^', ext_canon => '^', name => '30^', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 30h { bass => '', ext => 'h', ext_canon => 'h', name => '30h', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 30h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '30h7', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 30^7 { bass => '', ext => '^7', ext_canon => '^7', name => '30^7', qual => 0, qual_canon => 0, root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3+ { bass => '', ext => '', ext_canon => '', name => '3+', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3+7 { bass => '', ext => 7, ext_canon => 7, name => '3+7', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3+^ { bass => '', ext => '^', ext_canon => '^', name => '3+^', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3+h { bass => '', ext => 'h', ext_canon => 'h', name => '3+h', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '3+h7', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 3+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '3+^7', qual => '+', qual_canon => '+', root => 3, root_canon => 3, root_mod => 0, root_ord => 4, system => 'nashville' } 4 { bass => '', ext => '', ext_canon => '', name => 4, qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 47 { bass => '', ext => 7, ext_canon => 7, name => 47, qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4^ { bass => '', ext => '^', ext_canon => '^', name => '4^', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4h { bass => '', ext => 'h', ext_canon => 'h', name => '4h', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '4h7', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4^7 { bass => '', ext => '^7', ext_canon => '^7', name => '4^7', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4- { bass => '', ext => '', ext_canon => '', name => '4-', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4-7 { bass => '', ext => 7, ext_canon => 7, name => '4-7', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4-^ { bass => '', ext => '^', ext_canon => '^', name => '4-^', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4-h { bass => '', ext => 'h', ext_canon => 'h', name => '4-h', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '4-h7', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '4-^7', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 40 { bass => '', ext => '', ext_canon => '', name => 40, qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 407 { bass => '', ext => 7, ext_canon => 7, name => 407, qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 40^ { bass => '', ext => '^', ext_canon => '^', name => '40^', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 40h { bass => '', ext => 'h', ext_canon => 'h', name => '40h', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 40h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '40h7', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 40^7 { bass => '', ext => '^7', ext_canon => '^7', name => '40^7', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4+ { bass => '', ext => '', ext_canon => '', name => '4+', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4+7 { bass => '', ext => 7, ext_canon => 7, name => '4+7', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4+^ { bass => '', ext => '^', ext_canon => '^', name => '4+^', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4+h { bass => '', ext => 'h', ext_canon => 'h', name => '4+h', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '4+h7', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } 4+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '4+^7', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 0, root_ord => 5, system => 'nashville' } #4 { bass => '', ext => '', ext_canon => '', name => '#4', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #47 { bass => '', ext => 7, ext_canon => 7, name => '#47', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4^ { bass => '', ext => '^', ext_canon => '^', name => '#4^', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4h { bass => '', ext => 'h', ext_canon => 'h', name => '#4h', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#4h7', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#4^7', qual => '', qual_canon => '', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4- { bass => '', ext => '', ext_canon => '', name => '#4-', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4-7 { bass => '', ext => 7, ext_canon => 7, name => '#4-7', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4-^ { bass => '', ext => '^', ext_canon => '^', name => '#4-^', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4-h { bass => '', ext => 'h', ext_canon => 'h', name => '#4-h', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#4-h7', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#4-^7', qual => '-', qual_canon => '-', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #40 { bass => '', ext => '', ext_canon => '', name => '#40', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #407 { bass => '', ext => 7, ext_canon => 7, name => '#407', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #40^ { bass => '', ext => '^', ext_canon => '^', name => '#40^', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #40h { bass => '', ext => 'h', ext_canon => 'h', name => '#40h', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #40h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#40h7', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #40^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#40^7', qual => 0, qual_canon => 0, root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4+ { bass => '', ext => '', ext_canon => '', name => '#4+', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4+7 { bass => '', ext => 7, ext_canon => 7, name => '#4+7', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4+^ { bass => '', ext => '^', ext_canon => '^', name => '#4+^', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4+h { bass => '', ext => 'h', ext_canon => 'h', name => '#4+h', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#4+h7', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } #4+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#4+^7', qual => '+', qual_canon => '+', root => 4, root_canon => 4, root_mod => 1, root_ord => 6, system => 'nashville' } b5 { bass => '', ext => '', ext_canon => '', name => 'b5', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b57 { bass => '', ext => 7, ext_canon => 7, name => 'b57', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5^ { bass => '', ext => '^', ext_canon => '^', name => 'b5^', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5h { bass => '', ext => 'h', ext_canon => 'h', name => 'b5h', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b5h7', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b5^7', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5- { bass => '', ext => '', ext_canon => '', name => 'b5-', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5-7 { bass => '', ext => 7, ext_canon => 7, name => 'b5-7', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5-^ { bass => '', ext => '^', ext_canon => '^', name => 'b5-^', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5-h { bass => '', ext => 'h', ext_canon => 'h', name => 'b5-h', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b5-h7', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5-^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b5-^7', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b50 { bass => '', ext => '', ext_canon => '', name => 'b50', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b507 { bass => '', ext => 7, ext_canon => 7, name => 'b507', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b50^ { bass => '', ext => '^', ext_canon => '^', name => 'b50^', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b50h { bass => '', ext => 'h', ext_canon => 'h', name => 'b50h', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b50h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b50h7', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b50^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b50^7', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5+ { bass => '', ext => '', ext_canon => '', name => 'b5+', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5+7 { bass => '', ext => 7, ext_canon => 7, name => 'b5+7', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5+^ { bass => '', ext => '^', ext_canon => '^', name => 'b5+^', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5+h { bass => '', ext => 'h', ext_canon => 'h', name => 'b5+h', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b5+h7', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } b5+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b5+^7', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => -1, root_ord => 6, system => 'nashville' } 5 { bass => '', ext => '', ext_canon => '', name => 5, qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 57 { bass => '', ext => 7, ext_canon => 7, name => 57, qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5^ { bass => '', ext => '^', ext_canon => '^', name => '5^', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5h { bass => '', ext => 'h', ext_canon => 'h', name => '5h', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '5h7', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5^7 { bass => '', ext => '^7', ext_canon => '^7', name => '5^7', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5- { bass => '', ext => '', ext_canon => '', name => '5-', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5-7 { bass => '', ext => 7, ext_canon => 7, name => '5-7', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5-^ { bass => '', ext => '^', ext_canon => '^', name => '5-^', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5-h { bass => '', ext => 'h', ext_canon => 'h', name => '5-h', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '5-h7', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '5-^7', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 50 { bass => '', ext => '', ext_canon => '', name => 50, qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 507 { bass => '', ext => 7, ext_canon => 7, name => 507, qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 50^ { bass => '', ext => '^', ext_canon => '^', name => '50^', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 50h { bass => '', ext => 'h', ext_canon => 'h', name => '50h', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 50h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '50h7', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 50^7 { bass => '', ext => '^7', ext_canon => '^7', name => '50^7', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5+ { bass => '', ext => '', ext_canon => '', name => '5+', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5+7 { bass => '', ext => 7, ext_canon => 7, name => '5+7', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5+^ { bass => '', ext => '^', ext_canon => '^', name => '5+^', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5+h { bass => '', ext => 'h', ext_canon => 'h', name => '5+h', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '5+h7', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } 5+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '5+^7', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 0, root_ord => 7, system => 'nashville' } #5 { bass => '', ext => '', ext_canon => '', name => '#5', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #57 { bass => '', ext => 7, ext_canon => 7, name => '#57', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5^ { bass => '', ext => '^', ext_canon => '^', name => '#5^', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5h { bass => '', ext => 'h', ext_canon => 'h', name => '#5h', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#5h7', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#5^7', qual => '', qual_canon => '', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5- { bass => '', ext => '', ext_canon => '', name => '#5-', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5-7 { bass => '', ext => 7, ext_canon => 7, name => '#5-7', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5-^ { bass => '', ext => '^', ext_canon => '^', name => '#5-^', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5-h { bass => '', ext => 'h', ext_canon => 'h', name => '#5-h', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#5-h7', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#5-^7', qual => '-', qual_canon => '-', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #50 { bass => '', ext => '', ext_canon => '', name => '#50', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #507 { bass => '', ext => 7, ext_canon => 7, name => '#507', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #50^ { bass => '', ext => '^', ext_canon => '^', name => '#50^', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #50h { bass => '', ext => 'h', ext_canon => 'h', name => '#50h', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #50h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#50h7', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #50^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#50^7', qual => 0, qual_canon => 0, root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5+ { bass => '', ext => '', ext_canon => '', name => '#5+', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5+7 { bass => '', ext => 7, ext_canon => 7, name => '#5+7', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5+^ { bass => '', ext => '^', ext_canon => '^', name => '#5+^', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5+h { bass => '', ext => 'h', ext_canon => 'h', name => '#5+h', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#5+h7', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } #5+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#5+^7', qual => '+', qual_canon => '+', root => 5, root_canon => 5, root_mod => 1, root_ord => 8, system => 'nashville' } b6 { bass => '', ext => '', ext_canon => '', name => 'b6', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b67 { bass => '', ext => 7, ext_canon => 7, name => 'b67', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6^ { bass => '', ext => '^', ext_canon => '^', name => 'b6^', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6h { bass => '', ext => 'h', ext_canon => 'h', name => 'b6h', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b6h7', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b6^7', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6- { bass => '', ext => '', ext_canon => '', name => 'b6-', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6-7 { bass => '', ext => 7, ext_canon => 7, name => 'b6-7', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6-^ { bass => '', ext => '^', ext_canon => '^', name => 'b6-^', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6-h { bass => '', ext => 'h', ext_canon => 'h', name => 'b6-h', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b6-h7', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6-^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b6-^7', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b60 { bass => '', ext => '', ext_canon => '', name => 'b60', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b607 { bass => '', ext => 7, ext_canon => 7, name => 'b607', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b60^ { bass => '', ext => '^', ext_canon => '^', name => 'b60^', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b60h { bass => '', ext => 'h', ext_canon => 'h', name => 'b60h', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b60h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b60h7', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b60^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b60^7', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6+ { bass => '', ext => '', ext_canon => '', name => 'b6+', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6+7 { bass => '', ext => 7, ext_canon => 7, name => 'b6+7', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6+^ { bass => '', ext => '^', ext_canon => '^', name => 'b6+^', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6+h { bass => '', ext => 'h', ext_canon => 'h', name => 'b6+h', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b6+h7', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } b6+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b6+^7', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => -1, root_ord => 8, system => 'nashville' } 6 { bass => '', ext => '', ext_canon => '', name => 6, qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 67 { bass => '', ext => 7, ext_canon => 7, name => 67, qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6^ { bass => '', ext => '^', ext_canon => '^', name => '6^', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6h { bass => '', ext => 'h', ext_canon => 'h', name => '6h', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '6h7', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6^7 { bass => '', ext => '^7', ext_canon => '^7', name => '6^7', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6- { bass => '', ext => '', ext_canon => '', name => '6-', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6-7 { bass => '', ext => 7, ext_canon => 7, name => '6-7', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6-^ { bass => '', ext => '^', ext_canon => '^', name => '6-^', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6-h { bass => '', ext => 'h', ext_canon => 'h', name => '6-h', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '6-h7', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '6-^7', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 60 { bass => '', ext => '', ext_canon => '', name => 60, qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 607 { bass => '', ext => 7, ext_canon => 7, name => 607, qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 60^ { bass => '', ext => '^', ext_canon => '^', name => '60^', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 60h { bass => '', ext => 'h', ext_canon => 'h', name => '60h', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 60h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '60h7', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 60^7 { bass => '', ext => '^7', ext_canon => '^7', name => '60^7', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6+ { bass => '', ext => '', ext_canon => '', name => '6+', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6+7 { bass => '', ext => 7, ext_canon => 7, name => '6+7', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6+^ { bass => '', ext => '^', ext_canon => '^', name => '6+^', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6+h { bass => '', ext => 'h', ext_canon => 'h', name => '6+h', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '6+h7', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } 6+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '6+^7', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 0, root_ord => 9, system => 'nashville' } #6 { bass => '', ext => '', ext_canon => '', name => '#6', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #67 { bass => '', ext => 7, ext_canon => 7, name => '#67', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6^ { bass => '', ext => '^', ext_canon => '^', name => '#6^', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6h { bass => '', ext => 'h', ext_canon => 'h', name => '#6h', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#6h7', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#6^7', qual => '', qual_canon => '', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6- { bass => '', ext => '', ext_canon => '', name => '#6-', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6-7 { bass => '', ext => 7, ext_canon => 7, name => '#6-7', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6-^ { bass => '', ext => '^', ext_canon => '^', name => '#6-^', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6-h { bass => '', ext => 'h', ext_canon => 'h', name => '#6-h', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#6-h7', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#6-^7', qual => '-', qual_canon => '-', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #60 { bass => '', ext => '', ext_canon => '', name => '#60', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #607 { bass => '', ext => 7, ext_canon => 7, name => '#607', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #60^ { bass => '', ext => '^', ext_canon => '^', name => '#60^', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #60h { bass => '', ext => 'h', ext_canon => 'h', name => '#60h', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #60h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#60h7', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #60^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#60^7', qual => 0, qual_canon => 0, root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6+ { bass => '', ext => '', ext_canon => '', name => '#6+', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6+7 { bass => '', ext => 7, ext_canon => 7, name => '#6+7', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6+^ { bass => '', ext => '^', ext_canon => '^', name => '#6+^', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6+h { bass => '', ext => 'h', ext_canon => 'h', name => '#6+h', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '#6+h7', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } #6+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '#6+^7', qual => '+', qual_canon => '+', root => 6, root_canon => 6, root_mod => 1, root_ord => 10, system => 'nashville' } b7 { bass => '', ext => '', ext_canon => '', name => 'b7', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b77 { bass => '', ext => 7, ext_canon => 7, name => 'b77', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7^ { bass => '', ext => '^', ext_canon => '^', name => 'b7^', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7h { bass => '', ext => 'h', ext_canon => 'h', name => 'b7h', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b7h7', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b7^7', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7- { bass => '', ext => '', ext_canon => '', name => 'b7-', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7-7 { bass => '', ext => 7, ext_canon => 7, name => 'b7-7', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7-^ { bass => '', ext => '^', ext_canon => '^', name => 'b7-^', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7-h { bass => '', ext => 'h', ext_canon => 'h', name => 'b7-h', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b7-h7', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7-^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b7-^7', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b70 { bass => '', ext => '', ext_canon => '', name => 'b70', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b707 { bass => '', ext => 7, ext_canon => 7, name => 'b707', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b70^ { bass => '', ext => '^', ext_canon => '^', name => 'b70^', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b70h { bass => '', ext => 'h', ext_canon => 'h', name => 'b70h', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b70h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b70h7', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b70^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b70^7', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7+ { bass => '', ext => '', ext_canon => '', name => 'b7+', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7+7 { bass => '', ext => 7, ext_canon => 7, name => 'b7+7', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7+^ { bass => '', ext => '^', ext_canon => '^', name => 'b7+^', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7+h { bass => '', ext => 'h', ext_canon => 'h', name => 'b7+h', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => 'b7+h7', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } b7+^7 { bass => '', ext => '^7', ext_canon => '^7', name => 'b7+^7', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => -1, root_ord => 10, system => 'nashville' } 7 { bass => '', ext => '', ext_canon => '', name => 7, qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 77 { bass => '', ext => 7, ext_canon => 7, name => 77, qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7^ { bass => '', ext => '^', ext_canon => '^', name => '7^', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7h { bass => '', ext => 'h', ext_canon => 'h', name => '7h', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '7h7', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7^7 { bass => '', ext => '^7', ext_canon => '^7', name => '7^7', qual => '', qual_canon => '', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7- { bass => '', ext => '', ext_canon => '', name => '7-', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7-7 { bass => '', ext => 7, ext_canon => 7, name => '7-7', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7-^ { bass => '', ext => '^', ext_canon => '^', name => '7-^', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7-h { bass => '', ext => 'h', ext_canon => 'h', name => '7-h', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7-h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '7-h7', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7-^7 { bass => '', ext => '^7', ext_canon => '^7', name => '7-^7', qual => '-', qual_canon => '-', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 70 { bass => '', ext => '', ext_canon => '', name => 70, qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 707 { bass => '', ext => 7, ext_canon => 7, name => 707, qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 70^ { bass => '', ext => '^', ext_canon => '^', name => '70^', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 70h { bass => '', ext => 'h', ext_canon => 'h', name => '70h', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 70h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '70h7', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 70^7 { bass => '', ext => '^7', ext_canon => '^7', name => '70^7', qual => 0, qual_canon => 0, root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7+ { bass => '', ext => '', ext_canon => '', name => '7+', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7+7 { bass => '', ext => 7, ext_canon => 7, name => '7+7', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7+^ { bass => '', ext => '^', ext_canon => '^', name => '7+^', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7+h { bass => '', ext => 'h', ext_canon => 'h', name => '7+h', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7+h7 { bass => '', ext => 'h7', ext_canon => 'h7', name => '7+h7', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } 7+^7 { bass => '', ext => '^7', ext_canon => '^7', name => '7+^7', qual => '+', qual_canon => '+', root => 7, root_canon => 7, root_mod => 0, root_ord => 11, system => 'nashville' } App-Music-ChordPro-6.050.7/t/210_configs.t0000644000400000040000000000161414544300524015450 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Config; plan tests => 3; sub Config::new { my ( $pkg, $init ) = @_; bless { %$init } => 'ChordPro::Config'; } # Original content. my $orig = Config->new ( { a => { b => [ 'c', 'd' ], e => [[ 'f' ]] }, g => { h => 1, i => 1 } } ); # Actual content, initially a copy of original content. my $actual = Config->new ( { a => { b => [ 'c', 'd' ], e => [[ 'f' ]] }, g => { h => 1, i => 1 } } ); # Augmentation hash. my $aug = { a => { b => [ 'prepend', 'x' ], e => [ [ 'g' ] ] }, g => { i => 2 } }; # Expected new content. my $new = Config->new ( { a => { b => [ 'x', 'c', 'd' ], e => [[ 'g' ]] }, g => { h => 1, i => 2 } } ); is_deeply( $orig, $actual, "orig = actual" ); $actual->augment($aug); is_deeply( $actual, $new, "augmented" ); $actual->reduce($orig); is_deeply( $actual, $aug, "reduced" ); App-Music-ChordPro-6.050.7/t/105_chords.t0000644000400000040000000017056614544300524015322 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Chords; my %tbl; our $config = eval { ChordPro::Config::configurator ( { nosysconfig => 1, nolegacyconfig => 1, nouserconfig => 1, config => getresource("config/notes/common.json") } ); }; =for generating while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; my $res = ChordPro::Chords::parse_chord($c); unless ( $res ) { print( "$_\tFAIL\n"); next; } print("$chord\t", reformat($res), "\n"); } exit; =cut while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; $tbl{$c} = $info; } plan tests => 0 + keys(%tbl); while ( my ( $c, $info ) = each %tbl ) { my $res = ChordPro::Chords::parse_chord($c); $res //= "FAIL"; if ( UNIVERSAL::isa( $res, 'HASH' ) ) { $res = reformat($res); } is( $res, $info, "parsing chord $c"); } sub reformat { my ( $res ) = @_; $res = {%$res}; delete($res->{parser}); use Data::Dumper qw(); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Deparse = 1; local $Data::Dumper::Terse = 1; local $Data::Dumper::Trailingcomma = 1; local $Data::Dumper::Useperl = 1; local $Data::Dumper::Useqq = 0; # I want unicode visible my $s = Data::Dumper::Dumper($res); $s =~ s/\s+/ /gs; $s =~ s/, \}/ }/gs; $s =~ s/\s+$//; return $s; } __DATA__ C { bass => '', ext => '', ext_canon => '', name => 'C', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C+ { bass => '', ext => '', ext_canon => '', name => 'C+', qual => '+', qual_canon => '+', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C4 { bass => '', ext => 4, ext_canon => 4, name => 'C4', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C6 { bass => '', ext => 6, ext_canon => 6, name => 'C6', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C7 { bass => '', ext => 7, ext_canon => 7, name => 'C7', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C9 { bass => '', ext => 9, ext_canon => 9, name => 'C9', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C9(11) { bass => '', ext => 911, ext_canon => 911, name => 'C911', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C11 { bass => '', ext => 11, ext_canon => 11, name => 'C11', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Csus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Csus', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Csus2 { bass => '', ext => 'sus2', ext_canon => 'sus2', name => 'Csus2', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Csus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Csus4', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Csus9 { bass => '', ext => 'sus9', ext_canon => 'sus9', name => 'Csus9', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Cmaj { bass => '', ext => '', ext_canon => '', name => 'Cmaj', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Cmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Cmaj7', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Cm { bass => '', ext => '', ext_canon => '', name => 'Cm', qual => 'm', qual_canon => '-', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Cmin { bass => '', ext => '', ext_canon => '', name => 'Cmin', qual => 'min', qual_canon => '-', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Cdim { bass => '', ext => '', ext_canon => '', name => 'Cdim', qual => 'dim', qual_canon => 0, root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C/B { bass => 'B', bass_canon => 'B', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'C/B', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Cadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Cadd9', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C3 { bass => '', ext => 3, ext_canon => 3, name => 'C3', qual => '', qual_canon => '', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Cm7 { bass => '', ext => 7, ext_canon => 7, name => 'Cm7', qual => 'm', qual_canon => '-', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } Cm11 { bass => '', ext => 11, ext_canon => 11, name => 'Cm11', qual => 'm', qual_canon => '-', root => 'C', root_canon => 'C', root_mod => 0, root_ord => 0, system => 'common' } C# { bass => '', ext => '', ext_canon => '', name => 'C#', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#+ { bass => '', ext => '', ext_canon => '', name => 'C#+', qual => '+', qual_canon => '+', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#4 { bass => '', ext => 4, ext_canon => 4, name => 'C#4', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#7 { bass => '', ext => 7, ext_canon => 7, name => 'C#7', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#7(b5) { bass => '', ext => '7b5', ext_canon => '7b5', name => 'C#7b5', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'C#sus', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'C#sus4', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#maj { bass => '', ext => '', ext_canon => '', name => 'C#maj', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'C#maj7', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#dim { bass => '', ext => '', ext_canon => '', name => 'C#dim', qual => 'dim', qual_canon => 0, root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#m { bass => '', ext => '', ext_canon => '', name => 'C#m', qual => 'm', qual_canon => '-', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#min { bass => '', ext => '', ext_canon => '', name => 'C#min', qual => 'min', qual_canon => '-', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#add9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'C#add9', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#(add9) { bass => '', ext => 'add9', ext_canon => 'add9', name => 'C#add9', qual => '', qual_canon => '', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } C#m7 { bass => '', ext => 7, ext_canon => 7, name => 'C#m7', qual => 'm', qual_canon => '-', root => 'C#', root_canon => 'C#', root_mod => 1, root_ord => 1, system => 'common' } Db { bass => '', ext => '', ext_canon => '', name => 'Db', qual => '', qual_canon => '', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Db+ { bass => '', ext => '', ext_canon => '', name => 'Db+', qual => '+', qual_canon => '+', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Db7 { bass => '', ext => 7, ext_canon => 7, name => 'Db7', qual => '', qual_canon => '', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Dbsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Dbsus', qual => '', qual_canon => '', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Dbsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Dbsus4', qual => '', qual_canon => '', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Dbmaj { bass => '', ext => '', ext_canon => '', name => 'Dbmaj', qual => '', qual_canon => '', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Dbmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Dbmaj7', qual => '', qual_canon => '', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Dbdim { bass => '', ext => '', ext_canon => '', name => 'Dbdim', qual => 'dim', qual_canon => 0, root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Dbm { bass => '', ext => '', ext_canon => '', name => 'Dbm', qual => 'm', qual_canon => '-', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Dbmin { bass => '', ext => '', ext_canon => '', name => 'Dbmin', qual => 'min', qual_canon => '-', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } Dbm7 { bass => '', ext => 7, ext_canon => 7, name => 'Dbm7', qual => 'm', qual_canon => '-', root => 'Db', root_canon => 'Db', root_mod => -1, root_ord => 1, system => 'common' } D { bass => '', ext => '', ext_canon => '', name => 'D', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D+ { bass => '', ext => '', ext_canon => '', name => 'D+', qual => '+', qual_canon => '+', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D4 { bass => '', ext => 4, ext_canon => 4, name => 'D4', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D6 { bass => '', ext => 6, ext_canon => 6, name => 'D6', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D7 { bass => '', ext => 7, ext_canon => 7, name => 'D7', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'D7#9', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D7(#9) { bass => '', ext => '7#9', ext_canon => '7#9', name => 'D7#9', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D9 { bass => '', ext => 9, ext_canon => 9, name => 'D9', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D11 { bass => '', ext => 11, ext_canon => 11, name => 'D11', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Dsus', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dsus2 { bass => '', ext => 'sus2', ext_canon => 'sus2', name => 'Dsus2', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Dsus4', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D7sus2 { bass => '', ext => '7sus2', ext_canon => '7sus2', name => 'D7sus2', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'D7sus4', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dmaj { bass => '', ext => '', ext_canon => '', name => 'Dmaj', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Dmaj7', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Ddim { bass => '', ext => '', ext_canon => '', name => 'Ddim', qual => 'dim', qual_canon => 0, root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dm { bass => '', ext => '', ext_canon => '', name => 'Dm', qual => 'm', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dmin { bass => '', ext => '', ext_canon => '', name => 'Dmin', qual => 'min', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D/A { bass => 'A', bass_canon => 'A', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'D/A', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D/B { bass => 'B', bass_canon => 'B', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'D/B', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D/C { bass => 'C', bass_canon => 'C', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'D/C', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D/C# { bass => 'C#', bass_canon => 'C#', bass_mod => 1, bass_ord => 1, ext => '', ext_canon => '', name => 'D/C#', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D/E { bass => 'E', bass_canon => 'E', bass_mod => 0, bass_ord => 4, ext => '', ext_canon => '', name => 'D/E', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D/G { bass => 'G', bass_canon => 'G', bass_mod => 0, bass_ord => 7, ext => '', ext_canon => '', name => 'D/G', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D5/E { bass => 'E', bass_canon => 'E', bass_mod => 0, bass_ord => 4, ext => 5, ext_canon => 5, name => 'D5/E', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Dadd9', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D9add6 { bass => '', ext => '9add6', ext_canon => '9add6', name => 'D9add6', qual => '', qual_canon => '', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dm7 { bass => '', ext => 7, ext_canon => 7, name => 'Dm7', qual => 'm', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dm#5 { bass => '', ext => '#5', ext_canon => '#5', name => 'Dm#5', qual => 'm', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dm#7 { bass => '', ext => '#7', ext_canon => '#7', name => 'Dm#7', qual => 'm', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dm/A { bass => 'A', bass_canon => 'A', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'Dm/A', qual => 'm', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dm/B { bass => 'B', bass_canon => 'B', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Dm/B', qual => 'm', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dm/C { bass => 'C', bass_canon => 'C', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'Dm/C', qual => 'm', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dm/C# { bass => 'C#', bass_canon => 'C#', bass_mod => 1, bass_ord => 1, ext => '', ext_canon => '', name => 'Dm/C#', qual => 'm', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } Dm9 { bass => '', ext => 9, ext_canon => 9, name => 'Dm9', qual => 'm', qual_canon => '-', root => 'D', root_canon => 'D', root_mod => 0, root_ord => 2, system => 'common' } D# { bass => '', ext => '', ext_canon => '', name => 'D#', qual => '', qual_canon => '', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#+ { bass => '', ext => '', ext_canon => '', name => 'D#+', qual => '+', qual_canon => '+', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#4 { bass => '', ext => 4, ext_canon => 4, name => 'D#4', qual => '', qual_canon => '', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#7 { bass => '', ext => 7, ext_canon => 7, name => 'D#7', qual => '', qual_canon => '', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'D#sus', qual => '', qual_canon => '', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'D#sus4', qual => '', qual_canon => '', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#maj { bass => '', ext => '', ext_canon => '', name => 'D#maj', qual => '', qual_canon => '', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'D#maj7', qual => '', qual_canon => '', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#dim { bass => '', ext => '', ext_canon => '', name => 'D#dim', qual => 'dim', qual_canon => 0, root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#m { bass => '', ext => '', ext_canon => '', name => 'D#m', qual => 'm', qual_canon => '-', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#min { bass => '', ext => '', ext_canon => '', name => 'D#min', qual => 'min', qual_canon => '-', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } D#m7 { bass => '', ext => 7, ext_canon => 7, name => 'D#m7', qual => 'm', qual_canon => '-', root => 'D#', root_canon => 'D#', root_mod => 1, root_ord => 3, system => 'common' } Eb { bass => '', ext => '', ext_canon => '', name => 'Eb', qual => '', qual_canon => '', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Eb+ { bass => '', ext => '', ext_canon => '', name => 'Eb+', qual => '+', qual_canon => '+', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Eb4 { bass => '', ext => 4, ext_canon => 4, name => 'Eb4', qual => '', qual_canon => '', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Eb7 { bass => '', ext => 7, ext_canon => 7, name => 'Eb7', qual => '', qual_canon => '', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Ebsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Ebsus', qual => '', qual_canon => '', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Ebsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Ebsus4', qual => '', qual_canon => '', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Ebmaj { bass => '', ext => '', ext_canon => '', name => 'Ebmaj', qual => '', qual_canon => '', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Ebmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Ebmaj7', qual => '', qual_canon => '', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Ebdim { bass => '', ext => '', ext_canon => '', name => 'Ebdim', qual => 'dim', qual_canon => 0, root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Ebadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Ebadd9', qual => '', qual_canon => '', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Eb(add9) { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Ebadd9', qual => '', qual_canon => '', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Ebm { bass => '', ext => '', ext_canon => '', name => 'Ebm', qual => 'm', qual_canon => '-', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Ebmin { bass => '', ext => '', ext_canon => '', name => 'Ebmin', qual => 'min', qual_canon => '-', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } Ebm7 { bass => '', ext => 7, ext_canon => 7, name => 'Ebm7', qual => 'm', qual_canon => '-', root => 'Eb', root_canon => 'Eb', root_mod => -1, root_ord => 3, system => 'common' } E { bass => '', ext => '', ext_canon => '', name => 'E', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E+ { bass => '', ext => '', ext_canon => '', name => 'E+', qual => '+', qual_canon => '+', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E5 { bass => '', ext => 5, ext_canon => 5, name => 'E5', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E6 { bass => '', ext => 6, ext_canon => 6, name => 'E6', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E7 { bass => '', ext => 7, ext_canon => 7, name => 'E7', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'E7#9', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E7(#9) { bass => '', ext => '7#9', ext_canon => '7#9', name => 'E7#9', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E7(b5) { bass => '', ext => '7b5', ext_canon => '7b5', name => 'E7b5', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E7b9 { bass => '', ext => '7b9', ext_canon => '7b9', name => 'E7b9', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E7(11) { bass => '', ext => 711, ext_canon => 711, name => 'E711', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E9 { bass => '', ext => 9, ext_canon => 9, name => 'E9', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } E11 { bass => '', ext => 11, ext_canon => 11, name => 'E11', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Esus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Esus', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Emaj { bass => '', ext => '', ext_canon => '', name => 'Emaj', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Emaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Emaj7', qual => '', qual_canon => '', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Edim { bass => '', ext => '', ext_canon => '', name => 'Edim', qual => 'dim', qual_canon => 0, root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Em { bass => '', ext => '', ext_canon => '', name => 'Em', qual => 'm', qual_canon => '-', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Emin { bass => '', ext => '', ext_canon => '', name => 'Emin', qual => 'min', qual_canon => '-', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Em6 { bass => '', ext => 6, ext_canon => 6, name => 'Em6', qual => 'm', qual_canon => '-', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Em7 { bass => '', ext => 7, ext_canon => 7, name => 'Em7', qual => 'm', qual_canon => '-', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Em/B { bass => 'B', bass_canon => 'B', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Em/B', qual => 'm', qual_canon => '-', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Em/D { bass => 'D', bass_canon => 'D', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'Em/D', qual => 'm', qual_canon => '-', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Em7/D { bass => 'D', bass_canon => 'D', bass_mod => 0, bass_ord => 2, ext => 7, ext_canon => 7, name => 'Em7/D', qual => 'm', qual_canon => '-', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Emsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Emsus4', qual => 'm', qual_canon => '-', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } Emadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Emadd9', qual => 'm', qual_canon => '-', root => 'E', root_canon => 'E', root_mod => 0, root_ord => 4, system => 'common' } F { bass => '', ext => '', ext_canon => '', name => 'F', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F+ { bass => '', ext => '', ext_canon => '', name => 'F+', qual => '+', qual_canon => '+', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F4 { bass => '', ext => 4, ext_canon => 4, name => 'F4', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F6 { bass => '', ext => 6, ext_canon => 6, name => 'F6', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F7 { bass => '', ext => 7, ext_canon => 7, name => 'F7', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F9 { bass => '', ext => 9, ext_canon => 9, name => 'F9', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F11 { bass => '', ext => 11, ext_canon => 11, name => 'F11', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Fsus', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Fsus4', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fmaj { bass => '', ext => '', ext_canon => '', name => 'Fmaj', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Fmaj7', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fdim { bass => '', ext => '', ext_canon => '', name => 'Fdim', qual => 'dim', qual_canon => 0, root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fm { bass => '', ext => '', ext_canon => '', name => 'Fm', qual => 'm', qual_canon => '-', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fmin { bass => '', ext => '', ext_canon => '', name => 'Fmin', qual => 'min', qual_canon => '-', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F/A { bass => 'A', bass_canon => 'A', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'F/A', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F/C { bass => 'C', bass_canon => 'C', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'F/C', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F/D { bass => 'D', bass_canon => 'D', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'F/D', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F/G { bass => 'G', bass_canon => 'G', bass_mod => 0, bass_ord => 7, ext => '', ext_canon => '', name => 'F/G', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F7/A { bass => 'A', bass_canon => 'A', bass_mod => 0, bass_ord => 9, ext => 7, ext_canon => 7, name => 'F7/A', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fmaj7/A { bass => 'A', bass_canon => 'A', bass_mod => 0, bass_ord => 9, ext => 'maj7', ext_canon => 'maj7', name => 'Fmaj7/A', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fmaj7/C { bass => 'C', bass_canon => 'C', bass_mod => 0, bass_ord => 0, ext => 'maj7', ext_canon => 'maj7', name => 'Fmaj7/C', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Fadd9', qual => '', qual_canon => '', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fm6 { bass => '', ext => 6, ext_canon => 6, name => 'Fm6', qual => 'm', qual_canon => '-', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fm7 { bass => '', ext => 7, ext_canon => 7, name => 'Fm7', qual => 'm', qual_canon => '-', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } Fmmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Fmmaj7', qual => 'm', qual_canon => '-', root => 'F', root_canon => 'F', root_mod => 0, root_ord => 5, system => 'common' } F# { bass => '', ext => '', ext_canon => '', name => 'F#', qual => '', qual_canon => '', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#+ { bass => '', ext => '', ext_canon => '', name => 'F#+', qual => '+', qual_canon => '+', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#7 { bass => '', ext => 7, ext_canon => 7, name => 'F#7', qual => '', qual_canon => '', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#9 { bass => '', ext => 9, ext_canon => 9, name => 'F#9', qual => '', qual_canon => '', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#11 { bass => '', ext => 11, ext_canon => 11, name => 'F#11', qual => '', qual_canon => '', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'F#sus', qual => '', qual_canon => '', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'F#sus4', qual => '', qual_canon => '', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'F#maj7', qual => '', qual_canon => '', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#dim { bass => '', ext => '', ext_canon => '', name => 'F#dim', qual => 'dim', qual_canon => 0, root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#m { bass => '', ext => '', ext_canon => '', name => 'F#m', qual => 'm', qual_canon => '-', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#min { bass => '', ext => '', ext_canon => '', name => 'F#min', qual => 'min', qual_canon => '-', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#/E { bass => 'E', bass_canon => 'E', bass_mod => 0, bass_ord => 4, ext => '', ext_canon => '', name => 'F#/E', qual => '', qual_canon => '', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#4 { bass => '', ext => 4, ext_canon => 4, name => 'F#4', qual => '', qual_canon => '', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#m6 { bass => '', ext => 6, ext_canon => 6, name => 'F#m6', qual => 'm', qual_canon => '-', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#m7 { bass => '', ext => 7, ext_canon => 7, name => 'F#m7', qual => 'm', qual_canon => '-', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#m7b5 { bass => '', ext => '7b5', ext_canon => '7b5', name => 'F#m7b5', qual => 'm', qual_canon => '-', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } F#m/C { bass => 'C', bass_canon => 'C', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'F#m/C', qual => 'm', qual_canon => '-', root => 'F#', root_canon => 'F#', root_mod => 1, root_ord => 6, system => 'common' } Gb { bass => '', ext => '', ext_canon => '', name => 'Gb', qual => '', qual_canon => '', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gb+ { bass => '', ext => '', ext_canon => '', name => 'Gb+', qual => '+', qual_canon => '+', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gb7 { bass => '', ext => 7, ext_canon => 7, name => 'Gb7', qual => '', qual_canon => '', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gb9 { bass => '', ext => 9, ext_canon => 9, name => 'Gb9', qual => '', qual_canon => '', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gbsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Gbsus', qual => '', qual_canon => '', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gbsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Gbsus4', qual => '', qual_canon => '', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gbmaj { bass => '', ext => '', ext_canon => '', name => 'Gbmaj', qual => '', qual_canon => '', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gbmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Gbmaj7', qual => '', qual_canon => '', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gbdim { bass => '', ext => '', ext_canon => '', name => 'Gbdim', qual => 'dim', qual_canon => 0, root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gbm { bass => '', ext => '', ext_canon => '', name => 'Gbm', qual => 'm', qual_canon => '-', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gbmin { bass => '', ext => '', ext_canon => '', name => 'Gbmin', qual => 'min', qual_canon => '-', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } Gbm7 { bass => '', ext => 7, ext_canon => 7, name => 'Gbm7', qual => 'm', qual_canon => '-', root => 'Gb', root_canon => 'Gb', root_mod => -1, root_ord => 6, system => 'common' } G { bass => '', ext => '', ext_canon => '', name => 'G', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G+ { bass => '', ext => '', ext_canon => '', name => 'G+', qual => '+', qual_canon => '+', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G4 { bass => '', ext => 4, ext_canon => 4, name => 'G4', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G6 { bass => '', ext => 6, ext_canon => 6, name => 'G6', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G7 { bass => '', ext => 7, ext_canon => 7, name => 'G7', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G7b9 { bass => '', ext => '7b9', ext_canon => '7b9', name => 'G7b9', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'G7#9', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G9 { bass => '', ext => 9, ext_canon => 9, name => 'G9', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G9(11) { bass => '', ext => 911, ext_canon => 911, name => 'G911', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G11 { bass => '', ext => 11, ext_canon => 11, name => 'G11', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Gsus', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Gsus4', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G6sus4 { bass => '', ext => '6sus4', ext_canon => '6sus4', name => 'G6sus4', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'G7sus4', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gmaj { bass => '', ext => '', ext_canon => '', name => 'Gmaj', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Gmaj7', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gmaj7sus4 { bass => '', ext => 'maj7sus4', ext_canon => 'maj7sus4', name => 'Gmaj7sus4', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gmaj9 { bass => '', ext => 'maj9', ext_canon => 'maj9', name => 'Gmaj9', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gm { bass => '', ext => '', ext_canon => '', name => 'Gm', qual => 'm', qual_canon => '-', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gmin { bass => '', ext => '', ext_canon => '', name => 'Gmin', qual => 'min', qual_canon => '-', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gdim { bass => '', ext => '', ext_canon => '', name => 'Gdim', qual => 'dim', qual_canon => 0, root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Gadd9', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G(add9) { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Gadd9', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G/A { bass => 'A', bass_canon => 'A', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'G/A', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G/B { bass => 'B', bass_canon => 'B', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'G/B', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G/D { bass => 'D', bass_canon => 'D', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'G/D', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G/F# { bass => 'F#', bass_canon => 'F#', bass_mod => 1, bass_ord => 6, ext => '', ext_canon => '', name => 'G/F#', qual => '', qual_canon => '', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gm6 { bass => '', ext => 6, ext_canon => 6, name => 'Gm6', qual => 'm', qual_canon => '-', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gm7 { bass => '', ext => 7, ext_canon => 7, name => 'Gm7', qual => 'm', qual_canon => '-', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } Gm/Bb { bass => 'Bb', bass_canon => 'Bb', bass_mod => -1, bass_ord => 10, ext => '', ext_canon => '', name => 'Gm/Bb', qual => 'm', qual_canon => '-', root => 'G', root_canon => 'G', root_mod => 0, root_ord => 7, system => 'common' } G# { bass => '', ext => '', ext_canon => '', name => 'G#', qual => '', qual_canon => '', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#+ { bass => '', ext => '', ext_canon => '', name => 'G#+', qual => '+', qual_canon => '+', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#4 { bass => '', ext => 4, ext_canon => 4, name => 'G#4', qual => '', qual_canon => '', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#7 { bass => '', ext => 7, ext_canon => 7, name => 'G#7', qual => '', qual_canon => '', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'G#sus', qual => '', qual_canon => '', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'G#sus4', qual => '', qual_canon => '', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#maj { bass => '', ext => '', ext_canon => '', name => 'G#maj', qual => '', qual_canon => '', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'G#maj7', qual => '', qual_canon => '', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#dim { bass => '', ext => '', ext_canon => '', name => 'G#dim', qual => 'dim', qual_canon => 0, root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#m { bass => '', ext => '', ext_canon => '', name => 'G#m', qual => 'm', qual_canon => '-', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#min { bass => '', ext => '', ext_canon => '', name => 'G#min', qual => 'min', qual_canon => '-', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#m6 { bass => '', ext => 6, ext_canon => 6, name => 'G#m6', qual => 'm', qual_canon => '-', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#m7 { bass => '', ext => 7, ext_canon => 7, name => 'G#m7', qual => 'm', qual_canon => '-', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } G#m9maj7 { bass => '', ext => '9maj7', ext_canon => '9maj7', name => 'G#m9maj7', qual => 'm', qual_canon => '-', root => 'G#', root_canon => 'G#', root_mod => 1, root_ord => 8, system => 'common' } Ab { bass => '', ext => '', ext_canon => '', name => 'Ab', qual => '', qual_canon => '', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Ab+ { bass => '', ext => '', ext_canon => '', name => 'Ab+', qual => '+', qual_canon => '+', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Ab4 { bass => '', ext => 4, ext_canon => 4, name => 'Ab4', qual => '', qual_canon => '', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Ab7 { bass => '', ext => 7, ext_canon => 7, name => 'Ab7', qual => '', qual_canon => '', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Ab11 { bass => '', ext => 11, ext_canon => 11, name => 'Ab11', qual => '', qual_canon => '', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Absus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Absus', qual => '', qual_canon => '', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Absus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Absus4', qual => '', qual_canon => '', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Abdim { bass => '', ext => '', ext_canon => '', name => 'Abdim', qual => 'dim', qual_canon => 0, root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Abmaj { bass => '', ext => '', ext_canon => '', name => 'Abmaj', qual => '', qual_canon => '', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Abmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Abmaj7', qual => '', qual_canon => '', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Abm { bass => '', ext => '', ext_canon => '', name => 'Abm', qual => 'm', qual_canon => '-', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Abmin { bass => '', ext => '', ext_canon => '', name => 'Abmin', qual => 'min', qual_canon => '-', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } Abm7 { bass => '', ext => 7, ext_canon => 7, name => 'Abm7', qual => 'm', qual_canon => '-', root => 'Ab', root_canon => 'Ab', root_mod => -1, root_ord => 8, system => 'common' } A { bass => '', ext => '', ext_canon => '', name => 'A', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A+ { bass => '', ext => '', ext_canon => '', name => 'A+', qual => '+', qual_canon => '+', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A4 { bass => '', ext => 4, ext_canon => 4, name => 'A4', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A6 { bass => '', ext => 6, ext_canon => 6, name => 'A6', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A7 { bass => '', ext => 7, ext_canon => 7, name => 'A7', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A9 { bass => '', ext => 9, ext_canon => 9, name => 'A9', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A11 { bass => '', ext => 11, ext_canon => 11, name => 'A11', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A13 { bass => '', ext => 13, ext_canon => 13, name => 'A13', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'A7sus4', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A9sus { bass => '', ext => '9sus', ext_canon => '9sus', name => 'A9sus', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Asus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Asus', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Asus2 { bass => '', ext => 'sus2', ext_canon => 'sus2', name => 'Asus2', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Asus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Asus4', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Adim { bass => '', ext => '', ext_canon => '', name => 'Adim', qual => 'dim', qual_canon => 0, root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Amaj { bass => '', ext => '', ext_canon => '', name => 'Amaj', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Amaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Amaj7', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Am { bass => '', ext => '', ext_canon => '', name => 'Am', qual => 'm', qual_canon => '-', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Amin { bass => '', ext => '', ext_canon => '', name => 'Amin', qual => 'min', qual_canon => '-', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A/C# { bass => 'C#', bass_canon => 'C#', bass_mod => 1, bass_ord => 1, ext => '', ext_canon => '', name => 'A/C#', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A/D { bass => 'D', bass_canon => 'D', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'A/D', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A/E { bass => 'E', bass_canon => 'E', bass_mod => 0, bass_ord => 4, ext => '', ext_canon => '', name => 'A/E', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A/F# { bass => 'F#', bass_canon => 'F#', bass_mod => 1, bass_ord => 6, ext => '', ext_canon => '', name => 'A/F#', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A/G# { bass => 'G#', bass_canon => 'G#', bass_mod => 1, bass_ord => 8, ext => '', ext_canon => '', name => 'A/G#', qual => '', qual_canon => '', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Am#7 { bass => '', ext => '#7', ext_canon => '#7', name => 'Am#7', qual => 'm', qual_canon => '-', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Am6 { bass => '', ext => 6, ext_canon => 6, name => 'Am6', qual => 'm', qual_canon => '-', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Am7 { bass => '', ext => 7, ext_canon => 7, name => 'Am7', qual => 'm', qual_canon => '-', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Am7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'Am7sus4', qual => 'm', qual_canon => '-', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Am9 { bass => '', ext => 9, ext_canon => 9, name => 'Am9', qual => 'm', qual_canon => '-', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Am/G { bass => 'G', bass_canon => 'G', bass_mod => 0, bass_ord => 7, ext => '', ext_canon => '', name => 'Am/G', qual => 'm', qual_canon => '-', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } Amadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Amadd9', qual => 'm', qual_canon => '-', root => 'A', root_canon => 'A', root_mod => 0, root_ord => 9, system => 'common' } A# { bass => '', ext => '', ext_canon => '', name => 'A#', qual => '', qual_canon => '', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#+ { bass => '', ext => '', ext_canon => '', name => 'A#+', qual => '+', qual_canon => '+', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#4 { bass => '', ext => 4, ext_canon => 4, name => 'A#4', qual => '', qual_canon => '', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#7 { bass => '', ext => 7, ext_canon => 7, name => 'A#7', qual => '', qual_canon => '', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'A#sus', qual => '', qual_canon => '', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'A#sus4', qual => '', qual_canon => '', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#maj { bass => '', ext => '', ext_canon => '', name => 'A#maj', qual => '', qual_canon => '', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'A#maj7', qual => '', qual_canon => '', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#dim { bass => '', ext => '', ext_canon => '', name => 'A#dim', qual => 'dim', qual_canon => 0, root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#m { bass => '', ext => '', ext_canon => '', name => 'A#m', qual => 'm', qual_canon => '-', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#min { bass => '', ext => '', ext_canon => '', name => 'A#min', qual => 'min', qual_canon => '-', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } A#m7 { bass => '', ext => 7, ext_canon => 7, name => 'A#m7', qual => 'm', qual_canon => '-', root => 'A#', root_canon => 'A#', root_mod => 1, root_ord => 10, system => 'common' } Bb { bass => '', ext => '', ext_canon => '', name => 'Bb', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bb+ { bass => '', ext => '', ext_canon => '', name => 'Bb+', qual => '+', qual_canon => '+', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bb4 { bass => '', ext => 4, ext_canon => 4, name => 'Bb4', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bb6 { bass => '', ext => 6, ext_canon => 6, name => 'Bb6', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bb7 { bass => '', ext => 7, ext_canon => 7, name => 'Bb7', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bb9 { bass => '', ext => 9, ext_canon => 9, name => 'Bb9', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bb11 { bass => '', ext => 11, ext_canon => 11, name => 'Bb11', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bbsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Bbsus', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bbsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Bbsus4', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bbmaj { bass => '', ext => '', ext_canon => '', name => 'Bbmaj', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bbmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Bbmaj7', qual => '', qual_canon => '', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bbdim { bass => '', ext => '', ext_canon => '', name => 'Bbdim', qual => 'dim', qual_canon => 0, root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bbm { bass => '', ext => '', ext_canon => '', name => 'Bbm', qual => 'm', qual_canon => '-', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bbmin { bass => '', ext => '', ext_canon => '', name => 'Bbmin', qual => 'min', qual_canon => '-', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bbm7 { bass => '', ext => 7, ext_canon => 7, name => 'Bbm7', qual => 'm', qual_canon => '-', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } Bbm9 { bass => '', ext => 9, ext_canon => 9, name => 'Bbm9', qual => 'm', qual_canon => '-', root => 'Bb', root_canon => 'Bb', root_mod => -1, root_ord => 10, system => 'common' } B { bass => '', ext => '', ext_canon => '', name => 'B', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } B+ { bass => '', ext => '', ext_canon => '', name => 'B+', qual => '+', qual_canon => '+', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } B4 { bass => '', ext => 4, ext_canon => 4, name => 'B4', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } B7 { bass => '', ext => 7, ext_canon => 7, name => 'B7', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } B7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'B7#9', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } B9 { bass => '', ext => 9, ext_canon => 9, name => 'B9', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } B11 { bass => '', ext => 11, ext_canon => 11, name => 'B11', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } B13 { bass => '', ext => 13, ext_canon => 13, name => 'B13', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Bsus', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Bsus4', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bmaj { bass => '', ext => '', ext_canon => '', name => 'Bmaj', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Bmaj7', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bdim { bass => '', ext => '', ext_canon => '', name => 'Bdim', qual => 'dim', qual_canon => 0, root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bm { bass => '', ext => '', ext_canon => '', name => 'Bm', qual => 'm', qual_canon => '-', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bmin { bass => '', ext => '', ext_canon => '', name => 'Bmin', qual => 'min', qual_canon => '-', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } B/F# { bass => 'F#', bass_canon => 'F#', bass_mod => 1, bass_ord => 6, ext => '', ext_canon => '', name => 'B/F#', qual => '', qual_canon => '', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bm6 { bass => '', ext => 6, ext_canon => 6, name => 'Bm6', qual => 'm', qual_canon => '-', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bm7 { bass => '', ext => 7, ext_canon => 7, name => 'Bm7', qual => 'm', qual_canon => '-', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bmmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Bmmaj7', qual => 'm', qual_canon => '-', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bmsus9 { bass => '', ext => 'sus9', ext_canon => 'sus9', name => 'Bmsus9', qual => 'm', qual_canon => '-', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } Bm7b5 { bass => '', ext => '7b5', ext_canon => '7b5', name => 'Bm7b5', qual => 'm', qual_canon => '-', root => 'B', root_canon => 'B', root_mod => 0, root_ord => 11, system => 'common' } App-Music-ChordPro-6.050.7/t/200_markup.t0000644000400000040000000000110514544300524015311 0ustar jvjv#!/usr/bin/perl # https://github.com/ChordPro/chordpro/issues/111 use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Output::PDF; plan tests => 2; *defrag = \&ChordPro::Output::PDF::defrag; my $res; $res = defrag( [ "Comin’ for to carry me ", "home." ] ); my $xp = [ "Comin’ for to carry me ", "home." ]; is_deeply( $res, $xp, "defrag1"); $res = defrag( [ "Comin’ for to carry me ", "home." ] ); $xp = [ "Comin’ for to carry me ", "home." ]; is_deeply( $res, $xp, "defrag2"); App-Music-ChordPro-6.050.7/t/194_outlines.t0000644000400000040000000000573614544302032015702 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; use ChordPro::Output::Common; plan tests => 3; # For transcoding, both source and target notation systems must be # defined. The source system must be last, so it is current and used # to parse the the input data. my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 3, "three songs" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $book = [ map { { meta => $_->{meta} } } @{$s->{songs}} ]; # Test reverse sort. my $ctl = { fields => [ qw( -key ) ] }; my $res = ChordPro::Output::Common::prep_outlines( $book, $ctl ); my $xp = [ [ 'd', { meta => { artist => [ 'September', 'December', ], chords => [ ], key => [ 'D', ], key_actual => [ 'D', ], numchords => [ 0, ], songindex => 2, sorttitle => [ 'Vierentwintig Fietsen', ], title => [ '24 Fietsen', ], }, }, ], [ 'c', { meta => { artist => [ 'September', 'December', ], chords => [ ], key => [ 'C', ], key_actual => [ 'C', ], numchords => [ 0, ], songindex => 3, sorttitle => [ 'Fietsenhok, Het', 'Fietsenstalling, De', ], title => [ 'Het Fietsenhok', 'De Fietsenstalling', ], }, }, ], [ 'am', { meta => { artist => [ 'September', ], chords => [ ], key => [ 'Am', ], key_actual => [ 'Am', ], numchords => [ 0, ], songindex => 1, sorttitle => [ 'Fietspomp, De', ], title => [ 'De Fietspomp', ], }, }, ], [ 'a', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'A', ], key_actual => [ 'Am', ], songindex => 1, sorttitle => [ 'Fietspomp, De', ], title => [ 'De Fietspomp', ], }, }, ], ]; foreach ( @$res ) { delete $_->[1]->{meta}->{_configversion}; } is_deeply( $res, $xp, "outlined"); App-Music-ChordPro-6.050.7/t/162_newpage.t0000644000400000040000000000412314544300524015452 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 12; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # New page. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'body' => [ { 'context' => '', 'type' => 'newpage' } ], }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "New page" ); $s = ChordPro::Songbook->new; # New page. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); is_deeply( { %{ $s->{songs}->[0] } }, $song, "Newpage (np)" ); $s = ChordPro::Songbook->new; # New physical page. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); is_deeply( { %{ $s->{songs}->[0] } }, $song, "New physical page" ); $s = ChordPro::Songbook->new; # New physical page. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); is_deeply( { %{ $s->{songs}->[0] } }, $song, "New physical page (npp)" ); App-Music-ChordPro-6.050.7/t/140_chords.t0000644000400000040000000000502714544300524015306 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 6; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Chord definitions. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'define' => [ { 'name' => 'Fus', 'base' => '2', 'frets' => [ -1, '0', '3', '2', '1', '0' ], }, { 'name' => 'Fos', 'base' => '2', 'frets' => [ -1, '0', '3', '2', '1', '0' ], }, { 'name' => 'Fas', 'base' => '2', 'frets' => [ -1, '0', '3', '2', '1', '0' ], } ], 'chordsinfo' => { map { $_ => $_ } qw( Fus Fos Fas ) }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); $s = ChordPro::Songbook->new; # Chord definitions. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'define' => [ { 'name' => 'Fus', 'base' => '1', 'frets' => [ -1, '0', '3', '2', '1', '0' ], }, { 'name' => 'Fos', 'base' => '1', 'frets' => [ -1, '0', '3', '2', '1', '0' ], }, { 'name' => 'Fas', 'base' => '1', 'frets' => [ -1, '0', '3', '2', '1', '0' ], } ], 'chordsinfo' => { map { $_ => $_ } qw( Fus Fos Fas ) }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/315_escape.t0000644000400000040000000000233314544300524015265 0ustar jvjv#! perl use Test::More tests => 3; use ChordPro::Config::Properties; use utf8; my $cfg; $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ] ); a = "aa\nbb" b = "aa\\nbb" c = "aa\\\nbb" d = "aa\\\\nbb" e = "aa\\\\\nbb" EOD is_deeply( $cfg->data, { a => "aa\nbb", b => "aa\\nbb", c => "aa\\\nbb", d => "aa\\\\nbb", e => "aa\\\\\nbb", } ); $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ] ); a0 = aa\07bb a1 = "aa\07bb" a2 = "aa\1bb" a3 = "aa\11bb" a4 = "aa\111bb" a5 = "aa\1111bb" a6 = "aa\01111bb" b = "aa\x9bb" c = "aa\x{20ce}bb" EOD is_deeply( $cfg->data, { a0 => "aa\\07bb", a1 => "aa\07bb", a2 => "aa\1bb", a3 => "aa\11bb", a4 => "aa\111bb", a5 => "aa\1111bb", a6 => "aa\01111bb", b => "aa\x9bb", c => "aa\x{20ce}bb", } ); $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ] ); a0 = aa'bb a1 = aa\bb a2 = 'aa\'bb' a3 = 'aa\\bb' f1 = a'\nb f2 = 'a\'\nb' f3 = "a'\\nb" EOD is_deeply( $cfg->data, { a0 => "aa'bb", a1 => 'aa\\bb', a2 => "aa'bb", a3 => 'aa\\bb', f1 => 'a\'\nb', f2 => 'a\'\nb', f3 => 'a\'\nb', } ); App-Music-ChordPro-6.050.7/t/118_tab.t0000644000400000040000000000224414544300524014575 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Tabs. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'context' => 'tab', 'text' => 'Swing [D]low, sweet [G]chari[D]ot,', 'type' => 'tabline' }, { 'value' => '', 'context' => 'tab', 'name' => 'context', 'type' => 'set' } ], 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/latex/0000755000400000040000000000000014567360037014376 5ustar jvjvApp-Music-ChordPro-6.050.7/t/latex/t_image.tt0000644000400000040000000000001114240671432016334 0ustar jvjv[% uri %]App-Music-ChordPro-6.050.7/t/latex/table/0000755000400000040000000000000014567360037015465 5ustar jvjvApp-Music-ChordPro-6.050.7/t/latex/table/edge0000644000400000040000000000043214324567577016324 0ustar jvjv
[%- content -%]
App-Music-ChordPro-6.050.7/t/latex/table/head0000644000400000040000000000010714324567577016320 0ustar jvjv [% content %]: App-Music-ChordPro-6.050.7/t/latex/table/cell0000644000400000040000000000007714324567577016344 0ustar jvjv [% content %] App-Music-ChordPro-6.050.7/t/latex/table/row0000644000400000040000000000005014324567577016223 0ustar jvjv [% content %] App-Music-ChordPro-6.050.7/t/latex/debug/0000755000400000040000000000000014567360037015464 5ustar jvjvApp-Music-ChordPro-6.050.7/t/latex/debug/struct0000644000400000040000000000034514324567577016746 0ustar jvjv[% WRAPPER table/row; INCLUDE table/head content=key; WRAPPER table/cell; IF val.keys; INCLUDE debug/hash hash=val; ELSIF val.0 != ""; INCLUDE debug/array array=val; ELSE; val; END; END; END; -%] App-Music-ChordPro-6.050.7/t/latex/debug/array0000644000400000040000000000020314324567577016531 0ustar jvjv[% WRAPPER table/edge; i = 0; FOREACH val IN array; INCLUDE debug/struct key=i val=val; i = i + 1; END; END; -%] App-Music-ChordPro-6.050.7/t/latex/debug/hash0000644000400000040000000000017414324567577016345 0ustar jvjv[% WRAPPER table/edge; FOREACH key = hash.keys.sort; INCLUDE debug/struct key=key val=hash.$key; END; END; -%] App-Music-ChordPro-6.050.7/t/latex/30_cho_2.cho0000644000400000040000000000264414240671432016363 0ustar jvjv{title: Swing Low Sweet Chariot} {subtitle: Traditional} {lyricist: mylyricist} {composer: mycomposer} {define: D base-fret 1 frets X X 0 2 3 2 } {define: G base-fret 1 frets 3 2 0 0 0 3 } {define: A7 base-fret 1 frets X 0 2 0 2 0 } {define: D7 base-fret 1 frets X X 0 2 1 2 } {start_of_tab} e|---R-----R-R--------| B|-----M-------M---M--| G|-------Z-------Z----| D|---D-----D-D--------| A|--------------------| E|--------------------| {eot} {start_of_chorus} Swing [D]low, sweet [G]chari[D]ot, Comin’ for to carry me [A7]home. Swing [D7]low, sweet [G]chari[D]ot, Comin’ for to [A7]carry me [D]home. {end_of_chorus} I [D]looked over Jordan, and [G]what did I [D]see, “Comin’ for to carry me [A7]home.” A [D7]band of angels [G]comin’ after [D]me, Comin’ for to [A7]carry me [D]home. [G] [D] [G] {new_page} {comment: Chorus} If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. {column_break} {comment: Chorus} # Note the ' here will be changed into a decent apostrophe. I’m sometimes up and sometimes down, Comin’ for to carry me home. But still my soul feels heavenly bound. Comin’ for to carry me home. {comment: Chorus} {comment_italic: And a final chorus} App-Music-ChordPro-6.050.7/t/latex/30_cho_1.cho0000644000400000040000000000236214240671432016357 0ustar jvjv{title: Swing Low Sweet Chariot} {subtitle: Traditional} {columns: 2} {titles: center} {define: D base-fret 1 frets X X 0 2 3 2 } {define: G base-fret 1 frets 3 2 0 0 0 3 } {define: A7 base-fret 1 frets X 0 2 0 2 0 } {define: D7 base-fret 1 frets X X 0 2 1 2 } {start_of_chorus} Swing [D]low, sweet [G]chari[D]ot, Comin’ for to carry me [A7]home. Swing [D7]low, sweet [G]chari[D]ot, Comin’ for to [A7]carry me [D]home. {end_of_chorus} I [D]looked over Jordan, and [G]what did I [D]see, “Comin’ for to carry me [A7]home.” A [D7]band of angels [G]comin’ after [D]me, Comin’ for to [A7]carry me [D]home. [G]     [D] [G] {new_page} {comment: Chorus} If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. {column_break} {comment: Chorus} # Note the ' here will be changed into a decent apostrophe. I’m sometimes up and sometimes down, Comin’ for to carry me home. But still my soul feels heavenly bound. Comin’ for to carry me home. {comment: Chorus} {comment_italic: And a final chorus} App-Music-ChordPro-6.050.7/t/latex/t_songbook.tt0000644000400000040000000000177714240671432017117 0ustar jvjv[% # these variables are obligatory to set newpage_tag = "newpage\n"; emptyline_tag = "emptyline\n"; columnbreak_tag = "colbreak\n"; beginchorus_tag = "beginchorus\n"; endchorus_tag = "endchorus\n"; beginverse_tag = "beginverse\n"; endverse_tag = "endverse\n"; begingrid_tag = "begingrid\n"; endgrid_tag = "endgrid\n"; begintab_tag = "begintab\n"; endtab_tag = "endtab\n"; gchordstart_tag = "["; gchordend_tag = "]"; chorded_line = "wc "; unchorded_line = ""; start_spaces_songline = " "; eol = "\n"; beginabc_tag = "begin_abc\n"; endabc_tag = "end_abc\n"; beginlilypond_tag = "begin_lilypond\n"; endlilypond_tag = "end_lilypond\n"; FOREACH song IN songs; title; "\n"; FOREACH st IN song.subtitle; st; "\n"; END; song.meta.composer.0; "\n"; song.meta.lyricist.0; "\n"; song.meta.copyright.0; "\n"; IF song.meta.capo.0; "Capo "; song.meta.capo.0; "\n"; END; FOREACH chord IN song.chords; chord.chord; " "; chord.frets; "\n"; END; song.songlines | eval; END; -%] App-Music-ChordPro-6.050.7/t/latex/30_cho_1.tex0000644000400000040000000000206614240671432016407 0ustar jvjv Traditional D XX0232 G 320003 A7 X02020 D7 XX0212 beginchorus wc Swing [D]low, sweet [G]chari[D]ot, wc Comin{\textquoteright} for to carry me [A7]home. wc Swing [D7]low, sweet [G]chari[D]ot, wc Comin{\textquoteright} for to [A7]carry me [D]home. endchorus beginverse wc I [D]looked over Jordan, and [G]what did I [D]see, wc {\textquotedblleft}Comin{\textquoteright} for to carry me [A7]home.{\textquotedblright} wc A [D7]band of angels [G]comin{\textquoteright} after [D]me, wc Comin{\textquoteright} for to [A7]carry me [D]home. [G]~~~~~[D] [G] endverse newpage Chorus beginverse If you get there before I do, Comin{\textquoteright} for to carry me home. Just tell my friends that I{\textquoteright}m a comin{\textquoteright} too. Comin{\textquoteright} for to carry me home. endverse colbreak Chorus beginverse I{\textquoteright}m sometimes up and sometimes down, Comin{\textquoteright} for to carry me home. But still my soul feels heavenly bound. Comin{\textquoteright} for to carry me home. endverse Chorus \textit{And a final chorus} App-Music-ChordPro-6.050.7/t/latex/t_comment.tt0000644000400000040000000000001614240671432016721 0ustar jvjv[% comment %] App-Music-ChordPro-6.050.7/t/latex/t_config.json0000644000400000040000000000043214240671432017050 0ustar jvjv{ "latex" : { // please be aware that LaTeX Song Package require Verse "template_include_path" : ["./latex/"], "templates" : { "songbook" : "t_songbook.tt", "comment" : "t_comment.tt", "image" : "t_image.tt" } } }App-Music-ChordPro-6.050.7/t/latex/30_cho_2.tex0000644000400000040000000000235414240671432016410 0ustar jvjv Traditional mycomposer mylyricist D XX0232 G 320003 A7 X02020 D7 XX0212 begintab e|---R-----R-R--------| B|-----M-------M---M--| G|-------Z-------Z----| D|---D-----D-D--------| A|--------------------| E|--------------------| endtab beginchorus wc Swing [D]low, sweet [G]chari[D]ot, wc Comin{\textquoteright} for to carry me [A7]home. wc Swing [D7]low, sweet [G]chari[D]ot, wc Comin{\textquoteright} for to [A7]carry me [D]home. endchorus beginverse wc I [D]looked over Jordan, and [G]what did I [D]see, wc {\textquotedblleft}Comin{\textquoteright} for to carry me [A7]home.{\textquotedblright} wc A [D7]band of angels [G]comin{\textquoteright} after [D]me, wc Comin{\textquoteright} for to [A7]carry me [D]home. [G] [D] [G] endverse newpage Chorus beginverse If you get there before I do, Comin{\textquoteright} for to carry me home. Just tell my friends that I{\textquoteright}m a comin{\textquoteright} too. Comin{\textquoteright} for to carry me home. endverse colbreak Chorus beginverse I{\textquoteright}m sometimes up and sometimes down, Comin{\textquoteright} for to carry me home. But still my soul feels heavenly bound. Comin{\textquoteright} for to carry me home. endverse Chorus \textit{And a final chorus} App-Music-ChordPro-6.050.7/t/117_rechorus.t0000644000400000040000000000362714544300524015666 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Recall chorus. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'chords' => [ '', 'D', 'G', 'D' ], 'context' => 'chorus', 'phrases' => [ 'Swing ', 'low, sweet ', 'chari', 'ot,' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, { 'chorus' => [ { 'chords' => [ '', 'D', 'G', 'D' ], 'context' => 'chorus', 'phrases' => [ 'Swing ', 'low, sweet ', 'chari', 'ot,' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' } ], 'context' => '', 'type' => 'rechorus', } ], 'chordsinfo' => { map { $_ => $_ } qw( D G ) }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/130_image.jpg0000644000400000040000000000050514544300524015416 0ustar jvjvJFIFC       C 22  ?Ʃ?App-Music-ChordPro-6.050.7/t/132_image.t0000644000400000040000000001251714544302032015105 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Image (minimal). my $data = < {end_of_svg} {image id=green} {c Green box above (centered)} {start_of_svg} center=0 {end_of_svg} {c Blue box above (centered)} ##image: id=yellow persist=1 # /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkI # CQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQ # EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAyADIDAREA # AhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAn/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFgEB # AQEAAAAAAAAAAAAAAAAAAAYJ/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEQMRAD8AoKyq # XAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//2Q== ##asset: id=red type=svg persist=1 # # # EOD eval { $s->parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use DDP; p $s->{songs}->[0]; delete( $s->{songs}->[0]->{assets}->{yellow}->{data} ) if $s->{songs}->[0]->{assets}->{yellow}->{data} =~ /^\xff\xd8\xff\xe0/; my $song = { 'assets' => { 'yellow' => { 'height' => 50, 'width' => 50, 'type' => 'image', 'subtype' => 'jpg', opts => { id => 'yellow', 'persist' => 1 } }, 'red' => { 'data' => [ '', '', '' ], 'handler' => 'svg2svg', 'type' => 'image', 'subtype' => 'svg', opts => { id => 'red', type => 'svg', 'persist' => 1 }, 'module' => 'SVG' }, 'green' => { 'delegate' => 'SVG', 'data' => [ '', '', '' ], 'opts' => { 'id' => 'green', 'center' => '0' }, 'type' => 'image', 'handler' => 'svg2svg', 'subtype' => 'delegate' }, '_Image001' => { 'handler' => 'svg2svg', 'type' => 'image', 'subtype' => 'delegate', 'delegate' => 'SVG', 'data' => [ '', '', '' ], 'opts' => { 'center' => '0' } } }, 'meta' => { 'songindex' => 1, 'chords' => [], 'numchords' => [ 0 ], 'title' => [ 'Testing images' ] }, 'settings' => {}, 'title' => 'Testing images', 'chordsinfo' => {}, 'source' => { 'file' => '__STRING__', 'line' => 1 }, 'body' => [ { 'context' => '', 'type' => 'empty' }, { 'context' => '', 'opts' => { 'border' => '1', 'anchor' => 'line', 'scale' => '0.5', 'x' => '100%' }, 'id' => 'yellow', 'type' => 'image' }, { 'id' => 'yellow', 'type' => 'image', 'opts' => { 'border' => '1', 'scale' => '0.5', 'anchor' => 'line', 'x' => '-30' }, 'context' => '' }, { 'orig' => 'This line has two image hint', 'text' => 'This line has two image hint', 'type' => 'comment', 'context' => '' }, { 'type' => 'empty', 'context' => '' }, { 'value' => '', 'name' => 'context', 'type' => 'set', 'context' => 'svg' }, { 'context' => '', 'type' => 'empty' }, { 'opts' => {}, 'id' => 'green', 'type' => 'image', 'context' => '' }, { 'text' => 'Green box above (centered)', 'type' => 'comment', 'context' => '', 'orig' => 'Green box above (centered)' }, { 'type' => 'empty', 'context' => '' }, { 'context' => 'svg', 'type' => 'image', 'id' => '_Image001', 'opts' => { 'center' => '0' } }, { 'context' => 'svg', 'type' => 'set', 'name' => 'context', 'value' => '' }, { 'orig' => 'Blue box above (centered)', 'context' => '', 'type' => 'comment', 'text' => 'Blue box above (centered)' }, { 'type' => 'empty', 'context' => '' } ], 'structure' => 'linear', 'system' => 'common' }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/192_outlines.t0000644000400000040000000001133314544302032015666 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; use ChordPro::Output::Common; plan tests => 4; # For transcoding, both source and target notation systems must be # defined. The source system must be last, so it is current and used # to parse the the input data. ok( $config, "got config" ); my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 3, "three songs" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $book = [ map { { meta => $_->{meta} } } @{$s->{songs}} ]; my $ctl = { fields => [ qw( title artist ) ] }; my $res = ChordPro::Output::Common::prep_outlines( $book, $ctl ); my $xp = [ [ '24 fietsen', 'december', { meta => { artist => [ 'December', ], chords => [ ], numchords => [ 0, ], key => [ 'D', ], key_actual => [ 'D', ], songindex => 2, sortartist => [ 'December', ], sorttitle => [ 'Vierentwintig Fietsen', ], title => [ '24 Fietsen', ], }, }, ], [ '24 fietsen', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'D', ], key_actual => [ 'D', ], songindex => 2, sortartist => [ 'September', ], sorttitle => [ 'Vierentwintig Fietsen', ], title => [ '24 Fietsen', ], }, }, ], [ 'de fietsenstalling', 'december', { meta => { artist => [ 'December', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'December', ], sorttitle => [ 'Fietsenstalling, De', ], title => [ 'De Fietsenstalling', ], }, }, ], [ 'de fietsenstalling', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'September', ], sorttitle => [ 'Fietsenstalling, De', ], title => [ 'De Fietsenstalling', ], }, }, ], [ 'de fietspomp', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'A', 'Am', ], key_actual => [ 'Am', ], songindex => 1, sortartist => [ 'September', ], sorttitle => [ 'Fietspomp, De', ], title => [ 'De Fietspomp', ], }, }, ], [ 'het fietsenhok', 'december', { meta => { artist => [ 'December', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'December', ], sorttitle => [ 'Fietsenhok, Het', ], title => [ 'Het Fietsenhok', ], }, }, ], [ 'het fietsenhok', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'September', ], sorttitle => [ 'Fietsenhok, Het', ], title => [ 'Het Fietsenhok', ], }, }, ], ]; foreach ( @$res ) { delete $_->[2]->{meta}->{_configversion}; } is_deeply( $res, $xp, "outlined"); App-Music-ChordPro-6.050.7/t/163_columns.t0000644000400000040000000000324214544300524015506 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 6; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Columns. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => { 'columns' => '2', }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Two columns" ); $s = ChordPro::Songbook->new; # Columns. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'body' => [ { 'context' => '', 'type' => 'colb' } ], }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Column break" ); App-Music-ChordPro-6.050.7/t/107_chords_latin_dash.t0000644000400000040000000000715714544300524017505 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Chords; use ChordPro::Chords::Parser; my %tbl; our $options = { verbose => 0 }; our $config = eval { ChordPro::Config::configurator ( { nosysconfig => 1, nolegacyconfig => 1, nouserconfig => 1, config => getresource("config/notes/latin.json") } ); }; die unless ChordPro::Chords::Parser->get_parser($::config->{notes}->{system},1); =begin regenerate # Enable this section to generate new reference data. my $p = ChordPro::Chords::Parser->get_parser("latin"); open( my $fd, '<', "t/105_chords.t" ); my $skip = 1; while ( <$fd> ) { chomp; if ( $skip && /__DATA__/ ) { $skip = 0; next; } next if $skip; my ( $chord, $info ) = split( /\t/, $_ ); for ( $chord ) { s!^C#!Do#! or s!^C!Do! or s!^Db!Reb! or s!^D#!Re#! or s!^D!Re! or s!^Eb!Mib! or s!^E!Mi! or s!^F#!Fa#! or s!^F!Fa! or s!^Gb!Solb! or s!^G#!Sol#! or s!^G!Sol! or s!^Ab!Lab! or s!^A#!La#! or s!^A!La! or s!^Bb!Sib! or s!^B!Si!; s!/C#!/Do#! or s!/C!/Do! or s!/Db!/Reb! or s!/D#!/Re#! or s!/D!/Re! or s!/Eb!/Mib! or s!/E!/Mi! or s!/F#!/Fa#! or s!/F!/Fa! or s!/Gb!/Solb! or s!/G#!/Sol#! or s!/G!/Sol! or s!/Ab!/Lab! or s!/A#!/La#! or s!/A!/La! or s!/Bb!/Sib! or s!/B!/Si!; } my $c = $chord; $c =~ s/[()]//g; my $res = $p->parse($c); unless ( $res ) { print( "$chord\tFAIL\n"); next; } $res = {%$res}; delete($res->{parser}); print("$chord\t", reformat($res), "\n"); } exit; =cut while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; $tbl{$c} = $info; } plan tests => 0 + keys(%tbl); while ( my ( $c, $info ) = each %tbl ) { my $res = ChordPro::Chords::parse_chord($c); $res //= "FAIL"; if ( UNIVERSAL::isa( $res, 'HASH' ) ) { $res = reformat($res); } is( $res, $info, "parsing chord $c"); } sub reformat { my ( $res ) = @_; $res = {%$res}; delete($res->{parser}); use Data::Dumper qw(); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Deparse = 1; local $Data::Dumper::Terse = 1; local $Data::Dumper::Trailingcomma = 1; local $Data::Dumper::Useperl = 1; local $Data::Dumper::Useqq = 0; # I want unicode visible my $s = Data::Dumper::Dumper($res); $s =~ s/\s+/ /gs; $s =~ s/, \}/ }/gs; $s =~ s/\s+$//; return $s; } __DATA__ Do#7(-5) { bass => '', ext => '7-5', ext_canon => '7-5', name => 'Do#7-5', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Mi7(-5) { bass => '', ext => '7-5', ext_canon => '7-5', name => 'Mi7-5', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi7-9 { bass => '', ext => '7-9', ext_canon => '7-9', name => 'Mi7-9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Fa#m7-5 { bass => '', ext => '7-5', ext_canon => '7-5', name => 'Fa#m7-5', qual => 'm', qual_canon => '-', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Sol7-9 { bass => '', ext => '7-9', ext_canon => '7-9', name => 'Sol7-9', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sim7-5 { bass => '', ext => '7-5', ext_canon => '7-5', name => 'Sim7-5', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } App-Music-ChordPro-6.050.7/t/170_transpose.t0000644000400000040000000000662014544300524016045 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 6; my $s = ChordPro::Songbook->new; my $data = < [ "NC", "N.C." ], frets => [ -1, -1, -1, -1, -1, -1 ] } ); eval { $s->parse_file( \$data, { transpose => 0 } ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'key' => [ 'D' ], # 'key_from' => [ 'D' ], # 'key_actual' => [ 'E' ], 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'chords' => { 'origin' => 'song', 'type' => 'diagrams', 'show' => 'all', 'chords' => [ 'D', 'G/B', 'E', 'A/C#' ] }, 'body' => [ { 'context' => '', 'phrases' => [ 'I ', 'looked over Jordan, ', 'and ', 'what did I ', 'see,' ], 'chords' => [ '', 'D', 'NC', 'G/B', 'D' ], 'type' => 'songline' }, { 'context' => '', 'phrases' => [ 'I ', 'looked over Jordan, ', 'and ', 'what did I ', 'see, E' ], 'chords' => [ '', 'E', 'NC', 'A/C#', 'E' ], 'orig' => 'I [D]looked over Jordan, [NC]and [G/B]what did I [D]see, %{key_actual}', 'type' => 'songline' } ], 'chordsinfo' => { map { $_ => $_ } qw( D G/B E ), 'A/C#', 'NC' }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); $data = <parse_file( \$data, { transpose => 3 } ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 2, "One more song" ); isa_ok( $s->{songs}->[1], 'ChordPro::Song', "It's a song" ); $song = { 'settings' => {}, 'meta' => { 'songindex' => 2, 'key' => [ 'F' ], 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'chords' => { 'origin' => 'song', 'type' => 'diagrams', 'show' => 'all', 'chords' => [ 'F', 'A#/D' ] }, 'body' => [ { 'context' => '', 'phrases' => [ 'I ', 'looked over Jordan, ', 'and ', 'what did I ', 'see,' ], 'chords' => [ '', 'F', 'N.C.', 'A#/D', 'F' ], 'type' => 'songline' } ], 'chordsinfo' => { map { $_ => $_ } qw( F ), 'A#/D', 'N.C.' }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[1] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/111_chords_markup.t0000644000400000040000000000373514544302032016663 0ustar jvjv#!/usr/bin/perl # Testing truesf and markup use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Song; use ChordPro::Chords; my @tbl1; my @tbl2; my $tbl = \@tbl1; my $line = 0; while ( ) { chomp; $line++; last if /^#END/; next if /^#/; next unless /\S/; $tbl = \@tbl2, next if /^--/; my ( $chord, $disp, $info ) = split( /\t/, $_ ); my $c = $chord; push( @$tbl, [ $line, $c, $disp, $info ] ); } plan tests => 2 + @tbl1 + @tbl2; my $s = ChordPro::Song->new; ok( $s, "Got song"); $s->_diag( format => ', line %n, %m' ); ChordPro::Chords::set_parser("common"); $::config->{settings}->{truesf} = 1; $::config->{settings}->{chordnames} = "relaxed"; $::config->{settings}->{notenames} = 1; my $msg = ""; foreach ( @tbl1 ) { local $SIG{__WARN__} = sub { $msg .= "@_" }; doit($_); } ok( $msg =~ /, line (\d+), Invalid markup in chord: "Bbm7b5"/ && $1 == 16, "Warning given at line $1" ); $::config->{"chord-formats"}->{common} = "%{root}%{qual|%{}}%{ext|%{}}%{bass|/%{}}"; foreach ( @tbl2 ) { doit($_); } sub doit { my ( $line, $c, $info ) = @{$_[0]}; $s->_diag( line => $line ); my $ap = $s->chord($c); my $key = $ap->key; my $res = $s->{chordsinfo}->{$key}; unless ( $res ) { warn( "XXX |", join("|",keys %{$s->{chordsinfo}}), "|\n"); $res = "FAIL"; } $res = $ap->chord_display(0x3); is( $res, $info, "parsing chord $c" ); } __DATA__ Bm7 Bm7 (Bm7) (Bm7) (Besm7) (Besm7) Bbm7 B♭m7 Bbm7b5 B♭m7♭5 C#m7 C♯m7 C#m7#5 C♯m7♯5 B B b b Bb B♭ bb b♭ Bm7 Bm7 Bbm7 B♭m7 Bm7b5 Bm7♭5 Bbm7b5 B♭m7♭5 Bbm7b5 B♭m7♭5 (Bes) (Bes) (Bes) (Bes) -- Bbm7b5 B♭m7♭5 Bbm7b5 B♭m7♭5 (Bbm7b5) (B♭m7♭5) (Bbm7b5) (B♭m7♭5) App-Music-ChordPro-6.050.7/t/108_chords_solfege_o.t0000644000400000040000000001246414544300524017337 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Chords; use ChordPro::Chords::Parser; my %tbl; our $config = ChordPro::Config::configurator ( { nosysconfig => 1, nolegacyconfig => 1, nouserconfig => 1, config => getresource("config/notes/solfege.json") } ); =begin regenerate # Enable this section to generate new reference data. my $p = ChordPro::Chords::Parser->get_parser("solfege"); open( my $fd, '<', "t/105_chords.t" ); my $skip = 1; while ( <$fd> ) { chomp; if ( $skip && /__DATA__/ ) { $skip = 0; next; } next if $skip; my ( $chord, $info ) = split( /\t/, $_ ); for ( $chord ) { s!^C#!Di! or s!^C!Do! or s!^Db!Ra! or s!^D#!Ri! or s!^D!Re! or s!^Eb!Me! or s!^E!Mi! or s!^F#!Fi! or s!^F!Fa! or s!^Gb!Se! or s!^G#!Si! or s!^G!So! or s!^Ab!Le! or s!^A#!Li! or s!^A!La! or s!^Bb!Te! or s!^B!Ti!; s!/C#!/Di! or s!/C!/Do! or s!/Db!/Ra! or s!/D#!/Ri! or s!/D!/Re! or s!/Eb!/Me! or s!/E!/Mi! or s!/F#!/Fi! or s!/F!/Fa! or s!/Gb!/Se! or s!/G#!/Si! or s!/G!/So! or s!/Ab!/Le! or s!/A#!/Li! or s!/A!/La! or s!/Bb!/Te! or s!/B!/Ti!; } my $c = $chord; $c =~ s/[()]//g; my $res = $p->parse($c); unless ( $res ) { print( "$chord\tFAIL\n"); next; } $res = {%$res}; delete($res->{parser}); print("$chord\t", reformat($res), "\n"); } exit; =cut while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; $tbl{$c} = $info; } plan tests => 0 + keys(%tbl); while ( my ( $c, $info ) = each %tbl ) { my $res = ChordPro::Chords::parse_chord($c); $res //= "FAIL"; if ( UNIVERSAL::isa( $res, 'HASH' ) ) { $res = reformat($res); } is( $res, $info, "parsing chord $c"); } sub reformat { my ( $res ) = @_; $res = {%$res}; delete($res->{parser}); use Data::Dumper qw(); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Deparse = 1; local $Data::Dumper::Terse = 1; local $Data::Dumper::Trailingcomma = 1; local $Data::Dumper::Useperl = 1; local $Data::Dumper::Useqq = 0; # I want unicode visible my $s = Data::Dumper::Dumper($res); $s =~ s/\s+/ /gs; $s =~ s/, \}/ }/gs; $s =~ s/\s+$//; return $s; } __DATA__ Doo { bass => '', ext => '', ext_canon => '', name => 'Doo', qual => 'o', qual_canon => 0, root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'solfege' } Dio { bass => '', ext => '', ext_canon => '', name => 'Dio', qual => 'o', qual_canon => 0, root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Rao { bass => '', ext => '', ext_canon => '', name => 'Rao', qual => 'o', qual_canon => 0, root => 'Ra', root_canon => 'Ra', root_mod => -1, root_ord => 1, system => 'solfege' } Reo { bass => '', ext => '', ext_canon => '', name => 'Reo', qual => 'o', qual_canon => 0, root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'solfege' } Rio { bass => '', ext => '', ext_canon => '', name => 'Rio', qual => 'o', qual_canon => 0, root => 'Ri', root_canon => 'Ri', root_mod => 1, root_ord => 3, system => 'solfege' } Meo { bass => '', ext => '', ext_canon => '', name => 'Meo', qual => 'o', qual_canon => 0, root => 'Me', root_canon => 'Me', root_mod => -1, root_ord => 3, system => 'solfege' } Mio { bass => '', ext => '', ext_canon => '', name => 'Mio', qual => 'o', qual_canon => 0, root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Fao { bass => '', ext => '', ext_canon => '', name => 'Fao', qual => 'o', qual_canon => 0, root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'solfege' } Fio { bass => '', ext => '', ext_canon => '', name => 'Fio', qual => 'o', qual_canon => 0, root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } Seo { bass => '', ext => '', ext_canon => '', name => 'Seo', qual => 'o', qual_canon => 0, root => 'Se', root_canon => 'Se', root_mod => -1, root_ord => 6, system => 'solfege' } Soo { bass => '', ext => '', ext_canon => '', name => 'Soo', qual => 'o', qual_canon => 0, root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Sio { bass => '', ext => '', ext_canon => '', name => 'Sio', qual => 'o', qual_canon => 0, root => 'Si', root_canon => 'Si', root_mod => 1, root_ord => 8, system => 'solfege' } Leo { bass => '', ext => '', ext_canon => '', name => 'Leo', qual => 'o', qual_canon => 0, root => 'Le', root_canon => 'Le', root_mod => -1, root_ord => 8, system => 'solfege' } Lao { bass => '', ext => '', ext_canon => '', name => 'Lao', qual => 'o', qual_canon => 0, root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'solfege' } Lio { bass => '', ext => '', ext_canon => '', name => 'Lio', qual => 'o', qual_canon => 0, root => 'Li', root_canon => 'Li', root_mod => 1, root_ord => 10, system => 'solfege' } Teo { bass => '', ext => '', ext_canon => '', name => 'Teo', qual => 'o', qual_canon => 0, root => 'Te', root_canon => 'Te', root_mod => -1, root_ord => 10, system => 'solfege' } Tio { bass => '', ext => '', ext_canon => '', name => 'Tio', qual => 'o', qual_canon => 0, root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } App-Music-ChordPro-6.050.7/t/172_transpose.t0000644000400000040000000001326714544300524016054 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data, { transpose => 0 } ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { body => [ { context => '', type => 'empty', }, { chords => [ '', 'D', 'G', 'D', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, { context => '', type => 'empty', }, { chords => [ '', 'D', 'G', 'D', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'D', 'G', 'D', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, ], context => '', type => 'rechorus', }, { context => '', type => 'empty', }, { chords => [ '', 'E', 'A', 'E', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'E', 'A', 'E', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, ], context => '', type => 'rechorus', }, { context => '', type => 'empty', }, { chords => [ '', 'F#', 'B', 'F#', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'F#', 'B', 'F#', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, ], context => '', type => 'rechorus', }, { context => '', type => 'empty', }, { chords => [ '', 'E', 'A', 'E', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'E', 'A', 'E', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, ], context => '', type => 'rechorus', }, { context => '', type => 'empty', }, { chords => [ '', 'D', 'G', 'D', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'D', 'G', 'D', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, ], context => '', type => 'rechorus', }, ], chordsinfo => { map { $_ => $_ } qw( D G E A B), 'F#' }, chords => { origin => 'song', type => 'diagrams', show => 'all', chords => [ 'D', 'G', 'E', 'A', 'F#', 'B' ] }, meta => { songindex => 1, key => ['D'], # key_actual => ['D'], # key_from => ['E'], title => [ 'Transpositions', ], }, settings => {}, source => { file => "__STRING__", line => 1 }, structure => 'linear', system => 'common', title => 'Transpositions', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/72_mma.t0000644000400000040000000000175614544300524014527 0ustar jvjv#! perl use strict; use warnings; use utf8; use ChordPro::Testing; my $test = 0; BAIL_OUT("Missing MMA test data") unless -d "mma"; opendir( my $dh, "mma" ) || BAIL_OUT("Cannot open mma test data"); my @files = grep { /^.+\.cho$/ } readdir($dh); close($dh); diag("Testing ", scalar(@files), " cho files"); our $options; foreach my $file ( sort @files ) { $test++; my $decoda = $file =~ /^decoda/i; $file = "mma/$file"; #diag("Testing: $file"); ( my $out = $file ) =~ s/\.cho/.tmp/; ( my $ref = $file ) =~ s/\.cho/.mma/; @ARGV = ( "--no-default-configs", "--generate", "MMA", $decoda ? ( "--backend-option", "decoda=1" ) : (), "--backend-option", "groove=testing", "--output", $out, $file ); ::run(); my $ok = !differ( $out, $ref ); ok( $ok, $file ); unlink($out) if $ok; system( $ENV{CHORDPRO_DIFF}, $out, $ref) if $ENV{CHORDPRO_DIFF}; } ok( $test++ == @files, "Tested @{[0+@files]} files" ); done_testing($test); App-Music-ChordPro-6.050.7/t/190_outlines.t0000644000400000040000000001137614544302032015673 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; use ChordPro::Output::Common; plan tests => 4; # For transcoding, both source and target notation systems must be # defined. The source system must be last, so it is current and used # to parse the the input data. ok( $config, "got config" ); my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 3, "three songs" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $book = [ map { { meta => $_->{meta} } } @{$s->{songs}} ]; my $ctl = { fields => [ qw( sorttitle sortartist ) ] }; my $res = ChordPro::Output::Common::prep_outlines( $book, $ctl ); my $xp = [ [ 'fietsenhok, het', 'december', { meta => { artist => [ 'December', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'December', ], sorttitle => [ 'Fietsenhok, Het', ], title => [ 'Het Fietsenhok', ], }, }, ], [ 'fietsenhok, het', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'September', ], sorttitle => [ 'Fietsenhok, Het', ], title => [ 'Het Fietsenhok', ], }, }, ], [ 'fietsenstalling, de', 'december', { meta => { artist => [ 'December', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'December', ], sorttitle => [ 'Fietsenstalling, De', ], title => [ 'De Fietsenstalling', ], }, }, ], [ 'fietsenstalling, de', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'September', ], sorttitle => [ 'Fietsenstalling, De', ], title => [ 'De Fietsenstalling', ], }, }, ], [ 'fietspomp, de', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'A', 'Am', ], key_actual => [ 'Am', ], songindex => 1, sortartist => [ 'September', ], sorttitle => [ 'Fietspomp, De', ], title => [ 'De Fietspomp', ], }, }, ], [ 'vierentwintig fietsen', 'december', { meta => { artist => [ 'December', ], chords => [ ], numchords => [ 0, ], key => [ 'D', ], key_actual => [ 'D', ], songindex => 2, sortartist => [ 'December', ], sorttitle => [ 'Vierentwintig Fietsen', ], title => [ '24 Fietsen', ], }, }, ], [ 'vierentwintig fietsen', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'D', ], key_actual => [ 'D', ], songindex => 2, sortartist => [ 'September', ], sorttitle => [ 'Vierentwintig Fietsen', ], title => [ '24 Fietsen', ], }, }, ], ]; foreach ( @$res ) { delete $_->[2]->{meta}->{_configversion}; } is_deeply( $res, $xp, "outlined"); App-Music-ChordPro-6.050.7/t/107_chords_latin_o.t0000644000400000040000000001273514544300524017022 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Chords; use ChordPro::Chords::Parser; my %tbl; our $options = { verbose => 0 }; our $config = eval { ChordPro::Config::configurator ( { nosysconfig => 1, nolegacyconfig => 1, nouserconfig => 1, config => getresource("config/notes/latin.json") } ); }; die unless ChordPro::Chords::Parser->get_parser($::config->{notes}->{system},1); =begin regenerate # Enable this section to generate new reference data. my $p = ChordPro::Chords::Parser->get_parser("latin"); open( my $fd, '<', "t/105_chords_o.t" ); my $skip = 1; while ( <$fd> ) { chomp; if ( $skip && /__DATA__/ ) { $skip = 0; next; } next if $skip; my ( $chord, $info ) = split( /\t/, $_ ); for ( $chord ) { s!^C#!Do#! or s!^C!Do! or s!^Db!Reb! or s!^D#!Re#! or s!^D!Re! or s!^Eb!Mib! or s!^E!Mi! or s!^F#!Fa#! or s!^F!Fa! or s!^Gb!Solb! or s!^G#!Sol#! or s!^G!Sol! or s!^Ab!Lab! or s!^A#!La#! or s!^A!La! or s!^Bb!Sib! or s!^B!Si!; s!/C#!/Do#! or s!/C!/Do! or s!/Db!/Reb! or s!/D#!/Re#! or s!/D!/Re! or s!/Eb!/Mib! or s!/E!/Mi! or s!/F#!/Fa#! or s!/F!/Fa! or s!/Gb!/Solb! or s!/G#!/Sol#! or s!/G!/Sol! or s!/Ab!/Lab! or s!/A#!/La#! or s!/A!/La! or s!/Bb!/Sib! or s!/B!/Si!; } my $c = $chord; $c =~ s/[()]//g; my $res = $p->parse($c); unless ( $res ) { print( "$chord\tFAIL\n"); next; } $res = {%$res}; delete($res->{parser}); print("$chord\t", reformat($res), "\n"); } exit; =cut while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; $tbl{$c} = $info; } plan tests => 0 + keys(%tbl); while ( my ( $c, $info ) = each %tbl ) { my $res = ChordPro::Chords::parse_chord($c); $res //= "FAIL"; if ( UNIVERSAL::isa( $res, 'HASH' ) ) { $res = reformat($res); } is( $res, $info, "parsing chord $c"); } sub reformat { my ( $res ) = @_; $res = {%$res}; delete($res->{parser}); use Data::Dumper qw(); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Deparse = 1; local $Data::Dumper::Terse = 1; local $Data::Dumper::Trailingcomma = 1; local $Data::Dumper::Useperl = 1; local $Data::Dumper::Useqq = 0; # I want unicode visible my $s = Data::Dumper::Dumper($res); $s =~ s/\s+/ /gs; $s =~ s/, \}/ }/gs; $s =~ s/\s+$//; return $s; } __DATA__ Doo { bass => '', ext => '', ext_canon => '', name => 'Doo', qual => 'o', qual_canon => 0, root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do#o { bass => '', ext => '', ext_canon => '', name => 'Do#o', qual => 'o', qual_canon => 0, root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Rebo { bass => '', ext => '', ext_canon => '', name => 'Rebo', qual => 'o', qual_canon => 0, root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Reo { bass => '', ext => '', ext_canon => '', name => 'Reo', qual => 'o', qual_canon => 0, root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re#o { bass => '', ext => '', ext_canon => '', name => 'Re#o', qual => 'o', qual_canon => 0, root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Mibo { bass => '', ext => '', ext_canon => '', name => 'Mibo', qual => 'o', qual_canon => 0, root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mio { bass => '', ext => '', ext_canon => '', name => 'Mio', qual => 'o', qual_canon => 0, root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Fao { bass => '', ext => '', ext_canon => '', name => 'Fao', qual => 'o', qual_canon => 0, root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa#o { bass => '', ext => '', ext_canon => '', name => 'Fa#o', qual => 'o', qual_canon => 0, root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Solbo { bass => '', ext => '', ext_canon => '', name => 'Solbo', qual => 'o', qual_canon => 0, root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solo { bass => '', ext => '', ext_canon => '', name => 'Solo', qual => 'o', qual_canon => 0, root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol#o { bass => '', ext => '', ext_canon => '', name => 'Sol#o', qual => 'o', qual_canon => 0, root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Labo { bass => '', ext => '', ext_canon => '', name => 'Labo', qual => 'o', qual_canon => 0, root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Lao { bass => '', ext => '', ext_canon => '', name => 'Lao', qual => 'o', qual_canon => 0, root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La#o { bass => '', ext => '', ext_canon => '', name => 'La#o', qual => 'o', qual_canon => 0, root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } Sibo { bass => '', ext => '', ext_canon => '', name => 'Sibo', qual => 'o', qual_canon => 0, root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sio { bass => '', ext => '', ext_canon => '', name => 'Sio', qual => 'o', qual_canon => 0, root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } App-Music-ChordPro-6.050.7/t/380_roundtrip.t0000644000400000040000000002756514544300524016073 0ustar jvjv#! perl use Test::More tests => 1; use ChordPro::Config::Properties; use File::LoadLines; # Take a big properties file, convert to data and dump. my $orig = Data::Properties->new; my $lines = loadlines(\*DATA); $orig->parse_lines( $lines , '' ); my $sorig = $orig->dump; # Load the dump into a new properties file. my $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, $sorig ) ], '' ); # Should be identical. is_deeply( $cfg->data, $orig->data ); __DATA__ # Configuration for ChordPro. # Includes. These are processed first, before the rest of # the config file. # # "include" takes a list of either filenames or preset names. include.0 = "guitar" # General settings, to be changed by legacy configs and # command line. settings { # Titles flush: default center. titles = "center" # Columns, default one. columns = 1 # Suppress empty chord lines. # Overrides the -a (--single-space) command line options. suppress-empty-chords = true # Suppress blank lyrics lines. suppress-empty-lyrics = true # Suppress chords. # Overrides --lyrics-only command line option. lyrics-only = false # Memorize chords in sections, to be recalled by [^]. memorize = false # Chords inline. # May be a string containing pretext %s posttext. # Defaults to "[%s]" if true. inline-chords = false # Chords under the lyrics. chords-under = false # Transcoding. transcode = null # Always decapoize. decapo = false # Chords parsing strategy. # Strict (only known) or relaxed (anything that looks sane). chordnames = "strict" # Allow note names in []. notenames = false } # Metadata. # For these keys you can use {meta key ...} as well as {key ...}. # If strict is nonzero, only the keys named here are allowed. # If strict is zero, {meta ...} will accept any key. # Important: "title" and "subtitle" must always be in this list. # The separator is used to concatenate multiple values. metadata { keys = [ "title" "subtitle" "artist" "composer" "lyricist" "arranger" "album" "copyright" "year" "sorttitle" "key" "time" "tempo" "capo" "duration" ] strict = true separator = "; " } # Printing chord diagrams. # auto= automatically add unknown chords as empty diagrams. # show= prints the chords used in the song. # all= all chords used. # user= only prints user defined chords. # sorted= order the chords by key. diagrams { auto = false show = "all" sorted = false } # Diagnostig messages. diagnostics { format = "\"%f\", line %n, %m\n\t%l" } # Table of contents. contents = [ { fields = [ "songindex" ] label = "Table of Contents" line = "%{title}" fold = false omit = false } { fields = [ "sorttitle" "artist" ] label = "Contents by Title" line = "%{title}%{artist| - %{}}" fold = false omit = false } { fields = [ "artist" "sorttitle" ] label = "Contents by Artist" line = "%{artist|%{} - }%{title}" fold = false omit = true } ] # Table of contents, old style. # This will be ignored when new style contents is present. toc { # Title for ToC. title = "Table of Contents" line = "%{title}" # Sorting order. # Currently only sorting by page number and alpha is implemented. order = "page" } # Intrument definitions. instrument : null tuning = [ "E2" "A2" "D3" "G3" "B3" "E4" ] notes { flat = [ "C" ["Db" "Des" "D♭"] "D" ["Eb" "Es" "Ees" "E♭"] "E" "F" ["Gb" "Ges" "G♭"] "G" ["Ab" "As" "Aes" "A♭"] "A" ["Bb" "Bes" "B♭"] "B" ] sharp = [ "C" ["C#" "Cis" "C♯"] "D" ["D#" "Dis" "D♯"] "E" "F" ["F#" "Fis" "F♯"] "G" ["G#" "Gis" "G♯"] "A" ["A#" "Ais" "A♯"] "B" ] system : "common" movable : false } # Layout definitions for PDF output. pdf { # Papersize, 'a4' or [ 595, 842 ] etc. papersize = "a4" # Space between columns, in pt. columnspace = 20 # Page margins. # Note that top/bottom exclude the head/footspace. margintop = 80 marginbottom = 40 marginleft = 40 marginright = 40 headspace = 60 footspace = 20 # Special: head on first page only, add the headspace to # the other pages so they become larger. head-first-only = false # Spacings. # Baseline distances as a factor of the font size. spacing { title = 1.2 lyrics = 1.2 chords = 1.2 grid = 1.2 tab = 1.0 toc = 1.4 empty = 1.0 } # Note: By setting the font size and spacing for empty lines to # smaller values, you get a fine(r)-grained control over the # spacing between the various parts of the song. # Style of chorus. chorus { indent = 0 # Chorus side bar. # Suppress by setting offset and/or width to zero. bar { offset = 8 width = 1 color = "black" } tag = "Chorus" # Recall style: Print the tag using the type. # Alternatively quote the lines of the preceding chorus. recall { tag = "Chorus" type = "comment" quote = false } } # This opens a margin for margin labels. labels { # Margin width. Default is 0 (no margin labels). # "auto" will automatically reserve a margin if labels are used. width = "auto" # Alignment for the labels. Default is left. align = "left" } # Alternative songlines with chords in a side column. # Value is the column position. # chordscolumn = 400 chordscolumn = 0 capoheading = "%{capo|Capo: %{}}" # A {titles: left} may conflict with customized formats. # Set to non-zero to ignore the directive. titles-directive-ignore = false # Chord diagrams. # A chord diagram consists of a number of cells. # Cell dimensions are specified by "width" and "height". # The horizontal number of cells depends on the number of strings. # The vertical number of cells is "vcells", which should # be 4 or larger to accomodate most chords. # The horizontal distance between diagrams is "hspace" cells. # The vertical distance is "vspace" cells. # "linewidth" is the thickness of the lines as a fraction of "width". # Diagrams for all chords of the song can be shown at the # "top", "bottom" or "right" side of the first page # or "below" the last song line. diagrams { show = "bottom" width = 6 height = 6 hspace = 3.95 vspace = 3 vcells = 4 linewidth = 0.1 } # Even/odd pages. A value of -1 denotes odd/even pages. even-odd-pages = 1 # Align songs to even/odd pages. pagealign-songs = 1 # Formats. formats { # Titles/Footers. # Titles/footers have 3 parts, which are printed left # centered and right. # For even/odd printing, the order is reversed. # By default, a page has: default { # No title/subtitle. title = null subtitle = null # Footer is title -- page number. footer.0 = "%{title}" footer.1 = "" footer.2 = "%{page}" } # The first page of a song has: title { # Title and subtitle. title.0 = "" title.1 = "%{title}" title.2 = "" subtitle.0 = "" subtitle.1 = "%{subtitle}" subtitle.2 = "" # Footer with page number. footer.0 = "" footer.1 = "" footer.2 = "%{page}" } # The very first output page is slightly different: first { # It has title and subtitle, like normal 'first' pages. # But no footer. footer = null } } # Split marker for syllables that are smaller than chord width. # split-marker is a 3-part array: 'start', 'repeat', and 'final'. # 'final' is always printed, last. # 'start' is printed if there is enough room. # 'repeat' is printed repeatedly to fill the rest. # If split-marker is a single string, this is 'start'. # All elements may be left empty strings. split-marker = [ "" "" "" ] # Font families and properties. # "fontconfig" maps members of font families to physical fonts. # Optionally, additional properties of the fonts can be specified. # Physical fonts can be the names of TrueType/OpenType fonts # or names of built-in fonts (corefonts). # Relative filenames are looked up in the fontdir. # fontdir = [ "/usr/share/fonts/liberation", "/home/me/fonts" ] fontdir = null fontconfig { # alternatives: regular r normal # alternatives: bold b strong # alternatives: italic i oblique o emphasis # alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob times { '' = "Times-Roman" bold = "Times-Bold" italic = "Times-Italic" bolditalic = "Times-BoldItalic" } helvetica { '' = "Helvetica" bold = "Helvetica-Bold" oblique = "Helvetica-Oblique" boldoblique = "Helvetica-BoldOblique" } courier { '' = "Courier" bold = "Courier-Bold" italic = "Courier-Italic" bolditalic = "Courier-BoldItalic" } dingbats { "" = "ZapfDingbats" } } # "fonts" maps output elements to fonts as defined in "fontconfig". # The elements can have a background colour associated. # Colours are "#RRGGBB" or predefined names like "black", "white" # and lots of others. # NOTE: In the built-in config we use only "name" since that can # be overruled with user settings. fonts { title { name = "Times-Bold" size = 14 } text { name = "Times-Roman" size = 12 } chord { name = "Helvetica-Oblique" size = 10 } chordfingers { name = "ZapfDingbats" size = 10 } comment { name = "Helvetica" size = 12 background = "#E5E5E5" } comment_italic { name = "Helvetica-Oblique" size = 12 } comment_box { name = "Helvetica" size = 12 frame = 1 } tab { name = "Courier" size = 10 } toc { name = "Times-Roman" size = 11 } grid { name = "Helvetica" size = 10 } } # Element mappings that can be specified, but need not since # they default to other elements. # subtitle --> text # comment --> text # comment_italic --> chord # comment_box --> chord # annotation --> chord # toc --> text # grid --> chord # grid_margin --> comment # footer --> subtitle @ 60% # empty --> text # diagram --> comment # diagram_base --> text (but at a small size) # Bookmarks (PDF outlines). # fields: primary and (optional) secondary fields. # label: outline label # line: text of the outline element # collapse: initial display is collapsed # letter: sublevel with first letters if more # fold: group by primary (NYI) # omit: ignore this outlines = [ { fields = [ "sorttitle" "artist" ] label = "By Title" line = "%{title}%{artist| - %{}}" collapse = false letter = 5 fold = false } { fields = [ "artist" "sorttitle" ] label = "By Artist" line = "%{artist|%{} - }%{title}" collapse = false letter = 5 fold = false } ] # This will show the page layout if non-zero. showlayout = false } # Settings for ChordPro backend. chordpro { # Style of chorus. chorus { # Recall style: Print the tag using the type. # Alternatively quote the lines of the preceding chorus. # If no tag+type or quote: use {chorus}. # Note: Variant 'msp' always uses {chorus}. recall { # tag = "Chorus", type = "comment" tag = "" type = "" # quote = false quote = false } } } # Settings for HTML backend. html { # Stylesheet links. styles { display = "chordpro.css" print = "chordpro_print.css" } } # End of config. App-Music-ChordPro-6.050.7/t/310_basic.t0000644000400000040000000000124114544300524015076 0ustar jvjv#! perl use Test::More tests => 4; use ChordPro::Config::Properties; my $cfg = Data::Properties->new; $cfg->parse_lines(["version:1"]); is( $cfg->dump, "version = '1'\n" ); # Case insensitive. $cfg = Data::Properties->new; $cfg->parse_lines(["vERSION:1"]); is( $cfg->dump, "vERSION = '1'\n" ); is( $cfg->get_property("VERSION"), 1 ); # Content is appended. $cfg->parse_lines(["data = 0"]); is( $cfg->dump, <new; #$ENV{"_DATA__PROPERTIES_"} = "Yes!"; #$cfg->parse_lines([ 'foo = %{_DATA__PROPERTIES_}' ] ); #is( $cfg->get_property("foo"), "Yes!" ); App-Music-ChordPro-6.050.7/t/313_null.t0000644000400000040000000000071214544300524014774 0ustar jvjv#! perl use Test::More tests => 2; use ChordPro::Config::Properties; my $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <dump, <{_props}, { '@' => [ qw( a b x c d )], a => undef, b => undef, c => 'null', d => 'null', x => '', } ); App-Music-ChordPro-6.050.7/t/70_a2crd.t0000644000400000040000000000156414544300524014743 0ustar jvjv#! perl use strict; use warnings; use utf8; use ChordPro::Testing; use_ok "ChordPro::A2Crd"; my $test = 1; BAIL_OUT("Missing a2crd test data") unless -d "a2crd"; opendir( my $dh, "a2crd" ) || BAIL_OUT("Cannot open a2crd test data"); my @files = grep { /^.+\.crd$/ } readdir($dh); close($dh); diag("Testing ", scalar(@files), " crd files"); our $options; $options->{fragment} = 1; foreach my $file ( sort @files ) { $test++; $file = "a2crd/$file"; #diag("Testing: $file"); ( my $out = $file ) =~ s/\.crd/.tmp/; ( my $ref = $file ) =~ s/\.crd/.cho/; @ARGV = ( "--a2crd", "--no-default-configs", "--generate", "ChordPro", "--output", $out, $file ); ::run(); my $ok = !differ( $out, $ref ); ok( $ok, $file ); unlink($out) if $ok; } ok( $test++ == @files+1, "Tested @{[0+@files]} files" ); done_testing($test); App-Music-ChordPro-6.050.7/t/60_transpose.t0000644000400000040000000000022214447223440015756 0ustar jvjv#! perl use strict; if ( -d "t" ) { chdir "t"; $0 =~ s;(^|/)t/;$1;; } @::params = qw( 60 60_transpose crd ); require "./00_basic.pl"; App-Music-ChordPro-6.050.7/t/151_fonts.t0000644000400000040000000001002414544302032015144 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Fonts definitions. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'body' => [ { 'name' => 'text-font', 'type' => 'control', 'value' => 'Times', 'context' => '' }, { 'name' => 'chorus-font', 'type' => 'control', 'value' => 'Times', 'context' => '' }, { 'name' => 'text-size', 'type' => 'control', 'value' => '80%', 'context' => '' }, { 'name' => 'chorus-size', 'type' => 'control', 'value' => '80%', 'context' => '' }, { 'type' => 'control', 'name' => 'text-color', 'context' => '', 'value' => 'magenta' }, { 'type' => 'control', 'name' => 'chorus-color', 'context' => '', 'value' => 'magenta' }, { 'name' => 'chord-font', 'type' => 'control', 'value' => 'Times-Italic', 'context' => '' }, { 'name' => 'chord-size', 'type' => 'control', 'value' => '80%', 'context' => '' }, { 'type' => 'control', 'name' => 'chord-color', 'context' => '', 'value' => 'yellow' }, { 'context' => '', 'value' => 'Courier', 'type' => 'control', 'name' => 'tab-font' }, { 'type' => 'control', 'name' => 'tab-size', 'context' => '', 'value' => '80%' }, { 'value' => 'red', 'context' => '', 'type' => 'control', 'name' => 'tab-color' }, { 'name' => 'grid-font', 'type' => 'control', 'context' => '', 'value' => 'Arial' }, { 'name' => 'grid-size', 'type' => 'control', 'value' => '12', 'context' => '' }, { 'type' => 'control', 'name' => 'grid-color', 'value' => '#010203', 'context' => '' }, { 'name' => 'title-font', 'type' => 'control', 'context' => '', 'value' => 'Times-Bold' }, { 'type' => 'control', 'name' => 'title-size', 'context' => '', 'value' => '200%' }, { 'value' => 'black', 'context' => '', 'name' => 'title-color', 'type' => 'control' }, { 'context' => '', 'value' => 'Times-Italic', 'name' => 'footer-font', 'type' => 'control' }, { 'value' => '60%', 'context' => '', 'name' => 'footer-size', 'type' => 'control' }, { 'type' => 'control', 'name' => 'footer-color', 'value' => 'blue', 'context' => '' }, { 'context' => '', 'value' => 'Times-Roman', 'name' => 'toc-font', 'type' => 'control' }, { 'name' => 'toc-size', 'type' => 'control', 'context' => '', 'value' => '80%' }, { 'value' => 'cyan', 'context' => '', 'type' => 'control', 'name' => 'toc-color' } ] }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/164_pagesize.t0000644000400000040000000000251114544300524015634 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 6; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Columns. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => { 'papersize' => 'a4', }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Papersize A4" ); $s = ChordPro::Songbook->new; # Columns. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song->{settings}->{papersize} = 'letter'; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Papersize Letter" ); App-Music-ChordPro-6.050.7/t/113_comment.t0000644000400000040000000000456314544300524015472 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 6; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Transposition happens at compile time, %{...} is handled by the backends. my $data = <parse_file( \$data, { transpose => 2 } ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'type' => 'comment', 'context' => '', 'text' => 'This is a comment', 'orig' => 'This is a comment' }, { 'type' => 'comment', 'context' => '', 'phrases' => [ 'This song is Swing Low Sweet Chariot in the key of ', '', ], 'chords' => [ '', 'D' ], 'orig' => 'This song is %{title} in the key of [C]', }, { 'type' => 'comment', 'context' => '', 'text' => 'This is also a comment', 'orig' => 'This is also a comment', }, { 'context' => '', 'type' => 'comment_italic', 'text' => 'This is a comment_italic', 'orig' => 'This is a comment_italic' }, { 'text' => 'This is a comment_box', 'orig' => 'This is a comment_box', 'context' => '', 'type' => 'comment_box' } ], 'chordsinfo' => { 'D' => 'D' }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); # Same, with substitutions. $s = ChordPro::Songbook->new; eval { $s->parse_file( \$data, { 'no-substitute' => 0, transpose => 2 } ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); $song->{body}->[1]->{phrases}->[0] =~ s/\%\{title\}/$song->{title}/e; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/102_new_song.t0000644000400000040000000000126114544300524015635 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'meta' => { songindex => 1 }, 'settings' => {}, 'structure' => 'linear', 'source' => { file => "__STRING__", line => 1 }, 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/317_empty.t0000644000400000040000000000111314544300524015160 0ustar jvjv#! perl use Test::More tests => 4; use ChordPro::Config::Properties; my $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <dump, <{_props}, { '@' => [ qw( a b )], a => undef, b => undef, } ); $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <dump, <{_props}, { '@' => [ qw(x)], 'x.@' => [ qw(a) ], 'x.a' => undef, } ); App-Music-ChordPro-6.050.7/t/175_transpose.t0000644000400000040000000000506014544300524016047 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data, { transpose => 0 } ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'body' => [ { 'context' => '', 'type' => 'rechorus' }, { 'chords' => [ 'G' ], 'context' => 'chorus', 'phrases' => [ 'Chorus line in D.' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, { 'chords' => [ 'G' ], 'context' => '', 'phrases' => [ 'Song line in D.' ], 'type' => 'songline' }, { 'chorus' => [ { 'chords' => [ 'G' ], 'context' => 'chorus', 'phrases' => [ 'Chorus line in D.' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, ], 'context' => '', 'type' => 'rechorus' }, { 'chords' => [ 'G' ], 'context' => '', 'phrases' => [ 'Song line in D.' ], 'type' => 'songline' }, { 'chorus' => [ { 'chords' => [ 'G' ], 'context' => 'chorus', 'phrases' => [ 'Chorus line in D.' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, ], 'context' => '', 'type' => 'rechorus' }, { 'chorus' => [ { 'chords' => [ 'A' ], 'context' => 'chorus', 'phrases' => [ 'Chorus line in D.' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' }, ], 'context' => '', 'type' => 'rechorus' } ], 'chords' => { 'chords' => [ 'G' ], 'origin' => 'song', 'show' => 'all', 'type' => 'diagrams' }, 'meta' => { 'songindex' => 1, 'title' => [ 'Transpositions' ] }, settings => {}, chordsinfo => { map { $_ => $_ } qw( A G ) }, source => { file => "__STRING__", line => 1 }, structure => 'linear', system => 'common', title => 'Transpositions', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/312_context.t0000644000400000040000000000161214544300524015505 0ustar jvjv#! perl use Test::More tests => 2; use ChordPro::Config::Properties; my $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <dump, <{_props}, { '@' => [ 'base' ], 'base.@' => [ 'version', 'config', 'nested', ], 'base.config.@' => [ 'version', ], 'base.config.version' => 2, 'base.nested.@' => [ 'version', 'something', ], 'base.nested.something' => 4, 'base.nested.version' => 3, 'base.version' => 1, } ); App-Music-ChordPro-6.050.7/t/101_empty.t0000644000400000040000000000116314544300524015154 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 1; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; my $data = <parse_file(\$data); 1 } or diag("$@"); #use DDumper; DDumper( $s->{songs} ); ok( scalar( @{ $s->{songs} } ) == 0, "No song" ); #isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); # #my $song = { # 'settings' => {}, # 'structure' => 'linear', # 'system' => 'common', # }; # #is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/321_subst.t0000644000400000040000000000210114544300524015153 0ustar jvjv#! perl use Test::More tests => 1; use ChordPro::Config::Properties; my $cfg = Data::Properties->new; delete $ENV{version}; # yes, some systems seem to set this delete $ENV{XXDATAPROPERTIESXX}; delete $ENV{xxdatapropertiesxx}; $ENV{XXDATAPROPERTIESXX} = "env"; my $caseinsensitive = $ENV{xxdatapropertiesxx} && $ENV{xxdatapropertiesxx} eq "env"; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ] ); version = 1 nested { nothing = ${.version||xxx} version = 3 # version at this level something = ${.version} # version at global level else = ${version} } XXDATAPROPERTIESXX = "local" # This should be overridden by the env. var. test1 = ${XXDATAPROPERTIESXX} # But env var names are usually not case insensitive test2 = ${xxdatapropertiesxx} EOD my $xx = $caseinsensitive ? "env" : "local"; is( $cfg->dump, < 3; $config->{settings}->{memorize} = 1; $config->{settings}->{transpose} = 2; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; #### meta as meta. my $data = <parse_file(\$data) } or diag("$@"); } ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); # use ChordPro::Dumper; ddp($s->{songs}->[0]); my $song = { 'meta' => { 'songindex' => 1, 'title' => [ 'Test Memorize' ] }, 'body' => [ { 'type' => 'songline', 'phrases' => [ 'This is verse', ], 'context' => 'verse', 'chords' => [ 'B', ] }, { 'value' => '', 'context' => 'verse', 'name' => 'context', 'type' => 'set' }, { 'type' => 'songline', 'phrases' => [ 'Another verse' ], 'context' => 'verse', 'chords' => [ 'C', ] }, { 'value' => '', 'context' => 'verse', 'name' => 'context', 'type' => 'set' } ], 'source' => { 'line' => 1, 'file' => '__STRING__' }, 'chordsinfo' => { map { $_ => $_ } qw( B C ) }, 'title' => 'Test Memorize', 'system' => 'common', 'structure' => 'linear', 'settings' => {}, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/40_basic01_html.t0000644000400000040000000000021614447223440016207 0ustar jvjv#! perl use strict; if ( -d "t" ) { chdir "t"; $0 =~ s;(^|/)t/;$1;; } @::params = qw( 40 basic01 html ); require "./00_basic.pl"; App-Music-ChordPro-6.050.7/t/130_image.t0000644000400000040000000000444414544302032015103 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 6; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Image (minimal). my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { assets => { _Image001 => { type => 'image', uri => '130_image.jpg' } }, 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'context' => '', 'id' => '_Image001', 'type' => 'image', 'opts' => {} } ], 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); # Image (all options). $s = ChordPro::Songbook->new; $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use DDP; p($s->{songs}[0]); $song = { 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, assets => { _Image002 => { type => 'image', uri => '130_image.jpg' } }, 'settings' => {}, 'body' => [ { 'type' => 'image', 'opts' => { 'title' => 'A red image', 'height' => '150', 'border' => '2', 'scale' => '4', 'align' => 'center', 'width' => '200' }, 'id' => '_Image002', 'context' => '' } ], 'chordsinfo' => {}, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/161_titles.t0000644000400000040000000000626714544300524015342 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 12; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Tites flush left. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => { 'titles' => 'left' }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Titles flush left" ); $s = ChordPro::Songbook->new; # Titles flush right. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => { 'titles' => 'right' }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'chordsinfo' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Titles flush right" ); $s = ChordPro::Songbook->new; # Chord grids. Added automatically. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => { 'titles' => 'center', }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'chordsinfo' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Titles centered" ); $s = ChordPro::Songbook->new; # Chord grids. Added automatically. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => { 'titles' => 'center', }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'chordsinfo' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Titles centred" ); App-Music-ChordPro-6.050.7/t/116_chorus.t0000644000400000040000000000256514544300524015336 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Chorus. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'chords' => [ '', 'D', 'G', 'D' ], 'context' => 'chorus', 'phrases' => [ 'Swing ', 'low, sweet ', 'chari', 'ot,' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'chorus', 'name' => 'context', 'type' => 'set' } ], 'chordsinfo' => { map { $_ => $_ } qw( D G ) }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/122_memorize.t0000644000400000040000000001224014544300524015646 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 4; $config->{settings}->{memorize} = 1; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; #### meta as meta. my $data = <parse_file(\$data) } or diag("$@"); } ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); ok( $warning =~ /No chords memorized for verse3/, "You have been warned" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'meta' => { 'songindex' => 1, 'title' => [ 'Test Memorize' ] }, 'body' => [ { 'type' => 'songline', 'phrases' => [ 'The ', 'quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ], 'context' => 'verse', 'chords' => [ '', 'A', 'B', 'F', 'D' ] }, { 'chords' => [ 'A', 'NC', 'F', 'D' ], 'context' => 'verse', 'phrases' => [ 'The quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'verse', 'name' => 'context', 'type' => 'set' }, { 'type' => 'songline', 'phrases' => [ 'The quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ], 'context' => 'verse2', 'chords' => [ 'E', 'B', 'F', 'D' ] }, { 'type' => 'songline', 'chords' => [ '', 'E', 'C', 'F', 'D' ], 'context' => 'verse2', 'phrases' => [ 'The ', 'quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ] }, { 'value' => '', 'context' => 'verse2', 'name' => 'context', 'type' => 'set' }, { 'phrases' => [ 'The quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ], 'context' => 'verse', 'chords' => [ 'A', 'B', 'F', 'D' ], 'type' => 'songline' }, { 'chords' => [ 'A', 'G', 'F', 'D' ], 'context' => 'verse', 'phrases' => [ 'The quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'verse', 'name' => 'context', 'type' => 'set' }, { 'chords' => [ '', 'E', 'B', 'F', 'D' ], 'context' => 'verse2', 'phrases' => [ 'The ', 'quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ], 'type' => 'songline' }, { 'type' => 'songline', 'chords' => [ 'E', 'C', 'F', 'D' ], 'context' => 'verse2', 'phrases' => [ 'The quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ] }, { 'value' => '', 'context' => 'verse2', 'name' => 'context', 'type' => 'set' }, { 'phrases' => [ 'The quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ], 'chords' => [ '^', '^', '^', '^' ], 'type' => 'songline', 'context' => 'verse3' }, { 'phrases' => [ 'The quick ', 'brown ', 'ox jumps over the lazy ', 'dog' ], 'chords' => [ '^', '^', '^', '^' ], 'type' => 'songline', 'context' => 'verse3' }, { 'value' => '', 'context' => 'verse3', 'name' => 'context', 'type' => 'set' }, ], 'source' => { 'line' => 1, 'file' => '__STRING__' }, 'chordsinfo' => { map { $_ => $_ } qw( A B C D E F G NC ) }, 'title' => 'Test Memorize', 'system' => 'common', 'structure' => 'linear', 'settings' => {}, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/174_transpose.t0000644000400000040000000000440714544302032016046 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro; use Test::More; plan tests => 13; my $data1 = < 0 }; our $config = eval { ChordPro::Config::configurator ( { nosysconfig => 1, nolegacyconfig => 1, nouserconfig => 1, config => getresource("config/notes/latin.json") } ); }; die unless ChordPro::Chords::Parser->get_parser($::config->{notes}->{system},1); =begin regenerate # Enable this section to generate new reference data. my $p = ChordPro::Chords::Parser->get_parser("latin"); open( my $fd, '<', "t/105_chords.t" ); my $skip = 1; while ( <$fd> ) { chomp; if ( $skip && /__DATA__/ ) { $skip = 0; next; } next if $skip; my ( $chord, $info ) = split( /\t/, $_ ); for ( $chord ) { s!^C#!Do#! or s!^C!Do! or s!^Db!Reb! or s!^D#!Re#! or s!^D!Re! or s!^Eb!Mib! or s!^E!Mi! or s!^F#!Fa#! or s!^F!Fa! or s!^Gb!Solb! or s!^G#!Sol#! or s!^G!Sol! or s!^Ab!Lab! or s!^A#!La#! or s!^A!La! or s!^Bb!Sib! or s!^B!Si!; s!/C#!/Do#! or s!/C!/Do! or s!/Db!/Reb! or s!/D#!/Re#! or s!/D!/Re! or s!/Eb!/Mib! or s!/E!/Mi! or s!/F#!/Fa#! or s!/F!/Fa! or s!/Gb!/Solb! or s!/G#!/Sol#! or s!/G!/Sol! or s!/Ab!/Lab! or s!/A#!/La#! or s!/A!/La! or s!/Bb!/Sib! or s!/B!/Si!; } my $c = $chord; $c =~ s/[()]//g; my $res = $p->parse($c); unless ( $res ) { print( "$chord\tFAIL\n"); next; } $res = {%$res}; delete($res->{parser}); print("$chord\t", reformat($res), "\n"); } exit; =cut while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; $tbl{$c} = $info; } plan tests => 0 + keys(%tbl); while ( my ( $c, $info ) = each %tbl ) { my $res = ChordPro::Chords::parse_chord($c); $res //= "FAIL"; if ( UNIVERSAL::isa( $res, 'HASH' ) ) { $res = reformat($res); } is( $res, $info, "parsing chord $c"); } sub reformat { my ( $res ) = @_; $res = {%$res}; delete($res->{parser}); use Data::Dumper qw(); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Deparse = 1; local $Data::Dumper::Terse = 1; local $Data::Dumper::Trailingcomma = 1; local $Data::Dumper::Useperl = 1; local $Data::Dumper::Useqq = 0; # I want unicode visible my $s = Data::Dumper::Dumper($res); $s =~ s/\s+/ /gs; $s =~ s/, \}/ }/gs; $s =~ s/\s+$//; return $s; } __DATA__ Do { bass => '', ext => '', ext_canon => '', name => 'Do', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do+ { bass => '', ext => '', ext_canon => '', name => 'Do+', qual => '+', qual_canon => '+', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do4 { bass => '', ext => 4, ext_canon => 4, name => 'Do4', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do6 { bass => '', ext => 6, ext_canon => 6, name => 'Do6', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do7 { bass => '', ext => 7, ext_canon => 7, name => 'Do7', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do9 { bass => '', ext => 9, ext_canon => 9, name => 'Do9', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do9(11) { bass => '', ext => 911, ext_canon => 911, name => 'Do911', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do11 { bass => '', ext => 11, ext_canon => 11, name => 'Do11', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Dosus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Dosus', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Dosus2 { bass => '', ext => 'sus2', ext_canon => 'sus2', name => 'Dosus2', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Dosus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Dosus4', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Dosus9 { bass => '', ext => 'sus9', ext_canon => 'sus9', name => 'Dosus9', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Domaj { bass => '', ext => '', ext_canon => '', name => 'Domaj', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Domaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Domaj7', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Dom { bass => '', ext => '', ext_canon => '', name => 'Dom', qual => 'm', qual_canon => '-', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Domin { bass => '', ext => '', ext_canon => '', name => 'Domin', qual => 'min', qual_canon => '-', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Dodim { bass => '', ext => '', ext_canon => '', name => 'Dodim', qual => 'dim', qual_canon => 0, root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do/Si { bass => 'Si', bass_canon => 'Si', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Do/Si', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Doadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Doadd9', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do3 { bass => '', ext => 3, ext_canon => 3, name => 'Do3', qual => '', qual_canon => '', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Dom7 { bass => '', ext => 7, ext_canon => 7, name => 'Dom7', qual => 'm', qual_canon => '-', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Dom11 { bass => '', ext => 11, ext_canon => 11, name => 'Dom11', qual => 'm', qual_canon => '-', root => 'Do', root_canon => 'Do', root_mod => 0, root_ord => 0, system => 'latin' } Do# { bass => '', ext => '', ext_canon => '', name => 'Do#', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#+ { bass => '', ext => '', ext_canon => '', name => 'Do#+', qual => '+', qual_canon => '+', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#4 { bass => '', ext => 4, ext_canon => 4, name => 'Do#4', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#7 { bass => '', ext => 7, ext_canon => 7, name => 'Do#7', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#7(b5) { bass => '', ext => '7b5', ext_canon => '7b5', name => 'Do#7b5', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Do#sus', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Do#sus4', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#maj { bass => '', ext => '', ext_canon => '', name => 'Do#maj', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Do#maj7', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#dim { bass => '', ext => '', ext_canon => '', name => 'Do#dim', qual => 'dim', qual_canon => 0, root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#m { bass => '', ext => '', ext_canon => '', name => 'Do#m', qual => 'm', qual_canon => '-', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#min { bass => '', ext => '', ext_canon => '', name => 'Do#min', qual => 'min', qual_canon => '-', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#add9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Do#add9', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#(add9) { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Do#add9', qual => '', qual_canon => '', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Do#m7 { bass => '', ext => 7, ext_canon => 7, name => 'Do#m7', qual => 'm', qual_canon => '-', root => 'Do#', root_canon => 'Do#', root_mod => 1, root_ord => 1, system => 'latin' } Reb { bass => '', ext => '', ext_canon => '', name => 'Reb', qual => '', qual_canon => '', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Reb+ { bass => '', ext => '', ext_canon => '', name => 'Reb+', qual => '+', qual_canon => '+', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Reb7 { bass => '', ext => 7, ext_canon => 7, name => 'Reb7', qual => '', qual_canon => '', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Rebsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Rebsus', qual => '', qual_canon => '', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Rebsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Rebsus4', qual => '', qual_canon => '', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Rebmaj { bass => '', ext => '', ext_canon => '', name => 'Rebmaj', qual => '', qual_canon => '', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Rebmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Rebmaj7', qual => '', qual_canon => '', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Rebdim { bass => '', ext => '', ext_canon => '', name => 'Rebdim', qual => 'dim', qual_canon => 0, root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Rebm { bass => '', ext => '', ext_canon => '', name => 'Rebm', qual => 'm', qual_canon => '-', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Rebmin { bass => '', ext => '', ext_canon => '', name => 'Rebmin', qual => 'min', qual_canon => '-', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Rebm7 { bass => '', ext => 7, ext_canon => 7, name => 'Rebm7', qual => 'm', qual_canon => '-', root => 'Reb', root_canon => 'Reb', root_mod => -1, root_ord => 1, system => 'latin' } Re { bass => '', ext => '', ext_canon => '', name => 'Re', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re+ { bass => '', ext => '', ext_canon => '', name => 'Re+', qual => '+', qual_canon => '+', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re4 { bass => '', ext => 4, ext_canon => 4, name => 'Re4', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re6 { bass => '', ext => 6, ext_canon => 6, name => 'Re6', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re7 { bass => '', ext => 7, ext_canon => 7, name => 'Re7', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Re7#9', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re7(#9) { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Re7#9', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re9 { bass => '', ext => 9, ext_canon => 9, name => 'Re9', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re11 { bass => '', ext => 11, ext_canon => 11, name => 'Re11', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Resus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Resus', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Resus2 { bass => '', ext => 'sus2', ext_canon => 'sus2', name => 'Resus2', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Resus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Resus4', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re7sus2 { bass => '', ext => '7sus2', ext_canon => '7sus2', name => 'Re7sus2', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'Re7sus4', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Remaj { bass => '', ext => '', ext_canon => '', name => 'Remaj', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Remaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Remaj7', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Redim { bass => '', ext => '', ext_canon => '', name => 'Redim', qual => 'dim', qual_canon => 0, root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Rem { bass => '', ext => '', ext_canon => '', name => 'Rem', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Remin { bass => '', ext => '', ext_canon => '', name => 'Remin', qual => 'min', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'Re/La', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re/Si { bass => 'Si', bass_canon => 'Si', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Re/Si', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'Re/Do', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re/Do# { bass => 'Do#', bass_canon => 'Do#', bass_mod => 1, bass_ord => 1, ext => '', ext_canon => '', name => 'Re/Do#', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re/Mi { bass => 'Mi', bass_canon => 'Mi', bass_mod => 0, bass_ord => 4, ext => '', ext_canon => '', name => 'Re/Mi', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re/Sol { bass => 'Sol', bass_canon => 'Sol', bass_mod => 0, bass_ord => 7, ext => '', ext_canon => '', name => 'Re/Sol', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re5/Mi { bass => 'Mi', bass_canon => 'Mi', bass_mod => 0, bass_ord => 4, ext => 5, ext_canon => 5, name => 'Re5/Mi', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Readd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Readd9', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re9add6 { bass => '', ext => '9add6', ext_canon => '9add6', name => 'Re9add6', qual => '', qual_canon => '', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Rem7 { bass => '', ext => 7, ext_canon => 7, name => 'Rem7', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Rem#5 { bass => '', ext => '#5', ext_canon => '#5', name => 'Rem#5', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Rem#7 { bass => '', ext => '#7', ext_canon => '#7', name => 'Rem#7', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Rem/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'Rem/La', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Rem/Si { bass => 'Si', bass_canon => 'Si', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Rem/Si', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Rem/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'Rem/Do', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Rem/Do# { bass => 'Do#', bass_canon => 'Do#', bass_mod => 1, bass_ord => 1, ext => '', ext_canon => '', name => 'Rem/Do#', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Rem9 { bass => '', ext => 9, ext_canon => 9, name => 'Rem9', qual => 'm', qual_canon => '-', root => 'Re', root_canon => 'Re', root_mod => 0, root_ord => 2, system => 'latin' } Re# { bass => '', ext => '', ext_canon => '', name => 'Re#', qual => '', qual_canon => '', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#+ { bass => '', ext => '', ext_canon => '', name => 'Re#+', qual => '+', qual_canon => '+', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#4 { bass => '', ext => 4, ext_canon => 4, name => 'Re#4', qual => '', qual_canon => '', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#7 { bass => '', ext => 7, ext_canon => 7, name => 'Re#7', qual => '', qual_canon => '', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Re#sus', qual => '', qual_canon => '', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Re#sus4', qual => '', qual_canon => '', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#maj { bass => '', ext => '', ext_canon => '', name => 'Re#maj', qual => '', qual_canon => '', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Re#maj7', qual => '', qual_canon => '', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#dim { bass => '', ext => '', ext_canon => '', name => 'Re#dim', qual => 'dim', qual_canon => 0, root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#m { bass => '', ext => '', ext_canon => '', name => 'Re#m', qual => 'm', qual_canon => '-', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#min { bass => '', ext => '', ext_canon => '', name => 'Re#min', qual => 'min', qual_canon => '-', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Re#m7 { bass => '', ext => 7, ext_canon => 7, name => 'Re#m7', qual => 'm', qual_canon => '-', root => 'Re#', root_canon => 'Re#', root_mod => 1, root_ord => 3, system => 'latin' } Mib { bass => '', ext => '', ext_canon => '', name => 'Mib', qual => '', qual_canon => '', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mib+ { bass => '', ext => '', ext_canon => '', name => 'Mib+', qual => '+', qual_canon => '+', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mib4 { bass => '', ext => 4, ext_canon => 4, name => 'Mib4', qual => '', qual_canon => '', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mib7 { bass => '', ext => 7, ext_canon => 7, name => 'Mib7', qual => '', qual_canon => '', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mibsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Mibsus', qual => '', qual_canon => '', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mibsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Mibsus4', qual => '', qual_canon => '', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mibmaj { bass => '', ext => '', ext_canon => '', name => 'Mibmaj', qual => '', qual_canon => '', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mibmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Mibmaj7', qual => '', qual_canon => '', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mibdim { bass => '', ext => '', ext_canon => '', name => 'Mibdim', qual => 'dim', qual_canon => 0, root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mibadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Mibadd9', qual => '', qual_canon => '', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mib(add9) { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Mibadd9', qual => '', qual_canon => '', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mibm { bass => '', ext => '', ext_canon => '', name => 'Mibm', qual => 'm', qual_canon => '-', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mibmin { bass => '', ext => '', ext_canon => '', name => 'Mibmin', qual => 'min', qual_canon => '-', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mibm7 { bass => '', ext => 7, ext_canon => 7, name => 'Mibm7', qual => 'm', qual_canon => '-', root => 'Mib', root_canon => 'Mib', root_mod => -1, root_ord => 3, system => 'latin' } Mi { bass => '', ext => '', ext_canon => '', name => 'Mi', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi+ { bass => '', ext => '', ext_canon => '', name => 'Mi+', qual => '+', qual_canon => '+', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi5 { bass => '', ext => 5, ext_canon => 5, name => 'Mi5', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi6 { bass => '', ext => 6, ext_canon => 6, name => 'Mi6', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi7 { bass => '', ext => 7, ext_canon => 7, name => 'Mi7', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Mi7#9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi7(#9) { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Mi7#9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi7(b5) { bass => '', ext => '7b5', ext_canon => '7b5', name => 'Mi7b5', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi7b9 { bass => '', ext => '7b9', ext_canon => '7b9', name => 'Mi7b9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi7(11) { bass => '', ext => 711, ext_canon => 711, name => 'Mi711', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi9 { bass => '', ext => 9, ext_canon => 9, name => 'Mi9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mi11 { bass => '', ext => 11, ext_canon => 11, name => 'Mi11', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Misus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Misus', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mimaj { bass => '', ext => '', ext_canon => '', name => 'Mimaj', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mimaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Mimaj7', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Midim { bass => '', ext => '', ext_canon => '', name => 'Midim', qual => 'dim', qual_canon => 0, root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mim { bass => '', ext => '', ext_canon => '', name => 'Mim', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mimin { bass => '', ext => '', ext_canon => '', name => 'Mimin', qual => 'min', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mim6 { bass => '', ext => 6, ext_canon => 6, name => 'Mim6', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mim7 { bass => '', ext => 7, ext_canon => 7, name => 'Mim7', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mim/Si { bass => 'Si', bass_canon => 'Si', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Mim/Si', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mim/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'Mim/Re', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mim7/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => 7, ext_canon => 7, name => 'Mim7/Re', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mimsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Mimsus4', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Mimadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Mimadd9', qual => 'm', qual_canon => '-', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'latin' } Fa { bass => '', ext => '', ext_canon => '', name => 'Fa', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa+ { bass => '', ext => '', ext_canon => '', name => 'Fa+', qual => '+', qual_canon => '+', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa4 { bass => '', ext => 4, ext_canon => 4, name => 'Fa4', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa6 { bass => '', ext => 6, ext_canon => 6, name => 'Fa6', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa7 { bass => '', ext => 7, ext_canon => 7, name => 'Fa7', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa9 { bass => '', ext => 9, ext_canon => 9, name => 'Fa9', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa11 { bass => '', ext => 11, ext_canon => 11, name => 'Fa11', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fasus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Fasus', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fasus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Fasus4', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Famaj { bass => '', ext => '', ext_canon => '', name => 'Famaj', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Famaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Famaj7', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fadim { bass => '', ext => '', ext_canon => '', name => 'Fadim', qual => 'dim', qual_canon => 0, root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fam { bass => '', ext => '', ext_canon => '', name => 'Fam', qual => 'm', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Famin { bass => '', ext => '', ext_canon => '', name => 'Famin', qual => 'min', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'Fa/La', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'Fa/Do', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'Fa/Re', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa/Sol { bass => 'Sol', bass_canon => 'Sol', bass_mod => 0, bass_ord => 7, ext => '', ext_canon => '', name => 'Fa/Sol', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa7/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => 7, ext_canon => 7, name => 'Fa7/La', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Famaj7/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => 'maj7', ext_canon => 'maj7', name => 'Famaj7/La', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Famaj7/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => 'maj7', ext_canon => 'maj7', name => 'Famaj7/Do', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Faadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Faadd9', qual => '', qual_canon => '', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fam6 { bass => '', ext => 6, ext_canon => 6, name => 'Fam6', qual => 'm', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fam7 { bass => '', ext => 7, ext_canon => 7, name => 'Fam7', qual => 'm', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fammaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Fammaj7', qual => 'm', qual_canon => '-', root => 'Fa', root_canon => 'Fa', root_mod => 0, root_ord => 5, system => 'latin' } Fa# { bass => '', ext => '', ext_canon => '', name => 'Fa#', qual => '', qual_canon => '', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#+ { bass => '', ext => '', ext_canon => '', name => 'Fa#+', qual => '+', qual_canon => '+', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#7 { bass => '', ext => 7, ext_canon => 7, name => 'Fa#7', qual => '', qual_canon => '', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#9 { bass => '', ext => 9, ext_canon => 9, name => 'Fa#9', qual => '', qual_canon => '', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#11 { bass => '', ext => 11, ext_canon => 11, name => 'Fa#11', qual => '', qual_canon => '', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Fa#sus', qual => '', qual_canon => '', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Fa#sus4', qual => '', qual_canon => '', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Fa#maj7', qual => '', qual_canon => '', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#dim { bass => '', ext => '', ext_canon => '', name => 'Fa#dim', qual => 'dim', qual_canon => 0, root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#m { bass => '', ext => '', ext_canon => '', name => 'Fa#m', qual => 'm', qual_canon => '-', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#min { bass => '', ext => '', ext_canon => '', name => 'Fa#min', qual => 'min', qual_canon => '-', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#/Mi { bass => 'Mi', bass_canon => 'Mi', bass_mod => 0, bass_ord => 4, ext => '', ext_canon => '', name => 'Fa#/Mi', qual => '', qual_canon => '', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#4 { bass => '', ext => 4, ext_canon => 4, name => 'Fa#4', qual => '', qual_canon => '', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#m6 { bass => '', ext => 6, ext_canon => 6, name => 'Fa#m6', qual => 'm', qual_canon => '-', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#m7 { bass => '', ext => 7, ext_canon => 7, name => 'Fa#m7', qual => 'm', qual_canon => '-', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#m7b5 { bass => '', ext => '7b5', ext_canon => '7b5', name => 'Fa#m7b5', qual => 'm', qual_canon => '-', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Fa#m/Do { bass => 'Do', bass_canon => 'Do', bass_mod => 0, bass_ord => 0, ext => '', ext_canon => '', name => 'Fa#m/Do', qual => 'm', qual_canon => '-', root => 'Fa#', root_canon => 'Fa#', root_mod => 1, root_ord => 6, system => 'latin' } Solb { bass => '', ext => '', ext_canon => '', name => 'Solb', qual => '', qual_canon => '', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solb+ { bass => '', ext => '', ext_canon => '', name => 'Solb+', qual => '+', qual_canon => '+', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solb7 { bass => '', ext => 7, ext_canon => 7, name => 'Solb7', qual => '', qual_canon => '', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solb9 { bass => '', ext => 9, ext_canon => 9, name => 'Solb9', qual => '', qual_canon => '', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solbsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Solbsus', qual => '', qual_canon => '', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solbsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Solbsus4', qual => '', qual_canon => '', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solbmaj { bass => '', ext => '', ext_canon => '', name => 'Solbmaj', qual => '', qual_canon => '', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solbmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Solbmaj7', qual => '', qual_canon => '', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solbdim { bass => '', ext => '', ext_canon => '', name => 'Solbdim', qual => 'dim', qual_canon => 0, root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solbm { bass => '', ext => '', ext_canon => '', name => 'Solbm', qual => 'm', qual_canon => '-', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solbmin { bass => '', ext => '', ext_canon => '', name => 'Solbmin', qual => 'min', qual_canon => '-', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Solbm7 { bass => '', ext => 7, ext_canon => 7, name => 'Solbm7', qual => 'm', qual_canon => '-', root => 'Solb', root_canon => 'Solb', root_mod => -1, root_ord => 6, system => 'latin' } Sol { bass => '', ext => '', ext_canon => '', name => 'Sol', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol+ { bass => '', ext => '', ext_canon => '', name => 'Sol+', qual => '+', qual_canon => '+', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol4 { bass => '', ext => 4, ext_canon => 4, name => 'Sol4', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol6 { bass => '', ext => 6, ext_canon => 6, name => 'Sol6', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol7 { bass => '', ext => 7, ext_canon => 7, name => 'Sol7', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol7b9 { bass => '', ext => '7b9', ext_canon => '7b9', name => 'Sol7b9', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Sol7#9', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol9 { bass => '', ext => 9, ext_canon => 9, name => 'Sol9', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol9(11) { bass => '', ext => 911, ext_canon => 911, name => 'Sol911', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol11 { bass => '', ext => 11, ext_canon => 11, name => 'Sol11', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Solsus', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Solsus4', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol6sus4 { bass => '', ext => '6sus4', ext_canon => '6sus4', name => 'Sol6sus4', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'Sol7sus4', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solmaj { bass => '', ext => '', ext_canon => '', name => 'Solmaj', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Solmaj7', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solmaj7sus4 { bass => '', ext => 'maj7sus4', ext_canon => 'maj7sus4', name => 'Solmaj7sus4', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solmaj9 { bass => '', ext => 'maj9', ext_canon => 'maj9', name => 'Solmaj9', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solm { bass => '', ext => '', ext_canon => '', name => 'Solm', qual => 'm', qual_canon => '-', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solmin { bass => '', ext => '', ext_canon => '', name => 'Solmin', qual => 'min', qual_canon => '-', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Soldim { bass => '', ext => '', ext_canon => '', name => 'Soldim', qual => 'dim', qual_canon => 0, root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Soladd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Soladd9', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol(add9) { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Soladd9', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol/La { bass => 'La', bass_canon => 'La', bass_mod => 0, bass_ord => 9, ext => '', ext_canon => '', name => 'Sol/La', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol/Si { bass => 'Si', bass_canon => 'Si', bass_mod => 0, bass_ord => 11, ext => '', ext_canon => '', name => 'Sol/Si', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'Sol/Re', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol/Fa# { bass => 'Fa#', bass_canon => 'Fa#', bass_mod => 1, bass_ord => 6, ext => '', ext_canon => '', name => 'Sol/Fa#', qual => '', qual_canon => '', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solm6 { bass => '', ext => 6, ext_canon => 6, name => 'Solm6', qual => 'm', qual_canon => '-', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solm7 { bass => '', ext => 7, ext_canon => 7, name => 'Solm7', qual => 'm', qual_canon => '-', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Solm/Sib { bass => 'Sib', bass_canon => 'Sib', bass_mod => -1, bass_ord => 10, ext => '', ext_canon => '', name => 'Solm/Sib', qual => 'm', qual_canon => '-', root => 'Sol', root_canon => 'Sol', root_mod => 0, root_ord => 7, system => 'latin' } Sol# { bass => '', ext => '', ext_canon => '', name => 'Sol#', qual => '', qual_canon => '', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#+ { bass => '', ext => '', ext_canon => '', name => 'Sol#+', qual => '+', qual_canon => '+', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#4 { bass => '', ext => 4, ext_canon => 4, name => 'Sol#4', qual => '', qual_canon => '', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#7 { bass => '', ext => 7, ext_canon => 7, name => 'Sol#7', qual => '', qual_canon => '', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Sol#sus', qual => '', qual_canon => '', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Sol#sus4', qual => '', qual_canon => '', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#maj { bass => '', ext => '', ext_canon => '', name => 'Sol#maj', qual => '', qual_canon => '', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Sol#maj7', qual => '', qual_canon => '', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#dim { bass => '', ext => '', ext_canon => '', name => 'Sol#dim', qual => 'dim', qual_canon => 0, root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#m { bass => '', ext => '', ext_canon => '', name => 'Sol#m', qual => 'm', qual_canon => '-', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#min { bass => '', ext => '', ext_canon => '', name => 'Sol#min', qual => 'min', qual_canon => '-', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#m6 { bass => '', ext => 6, ext_canon => 6, name => 'Sol#m6', qual => 'm', qual_canon => '-', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#m7 { bass => '', ext => 7, ext_canon => 7, name => 'Sol#m7', qual => 'm', qual_canon => '-', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Sol#m9maj7 { bass => '', ext => '9maj7', ext_canon => '9maj7', name => 'Sol#m9maj7', qual => 'm', qual_canon => '-', root => 'Sol#', root_canon => 'Sol#', root_mod => 1, root_ord => 8, system => 'latin' } Lab { bass => '', ext => '', ext_canon => '', name => 'Lab', qual => '', qual_canon => '', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Lab+ { bass => '', ext => '', ext_canon => '', name => 'Lab+', qual => '+', qual_canon => '+', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Lab4 { bass => '', ext => 4, ext_canon => 4, name => 'Lab4', qual => '', qual_canon => '', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Lab7 { bass => '', ext => 7, ext_canon => 7, name => 'Lab7', qual => '', qual_canon => '', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Lab11 { bass => '', ext => 11, ext_canon => 11, name => 'Lab11', qual => '', qual_canon => '', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Labsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Labsus', qual => '', qual_canon => '', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Labsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Labsus4', qual => '', qual_canon => '', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Labdim { bass => '', ext => '', ext_canon => '', name => 'Labdim', qual => 'dim', qual_canon => 0, root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Labmaj { bass => '', ext => '', ext_canon => '', name => 'Labmaj', qual => '', qual_canon => '', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Labmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Labmaj7', qual => '', qual_canon => '', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Labm { bass => '', ext => '', ext_canon => '', name => 'Labm', qual => 'm', qual_canon => '-', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Labmin { bass => '', ext => '', ext_canon => '', name => 'Labmin', qual => 'min', qual_canon => '-', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } Labm7 { bass => '', ext => 7, ext_canon => 7, name => 'Labm7', qual => 'm', qual_canon => '-', root => 'Lab', root_canon => 'Lab', root_mod => -1, root_ord => 8, system => 'latin' } La { bass => '', ext => '', ext_canon => '', name => 'La', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La+ { bass => '', ext => '', ext_canon => '', name => 'La+', qual => '+', qual_canon => '+', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La4 { bass => '', ext => 4, ext_canon => 4, name => 'La4', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La6 { bass => '', ext => 6, ext_canon => 6, name => 'La6', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La7 { bass => '', ext => 7, ext_canon => 7, name => 'La7', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La9 { bass => '', ext => 9, ext_canon => 9, name => 'La9', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La11 { bass => '', ext => 11, ext_canon => 11, name => 'La11', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La13 { bass => '', ext => 13, ext_canon => 13, name => 'La13', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'La7sus4', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La9sus { bass => '', ext => '9sus', ext_canon => '9sus', name => 'La9sus', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lasus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Lasus', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lasus2 { bass => '', ext => 'sus2', ext_canon => 'sus2', name => 'Lasus2', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lasus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Lasus4', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Ladim { bass => '', ext => '', ext_canon => '', name => 'Ladim', qual => 'dim', qual_canon => 0, root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lamaj { bass => '', ext => '', ext_canon => '', name => 'Lamaj', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lamaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Lamaj7', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lam { bass => '', ext => '', ext_canon => '', name => 'Lam', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lamin { bass => '', ext => '', ext_canon => '', name => 'Lamin', qual => 'min', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La/Do# { bass => 'Do#', bass_canon => 'Do#', bass_mod => 1, bass_ord => 1, ext => '', ext_canon => '', name => 'La/Do#', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La/Re { bass => 'Re', bass_canon => 'Re', bass_mod => 0, bass_ord => 2, ext => '', ext_canon => '', name => 'La/Re', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La/Mi { bass => 'Mi', bass_canon => 'Mi', bass_mod => 0, bass_ord => 4, ext => '', ext_canon => '', name => 'La/Mi', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La/Fa# { bass => 'Fa#', bass_canon => 'Fa#', bass_mod => 1, bass_ord => 6, ext => '', ext_canon => '', name => 'La/Fa#', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La/Sol# { bass => 'Sol#', bass_canon => 'Sol#', bass_mod => 1, bass_ord => 8, ext => '', ext_canon => '', name => 'La/Sol#', qual => '', qual_canon => '', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lam#7 { bass => '', ext => '#7', ext_canon => '#7', name => 'Lam#7', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lam6 { bass => '', ext => 6, ext_canon => 6, name => 'Lam6', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lam7 { bass => '', ext => 7, ext_canon => 7, name => 'Lam7', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lam7sus4 { bass => '', ext => '7sus4', ext_canon => '7sus4', name => 'Lam7sus4', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lam9 { bass => '', ext => 9, ext_canon => 9, name => 'Lam9', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lam/Sol { bass => 'Sol', bass_canon => 'Sol', bass_mod => 0, bass_ord => 7, ext => '', ext_canon => '', name => 'Lam/Sol', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } Lamadd9 { bass => '', ext => 'add9', ext_canon => 'add9', name => 'Lamadd9', qual => 'm', qual_canon => '-', root => 'La', root_canon => 'La', root_mod => 0, root_ord => 9, system => 'latin' } La# { bass => '', ext => '', ext_canon => '', name => 'La#', qual => '', qual_canon => '', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#+ { bass => '', ext => '', ext_canon => '', name => 'La#+', qual => '+', qual_canon => '+', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#4 { bass => '', ext => 4, ext_canon => 4, name => 'La#4', qual => '', qual_canon => '', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#7 { bass => '', ext => 7, ext_canon => 7, name => 'La#7', qual => '', qual_canon => '', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#sus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'La#sus', qual => '', qual_canon => '', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#sus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'La#sus4', qual => '', qual_canon => '', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#maj { bass => '', ext => '', ext_canon => '', name => 'La#maj', qual => '', qual_canon => '', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#maj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'La#maj7', qual => '', qual_canon => '', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#dim { bass => '', ext => '', ext_canon => '', name => 'La#dim', qual => 'dim', qual_canon => 0, root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#m { bass => '', ext => '', ext_canon => '', name => 'La#m', qual => 'm', qual_canon => '-', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#min { bass => '', ext => '', ext_canon => '', name => 'La#min', qual => 'min', qual_canon => '-', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } La#m7 { bass => '', ext => 7, ext_canon => 7, name => 'La#m7', qual => 'm', qual_canon => '-', root => 'La#', root_canon => 'La#', root_mod => 1, root_ord => 10, system => 'latin' } Sib { bass => '', ext => '', ext_canon => '', name => 'Sib', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sib+ { bass => '', ext => '', ext_canon => '', name => 'Sib+', qual => '+', qual_canon => '+', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sib4 { bass => '', ext => 4, ext_canon => 4, name => 'Sib4', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sib6 { bass => '', ext => 6, ext_canon => 6, name => 'Sib6', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sib7 { bass => '', ext => 7, ext_canon => 7, name => 'Sib7', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sib9 { bass => '', ext => 9, ext_canon => 9, name => 'Sib9', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sib11 { bass => '', ext => 11, ext_canon => 11, name => 'Sib11', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sibsus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Sibsus', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sibsus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Sibsus4', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sibmaj { bass => '', ext => '', ext_canon => '', name => 'Sibmaj', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sibmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Sibmaj7', qual => '', qual_canon => '', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sibdim { bass => '', ext => '', ext_canon => '', name => 'Sibdim', qual => 'dim', qual_canon => 0, root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sibm { bass => '', ext => '', ext_canon => '', name => 'Sibm', qual => 'm', qual_canon => '-', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sibmin { bass => '', ext => '', ext_canon => '', name => 'Sibmin', qual => 'min', qual_canon => '-', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sibm7 { bass => '', ext => 7, ext_canon => 7, name => 'Sibm7', qual => 'm', qual_canon => '-', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Sibm9 { bass => '', ext => 9, ext_canon => 9, name => 'Sibm9', qual => 'm', qual_canon => '-', root => 'Sib', root_canon => 'Sib', root_mod => -1, root_ord => 10, system => 'latin' } Si { bass => '', ext => '', ext_canon => '', name => 'Si', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Si+ { bass => '', ext => '', ext_canon => '', name => 'Si+', qual => '+', qual_canon => '+', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Si4 { bass => '', ext => 4, ext_canon => 4, name => 'Si4', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Si7 { bass => '', ext => 7, ext_canon => 7, name => 'Si7', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Si7#9 { bass => '', ext => '7#9', ext_canon => '7#9', name => 'Si7#9', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Si9 { bass => '', ext => 9, ext_canon => 9, name => 'Si9', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Si11 { bass => '', ext => 11, ext_canon => 11, name => 'Si11', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Si13 { bass => '', ext => 13, ext_canon => 13, name => 'Si13', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Sisus { bass => '', ext => 'sus', ext_canon => 'sus4', name => 'Sisus', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Sisus4 { bass => '', ext => 'sus4', ext_canon => 'sus4', name => 'Sisus4', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Simaj { bass => '', ext => '', ext_canon => '', name => 'Simaj', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Simaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Simaj7', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Sidim { bass => '', ext => '', ext_canon => '', name => 'Sidim', qual => 'dim', qual_canon => 0, root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Sim { bass => '', ext => '', ext_canon => '', name => 'Sim', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Simin { bass => '', ext => '', ext_canon => '', name => 'Simin', qual => 'min', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Si/Fa# { bass => 'Fa#', bass_canon => 'Fa#', bass_mod => 1, bass_ord => 6, ext => '', ext_canon => '', name => 'Si/Fa#', qual => '', qual_canon => '', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Sim6 { bass => '', ext => 6, ext_canon => 6, name => 'Sim6', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Sim7 { bass => '', ext => 7, ext_canon => 7, name => 'Sim7', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Simmaj7 { bass => '', ext => 'maj7', ext_canon => 'maj7', name => 'Simmaj7', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Simsus9 { bass => '', ext => 'sus9', ext_canon => 'sus9', name => 'Simsus9', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } Sim7b5 { bass => '', ext => '7b5', ext_canon => '7b5', name => 'Sim7b5', qual => 'm', qual_canon => '-', root => 'Si', root_canon => 'Si', root_mod => 0, root_ord => 11, system => 'latin' } App-Music-ChordPro-6.050.7/t/142_chords.t0000644000400000040000000000372714544300524015315 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Chord definitions. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; $Data::Dumper::Indent=1; warn(Dumper($s)); my $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'define' => [ { 'name' => 'Fas', 'base' => '2', 'frets' => [ -1, '0', '3', '2', '1', '0' ], 'fingers' => [ -1, -1, '3', '2', '1', -1 ], }, ], 'body' => [ { 'context' => '', 'type' => 'empty' }, { 'context' => '', 'type' => 'songline', 'phrases' => [ 'Hello World!' ] }, { 'type' => 'empty', 'context' => '' }, { 'context' => '', 'origin' => 'chord', 'type' => 'diagrams', 'show' => 'user', 'chords' => [ ' ch0000', ' ch0001' ], }, { 'type' => 'empty', 'context' => '' }, { 'context' => '', 'type' => 'songline', 'phrases' => [ 'Hi there.' ] } ], 'chordsinfo' => { 'Fus' => 'Fus', 'Fos' => 'Fos', ' ch0000' => 'Fus', ' ch0001' => 'Fos', Fas => 'Fas' }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/02_load.t0000644000400000040000000000111714544300524014654 0ustar jvjv#! perl use strict; use warnings; use Test::More; if ( -d "t" ) { chdir "t"; $0 =~ s;(^|/)t/;$1;; } use lib "../script"; my $test; ++$test; use_ok("ChordPro"); ++$test; use_ok("ChordPro::Config"); ++$test; use_ok("ChordPro::Testing"); ++$test; use_ok("ChordPro::Songbook"); ++$test; use_ok("ChordPro::Output::Debug"); ++$test; use_ok("ChordPro::Output::Text"); ++$test; use_ok("ChordPro::Output::ChordPro"); ++$test; use_ok("ChordPro::Output::HTML"); ++$test; use_ok("ChordPro::Output::PDF"); diag( "Testing ChordPro $ChordPro::VERSION, Perl $], $^X" ); done_testing($test); App-Music-ChordPro-6.050.7/t/74_latex.t0000644000400000040000000000234014544300524015062 0ustar jvjv#! perl use strict; use warnings; use utf8; use ChordPro::Testing; BAIL_OUT("Missing md test data") unless -d "md"; opendir( my $dh, "latex" ) || BAIL_OUT("Cannot open md test data"); my @files = grep { /^.+\.cho$/ } readdir($dh); close($dh); my $numtests = @files; my $test = 0; plan tests => 1+$numtests; SKIP: { unless ( eval { require Template } ) { diag( 'Skipped all tests -- missing Template module' ); skip( 'Missing Template module', 1+$numtests ); } unless ( eval { require LaTeX::Encode } ) { diag( 'Skipped all tests -- missing LaTeX::Encode module' ); skip( 'Missing LaTeX::Encode module', 1+$numtests ); } diag("Testing ", scalar(@files), " cho files"); our $options; foreach my $file ( sort @files ) { $test++; $file = "latex/$file"; #diag("Testing: $file"); ( my $out = $file ) =~ s/\.cho/.tmp/; ( my $ref = $file ) =~ s/\.cho/.tex/; @ARGV = ( "--no-default-configs", "--config", "./latex/t_config.json", "--generate", "LaTeX", "--output", $out, $file ); ::run(); my $ok = !differ( $out, $ref ); ok( $ok, $file ); unlink($out) if $ok; system( $ENV{CHORDPRO_DIFF}, $out, $ref) if $ENV{CHORDPRO_DIFF}; } ok( $test++ == $numtests, "Tested $numtests files" ); } App-Music-ChordPro-6.050.7/t/85_pages.t0000644000400000040000000000347414544300524015057 0ustar jvjv#! perl use strict; use warnings; use utf8; use Test::More tests => 7; use ChordPro::Testing; use_ok "ChordPro"; my $test = 1; BAIL_OUT("Missing out dir") unless -d "out"; my $base = "out/85_pages."; my $pdf = $base . "pdf"; my $cho = $base . "cho"; my $csv = $base . "csv"; ( my $ref = $csv ) =~ s/out/ref/; my $front = $base . "front.pdf"; my $back = $base . "back.pdf"; use ChordPro::Output::PDF; my $api = ChordPro::Output::PDF::config_pdfapi; my $p = $api->new( file => $front ); my $page = $p->page; $page->mediabox("A4"); my $text = $page->text; $text->font( $p->corefont("Times-Roman"), 100 ); $text->translate( 297, 600 ); $text->text( "FRONT", align => "center" ); $p->saveas($front); $p = $api->new( file => $back ); $page = $p->page; $page->mediabox("A4"); $text = $page->text; $text->font( $p->corefont("Times-Roman"),100 ); $text->translate( 297, 300 ); $text->text( "BACK", align => "center" ); $p->saveas($back); our $options; ok( open( my $fd, '>:utf8', $cho ), "Create $cho" ); print $fd <new; # Just see if all directives are recognized and handled. my $data = < 1 + 2 * @data; eval { $s->parse_file(\$data); 1 } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "Directives parsed" ); my $song = $s->{songs}->[0]; # Add a dummy selector to each directive. for ( @data ) { s/^\{(.*?)(\s*:.*)\}$/$1-foo$2/mg or s/^\{(.*?)\}$/$1-foo/mg; } # Verify that the directives are ignored. for my $dir ( @data ) { my $result = $song->parse_directive($dir); ok( $result->{omit} > 0, "ignored ok: $dir ($result->{name})" ); p($result) unless $result->{omit} > 0; } $song->{meta}->{foo} = [ 1 ]; # Verify that the directives are not ignored. for my $dir ( @data ) { my $result = $song->parse_directive($dir); ok( $result->{omit} == 0, "not ignored ok: $dir ($result->{name})" ); p($result) unless $result->{omit} == 0; } App-Music-ChordPro-6.050.7/t/84_pages.t0000644000400000040000000000207014544300524015045 0ustar jvjv#! perl use strict; use warnings; use utf8; use Test::More tests => 5; use ChordPro::Testing; use_ok "ChordPro"; my $test = 1; BAIL_OUT("Missing out dir") unless -d "out"; my $base = "out/84_pages."; my $pdf = $base . "pdf"; my $cho = $base . "cho"; my $csv = $base . "csv"; ( my $ref = $csv ) =~ s/out/ref/; our $options; ok( open( my $fd, '>:utf8', $cho ), "Create $cho" ); print $fd < 2; is_deeply( parse_kv ( "foo bar foo xxx=yyy zzz='abc def'" ), { foo => 2, bar => 1, xxx => 'yyy', zzz => 'abc def' } ); is_deeply( parse_kv ( "no-foo no_bar noblech=1" ), { foo => 0, bar => 0, noblech => 1 } ); App-Music-ChordPro-6.050.7/t/87_pages.t0000644000400000040000000000354414544300524015057 0ustar jvjv#! perl use strict; use warnings; use utf8; use Test::More tests => 7; use ChordPro::Testing; use_ok "ChordPro"; my $test = 1; BAIL_OUT("Missing out dir") unless -d "out"; my $base = "out/87_pages."; my $pdf = $base . "pdf"; my $cho = $base . "cho"; my $csv = $base . "csv"; ( my $ref = $csv ) =~ s/out/ref/; my $front = $base . "front.pdf"; my $back = $base . "back.pdf"; use ChordPro::Output::PDF; my $api = ChordPro::Output::PDF::config_pdfapi; my $p = $api->new( file => $front ); my $page = $p->page; $page->mediabox("A4"); my $text = $page->text; $text->font( $p->corefont("Times-Roman"), 100 ); $text->translate( 297, 600 ); $text->text( "FRONT", align => "center" ); $p->saveas($front); $p = $api->new( file => $back ); $page = $p->page; $page->mediabox("A4"); $text = $page->text; $text->font( $p->corefont("Times-Roman"),100 ); $text->translate( 297, 300 ); $text->text( "BACK", align => "center" ); $p->saveas($back); our $options; ok( open( my $fd, '>:utf8', $cho ), "Create $cho" ); print $fd < 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Song line with leading / trailing chords. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'type' => 'empty', 'context' => '' }, { 'context' => '', 'phrases' => [ 'I looked over Jordan, and ', 'what did I see,', '' ], 'chords' => [ 'D', 'G', 'D' ], 'type' => 'songline' }, { 'type' => 'empty', 'context' => '' } ], 'chordsinfo' => { map { $_ => $_ } qw( D G ) }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/152_fonts.t0000644000400000040000000000531314544302032015152 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Fonts definitions. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'body' => [ { 'value' => 'blue', 'context' => '', 'name' => 'text-color', 'type' => 'control' }, { 'value' => 'blue', 'context' => '', 'name' => 'chorus-color', 'type' => 'control' }, { 'name' => 'chorus-color', 'type' => 'control', 'context' => '', 'value' => 'red' }, { 'type' => 'songline', 'context' => '', 'phrases' => [ 'Song line in blue' ] }, { 'value' => 'green', 'context' => '', 'name' => 'text-color', 'type' => 'control' }, { 'name' => 'chorus-color', 'type' => 'control', 'value' => 'green', 'context' => '' }, { 'phrases' => [ 'Song line in green (chorus would be green as well)' ], 'context' => '', 'type' => 'songline' }, { 'type' => 'control', 'name' => 'text-color', 'value' => 'blue', 'context' => '' }, { 'value' => 'red', 'context' => '', 'name' => 'chorus-color', 'type' => 'control' }, { 'phrases' => [ 'Song line in blue again' ], 'context' => '', 'type' => 'songline' }, { 'context' => 'chorus', 'phrases' => [ 'Chorus line should be red again' ], 'type' => 'songline' }, { 'type' => 'set', 'name' => 'context', 'value' => '', 'context' => 'chorus' } ] }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/73_md.t0000644000400000040000000000160614544300524014350 0ustar jvjv#! perl use strict; use warnings; use utf8; use ChordPro::Testing; my $test = 0; BAIL_OUT("Missing md test data") unless -d "md"; opendir( my $dh, "md" ) || BAIL_OUT("Cannot open md test data"); my @files = grep { /^.+\.cho$/ } readdir($dh); close($dh); diag("Testing ", scalar(@files), " cho files"); our $options; foreach my $file ( sort @files ) { $test++; my $decoda = $file =~ /^decoda/i; $file = "md/$file"; #diag("Testing: $file"); ( my $out = $file ) =~ s/\.cho/.tmp/; ( my $ref = $file ) =~ s/\.cho/.md/; @ARGV = ( "--no-default-configs", "--generate", "Markdown", "--output", $out, $file ); ::run(); my $ok = !differ( $out, $ref ); ok( $ok, $file ); unlink($out) if $ok; system( $ENV{CHORDPRO_DIFF}, $out, $ref) if $ENV{CHORDPRO_DIFF}; } ok( $test++ == @files, "Tested @{[0+@files]} files" ); done_testing($test); App-Music-ChordPro-6.050.7/t/31_basic02_cho.t0000644000400000040000000000021514447223440016014 0ustar jvjv#! perl use strict; if ( -d "t" ) { chdir "t"; $0 =~ s;(^|/)t/;$1;; } @::params = qw( 31 basic02 cho ); require "./00_basic.pl"; App-Music-ChordPro-6.050.7/t/211_config.t0000644000400000040000000000151514544300524015266 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Config; plan tests => 12; our $config = bless { outer => { foo => 1, bar => [ qw(aap noot mies) ], blech => 'q', }, inner => { foo => 2, bar => [ qw(aap noot mies) ], outer => { foo => 3, bar => [ qw(three blind mice) ], blech => 'a', } }, } => 'ChordPro::Config'; is( _c("outer.foo"), "1" ); is( _c("outer.bar.1"), "noot" ); is( _c("inner.foo"), "2" ); is( _c("inner.bar.1"), "noot" ); is( _c("inner.outer.foo"), "3" ); is( _c("inner.outer.bar.1"), "blind" ); $config->set_context("inner"); is( _c("foo"), "2" ); is( _c("bar.1"), "noot" ); is( _c("blech","x"), "x" ); $config->set_context("inner.outer"); is( _c("foo"), "3" ); is( _c("bar.1"), "blind" ); is( _c("blech","x"), "a" ); App-Music-ChordPro-6.050.7/t/180_grids.t0000644000400000040000000002646514544300524015151 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 6; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use DDumper; warn(DDumper($s)); my $song = { meta => { songindex => 1, title => ['Grids'], }, settings => {}, source => { file => "__STRING__", line => 1 }, structure => 'linear', 'system' => 'common', title => 'Grids', chordsinfo => { map { $_ => $_ } qw( B C D E ) }, body => [ { context => 'grid', name => 'gridparams', type => 'set', value => [4, 3, 0, 0, '']}, { context => 'grid', type => 'gridline', tokens => [ { class => 'bar', symbol => '|' }, { chord => 'B', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'C', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chords => ['D','C'], class => 'chords' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'E', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, ], }, { context => 'grid', type => 'gridline', tokens => [ { class => 'bar', symbol => '|' }, { chord => 'B', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'C', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'D', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'E', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, ], }, { context => 'grid', type => 'gridline', tokens => [ { class => 'bar', symbol => '|' }, { chord => 'B', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'C', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'D', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'E', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, ], }, { context => 'grid', type => 'gridline', tokens => [ { class => 'bar', symbol => '|' }, { chord => 'B', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'C', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'D', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, { chord => 'E', class => 'chord' }, { class => 'space', symbol => '.' }, { class => 'space', symbol => '.' }, { class => 'bar', symbol => '|' }, ], }, { 'value' => '', 'context' => 'grid', 'name' => 'context', 'type' => 'set' }, ], }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); # Chord definitions. $data = <parse_file( \$data, { transpose => 2 } ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 2, "One more song" ); isa_ok( $s->{songs}->[1], 'ChordPro::Song', "It's a song" ); $song = { body => [ { context => 'grid', name => 'gridparams', type => 'set', value => [ 4, 3, 1, 2, '' ], }, { context => 'grid', tokens => [ { class => 'bar', symbol => '|', }, { chord => 'C#', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'D', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'E', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'F#', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, ], type => 'gridline', }, { context => 'grid', name => 'context', type => 'set', value => '', }, { context => 'grid', name => 'gridparams', type => 'set', value => [ 4, 3, 1, 2 ], }, { context => 'grid', tokens => [ { class => 'bar', symbol => '|', }, { chord => 'C#', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'D', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'E', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'F#', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, ], type => 'gridline', }, { context => 'grid', name => 'context', type => 'set', value => '', }, { context => 'grid', name => 'gridparams', type => 'set', value => [ 4, 3, 1, 2 ], }, { context => 'grid', tokens => [ { class => 'bar', symbol => '|', }, { chord => 'C#', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'D', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'E', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'F#', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, ], type => 'gridline', }, { context => 'grid', name => 'context', type => 'set', value => '', }, { context => 'grid', name => 'gridparams', type => 'set', value => [ 4, 3, 1, 2 ], }, { context => 'grid', tokens => [ { class => 'bar', symbol => '|', }, { chord => 'C#', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'D', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'E', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, { chord => 'F#', class => 'chord', }, { class => 'space', symbol => '.', }, { class => 'space', symbol => '.', }, { class => 'bar', symbol => '|', }, ], type => 'gridline', }, { context => 'grid', name => 'context', type => 'set', value => '', }, ], meta => { songindex => 2, title => [ 'Grids', ], }, settings => {}, source => { file => '__STRING__', line => 1, }, structure => 'linear', system => 'common', title => 'Grids', chordsinfo => { map { $_ => $_ } qw ( D E ), 'C#', 'F#' }, }; is_deeply( { %{ $s->{songs}->[1] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/21_basic02_crd.t0000644000400000040000000000021514447223440016012 0ustar jvjv#! perl use strict; if ( -d "t" ) { chdir "t"; $0 =~ s;(^|/)t/;$1;; } @::params = qw( 21 basic02 crd ); require "./00_basic.pl"; App-Music-ChordPro-6.050.7/t/193_outlines.t0000644000400000040000000000571014544302032015671 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; use ChordPro::Output::Common; plan tests => 3; # For transcoding, both source and target notation systems must be # defined. The source system must be last, so it is current and used # to parse the the input data. my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 3, "three songs" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $book = [ map { { meta => $_->{meta} } } @{$s->{songs}} ]; my $ctl = { fields => [ qw( key ) ] }; my $res = ChordPro::Output::Common::prep_outlines( $book, $ctl ); my $xp = [ [ 'a', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'A', ], key_actual => [ 'Am', ], songindex => 1, sorttitle => [ 'Fietspomp, De', ], title => [ 'De Fietspomp', ], }, }, ], [ 'am', { meta => { artist => [ 'September', ], chords => [ ], key => [ 'Am', ], key_actual => [ 'Am', ], numchords => [ 0, ], songindex => 1, sorttitle => [ 'Fietspomp, De', ], title => [ 'De Fietspomp', ], }, }, ], [ 'c', { meta => { artist => [ 'September', 'December', ], chords => [ ], key => [ 'C', ], key_actual => [ 'C', ], numchords => [ 0, ], songindex => 3, sorttitle => [ 'Fietsenhok, Het', 'Fietsenstalling, De', ], title => [ 'Het Fietsenhok', 'De Fietsenstalling', ], }, }, ], [ 'd', { meta => { artist => [ 'September', 'December', ], chords => [ ], key => [ 'D', ], key_actual => [ 'D', ], numchords => [ 0, ], songindex => 2, sorttitle => [ 'Vierentwintig Fietsen', ], title => [ '24 Fietsen', ], }, }, ], ]; foreach ( @$res ) { delete $_->[1]->{meta}->{_configversion}; } is_deeply( $res, $xp, "outlined"); App-Music-ChordPro-6.050.7/t/171_transpose.t0000644000400000040000000000256314544300524016050 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data, { transpose => -4 } ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'key' => [ 'Bb' ], 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'chords' => { 'origin' => 'song', 'type' => 'diagrams', 'show' => 'all', 'chords' => [ 'Bb', 'Ebm7' ] }, 'body' => [ { 'context' => '', 'phrases' => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,' ], 'chords' => [ '', 'Bb', 'Ebm7', 'Bb' ], 'type' => 'songline' } ], 'chordsinfo' => { map { $_ => $_ } qw( Bb Ebm7 ) }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/119_verse.t0000644000400000040000000000262714544300524015161 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Tabs. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'context' => 'verse', 'phrases' => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,' ], 'chords' => [ '', 'D', 'G', 'D' ], 'type' => 'songline' }, { 'value' => '', 'context' => 'verse', 'name' => 'context', 'type' => 'set' } ], 'chordsinfo' => { map { $_ => $_ } qw( D G ) }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/15_subst.t0000644000400000040000000000371414544300524015106 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; my $tests = 0; use_ok( qw(ChordPro::Output::Common) ); $tests++; use ChordPro::Config; $::config->{metadata}->{separator} = ":"; our $options = { verbose => 0, debug => 0 }; my $s = { page => 24, meta => { title => "Hi There!", subtitle => ["%{capo|CAPO %{}}"], capo => [1], key => [ "G" ], h => ["Z"], multi => [ "Alpha", "Beta" ], head => ["yes"], true => 1, false => 0, }, }; while ( ) { next if /^#/; next unless /\S/; chomp; my ( $tpl, $exp ) = split( /\t+/, $_ ); my $res = ChordPro::Output::Common::fmt_subst( $s, $tpl ); is( $res, $exp, "$tpl -> $exp" ); $tests++; } done_testing($tests); __END__ # No substitutions abcd abcd # Percent -> % ab%%cd ab%%cd # Lone brace ab}cd ab}cd # Short (single character) variable -- nope, we don't do this anymore. ab%pead ab%pead # Meta variable ab%{head}def abyesdef ab%{head}def%{head}xy abyesdefyesxy %{head}def yesdef %{h}def Zdef %{true} 1 %{false} 0 # Subtitute the value X%{head}Y XyesY # Subtitute the 'true' part X%{head|foo}Y XfooY X%{hexd|foo}Y XY # %{} refers to the value of the key. X%{head|This is %{}!}Y XThis is yes!Y X%{head=yes|This is %{}!}Y XThis is yes!Y X%{head=no|This is %{}!}Y XY # But only within a %{ ... } X%{}Y X%{}Y # Subtitute the 'false' part X%{head=no|foo|bar}Y XbarY X%{hexd|foo|bar}Y XbarY X%{head|0|foo}Y X0Y X%{hexd|foo|0}Y X0Y X%{hexd=yes|foo|bar}Y XbarY X%{hexd=no|foo|bar}Y XbarY X%{hexd=|foo|bar}Y XfooY X%{h|foo|bar}Y XfooY X%{h=Z|foo|bar}Y XfooY # Nested. X%{head|x%{foo}y|bar}Y XxyY X%{hexd|x%{foo}y|bar}Y XbarY # Note that %{} is the value of foo (inner), not head (outer). X%{head|x%{foo|ab|f%{}g}y}Y XxfgyY # Multi values. %{multi} Alpha:Beta %{multi.1} Alpha %{multi.2} Beta %{multi.-1} Beta # Recursive substitution for (sub)title. %{subtitle} CAPO 1 # Transpose. %{key} G %{_key} G# App-Music-ChordPro-6.050.7/t/103_title.t0000644000400000040000000000203514544300524015140 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 4; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; my $data = <parse_file(\$data) } or diag("$@"); #use DDumper; DDumper( $s->{songs} ); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); $s = ChordPro::Songbook->new; $data = <parse_file(\$data) } or diag("$@"); is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/50_encodings.t0000644000400000040000000000465314544302032015715 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; use Encode qw( encode from_to ); # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "__STRING__: One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], 'subtitle' => [ 'Sub Títlë', ] }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'subtitle' => [ 'Sub Títlë', ] }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); mkdir("out") unless -d "out"; $data = encode("UTF-8", $data); my @BOMs = qw( UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE ); my @noBOMs = qw( ISO-8859-1 UTF-8 ); my %enc2bom = map { $_ => encode($_, "\x{feff}") } @BOMs; enctest( $_, 1 ) for @noBOMs; enctest($_) for @BOMs; done_testing( 3 * ( 1 + 4*(@noBOMs + @BOMs) ) ); sub enctest { my ( $enc, $nobom ) = @_; my $encoded = $data; _enctest( $encoded, $enc, $nobom ); $encoded = $data; $encoded =~ s/\n/\x0a/g; _enctest( $encoded, $enc, $nobom, "LF" ); $encoded = $data; $encoded =~ s/\n/\x0d/g; _enctest( $encoded, $enc, $nobom, "CR" ); $encoded = $data; $encoded =~ s/\n/\x0d\x0a/g; _enctest( $encoded, $enc, $nobom, "CRLF" ); } sub _enctest { my ( $encoded, $enc, $nobom, $crlf ) = @_; from_to( $encoded, "UTF-8", $enc ); unless ( $nobom ) { BAIL_OUT("Unknown encoding: $enc") unless $enc2bom{$enc}; $encoded = $enc2bom{$enc} . $encoded; } my $fn = "out/$enc.cho"; open( my $fh, ">:raw", $fn ) or die("$fn: $!\n"); print $fh $encoded; close($fh); $enc .= " (no BOM)" if $nobom; $enc .= " ($crlf)" if $crlf; my $s = ChordPro::Songbook->new; eval { $s->parse_file($fn) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "$enc: One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); $song->{source}->{file} = $fn; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); unlink($fn); } App-Music-ChordPro-6.050.7/t/82_pages.t0000644000400000040000000000213714544300524015047 0ustar jvjv#! perl use strict; use warnings; use utf8; use Test::More tests => 5; use ChordPro::Testing; use_ok "ChordPro"; my $test = 1; BAIL_OUT("Missing out dir") unless -d "out"; my $base = "out/82_pages."; my $pdf = $base . "pdf"; my $cho = $base . "cho"; my $csv = $base . "csv"; ( my $ref = $csv ) =~ s/out/ref/; our $options; ok( open( my $fd, '>:utf8', $cho ), "Create $cho" ); print $fd < 6; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Chord definitions. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; $Data::Dumper::Indent=1; warn(Dumper($s)); my $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'define' => [ { 'name' => 'Fas', 'base' => '2', 'frets' => [ -1, '0', '3', '2', '1', '0' ], }, ], 'body' => [ { 'context' => '', 'type' => 'empty' }, { 'context' => '', 'type' => 'songline', 'phrases' => [ 'Hello World!' ] }, { 'type' => 'empty', 'context' => '' }, { 'context' => '', 'origin' => 'chord', 'type' => 'diagrams', 'show' => 'user', 'chords' => [ ' ch0000', ' ch0001' ], }, { 'type' => 'empty', 'context' => '' }, { 'context' => '', 'type' => 'songline', 'phrases' => [ 'Hi there.' ] } ], 'chordsinfo' => { 'Fus' => 'Fus', 'Fos' => 'Fos', ' ch0000' => 'Fus', ' ch0001' => 'Fos', Fas => 'Fas' }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); $s = ChordPro::Songbook->new; # Chord definitions. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'define' => [ { 'name' => 'Fus', 'base' => '1', 'frets' => [ -1, '0', '3', '2', '1', '0' ], }, { 'name' => 'Fos', 'base' => '1', 'frets' => [ -1, '0', '3', '2', '1', '0' ], }, { 'name' => 'Fas', 'base' => '1', 'frets' => [ -1, '0', '3', '2', '1', '0' ], } ], 'chordsinfo' => { 'Fus' => 'Fus', 'Fos' => 'Fos', Fas => 'Fas' }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/basic01.cho0000644000400000040000000000161012731610165015163 0ustar jvjv{titles: center} {columns: 2} {title:Swing Low Sweet Chariot} {st:Traditional} {start_of_chorus} Swing [D]low, sweet [G]chari[D]ot, Comin’ for to carry me [A7]home. Swing [D7]low, sweet [G]chari[D]ot, Comin’ for to [A7]carry me [D]home. {end_of_chorus} I [D]looked over Jordan, and [G]what did I [D]see, “Comin’ for to carry me [A7]home.” A [D7]band of angels [G]comin’ after [D]me, Comin’ for to [A7]carry me [D]home. [G]     [D] [G] {np} {c:Chorus} If you get there before I do, Comin’ for to carry me home. Just tell my friends that I’m a comin’ too. Comin’ for to carry me home. {colb} {c:Chorus} # Note the ' here will be changed into a decent apostrophe. I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. {c:Chorus} {ci:And a final chorus} App-Music-ChordPro-6.050.7/t/114_songline.t0000644000400000040000000000233114544300524015636 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'context' => '', 'phrases' => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,' ], 'chords' => [ '', 'D', 'G', 'D' ], 'type' => 'songline' } ], 'chordsinfo' => { map { $_ => $_ } qw( D G ) }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/160_diagrams.t0000644000400000040000000000576714544300524015630 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 9; my $s = ChordPro::Songbook->new; # Chord grids. Added automatically. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'body' => [ { 'chords' => [ 'G', 'D', 'G', 'D' ], 'type' => 'songline', 'phrases' => [ 'Swing ', 'low, sweet ', 'chari', 'ot,' ], 'context' => '' }, ], 'chordsinfo' => { map { $_ => $_ } qw( D G ) }, 'chords' => { 'chords' => [ 'G', 'D' ], 'origin' => 'song', 'show' => 'all', 'type' => 'diagrams' }, }; is_deeply( { %{ $s->{songs}->[-1] } }, $song, "Grids are shown by default" ); $s = ChordPro::Songbook->new; # Chord grids. Added automatically. Suppressed. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => { 'diagrams' => 0 }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Grids suppressed" ); # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; $s = ChordPro::Songbook->new; # Chord grids. Added automatically. $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => { 'diagrams' => 1 }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'chordsinfo' => {}, 'chords' => { 'chords' => [], 'origin' => 'song', 'show' => 'all', 'type' => 'diagrams' }, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Grids hidden, but forced" ); App-Music-ChordPro-6.050.7/t/00_basic.pl0000644000400000040000000000142314544300524015164 0ustar jvjv#! perl use strict; use warnings; use utf8; use ChordPro::Testing; mkdir("out") unless -d "out"; my $test; my ( $num, $basic, $backend ) = @::params; my $base = "${num}_${backend}"; my @argv = ( "--no-default-configs", "$basic.cho" ); # Some basic tests. my $out = "${base}_" . ++$test . ".$backend"; @ARGV = ( @argv, "--no-single-space", "--output=out/$out" ); ::run(); ok( !differ( "out/$out", "ref/$out" ) ); # Single space. $out = "${base}_" . ++$test . ".$backend"; @ARGV = ( @argv, "--single-space", "--output=out/$out" ); ::run(); ok( !differ( "out/$out", "ref/$out" ) ); # Lyrics only. $out = "${base}_" . ++$test . ".$backend"; @ARGV = ( @argv, "--lyrics-only", "--output=out/$out" ); ::run(); ok( !differ( "out/$out", "ref/$out" ) ); done_testing($test); App-Music-ChordPro-6.050.7/t/108_chords_solfege_dash.t0000644000400000040000000000723114544300524020014 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Chords; use ChordPro::Chords::Parser; my %tbl; our $config = ChordPro::Config::configurator ( { nosysconfig => 1, nolegacyconfig => 1, nouserconfig => 1, config => getresource("config/notes/solfege.json") } ); =begin regenerate # Enable this section to generate new reference data. my $p = ChordPro::Chords::Parser->get_parser("solfege"); open( my $fd, '<', "t/105_chords.t" ); my $skip = 1; while ( <$fd> ) { chomp; if ( $skip && /__DATA__/ ) { $skip = 0; next; } next if $skip; my ( $chord, $info ) = split( /\t/, $_ ); for ( $chord ) { s!^C#!Di! or s!^C!Do! or s!^Db!Ra! or s!^D#!Ri! or s!^D!Re! or s!^Eb!Me! or s!^E!Mi! or s!^F#!Fi! or s!^F!Fa! or s!^Gb!Se! or s!^G#!Si! or s!^G!So! or s!^Ab!Le! or s!^A#!Li! or s!^A!La! or s!^Bb!Te! or s!^B!Ti!; s!/C#!/Di! or s!/C!/Do! or s!/Db!/Ra! or s!/D#!/Ri! or s!/D!/Re! or s!/Eb!/Me! or s!/E!/Mi! or s!/F#!/Fi! or s!/F!/Fa! or s!/Gb!/Se! or s!/G#!/Si! or s!/G!/So! or s!/Ab!/Le! or s!/A#!/Li! or s!/A!/La! or s!/Bb!/Te! or s!/B!/Ti!; } my $c = $chord; $c =~ s/[()]//g; my $res = $p->parse($c); unless ( $res ) { print( "$chord\tFAIL\n"); next; } $res = {%$res}; delete($res->{parser}); print("$chord\t", reformat($res), "\n"); } exit; =cut while ( ) { chomp; my ( $chord, $info ) = split( /\t/, $_ ); my $c = $chord; $c =~ s/[()]//g; $tbl{$c} = $info; } plan tests => 0 + keys(%tbl); while ( my ( $c, $info ) = each %tbl ) { my $res = ChordPro::Chords::parse_chord($c); $res //= "FAIL"; if ( UNIVERSAL::isa( $res, 'HASH' ) ) { $res = reformat($res); } is( $res, $info, "parsing chord $c"); } sub reformat { my ( $res ) = @_; $res = {%$res}; delete($res->{parser}); use Data::Dumper qw(); local $Data::Dumper::Sortkeys = 1; local $Data::Dumper::Indent = 1; local $Data::Dumper::Quotekeys = 0; local $Data::Dumper::Deparse = 1; local $Data::Dumper::Terse = 1; local $Data::Dumper::Trailingcomma = 1; local $Data::Dumper::Useperl = 1; local $Data::Dumper::Useqq = 0; # I want unicode visible my $s = Data::Dumper::Dumper($res); $s =~ s/\s+/ /gs; $s =~ s/, \}/ }/gs; $s =~ s/\s+$//; return $s; } __DATA__ Tim7-5 { bass => '', ext => '7-5', ext_canon => '7-5', name => 'Tim7-5', qual => 'm', qual_canon => '-', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } Di7(-5) { bass => '', ext => '7-5', ext_canon => '7-5', name => 'Di7-5', qual => '', qual_canon => '', root => 'Di', root_canon => 'Di', root_mod => 1, root_ord => 1, system => 'solfege' } Mi7(-5) { bass => '', ext => '7-5', ext_canon => '7-5', name => 'Mi7-5', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Mi7-9 { bass => '', ext => '7-9', ext_canon => '7-9', name => 'Mi7-9', qual => '', qual_canon => '', root => 'Mi', root_canon => 'Mi', root_mod => 0, root_ord => 4, system => 'solfege' } Fim7-5 { bass => '', ext => '7-5', ext_canon => '7-5', name => 'Fim7-5', qual => 'm', qual_canon => '-', root => 'Fi', root_canon => 'Fi', root_mod => 1, root_ord => 6, system => 'solfege' } So7-9 { bass => '', ext => '7-9', ext_canon => '7-9', name => 'So7-9', qual => '', qual_canon => '', root => 'So', root_canon => 'So', root_mod => 0, root_ord => 7, system => 'solfege' } Tim7-5 { bass => '', ext => '7-5', ext_canon => '7-5', name => 'Tim7-5', qual => 'm', qual_canon => '-', root => 'Ti', root_canon => 'Ti', root_mod => 0, root_ord => 11, system => 'solfege' } App-Music-ChordPro-6.050.7/t/322_subst.t0000644000400000040000000000067514544300524015172 0ustar jvjv#! perl use Test::More tests => 3; use ChordPro::Config::Properties; delete $ENV{version}; # yes, some systems seem to set this my $cfg = Data::Properties->new; $cfg->set_property( "version", 1 ); $cfg->parse_lines( [ 'vv = ${version:2}' ] ); is( $cfg->gps("vv"), 1, "v1" ); $cfg->parse_lines( [ 'vv = ${vercsion:2}' ] ); is( $cfg->gps("vv"), 2, "v2" ); $cfg->parse_lines( [ 'vv = ${vercsion:2:3}' ] ); is( $cfg->gps("vv"), '2:3', "v3" ); App-Music-ChordPro-6.050.7/t/316_array.t0000644000400000040000000000334714544300524015152 0ustar jvjv#! perl use Test::More tests => 7; use ChordPro::Config::Properties; my $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ], '', 'base' ); a = 1 b = 2 nested { 0 { c = 3 } 1 = 5 2 = 6 } EOD my $xp = { base => { a => 1, b => 2, nested => [ { c => 3, }, 5, 6, ], }, }; is_deeply( $cfg->data, $xp, "one" ); $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ], '', 'base' ); a = 1 b = 2 nested = [ { c = 3 } 5 6 ] EOD is_deeply( $cfg->data, $xp, "two" ); $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ], '', 'base' ); a = 1 b = 2 nested = [ { c = 3 } 5 6 ] EOD is_deeply( $cfg->data, $xp, "three" ); $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ], '', 'base' ); nested: [ aap noot mies ] EOD $xp = { base => { nested => [ qw( aap noot mies ) ] } }; is_deeply( $cfg->data, $xp, "four" ); $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ], '', 'base' ); nested : [ aap noot mies ] EOD $xp = { base => { nested => [ qw( aap noot mies ) ] } }; is_deeply( $cfg->data, $xp, "five" ); $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ], '', 'base' ); nested =[ aap noot mies ] EOD $xp = { base => { nested => [ qw( aap noot mies ) ] } }; is_deeply( $cfg->data, $xp, "six" ); $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ], '', 'base' ); nested=[ aap noot mies ] EOD $xp = { base => { nested => [ qw( aap noot mies ) ] } }; is_deeply( $cfg->data, $xp, "seven" ); App-Music-ChordPro-6.050.7/t/169_custom.t0000644000400000040000000000200014544300524015335 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Columns. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => { }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'body' => [ { type => "ignore", text => "{x_custom: foo}", context => "", }, ], }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Custom directives" ); App-Music-ChordPro-6.050.7/t/314_data.t0000644000400000040000000000123314544300524014733 0ustar jvjv#! perl use Test::More tests => 1; use ChordPro::Config::Properties; use utf8; my $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <data, { base => { config => { version => 2, }, list => [ { beest => 'aap', }, "noot\x{2669}", 'mies', ], nested => { something => 4, version => 3, }, version => 1, }, } ); App-Music-ChordPro-6.050.7/t/100_basic.t0000644000400000040000000000040514544300524015074 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Config; use ChordPro::Songbook; plan tests => 2; !$config && diag("$@"); ok($config, "Configuration set up"); my $s = ChordPro::Songbook->new; ok($s, "Song set up"); App-Music-ChordPro-6.050.7/t/131_image.t0000644000400000040000000000326514544302032015104 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Image (minimal). my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use DDP; p $s->{songs}->[0]; delete( $s->{songs}->[0]->{assets}->{white}->{data} ) if $s->{songs}->[0]->{assets}->{white}->{data} =~ /^\xff\xd8\xff\xe0/; my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'context' => '', 'id' => 'white', 'type' => 'image', 'opts' => {} } ], 'assets' => { white => { type => 'image', subtype => 'jpg', width => 1, height => 1, opts => { enc => 'base64', height => 1, id => 'white', type => 'jpg', width => 1 }, }, }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/143_chords.t0000644000400000040000000000632414544300524015312 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; use ChordPro::Song; $config->{debug}->{chords} = 0; $config->{debug}->{x1} = 0; bless $config => ChordPro::Config::; my $tests = 0; my $sb = ChordPro::Songbook->new; my $s = ChordPro::Song->new; my $info = $s->parse_chord("Am7"); my $chord = $info->name; is ( $info->name, "Am7", "$chord name" ); is ( $info->root, "A", "$chord root" ); is ( $info->qual, "m", "$chord qual" ); is ( $info->ext, "7", "$chord ext" ); is ( $info->bass, "", "$chord bass" ); is ( $info->canonical, "Am7", "show $chord" ); is ( $info->chord_display, "Am7", "display $chord" ); $tests += 7; $info = $s->parse_chord("Bes"); $chord = $info->name; is ( $info->name, "Bes", "$chord name" ); is ( $info->root, "Bes", "$chord root" ); is ( $info->qual, "", "$chord qual" ); is ( $info->ext, "", "$chord ext" ); is ( $info->bass, "", "$chord bass" ); is ( $info->canonical, "Bb", "show $chord" ); is ( $info->chord_display, "Bes", "display $chord" ); is ( $info->base, undef, "no base for $chord" ); $tests += 8; my $def = "Bes"; $s->define_chord( "define", $def ); $info = $s->parse_chord("Bes"); $chord = $info->name; is ( $info->name, "Bes", "$chord name" ); is ( $info->root, "Bes", "$chord root" ); is ( $info->qual, "", "$chord qual" ); is ( $info->ext, "", "$chord ext" ); is ( $info->bass, "", "$chord bass" ); is ( $info->canonical, "Bb", "show $chord" ); is ( $info->chord_display, "Bes", "display $chord" ); is ( $info->base, 1, "$chord base" ); $tests += 8; $def .= " base-fret 2"; $s->define_chord( "define", $def ); $info = $s->parse_chord("Bes"); $chord = $info->name; is ( $info->name, "Bes", "$chord name" ); is ( $info->root, "Bes", "$chord root" ); is ( $info->qual, "", "$chord qual" ); is ( $info->ext, "", "$chord ext" ); is ( $info->bass, "", "$chord bass" ); is ( $info->canonical, "Bb", "show $chord" ); is ( $info->chord_display, "Bes", "display $chord" ); is ( $info->base, 2, "$chord base" ); $tests += 8; $def .= " frets 0 0 1 1 1 0"; $s->define_chord( "define", $def ); $info = $s->parse_chord("Bes"); $chord = $info->name; is ( $info->name, "Bes", "$chord name" ); is_deeply ( $info->frets, [0,0,1,1,1,0], "$chord frets" ); $tests += 2; $def .= " fingers 1 1 2 3 4 1"; $s->define_chord( "define", $def ); $info = $s->parse_chord("Bes"); $chord = $info->name; is ( $info->name, "Bes", "$chord name" ); is_deeply ( $info->fingers, [1,1,2,3,4,1], "$chord fingers" ); $tests += 2; $def .= " keys 4 7 0"; $s->define_chord( "define", $def ); $info = $s->parse_chord("Bes"); $chord = $info->name; is ( $info->name, "Bes", "$chord name" ); is_deeply ( $info->kbkeys, [4,7,0], "$chord keys" ); $tests += 2; for ( qw( on true 1 ) ) { $s->define_chord( "define", "$def diagram $_" ); $info = $s->parse_chord("Bes"); is ( $info->diagram, undef, "diagram $_ stripped" ); $tests++; } for ( qw( off false 0 ) ) { $s->define_chord( "define", "$def diagram $_" ); $info = $s->parse_chord("Bes"); is ( $info->diagram, 0, "diagram $_ is 0" ); $tests++; } $s->define_chord( "define", "$def diagram red" ); $info = $s->parse_chord("Bes"); $chord = $info->name; is ( $info->diagram, "red", "$chord diagram" ); $tests++; done_testing($tests); App-Music-ChordPro-6.050.7/t/311_keys.t0000644000400000040000000000143014544300524014771 0ustar jvjv#! perl use Test::More tests => 2; use ChordPro::Config::Properties; my $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <dump, <{_props}, { '@' => [ 'version', 'config', 'nested', ], 'config.@' => [ 'version', ], 'config.version' => 2, 'nested.@' => [ 'version', 'some thing', ], 'nested.some thing' => 4, 'nested.version' => 3, nested => 5, version => 1, } ); App-Music-ChordPro-6.050.7/t/120_meta.t0000644000400000040000000001350214544300524014745 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 16; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; #### meta as meta. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], 'artist' => [ 'The Artist' ], 'composer' => [ 'The Composer' ], 'album' => [ 'The Album' ], 'key' => [ 'F' ], 'tempo' => [ '320' ], 'time' => [ '3/4' ], 'capo' => [ '2' ], }, 'chordsinfo' => { map { $_ => $_ } qw( F G ) }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "[meta meta] Song contents" ); #### meta as directives. $s = ChordPro::Songbook->new; $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); is_deeply( { %{ $s->{songs}->[0] } }, $song, "[meta as dir] Song contents" ); #### combinations. $s = ChordPro::Songbook->new; $data = <parse_file(\$data) } or diag("$@"); } ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); ok( $warning =~ /Multiple capo settings may yield surprising results/, "You have been warned" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], 'artist' => [ 'The Artist', 'Another Artist' ], 'composer' => [ 'The Composer', 'Another Composer' ], 'album' => [ 'The Album', 'Another Album' ], 'capo' => [ '2', '3' ], 'key' => [ 'F', 'G' ], 'tempo' => [ '320', '220' ], 'time' => [ '3/4', '4/4' ], }, 'chordsinfo' => { map { $_ => $_ } qw( F G A C ) }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'body' => [ { context => '', orig => '%%', text => '%%', type => 'comment', }, ], }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "[combi's] Song contents" ); #### autosplit (default, on). $s = ChordPro::Songbook->new; $data = <parse_file(\$data); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], 'artist' => [ 'Another Artist' ], 'composer' => [ 'Another Composer' ], 'album' => [ 'Another Album' ], 'capo' => [ '2' ], 'key' => [ 'G' ], 'tempo' => [ '220', '260' ], 'time' => [ '4/4' ], }, 'chordsinfo' => { map { $_ => $_ } qw( G A ) }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'body' => [ { context => '', orig => '%%', text => '%%', type => 'comment', }, ], }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "[autosplit] Song contents" ); #### No autosplit $::config->{metadata}->{autosplit} = 0; $s = ChordPro::Songbook->new; $data = <parse_file(\$data); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], 'artist' => [ 'Another Artist' ], 'composer' => [ 'Another Composer' ], 'album' => [ 'Another Album' ], 'capo' => [ '2' ], 'key' => [ 'G' ], 'tempo' => [ '220; 260' ], 'time' => [ '4/4' ], }, 'chordsinfo' => { map { $_ => $_ } qw( G A ) }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'body' => [ { context => '', orig => '%%', text => '%%', type => 'comment', }, ], }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "[no autosplit] Song contents" ); App-Music-ChordPro-6.050.7/t/71_cho.t0000644000400000040000000000172314544300524014517 0ustar jvjv#! perl use strict; use warnings; use utf8; use ChordPro::Testing; use_ok "ChordPro"; my $test = 1; BAIL_OUT("Missing chordpro test data") unless -d "cho"; opendir( my $dh, "cho" ) || BAIL_OUT("Cannot open chordpro test data"); my @files = grep { /^.+\.cho$/ } readdir($dh); close($dh); diag("Testing ", scalar(@files), " chordpro files"); our $options; #$options->{fragment} = 1; foreach my $file ( sort @files ) { $test++; $file = "cho/$file"; #diag("Testing: $file"); ( my $out = $file ) =~ s/\.cho/.out/; ( my $ref = $file ) =~ s/\.cho/.ref/; @ARGV = ( "--no-default-configs", "--generate", "ChordPro", "--backend-option", "expand=1", "--output", $out, $file ); ::run(); my $ok = !differ( $out, $ref ); ok( $ok, $file ); unlink($out), next if $ok; system( $ENV{CHORDPRO_DIFF}, $out, $ref) if $ENV{CHORDPRO_DIFF}; } ok( $test++ == @files+1, "Tested @{[0+@files]} files" ); done_testing($test); App-Music-ChordPro-6.050.7/t/177_transcode.t0000644000400000040000000000353714544300524016024 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Config; use ChordPro::Songbook; plan tests => 4; # For transcoding, both source and target notation systems must be # defined. The source system must be last, so it is current and used # to parse the the input data. our $config = eval { ChordPro::Config::configurator ( { nosysconfig => 1, nolegacyconfig => 1, nouserconfig => 1, config => [ getresource("config/notes/latin.json"), getresource("config/notes/common.json") ], transcode => "latin" } ); }; ok( $config, "got config" ); my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'key' => [ 'Re' ], 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'chords' => { 'origin' => 'song', 'type' => 'diagrams', 'show' => 'all', 'chords' => [ 'Re', 'Solm7' ] }, 'body' => [ { 'context' => '', 'line' => 3, 'phrases' => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,' ], 'chords' => [ '', 'Re', 'Solm7', 'Re' ], 'type' => 'songline' } ], chordsinfo => { map { $_ => $_ } qw( Re Solm7 ) }, 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/320_subst.t0000644000400000040000000000155014544300524015161 0ustar jvjv#! perl use Test::More tests => 2; use ChordPro::Config::Properties; delete $ENV{version}; # yes, some systems seem to set this my $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ] ); version = 1 nested { version = 3 # version at this level something = ${.version} # version at global level else = ${version} } EOD # Note that single key elements have no @ line in the dump. is( $cfg->dump, <{_props}, { '@' => [ 'version', 'nested', ], 'nested.@' => [ 'version', 'something', 'else', ], 'nested.something' => 3, 'nested.version' => 3, 'nested.else' => 1, 'version' => 1, } ); App-Music-ChordPro-6.050.7/t/83_pages.t0000644000400000040000000000206714544300524015052 0ustar jvjv#! perl use strict; use warnings; use utf8; use Test::More tests => 5; use ChordPro::Testing; use_ok "ChordPro"; my $test = 1; BAIL_OUT("Missing out dir") unless -d "out"; my $base = "out/83_pages."; my $pdf = $base . "pdf"; my $cho = $base . "cho"; my $csv = $base . "csv"; ( my $ref = $csv ) =~ s/out/ref/; our $options; ok( open( my $fd, '>:utf8', $cho ), "Create $cho" ); print $fd < 3; $config->{settings}->{memorize} = 1; $config->{settings}->{transpose} = 2; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; #### meta as meta. my $data = <parse_file(\$data) } or diag("$@"); } ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); # use ChordPro::Dumper; ddp($s->{songs}->[0]); my $song = { 'meta' => { 'songindex' => 1, 'title' => [ 'Test Memorize' ] }, 'body' => [ { 'type' => 'songline', 'phrases' => [ 'This is verse', ], 'context' => 'verse', 'chords' => [ 'B', ] }, { 'value' => '', 'context' => 'verse', 'name' => 'context', 'type' => 'set' }, { 'type' => 'songline', 'phrases' => [ 'Another verse' ], 'context' => 'verse', 'chords' => [ 'B', ] }, { 'value' => '', 'context' => 'verse', 'name' => 'context', 'type' => 'set' } ], 'source' => { 'line' => 1, 'file' => '__STRING__' }, 'chordsinfo' => { map { $_ => $_ } qw( B ) }, 'title' => 'Test Memorize', 'system' => 'common', 'structure' => 'linear', 'settings' => {}, }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/323_null.t0000644000400000040000000000074214544300524015000 0ustar jvjv#! perl use Test::More tests => 2; use ChordPro::Config::Properties; my $cfg = Data::Properties->new; $cfg->parse_lines( [ split( /[\r\n]+/, <<'EOD' ) ] ); a = null x = c = a: ${a?|${a|value|empty}|null} d = x: ${x?|${x|value|empty}|null} EOD is( $cfg->dump, <{_props}, { '@' => [ qw( a x c d )], a => undef, c => 'a: null', d => 'x: empty', x => '', } ); App-Music-ChordPro-6.050.7/t/80_pages.t0000644000400000040000000000202014544300524015034 0ustar jvjv#! perl use strict; use warnings; use utf8; use Test::More tests => 5; use ChordPro::Testing; use_ok "ChordPro"; my $test = 1; BAIL_OUT("Missing out dir") unless -d "out"; my $base = "out/80_pages."; my $pdf = $base . "pdf"; my $cho = $base . "cho"; my $csv = $base . "csv"; ( my $ref = $csv ) =~ s/out/ref/; our $options; ok( open( my $fd, '>:utf8', $cho ), "Create $cho" ); print $fd < 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], }, 'title' => 'Swing Low Sweet Chariot', 'body' => [ { 'type' => 'comment', 'context' => '', 'text' => 'This is a comment', 'orig' => 'This is a comment' }, { 'context' => '', 'type' => 'comment_italic', 'text' => 'This is a comment_italic', 'orig' => 'This is a comment_italic' }, { 'text' => 'This is a comment_box', 'orig' => 'This is a comment_box', 'context' => '', 'type' => 'comment_box' } ], 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/150_fonts.t0000644000400000040000000000322214544302032015145 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; # Fonts definitions. my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); #use Data::Dumper; warn(Dumper($s)); my $song = { 'settings' => {}, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ] }, 'body' => [ { 'type' => 'control', 'name' => 'text-font', 'value' => 'Times-Italic', 'context' => '' }, { 'type' => 'control', 'name' => 'chorus-font', 'value' => 'Times-Italic', 'context' => '' }, { 'context' => '', 'type' => 'control', 'value' => '80%', 'name' => 'text-size' }, { 'context' => '', 'type' => 'control', 'value' => '80%', 'name' => 'chorus-size' }, { 'context' => '', 'type' => 'control', 'value' => 'yellow', 'name' => 'text-color' }, { 'context' => '', 'type' => 'control', 'value' => 'yellow', 'name' => 'chorus-color' } ] }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/basic02.cho0000644000400000040000000000144112731610347015170 0ustar jvjv{titles: center} {columns: 2} {title:Swing Low Sweet Chariot} {st:Traditional} {start_of_chorus} Swing [D]low, sweet [G]chari[D]ot, Comin’ for to carry me [A7]home. Swing [D7]low, sweet [G]chari[D]ot, Comin’ for to [A7]carry me [D]home. {end_of_chorus} I [D]looked over Jordan, and [G]what did I [D]see, “Comin’ for to carry me [A7]home.” A [D7]band of angels [G]comin’ after [D]me, Comin’ for to [A7]carry me [D]home. [G]     [D] [G] {sot} I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. {eot} {c:Chorus} {sot} I'm sometimes up and sometimes down, Comin' for to carry me home. But still my soul feels heavenly bound. Comin' for to carry me home. {eot} App-Music-ChordPro-6.050.7/t/191_outlines.t0000644000400000040000000001121514544302032015664 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; use ChordPro::Output::Common; plan tests => 3; # For transcoding, both source and target notation systems must be # defined. The source system must be last, so it is current and used # to parse the the input data. my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 3, "three songs" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $book = [ map { { meta => $_->{meta} } } @{$s->{songs}} ]; my $ctl = { fields => [ qw( title sortartist ) ] }; my $res = ChordPro::Output::Common::prep_outlines( $book, $ctl ); my $xp = [ [ '24 fietsen', 'december', { meta => { artist => [ 'December', ], chords => [ ], numchords => [ 0, ], key => [ 'D', ], key_actual => [ 'D', ], songindex => 2, sortartist => [ 'December', ], sorttitle => [ 'Vierentwintig Fietsen', ], title => [ '24 Fietsen', ], }, }, ], [ '24 fietsen', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'D', ], key_actual => [ 'D', ], songindex => 2, sortartist => [ 'September', ], sorttitle => [ 'Vierentwintig Fietsen', ], title => [ '24 Fietsen', ], }, }, ], [ 'de fietsenstalling', 'december', { meta => { artist => [ 'December', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'December', ], sorttitle => [ 'Fietsenstalling, De', ], title => [ 'De Fietsenstalling', ], }, }, ], [ 'de fietsenstalling', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'September', ], sorttitle => [ 'Fietsenstalling, De', ], title => [ 'De Fietsenstalling', ], }, }, ], [ 'de fietspomp', '', { meta => { artist => [ '', ], chords => [ ], numchords => [ 0, ], key => [ 'A', 'Am', ], key_actual => [ 'Am', ], songindex => 1, sortartist => [ '', ], sorttitle => [ 'Fietspomp, De', ], title => [ 'De Fietspomp', ], }, }, ], [ 'het fietsenhok', 'december', { meta => { artist => [ 'December', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'December', ], sorttitle => [ 'Fietsenhok, Het', ], title => [ 'Het Fietsenhok', ], }, }, ], [ 'het fietsenhok', 'september', { meta => { artist => [ 'September', ], chords => [ ], numchords => [ 0, ], key => [ 'C', ], key_actual => [ 'C', ], songindex => 3, sortartist => [ 'September', ], sorttitle => [ 'Fietsenhok, Het', ], title => [ 'Het Fietsenhok', ], }, }, ], ]; foreach ( @$res ) { delete $_->[2]->{meta}->{_configversion}; } is_deeply( $res, $xp, "outlined"); App-Music-ChordPro-6.050.7/t/01_prereq.t0000644000400000040000000000310214544302032015222 0ustar jvjv#! perl use strict; use warnings; use Test::More; my $pdfapi = "PDF::API2"; my $pdfapiv = "2.036"; # PDF::API2 2.036 is ok, 2.042 is better, 2.043 is best. for ( qw( PDF::Builder@3.023 PDF::API2@2.036 ) ) { ( $pdfapi, $pdfapiv ) = split( '@', $_ ); eval "require $pdfapi" or next; eval '$pdfapiv = $pdfapi->VERSION($pdfapiv)' or next; last; } my $test; ++$test; ok( $] >= 5.026000, "Perl version $] is 5.026 or newer" ); ++$test; use_ok( "ChordPro::Testing" ); if ( $pdfapi =~ /^pdf/i ) { ++$test; use_ok( "IO::String", 1.08 ); # for Font::TTF # Font::TTF 1.04 is ok, 1.05 is better, 1.06 is best. ++$test; use_ok( "Font::TTF", 1.05 ); # for PDF::API2 ++$test; use_ok( $pdfapi, $pdfapiv ); diag("Using $pdfapi $pdfapiv and Font::TTF $Font::TTF::VERSION for PDF generation"); } else { ++$test; use_ok( $pdfapi, $pdfapiv ); diag("Using $pdfapi $pdfapiv for PDF generation"); } ++$test; use_ok( "Text::Layout", 0.032 ); eval { require HarfBuzz::Shaper; HarfBuzz::Shaper->VERSION(0.026); diag( "Shaping enabled (HarfBuzz::Shaper $HarfBuzz::Shaper::VERSION)" ); 1; } || diag( "Shaping disabled (HarfBuzz::Shaper not found)" ); ++$test; use_ok( "JSON::PP", 2.27203 ); ++$test; use_ok( "String::Interpolate::Named", 1.030 ); ++$test; use_ok( "File::HomeDir", 1.004 ); ++$test; use_ok( "File::LoadLines", 1.042 ); ++$test; use_ok( "SVGPDF", 0.080 ); ++$test; use_ok( "Image::Info", 1.41 ); ++$test; use_ok( "List::Util", 1.33 ); ++$test; use_ok( "Storable", 3.08 ); ++$test; use_ok( "Object::Pad", 0.78 ); done_testing($test); App-Music-ChordPro-6.050.7/t/60_transpose.cho0000644000400000040000000000150414236032144016263 0ustar jvjv{+text.chorus.recall.quote 1} {title: Transpositions} {key: D} {c: Key is %{key}%{key_actual|, actual %{}}%{key_from|, from %{}}} {start_of_chorus} Swing [D]low, sweet [G]chari[D]ot, {end_of_chorus} I [D]looked over Jordan, and [G]what did I [D]see, {chorus} {transpose +2} {c: Key is %{key}%{key_actual|, actual %{}}%{key_from|, from %{}}} I [D]looked over Jordan, and [G]what did I [D]see, {chorus} {transpose +2} {c: Key is %{key}%{key_actual|, actual %{}}%{key_from|, from %{}}} I [D]looked over Jordan, and [G]what did I [D]see, {chorus} {transpose} {c: Key is %{key}%{key_actual|, actual %{}}%{key_from|, from %{}}} I [D]looked over Jordan, and [G]what did I [D]see, {chorus} {transpose} {c: Key is %{key}%{key_actual|, actual %{}}%{key_from|, from %{}}} I [D]looked over Jordan, and [G]what did I [D]see, {chorus} App-Music-ChordPro-6.050.7/t/86_pages.t0000644000400000040000000000354414544300524015056 0ustar jvjv#! perl use strict; use warnings; use utf8; use Test::More tests => 7; use ChordPro::Testing; use_ok "ChordPro"; my $test = 1; BAIL_OUT("Missing out dir") unless -d "out"; my $base = "out/86_pages."; my $pdf = $base . "pdf"; my $cho = $base . "cho"; my $csv = $base . "csv"; ( my $ref = $csv ) =~ s/out/ref/; my $front = $base . "front.pdf"; my $back = $base . "back.pdf"; use ChordPro::Output::PDF; my $api = ChordPro::Output::PDF::config_pdfapi; my $p = $api->new( file => $front ); my $page = $p->page; $page->mediabox("A4"); my $text = $page->text; $text->font( $p->corefont("Times-Roman"), 100 ); $text->translate( 297, 600 ); $text->text( "FRONT", align => "center" ); $p->saveas($front); $p = $api->new( file => $back ); $page = $p->page; $page->mediabox("A4"); $text = $page->text; $text->font( $p->corefont("Times-Roman"),100 ); $text->translate( 297, 300 ); $text->text( "BACK", align => "center" ); $p->saveas($back); our $options; ok( open( my $fd, '>:utf8', $cho ), "Create $cho" ); print $fd < 3; my $s = ChordPro::Songbook->new; my $data = <parse_file( \$data, { transpose => -10 } ) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { body => [ { context => '', type => 'empty', }, { chords => [ '', 'E', 'A', 'E', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { context => 'chorus', type => 'set', value => '', name => 'context' }, { context => '', type => 'empty', }, { chords => [ '', 'E', 'A', 'E', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'E', 'A', 'E', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { context => 'chorus', type => 'set', value => '', name => 'context' }, ], context => '', type => 'rechorus', }, { context => '', type => 'empty', }, { chords => [ '', 'F#', 'B', 'F#', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'F#', 'B', 'F#', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { context => 'chorus', type => 'set', value => '', name => 'context' }, ], context => '', type => 'rechorus', }, { context => '', type => 'empty', }, { chords => [ '', 'G#', 'C#', 'G#', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'G#', 'C#', 'G#', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { context => 'chorus', type => 'set', value => '', name => 'context' }, ], context => '', type => 'rechorus', }, { context => '', type => 'empty', }, { chords => [ '', 'F#', 'B', 'F#', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'F#', 'B', 'F#', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { context => 'chorus', type => 'set', value => '', name => 'context' }, ], context => '', type => 'rechorus', }, { context => '', type => 'empty', }, { chords => [ '', 'E', 'A', 'E', ], context => '', phrases => [ 'I ', 'looked over Jordan, and ', 'what did I ', 'see,', ], type => 'songline', }, { chorus => [ { chords => [ '', 'E', 'A', 'E', ], context => 'chorus', phrases => [ 'Swing ', 'low, sweet ', 'chari', 'ot,', ], type => 'songline', }, { context => 'chorus', type => 'set', value => '', name => 'context' }, ], context => '', type => 'rechorus', }, ], chords => { chords => [ 'E', 'A', 'F#', 'B', 'G#', 'C#' ], origin => 'song', show => 'all', type => 'diagrams' }, chordsinfo => { map { $_ => $_ } qw( E A B ), 'F#', 'C#', 'G#' }, meta => { songindex => 1, key => [ 'E', ], title => [ 'Transpositions', ], }, settings => {}, source => { file => "__STRING__", line => 1 }, structure => 'linear', system => 'common', title => 'Transpositions', }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/104_subtitles.t0000644000400000040000000000177514544300524016050 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use ChordPro::Testing; use ChordPro::Songbook; plan tests => 3; # Prevent a dummy {body} for chord grids. $config->{diagrams}->{show} = 0; my $s = ChordPro::Songbook->new; my $data = <parse_file(\$data) } or diag("$@"); ok( scalar( @{ $s->{songs} } ) == 1, "One song" ); isa_ok( $s->{songs}->[0], 'ChordPro::Song', "It's a song" ); my $song = { 'settings' => {}, 'meta' => { 'songindex' => 1, 'title' => [ 'Swing Low Sweet Chariot' ], 'subtitle' => [ 'Sub Title 1', 'Sub Title 2', ] }, 'title' => 'Swing Low Sweet Chariot', 'source' => { file => "__STRING__", line => 1 }, 'structure' => 'linear', 'system' => 'common', 'subtitle' => [ 'Sub Title 1', 'Sub Title 2', ] }; is_deeply( { %{ $s->{songs}->[0] } }, $song, "Song contents" ); App-Music-ChordPro-6.050.7/t/20_basic01_crd.t0000644000400000040000000000021514447223440016010 0ustar jvjv#! perl use strict; if ( -d "t" ) { chdir "t"; $0 =~ s;(^|/)t/;$1;; } @::params = qw( 20 basic01 crd ); require "./00_basic.pl"; App-Music-ChordPro-6.050.7/t/30_basic01_cho.t0000644000400000040000000000021514447223440016012 0ustar jvjv#! perl use strict; if ( -d "t" ) { chdir "t"; $0 =~ s;(^|/)t/;$1;; } @::params = qw( 30 basic01 cho ); require "./00_basic.pl"; App-Music-ChordPro-6.050.7/pp/0000755000400000040000000000000014567360037013435 5ustar jvjvApp-Music-ChordPro-6.050.7/pp/macos/0000755000400000040000000000000014567360037014537 5ustar jvjvApp-Music-ChordPro-6.050.7/pp/macos/ppl.c0000644000400000040000000000773214120712212015464 0ustar jvjv/* Portable Perl Loader (Linux version) */ #include #include #include #include #include #include #include #include "XSUB.h" static pTHX; /* Set up DynaLoader so modules can load modules. */ void xs_init(pTHX); /* Main. */ static char selfpath[PATH_MAX]; /* /foo/bar */ int main( int argc, char **argv, char **env ) { // Make ourselves known. putenv( "PPL_PACKAGED=1.00" ); /* Assuming the program binary /foo/bar/blech */ char scriptname[PATH_MAX]; /* blech */ memset (selfpath, 0, PATH_MAX); memset (scriptname, 0, PATH_MAX); if ( readlink ("/proc/self/exe", selfpath, PATH_MAX-1 ) > 0 ) { char *p = rindex( selfpath, '/' ); if ( p ) { p++; strcpy( scriptname, p ); *p = 0; } else strcpy( scriptname, selfpath ); #ifdef DEBUG fprintf( stderr, "selfpath: %s\n", selfpath ); fprintf( stderr, "scriptname: %s\n", scriptname ); #endif } else { strncpy( selfpath, argv[0], PATH_MAX-1 ); char *p = rindex( selfpath, '/' ); if ( p ) { p++; strcpy( scriptname, p ); *p = 0; } else { p = getcwd( selfpath, PATH_MAX-1 ); strcat( selfpath, "/" ); strncpy( scriptname, argv[0], PATH_MAX-1 ); } #ifdef DEBUG fprintf( stderr, "cwdpath: %s\n", selfpath ); fprintf( stderr, "scriptname: %s\n", scriptname ); #endif } /* Start perl environment. */ PERL_SYS_INIT3( &argc, &argv, &env ); /* Create a perl interpreter. */ my_perl = perl_alloc(); perl_construct(my_perl); /* Strip unwanted environment variables. */ static char **ourenv = NULL; if ( env ) { int envc = 0; while ( env[envc] ) envc++; ourenv = (char **)calloc( envc+1, sizeof(char**) ); int j = 0; for ( int i=0; i All contents of will be copied into the disk image. Options: --volname set volume name (displayed in the Finder sidebar and window title) --volicon set volume icon --background set folder background image (provide png, gif, or jpg) --window-pos set position the folder window --window-size set size of the folder window --text-size set window text size (10-16) --icon-size set window icons size (up to 128) --icon file_name set position of the file's icon --hide-extension hide the extension of file --app-drop-link make a drop link to Applications, at location x,y --ql-drop-link make a drop link to user QuickLook install dir, at location x,y --eula attach a license file to the dmg (plain text or RTF) --no-internet-enable disable automatic mount & copy --format specify the final image format (default is UDZO) --add-file | add additional file or folder (can be used multiple times) --disk-image-size set the disk image size manually to x MB --hdiutil-verbose execute hdiutil in verbose mode --hdiutil-quiet execute hdiutil in quiet mode --sandbox-safe execute hdiutil with sandbox compatibility and do not bless --rez use custom path to Rez tool --version show create-dmg version number -h, --help display this help screen EOHELP exit 0 } # Argument parsing while [[ "${1:0:1}" = "-" ]]; do case $1 in --volname) VOLUME_NAME="$2" shift; shift;; --volicon) VOLUME_ICON_FILE="$2" shift; shift;; --background) BACKGROUND_FILE="$2" BACKGROUND_FILE_NAME="$(basename "$BACKGROUND_FILE")" BACKGROUND_CLAUSE="set background picture of opts to file \".background:$BACKGROUND_FILE_NAME\"" REPOSITION_HIDDEN_FILES_CLAUSE="set position of every item to {theBottomRightX + 100, 100}" shift; shift;; --icon-size) ICON_SIZE="$2" shift; shift;; --text-size) TEXT_SIZE="$2" shift; shift;; --window-pos) WINX=$2; WINY=$3 shift; shift; shift;; --window-size) WINW=$2; WINH=$3 shift; shift; shift;; --icon) POSITION_CLAUSE="${POSITION_CLAUSE}set position of item \"$2\" to {$3, $4} " shift; shift; shift; shift;; --hide-extension) HIDING_CLAUSE="${HIDING_CLAUSE}set the extension hidden of item \"$2\" to true " shift; shift;; -h | --help) usage;; --version) version; exit 0;; --pure-version) pure_version; exit 0;; --ql-drop-link) QL_LINK=$2 QL_CLAUSE="set position of item \"QuickLook\" to {$2, $3} " shift; shift; shift;; --app-drop-link) APPLICATION_LINK=$2 APPLICATION_CLAUSE="set position of item \"Applications\" to {$2, $3} " shift; shift; shift;; --eula) EULA_RSRC=$2 shift; shift;; --no-internet-enable) NOINTERNET=1 shift;; --format) FORMAT="$2" shift; shift;; --add-file | --add-folder) ADD_FILE_TARGETS+=("$2") ADD_FILE_SOURCES+=("$3") POSITION_CLAUSE="${POSITION_CLAUSE} set position of item \"$2\" to {$4, $5} " shift; shift; shift; shift; shift;; --disk-image-size) DISK_IMAGE_SIZE="$2" shift; shift;; --hdiutil-verbose) HDIUTIL_VERBOSITY='-verbose' shift;; --hdiutil-quiet) HDIUTIL_VERBOSITY='-quiet' shift;; --sandbox-safe) SANDBOX_SAFE=1 shift;; --rez) REZ_PATH="$2" shift; shift;; --skip-jenkins) SKIP_JENKINS=1 shift;; -*) echo "Unknown option: $1. Run 'create-dmg --help' for help." exit 1;; esac case $FORMAT in UDZO) IMAGEKEY="-imagekey zlib-level=9";; UDBZ) IMAGEKEY="-imagekey bzip2-level=9";; *) echo >&2 "Unknown format: $FORMAT" exit 1;; esac done if [[ -z "$2" ]]; then echo "Not enough arguments. Run 'create-dmg --help' for help." exit 1 fi DMG_PATH="$1" SRC_FOLDER="$(cd "$2" > /dev/null; pwd)" # Main script logic SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DMG_DIRNAME="$(dirname "$DMG_PATH")" DMG_DIR="$(cd "$DMG_DIRNAME" > /dev/null; pwd)" DMG_NAME="$(basename "$DMG_PATH")" DMG_TEMP_NAME="$DMG_DIR/rw.${DMG_NAME}" # We're running inside an installed location bin_dir="$SCRIPT_DIR" prefix_dir=$(dirname "$bin_dir") if [[ -z "$VOLUME_NAME" ]]; then VOLUME_NAME="$(basename "$DMG_PATH" .dmg)" fi if [[ -f "$SRC_FOLDER/.DS_Store" ]]; then echo "Deleting .DS_Store found in source folder" rm "$SRC_FOLDER/.DS_Store" fi # Create the image echo "Creating disk image..." if [[ -f "${DMG_TEMP_NAME}" ]]; then rm -f "${DMG_TEMP_NAME}" fi # Use Megabytes since hdiutil fails with very large byte numbers function blocks_to_megabytes() { # Add 1 extra MB, since there's no decimal retention here MB_SIZE=$((($1 * 512 / 1000 / 1000) + 1)) echo $MB_SIZE } function get_size() { # Get block size in disk bytes_size=$(du -s "$1" | sed -e 's/ .*//g') echo $(blocks_to_megabytes $bytes_size) } # Create the DMG with the specified size or the hdiutil estimation CUSTOM_SIZE='' if [[ -n "$DISK_IMAGE_SIZE" ]]; then CUSTOM_SIZE="-size ${DISK_IMAGE_SIZE}m" fi if [[ $SANDBOX_SAFE -eq 0 ]]; then hdiutil create ${HDIUTIL_VERBOSITY} -srcfolder "$SRC_FOLDER" -volname "${VOLUME_NAME}" \ -fs HFS+ -fsargs "-c c=64,a=16,e=16" -format UDRW ${CUSTOM_SIZE} "${DMG_TEMP_NAME}" else hdiutil makehybrid ${HDIUTIL_VERBOSITY} -default-volume-name "${VOLUME_NAME}" -hfs -o "${DMG_TEMP_NAME}" "$SRC_FOLDER" hdiutil convert -format UDRW -ov -o "${DMG_TEMP_NAME}" "${DMG_TEMP_NAME}" DISK_IMAGE_SIZE_CUSTOM=$DISK_IMAGE_SIZE fi # Get the created DMG actual size DISK_IMAGE_SIZE=$(get_size "${DMG_TEMP_NAME}") # Use the custom size if bigger if [[ $SANDBOX_SAFE -eq 1 ]] && [[ ! -z "$DISK_IMAGE_SIZE_CUSTOM" ]] && [[ $DISK_IMAGE_SIZE_CUSTOM -gt $DISK_IMAGE_SIZE ]]; then DISK_IMAGE_SIZE=$DISK_IMAGE_SIZE_CUSTOM fi # Estimate the additional soruces size if [[ -n "$ADD_FILE_SOURCES" ]]; then for i in "${!ADD_FILE_SOURCES[@]}"; do SOURCE_SIZE=$(get_size "${ADD_FILE_SOURCES[$i]}") DISK_IMAGE_SIZE=$(expr $DISK_IMAGE_SIZE + $SOURCE_SIZE) done fi # Add extra space for additional resources DISK_IMAGE_SIZE=$(expr $DISK_IMAGE_SIZE + 20) # Resize the image for the extra stuff hdiutil resize ${HDIUTIL_VERBOSITY} -size ${DISK_IMAGE_SIZE}m "${DMG_TEMP_NAME}" # Mount the new DMG MOUNT_DIR="/Volumes/${VOLUME_NAME}" # Unmount leftover dmg if it was mounted previously (e.g. developer mounted dmg, installed app and forgot to unmount it) if [[ -d "${MOUNT_DIR}" ]]; then echo "Unmounting old disk image from $MOUNT_DIR..." DEV_NAME=$(hdiutil info | egrep --color=never '^/dev/' | sed 1q | awk '{print $1}') hdiutil detach "${DEV_NAME}" fi echo "Mounting disk image..." echo "Mount directory: $MOUNT_DIR" DEV_NAME=$(hdiutil attach -readwrite -noverify -noautoopen "${DMG_TEMP_NAME}" | egrep --color=never '^/dev/' | sed 1q | awk '{print $1}') echo "Device name: $DEV_NAME" if [[ -n "$BACKGROUND_FILE" ]]; then echo "Copying background file..." [[ -d "$MOUNT_DIR/.background" ]] || mkdir "$MOUNT_DIR/.background" cp "$BACKGROUND_FILE" "$MOUNT_DIR/.background/$BACKGROUND_FILE_NAME" fi if [[ -n "$APPLICATION_LINK" ]]; then echo "Making link to Applications dir..." echo $MOUNT_DIR ln -s /Applications "$MOUNT_DIR/Applications" fi if [[ -n "$QL_LINK" ]]; then echo "Making link to QuickLook install dir..." echo $MOUNT_DIR ln -s "/Library/QuickLook" "$MOUNT_DIR/QuickLook" fi if [[ -n "$VOLUME_ICON_FILE" ]]; then echo "Copying volume icon file '$VOLUME_ICON_FILE'..." cp "$VOLUME_ICON_FILE" "$MOUNT_DIR/.VolumeIcon.icns" SetFile -c icnC "$MOUNT_DIR/.VolumeIcon.icns" fi if [[ -n "$ADD_FILE_SOURCES" ]]; then echo "Copying custom files..." for i in "${!ADD_FILE_SOURCES[@]}"; do echo "${ADD_FILE_SOURCES[$i]}" cp -a "${ADD_FILE_SOURCES[$i]}" "$MOUNT_DIR/${ADD_FILE_TARGETS[$i]}" done fi # Run AppleScript to do all the Finder cosmetic stuff APPLESCRIPT_FILE=$(mktemp -t createdmg.tmp.XXXXXXXXXX) APPLESCRIPT_TMPL=$(mktemp -t createdmg.tmpl.XXXXXXXXXX) cat > ${APPLESCRIPT_TMPL} <<'EE_OO_DD' on run (volumeName) tell application "Finder" tell disk (volumeName as string) open set theXOrigin to WINX set theYOrigin to WINY set theWidth to WINW set theHeight to WINH set theBottomRightX to (theXOrigin + theWidth) set theBottomRightY to (theYOrigin + theHeight) set dsStore to "\"" & "/Volumes/" & volumeName & "/" & ".DS_STORE\"" tell container window set current view to icon view set toolbar visible to false set statusbar visible to false set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY} set statusbar visible to false REPOSITION_HIDDEN_FILES_CLAUSE end tell set opts to the icon view options of container window tell opts set icon size to ICON_SIZE set text size to TEXT_SIZE set arrangement to not arranged end tell BACKGROUND_CLAUSE -- Positioning POSITION_CLAUSE -- Hiding HIDING_CLAUSE -- Application and QL Link Clauses APPLICATION_CLAUSE QL_CLAUSE close open -- Force saving of the size delay 1 tell container window set statusbar visible to false set the bounds to {theXOrigin, theYOrigin, theBottomRightX - 10, theBottomRightY - 10} end tell end tell delay 1 tell disk (volumeName as string) tell container window set statusbar visible to false set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY} end tell end tell --give the finder some time to write the .DS_Store file delay 3 set waitTime to 0 set ejectMe to false repeat while ejectMe is false delay 1 set waitTime to waitTime + 1 if (do shell script "[ -f " & dsStore & " ]; echo $?") = "0" then set ejectMe to true end repeat log "waited " & waitTime & " seconds for .DS_STORE to be created." end tell end run EE_OO_DD if [[ $SANDBOX_SAFE -eq 1 ]]; then echo "Skipping Finder-prettifying AppleScript because we are in Sandbox..." else if [[ $SKIP_JENKINS -eq 0 ]]; then cat "$APPLESCRIPT_TMPL" \ | sed -e "s/WINX/$WINX/g" -e "s/WINY/$WINY/g" -e "s/WINW/$WINW/g" \ -e "s/WINH/$WINH/g" -e "s/BACKGROUND_CLAUSE/$BACKGROUND_CLAUSE/g" \ -e "s/REPOSITION_HIDDEN_FILES_CLAUSE/$REPOSITION_HIDDEN_FILES_CLAUSE/g" \ -e "s/ICON_SIZE/$ICON_SIZE/g" -e "s/TEXT_SIZE/$TEXT_SIZE/g" \ | perl -pe "s/POSITION_CLAUSE/$POSITION_CLAUSE/g" \ | perl -pe "s/QL_CLAUSE/$QL_CLAUSE/g" \ | perl -pe "s/APPLICATION_CLAUSE/$APPLICATION_CLAUSE/g" \ | perl -pe "s/HIDING_CLAUSE/$HIDING_CLAUSE/" \ > "$APPLESCRIPT_FILE" sleep 2 # pause to workaround occasional "Can’t get disk" (-1728) issues echo "Running AppleScript to make Finder stuff pretty: /usr/bin/osascript \"${APPLESCRIPT_FILE}\" \"${VOLUME_NAME}\"" if /usr/bin/osascript "${APPLESCRIPT_FILE}" "${VOLUME_NAME}"; then # Okay, we're cool true else echo >&2 "Failed running AppleScript" hdiutil detach "${DEV_NAME}" exit 64 fi echo "Done running the AppleScript..." sleep 4 rm "$APPLESCRIPT_FILE" fi fi # Make sure it's not world writeable echo "Fixing permissions..." chmod -Rf go-w "${MOUNT_DIR}" &> /dev/null || true echo "Done fixing permissions" # Make the top window open itself on mount: if [[ $SANDBOX_SAFE -eq 0 ]]; then echo "Blessing started" bless --folder "${MOUNT_DIR}" --openfolder "${MOUNT_DIR}" echo "Blessing finished" else echo "Skipping blessing on sandbox" fi if [[ -n "$VOLUME_ICON_FILE" ]]; then # Tell the volume that it has a special file attribute SetFile -a C "$MOUNT_DIR" fi # Unmount echo "Unmounting disk image..." hdiutil detach "${DEV_NAME}" # Compress image echo "Compressing disk image..." hdiutil convert ${HDIUTIL_VERBOSITY} "${DMG_TEMP_NAME}" -format ${FORMAT} ${IMAGEKEY} -o "${DMG_DIR}/${DMG_NAME}" rm -f "${DMG_TEMP_NAME}" # All done! echo "Disk image done" exit 0 App-Music-ChordPro-6.050.7/pp/macos/PkgInfo0000644000400000040000000000001114120712212015764 0ustar jvjvAPPLCHO# App-Music-ChordPro-6.050.7/pp/macos/Makefile0000644000400000040000000000746514544302032016175 0ustar jvjv#! /bin/make -f # GNUmakefile for MacOS, Citrus Perl default : dmg .PHONY :: chordpro wxchordpro ROOT := ../.. COMMON := ../common SCRIPT := ${ROOT}/script DEST := build VERSION := $(shell perl ../../lib/ChordPro/Version.pm) PP := pp # PAR Packer. chordpro : chordpro.pp ${COMMON}/chordpro.pp ${PP} --output=chordpro @chordpro.pp ${SCRIPT}/chordpro.pl wxchordpro : chordpro.pp ${COMMON}/chordpro.pp ${COMMON}/wxchordpro.pp perl wxchordpro_pp.pl > wxchordpro.pp ${PP} --output=wxchordpro @wxchordpro.pp ${SCRIPT}/wxchordpro.pl clean :: rm -f *.pp.deps *~ chordpro wxchordpro # Portable Perl Launcher PERLLIB := $(shell perl -MConfig -E 'say $$Config{archlib}') PERLINC := ${PERLLIB}/perl5/CORE PERLSO := $(shell perl -MConfig -E 'say $$Config{libperl}') .PHONY :: ppl ppl :: clean wxchordpro unpack copy_coredll loaders .PHONY :: unpack unpack : perl ../pp2ppl.pl --dest=${DEST} wxchordpro --quiet cp -p "${ROOT}/script/chordpro.pl" "${DEST}/script/chordpro.pl" cp -p "${ROOT}/lib/ChordPro/Config.pm" "${DEST}/lib/ChordPro/Config.pm" cp "${DEST}/res/icons/chordpro.icns" ${DEST} cp "${DEST}/res/icons/chordpro-doc.icns" ${DEST} mv "${DEST}/res" "${DEST}/lib/ChordPro/" rm -f "${DEST}/script/main.pl" # Copy core DLLs. # We assume that the target system has the primary ddls like -ldl, -lc, ... copy_coredll : cp -pL ${PERLLIB}/CORE/libperl.dylib ${DEST}/${PERLSO} install_name_tool -id @executable_path/${PERLSO} ${DEST}/${PERLSO} PREFIX := '"script/"' CCOPTS := $(shell perl -MExtUtils::Embed -e ccopts) -DSCRIPTPREFIX=${PREFIX} LDOPTS := ${DEST}/${PERLSO} -lpthread WXCLIB := $(shell perl -MAlien::wxWidgets -E 'say Alien::wxWidgets->prefix') WXCNFG := ${WXCLIB}/bin/wx-config .PHONY :: loaders loaders : "${DEST}/wxchordpro" "${DEST}/chordpro" # For testing... .PHONY :: _loaders _loaders : "${DEST}/chordpro" cp "${DEST}/chordpro" "${DEST}/perl" "${DEST}/wxchordpro" : ppl.c dndhandler.cpp ${CC} ${CCOPTS} -o "${DEST}/wxchordpro" -L${DEST} ${LDOPTS} ppl.c ${CXX} -o "${DEST}/dndhandler" \ dndhandler.cpp `${WXCNFG} --cxxflags --libs` perl ./reloc.pl "${DEST}/dndhandler" "${DEST}/chordpro" : ppl.c ${CC} ${CCOPTS} -o "${DEST}/chordpro" -L${DEST} ${LDOPTS} ppl.c clean :: rm -fr ${DEST} # Truly dynamic version of loader. Rename to {wx}chordpro if you dare. "${DEST}/poc" : poc.c ${CC} ${CCOPTS} -o "${DEST}/poc" poc.c -ldl APPDIR := ChordPro.app # http://www.mactipsandtricks.com/website/articles/Wiley_HT_appBundles2.lasso app : ppl app1 app1 : rm -fr ${APPDIR} || rm -fr ${APPDIR} cp -a "${DEST}/" "${APPDIR}/" mkdir -p "${APPDIR}/Contents/MacOS" mkdir -p "${APPDIR}/Contents/Resources" cp PkgInfo Info.plist "${APPDIR}/Contents" cp "${DEST}/chordpro.icns" "${APPDIR}/Contents/Resources" cp "${DEST}/chordpro-doc.icns" "${APPDIR}/Contents/Resources" cd "${APPDIR}"; mv chordpro wxchordpro dndhandler Contents/MacOS cd "${APPDIR}"; mv script lib Contents/MacOS cd "${APPDIR}"; mv lib*.dylib Contents/MacOS cd "${APPDIR}"; rm -f MANIFEST META.yml perl ./reloc.pl ${APPDIR}/Contents/MacOS/libpng16.*.dylib perl ./reloc.pl ${APPDIR}/Contents/MacOS/libjpeg.*.dylib perl ./reloc.pl ${APPDIR}/Contents/MacOS/libtiff.*.dylib perl ./reloc.pl ${APPDIR}/Contents/MacOS/libz*.dylib perl ./reloc.pl ${APPDIR}/Contents/MacOS/libpcre2-32.*.dylib perl ./reloc.pl ${APPDIR}/Contents/MacOS/libwx*.dylib perl ./reloc.pl ${APPDIR}/Contents/MacOS/lib/auto/Wx/*.bundle perl ./reloc.pl ${APPDIR}/Contents/MacOS/lib/auto/Wx/*/*.bundle dmg : app dmg1 DMG := ChordPro-Installer-${VERSION}.dmg dmg1 : rm -f ${DMG} bash ./create-dmg \ --volname "ChordPro Installer" \ --volicon "${APPDIR}/chordpro.icns" \ --window-pos 200 200 \ --window-size 600 300 \ --icon "${APPDIR}" 50 50 \ --add-file "READ ME FIRST.html" README.html 300 50 \ --hide-extension "READ ME FIRST.html" \ ${DMG} "${APPDIR}" clean :: rm -fr ${APPDIR} App-Music-ChordPro-6.050.7/pp/macos/chordpro.pp0000644000400000040000000000013414120712212016673 0ustar jvjv@../common/chordpro.pp # HarfBuzz library. #--link=C:/Strawberry/c/bin/libharfbuzz-0__.dll App-Music-ChordPro-6.050.7/pp/macos/reloc.pl0000644000400000040000000000223414544302032016163 0ustar jvjv#!/usr/bin/perl use strict; use warnings; use utf8; use Alien::wxWidgets; my $verbose = 1; my $prefix = Alien::wxWidgets->prefix; my @libs = Alien::wxWidgets->shared_libraries("core"); die("Cannot find libs version\n") unless $libs[0] =~ /-([0-9._]+)\.dylib/; my $lv = $1; my $srcpat = qr;($prefix.*?)/([-\w.]+\.(?:dylib|bundle));; my $dst = '@executable_path'; if ( @ARGV && $ARGV[0] =~ /^--?q(?:iet)?$/ ) { $verbose = 0; shift; } relocate($_) for @ARGV; ################ Subroutines ################ sub relocate { my ( $lib ) = @_; die("$lib: $!") unless -w $lib; my $odata = `otool -L "$lib"`; while ( $odata =~ m/$srcpat/g ) { my $orig = $1; my $name = $2; my $oname = $name; if ( $lib =~ m;/$name$; ) { warn("+ install_name_tool -id \"$dst/$name\" \"$lib\"\n") if $verbose; system("install_name_tool", "-id", "$dst/$name", $lib); } else { $name =~ s/-[.0-9_]+\.dylib/-$lv.dylib/ unless $name =~ m;libpcre2;; warn("+ install_name_tool -change \"$orig/$oname\" \"$dst/$name\" \"$lib\"\n") if $verbose; system("install_name_tool", "-change", "$orig/$oname", "$dst/$name", $lib); } } } App-Music-ChordPro-6.050.7/pp/macos/Info.plist0000644000400000040000000000265014120712212016467 0ustar jvjv CFBundleName ChordPro CFBundleDevelopmentRegion English CFBundleExecutable dndhandler CFBundleIconFile chordpro.icns CFBundleIdentifier org.chordpro.chordpro CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleSignature CHO# CFBundleShortVersion 0.977 CFBundleVersion 0.977.13.1 NSHumanReadableCopyright ⓒ 2010,2020 The ChordPro Team, www.chordpro.org CFBundleDocumentTypes CFBundleTypeExtensions cho chordpro chopro crd CFBundleTypeIconFile chordpro-doc.icns LSIsAppleDefaultForType Yes CFBundleTypeRole Editor CFBundleTypeName ChordPro document App-Music-ChordPro-6.050.7/pp/macos/wxchordpro_pp.pl0000644000400000040000000000154614544302032017762 0ustar jvjv#! perl use strict; use warnings; use Alien::wxWidgets; my $prefix = Alien::wxWidgets->prefix; my $perltype = "Generic"; $perltype = "Citrus Perl" if $^X =~ /citrusperl/; $perltype = "HomeBrew Perl" if $^X =~ /Cellar/; print <shared_libraries ) { my $lib = "$prefix/lib/$_"; warn("Skipped: $_\n"),next unless -f $lib; print( "--link=$lib\n"); if ( /_webview-/ ) { print( "--module=Wx::WebView\n" ); } } App-Music-ChordPro-6.050.7/pp/macos/README.html0000644000400000040000000000445714544302032016356 0ustar jvjv

Important notes for running ChordPro on MacOS

Due to MacOS security constraints it is currently not possible to use the standard file open and save dialogs. As a consequence, you will not be able to open arbitrary (ChordPro) files, and save changes into arbitrary files.

The advised way of using ChordPro is by double-clicking a ChordPro document in the Finder. Alternatively you can use drag and drop, or the Finder Open with ... command.

ChordPro is currently limited to one document at a time.

When ChordPro is started with a document, you can make changes and save your changes into the document.

When ChordPro is started without document, it presents a dummy document containing just a title. You can edit and preview but you will not be able to save your changes.

Hint: You can use Select All (command-A) and copy and paste to transfer the contents to somewhere else.

Due to MacOS security constraints it is currently not possible to select a custon config file, library or template using a file chooser. Instead, you must type the name of an existing file or folder in the Preferences dialog.

Printing from the Finder is like opening.

ChordPro on MacOS currently does not support embedding of LilyPond. Embedding of ABC is integrated.

Warning!

Song files in MS-Windows format (with CR/LF line terminators) may misbehave.

Hints

The ChordPro 'app' has two binaries: the ChordPro GUI program (default) and the command line version. Provided the dmg is mounted in its default location, you can run the command line version from a Terminal window with:

/Volumes/ChordPro\ Installer/ChordPro.app/Contents/MacOS/chordpro

Support

Please join the user forum for support and the latest updates.

Are you a MacOS developer? Please help us to overcome the MacOS limitations and improve the user experience!

App-Music-ChordPro-6.050.7/pp/macos/dndhandler.cpp0000644000400000040000000001162214120712212017325 0ustar jvjv/* Drag'n'Drop and Finder command handling. * * This small Wx program intercepts the initial Finder calls and * then execs the real program. */ #include "wx/wx.h" #include "wx/wxprec.h" #include "wx/event.h" #ifdef DEBUG #include FILE *dbgf; void openlog() { if ( !dbgf ) { dbgf = fopen( DEBUG, "a" ); time_t t; time(&t); fprintf( dbgf, "%s", ctime(&t) ); } } #endif class DnDHandler : public wxApp { public: // The usual... virtual bool OnInit(); // We're going to override these to catch Finder calls. virtual void MacPrintFile(const wxString &fileName); virtual void MacOpenFiles(const wxArrayString &fileNames); virtual void MacNewFile(); virtual void MacReopenApp(); // Our Idle handler. // As soon as the app becomes idle, we are done and we'll // exec the real program. void DoIdle(wxIdleEvent &event); DECLARE_EVENT_TABLE() }; // Standard Wx stuff. DECLARE_APP(DnDHandler) BEGIN_EVENT_TABLE( DnDHandler, wxApp ) EVT_IDLE( DnDHandler::DoIdle ) END_EVENT_TABLE() IMPLEMENT_APP(DnDHandler) /* Alternatively: int main( int argc, char **argv ) { // MyWxApp derives from wxApp wxApp::SetInstance( new DnDHandler() ); wxEntryStart( argc, argv ); wxTheApp->CallOnInit(); fprintf( dbgf, "OnRun...\n" ); wxTheApp->OnRun(); fprintf( dbgf, "OnExit...\n" ); wxTheApp->OnExit(); fprintf( dbgf, "Cleanup...\n" ); wxEntryCleanup(); } */ // Register Finder calls. const char *calltype = "Normal"; char argfile[PATH_MAX] = ""; // Chain handler. int chain( int argc, char **argv ); // Wx init function. Must return 'true'. bool DnDHandler::OnInit() { #ifdef DEBUG openlog(); fprintf( dbgf, "OnInit entry\n" ); #endif #if 0 // A frame to show... wxFrame* frame = new wxFrame( (wxFrame*) NULL, -1, "Drag'n'Drop Handler" ); frame->Show(true); SetTopWindow(frame); #endif #ifdef DEBUG fprintf( dbgf, "OnInit return\n" ); #endif return true; } // Handlers for Finder calls. void DnDHandler::MacPrintFile(const wxString &fileName) { calltype = "Print"; strcpy( argfile, fileName.c_str() ); #ifdef DEBUG openlog(); fprintf( dbgf, "MacPrintFile called (%s)\n", argfile ); #endif } void DnDHandler::MacOpenFiles(const wxArrayString &fileNames) { #ifdef DEBUG openlog(); fprintf( dbgf, "MacOpenFiles called, %lu args\n", fileNames.GetCount() ); #endif if ( fileNames.GetCount() > 0 ) { strcpy( argfile, fileNames.Item(0).c_str() ); } calltype = "Open"; } void DnDHandler::MacNewFile() { #ifdef DEBUG openlog(); fprintf( dbgf, "MacNewFile called\n" ); #endif // Always called. Ignore. } void DnDHandler::MacReopenApp() { #ifdef DEBUG openlog(); fprintf( dbgf, "MacReopenApp called\n" ); #endif // Ignore. } // Event(ually). void DnDHandler::DoIdle( wxIdleEvent& evt ) { #ifdef DEBUG openlog(); fprintf( dbgf, "DoIdle called\n" ); #endif // The app is set up. Pass control to the chain routine. chain( wxTheApp->argc, wxTheApp->argv ); // We'll not supposed to get here. } /**************** Chain Handler ****************/ static char selfpath[PATH_MAX]; /* /foo/bar */ int chain( int argc, char **argv ) { #ifdef DEBUG fprintf( dbgf, "entry: %s (%s)\n", calltype, argfile ); #endif /* Assuming the program binary /foo/bar/blech */ char scriptname[PATH_MAX]; /* blech */ memset (selfpath, 0, PATH_MAX); memset (scriptname, 0, PATH_MAX); if ( readlink ("/proc/self/exe", selfpath, PATH_MAX-1 ) > 0 ) { char *p = rindex( selfpath, '/' ); if ( p ) { p++; strcpy( scriptname, p ); *p = 0; } else strcpy( scriptname, selfpath ); #ifdef DEBUG fprintf( dbgf, "selfpath: %s\n", selfpath ); fprintf( dbgf, "scriptname: %s\n", scriptname ); #endif } else { strncpy( selfpath, argv[0], PATH_MAX-1 ); char *p = rindex( selfpath, '/' ); if ( p ) { p++; strcpy( scriptname, p ); *p = 0; } else { p = getcwd( selfpath, PATH_MAX-1 ); strcat( selfpath, "/" ); strncpy( scriptname, argv[0], PATH_MAX-1 ); } #ifdef DEBUG fprintf( dbgf, "cwdpath: %s\n", selfpath ); fprintf( dbgf, "scriptname: %s\n", scriptname ); #endif } /* Insert script name in argv. */ char scriptpath[PATH_MAX]; /* /foo/bar/SCRIPTPREFIXblech.pl */ strcpy( scriptpath, selfpath ); strcat( scriptpath, "wxchordpro" ); #ifdef DEBUG fprintf( dbgf, "scriptpath: %s\n", scriptpath ); #endif static char **ourarg = NULL; ourarg = (char **)calloc( argc+2, sizeof(char**) ); /* Set argv0 to scriptpath for FindBin and friends. */ ourarg[0] = scriptpath; /* Copy rest of the arguments. */ for ( int i=1; i ) { s/(^#\s+define\s+V_MAJ\s+)(\d+)/$1$maj/ and $2 != $maj and $resetbuildnum++; s/(^#\s+define\s+V_MIN\s+)(\d+)/$1$min/ and $2 != $min and $resetbuildnum++; if ( defined($aux) ) { s/(^#\s+define\s+V_AUX\s+)(\d+)/$1$aux/ and $2 != $aux and $resetbuildnum++; } s/(^\#\s+define\s+BuildNum\s+)(\d+).* /sprintf("%s%d", $1, $resetbuildnum ? 1 : 1+$2) /ex; } continue { print; } } elsif ( $ARGV[0] =~ /\.rc$/ ) { my $resetbuildnum = 0; while ( <> ) { if ( /^((?:PRODUCT|FILE)VERSION\s+)(\d+),(\d+),(\d+),(\d+)/ ) { $2 != $maj and $resetbuildnum++; $3 != $min and $resetbuildnum++; if ( defined($aux) ) { $4 != $aux and $resetbuildnum++; } $_ = sprintf("%s%d,%d,%d,%d\n", $1, $maj, $min, $aux//0, $resetbuildnum ? 1 : 1+$5); next; } if ( /^(\s*VALUE\s+"(?:Product|File)Version",\s+)/ ) { $_ = "$1 \"$vv\"\n"; next; } } continue { print; } } App-Music-ChordPro-6.050.7/pp/windows/ppl.c0000644000400000040000000001515214324567577016103 0ustar jvjv/* Portable Perl launcher for Windows, Strawberry Perl + GCC only. * * Based on ppl.c by Oliver Betz, inspired by "runperl.c" used to * compile the original perl.exe * https://oliverbetz.de/pages/Artikel/Portable-Perl-Applications */ #define CASSERT(pred) switch(0){case 0:case (pred):;} // compile time assert #include #include // - ".pl" replaces just the extension from .exe to .pl // variable path conversion to Perl script static const char pathreplace[] = ".pl"; // search string for Perl DLL static const char dllsearch[] = "perl5*.dll"; // act like the original "perl.exe" if our name is "perl.exe" (case sensitive!) static const char perlexe[] = "perl.exe"; // set to NULL to disable // number of arguments we put in front of the user provided args #define ARGS_ADDED 1 // debug output. Comment to disable debug output #ifdef DEBUG #define DEBUGOUT stderr #endif // PATH_MAX is obsolete since Win10? #define PATHBUFLEN 1000 // additional buffer size for the dll name #define NAMEBUFLEN 50 // RunPerl. typedef int (* RunPerl_t)(int argc, char **argv, char **env); // Main. int main( int argc, char **argv, char **env ) { // Make ourselves known. putenv( "PPL_PACKAGED=1.00" ); // to construct script path from exe path char scriptpath[PATHBUFLEN]; // to construct DLL (search) path from exe path char dllpath[PATHBUFLEN+NAMEBUFLEN]; // pointer past (!) last backslash in dllpath buffer char *dlldir; int i, emulate_perlexe; scriptpath[0] = 0; // ensure a null terminated string i = GetModuleFileName( NULL, scriptpath, sizeof(scriptpath) ); if ( (i > 0) && (i < ((int)ARRAYSIZE(scriptpath)-(int)ARRAYSIZE(pathreplace)-(int)1)) ) { // limit strrchr search in case of errors (paranoia) scriptpath[ARRAYSIZE(scriptpath)-1] = 0; (void)memmove(dllpath, scriptpath, sizeof scriptpath); dlldir = strrchr(dllpath, '\\'); // find the last backslash dlldir = dlldir ? (dlldir + 1) : dllpath; // if find no backslash (unlikely), use the whole buffer emulate_perlexe = ((perlexe != 0) && (!strncmp(dlldir, perlexe, sizeof perlexe))); char *rep = strrchr(scriptpath, pathreplace[0]); // find the last delimiter in path if( !rep ) { (void)fprintf(stderr, "Failed to find '%c' in %s\n", pathreplace[0], scriptpath); return 1; // ---> early return } if ( (pathreplace[0] == '.') && (pathreplace[1] == 0) ) { *rep = 0; // Perl script without extension, drop the '.' } else { (void)memmove(rep, pathreplace, sizeof pathreplace); // paste replacement } #ifdef SCRIPTPREFIX rep = strrchr( scriptpath, '\\' ); if ( !rep ) rep = scriptpath; (void)memmove(rep+strlen(SCRIPTPREFIX)+1, rep+1, strlen(rep) ); (void)memmove(rep+1, SCRIPTPREFIX, strlen(SCRIPTPREFIX) ); rep[strlen(rep)+strlen(SCRIPTPREFIX)+1] = 0; #endif } else { (void)fprintf(stderr, "Path to %s is too long for my %I64i bytes buffer \n", argv[0], sizeof(scriptpath)); return 1; // ---> early return } #ifdef DEBUGOUT fprintf( DEBUGOUT, "***** debug info *****\n" ); fprintf( DEBUGOUT, "%i argv parameters were passed to the exe:\n", argc); for ( i = 0; i < argc; i++ ) { fprintf( DEBUGOUT, "%i:%s\n", i, argv[i] ); } if ( !emulate_perlexe ) fprintf(DEBUGOUT, "\nScript to be called: \"%s\"\n", scriptpath); else fprintf(DEBUGOUT, "\nWe emulate the original perl.exe\n"); #endif HINSTANCE hDLL; // Handle to Perl DLL RunPerl_t RunPerl; // Function pointer HANDLE hFind; // for FindFirstFile() WIN32_FIND_DATA ffd; // for FindFirstFile() CASSERT(((sizeof dllsearch)+(sizeof scriptpath)) < (sizeof dllpath)); // the dllpath buffer is longer than the scriptpath buffer => memmove can't overflow: (void)memmove(dlldir, dllsearch, sizeof dllsearch); // build search spec for the Perl DLL // remember that dlldir currently points to the last backslash of the path to the exe #ifdef DEBUGOUT fprintf(DEBUGOUT, "DLL search spec: \"%s\"\n", dllpath); #endif hFind = FindFirstFile( dllpath, &ffd ); if ( hFind == INVALID_HANDLE_VALUE ) { (void)fprintf( stderr, "Could not find %s\n", dllpath ); return 1; // ---> early return } // now we have the name of the DLL (without path) in ffd.cFileName // search again since our DLL search spec could contain again a backslash! dlldir = strrchr(dllpath, '\\'); // find the last backslash in path to get the DLL directory dlldir = dlldir ? (dlldir + 1) : dllpath; // if find no backslash (unlikely), set to start of buffer // note: a trailing slash is needed for the (stupid) case of a directory with a trailing space! *dlldir = 0; // strip the file name spec from the path (void)SetDllDirectory(dllpath); // add the directory to the search path // as a positive side-effect, this removes the current directory from the search path #ifdef DEBUGOUT fprintf( DEBUGOUT, "DLL name found: \"%s\" (length = %I64i)\n", ffd.cFileName, strlen(ffd.cFileName) ); fprintf( DEBUGOUT, "DLL search path set: \"%s\"\n", dllpath); #endif // search first in the directory set by SetDllDirectory() hDLL = LoadLibrary(ffd.cFileName); if ( !hDLL ) { (void)fprintf(stderr, "Failed to load Perl DLL \"%s\" code %li\n", ffd.cFileName, GetLastError()); (void)FindClose(hFind); return 1; // ---> early return } (void)FindClose(hFind); // "RunPerl" works with ActiveState and Strawberry RunPerl = (RunPerl_t)GetProcAddress(hDLL, "RunPerl"); if ( !RunPerl ) { (void)FreeLibrary(hDLL); (void)fprintf( stderr, "Failed to get RunPerl address in DLL. Check the DLL for name mangling.\n" ); return 1; // ---> early return } #ifdef DEBUGOUT fprintf( DEBUGOUT, "***** end of debug info *****\n" ); #endif if( emulate_perlexe ) { // emulate the standard perl.exe i = RunPerl( argc, argv, env ); if( hDLL ) (void)FreeLibrary(hDLL); } else { // generate "our" argument list with additional entries and terminating NULL pointer char **ourargv = (char **)malloc((argc+ARGS_ADDED+1) * sizeof(char**)); if ( !ourargv ) { (void)fprintf(stderr, "Out of memory building new arg list\n"); return 1; // ---> early return } ourargv[0] = argv[0]; // keep filename, although it seems to be dropped by perl ourargv[1] = scriptpath; // pass script path to Perl interpreter // copy the remaining user provided arguments and the terminating NULL pointer for ( i=1; i<=argc; ++i ) { ourargv[i+ARGS_ADDED] = argv[i]; } i = RunPerl( argc+ARGS_ADDED, ourargv, env ); if( hDLL ) (void)FreeLibrary(hDLL); free(ourargv); } // Pass the return code like original perl.exe does return i; } App-Music-ChordPro-6.050.7/pp/windows/chordpro.rc0000644000400000040000000000151714544300524017267 0ustar jvjv// see https://docs.microsoft.com/en-us/windows/desktop/menurc/versioninfo-resource 4711 ICON "..\\..\\lib\\ChordPro\\res\\icons\\chordpro.ico" 1 VERSIONINFO FILEVERSION 0,975,0,0 PRODUCTVERSION 0,975,0,0 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" // US English CP 1252 (Windows ANSI) BEGIN VALUE "Comments", "Beta version, use at your own risk" VALUE "CompanyName", "ChordPro.ORG" VALUE "FileDescription", "ChordPro CLI Launcher" VALUE "FileVersion", "0.975" VALUE "InternalName", "chordpro" VALUE "LegalCopyright", "Copyright 2010,2018,2020 The ChordPro Team" VALUE "OriginalFilename", "chordpro.exe" VALUE "ProductName", "ChordPro" VALUE "ProductVersion", "0.975.beta" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END App-Music-ChordPro-6.050.7/pp/windows/Makefile0000644000400000040000000000532114544302032016552 0ustar jvjv# GNUmakefile for MS Windows + Strawberry Perl + wxWidgets 3.0. # Needed for the environment settings. # .ONESHELL : ROOT := ../.. COMMON := ../common SCRIPT := ${ROOT}/script RES := ..\..\lib\ChordPro\res PP := pp -u all : ppl iss .PHONY :: chordpro wxchordpro chordpro : chordpro.exe wxchordpro : wxchordpro.exe # PAR_VERBATIM=1 prevents POD filtering. ChordPro/Config.pm needs POD! chordpro.exe : chordpro.pp ${PP} --output=chordpro.exe @chordpro.pp ${SCRIPT}/chordpro.pl # For wxWidgets, explicit libraries are specified in wxchordpro.pp and the # PATH must be set accordingly. # To set PATH from the makefile, we need .ONESHELL pseudo target above. wxchordpro.exe : chordpro.pp wxchordpro.pp ${PP} --output=wxchordpro.exe @wxchordpro.pp ${SCRIPT}/wxchordpro.pl clean :: del *chordpro.exe del *.pp.deps #### NOTE: THIS MAY NEED ADJUSTMENT FOR YOUR SYSTEM ISCC := "C:\Program Files (x86)\Inno Setup 6\iscc.exe" .PHONY :: iss_par iss_par :: chordpro.exe wxchordpro.exe innosetup_par.iss chordproinst.bmp copy ${RES}\icons\chordpro.ico . copy ${RES}\icons\chordpro-doc.ico . perl vfix.pl innosetup_par.iss ${ISCC} innosetup_par.iss PLIBS := C:\Strawberry\perl\bin CLIBS := C:\Strawberry\c\bin DEST := build .PHONY :: ppl ppl : clean wxchordpro.exe unpack copy_coredll loaders # Unpack the pp generated kits. # We need to restore ChordPro/Config.pm since that has been mangled by par filters. .PHONY :: unpack unpack : perl ..\pp2ppl.pl --quiet --dest="${DEST}" wxchordpro.exe copy "${ROOT}\script\chordpro.pl" "${DEST}\script\chordpro.pl" copy "${ROOT}\lib\ChordPro\Config.pm" "${DEST}\lib\ChordPro\Config.pm" copy "${DEST}\res\icons\chordpro.ico" "${DEST}" copy "${DEST}\res\icons\chordpro-doc.ico" "${DEST}" move "${DEST}\res" "${DEST}\lib\ChordPro" # The core DLLs needed for Strawberry perl. The Wx libs have been unpacked already. copy_coredll : copy "${CLIBS}\libgcc_s_seh-1.dll" "${DEST}" copy "${CLIBS}\libstdc++-6.dll" "${DEST}" copy "${CLIBS}\libwinpthread-1.dll" "${DEST}" copy "${PLIBS}\perl530.dll" "${DEST}" # Portable Perl Loaders PFX := "\"script\\\"" CC := gcc -Wall -Wstrict-prototypes -O2 -s -mms-bitfields -fwrapv -DSCRIPTPREFIX=${PFX} .PHONY :: loaders loaders : "${DEST}\wxchordpro.exe" "${DEST}\chordpro.exe" "${DEST}\wxchordpro.exe" : ppl.c wxchordpro.rc perl vfix.pl wxchordpro.rc windres wxchordpro.rc ppl.o ${CC} -mwindows -o "${DEST}\wxchordpro.exe" ppl.c ppl.o "${DEST}\chordpro.exe" : ppl.c chordpro.rc perl vfix.pl chordpro.rc windres chordpro.rc ppl.o ${CC} -mconsole -o "${DEST}\chordpro.exe" ppl.c ppl.o # And finally, the installer. .PHONY :: iss iss : "${DEST}\wxchordpro.exe" innosetup.iss perl vfix.pl ${ISCC} /q innosetup.iss clean :: -rmdir /q/s "${DEST}" App-Music-ChordPro-6.050.7/pp/windows/wxchordpro.rc0000644000400000040000000000152314544300524017643 0ustar jvjv// see https://docs.microsoft.com/en-us/windows/desktop/menurc/versioninfo-resource 4711 ICON "..\\..\\lib\\ChordPro\\res\\icons\\chordpro.ico" 1 VERSIONINFO FILEVERSION 0,0,975,0 PRODUCTVERSION 0,0,975,0 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904E4" // US English CP 1252 (Windows ANSI) BEGIN VALUE "Comments", "Beta version, use at your own risk" VALUE "CompanyName", "ChordPro.ORG" VALUE "FileDescription", "ChordPro GUI Launcher" VALUE "FileVersion", "0.975" VALUE "InternalName", "wxchordpro" VALUE "LegalCopyright", "Copyright 2010,2018,2020 The ChordPro Team" VALUE "OriginalFilename", "wxchordpro.exe" VALUE "ProductName", "ChordPro" VALUE "ProductVersion", "0.975.beta" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1252 END END App-Music-ChordPro-6.050.7/pp/windows/innosetup_par.iss0000644000400000040000000000555614120712212020525 0ustar jvjv# define APP "ChordPro" # define V_MAJ 0 # define V_MIN 85 # define V_AUX 0 # define BuildNum 27 # define PUBLISHER "ChordPro.ORG" # define SRC "C:\Users\Johan\Documents\ChordPro" # define DEST "C:\Users\Johan\Documents\ChordPro" ; Increment the build number by one. ;#define BuildNum Int(ReadIni(SourcePath + "BuildInfo.ini","Info","Build","0")) ;#expr BuildNum = BuildNum + 1 ;#expr WriteIni(SourcePath + "BuildInfo.ini","Info","Build", BuildNum) [Setup] AppID={{F8D1018C-AAE3-45E6-9447-5997F512F932} AppName={#APP} AppVersion={#V_MAJ}.{#V_MIN}.{#V_AUX}.{#BuildNum}.0 AppVerName={#APP} {#V_MAJ}.{#V_MIN} AppPublisher={#PUBLISHER} AppPublisherURL=https://www.chordpro.org DefaultDirName={pf}\{#PUBLISHER}\{#APP} DefaultGroupName=\{#PUBLISHER}\{#APP} OutputDir=. OutputBaseFilename={#APP}-GUI-installer-{#V_MAJ}-{#V_MIN}-{#V_AUX}-{#BuildNum}-msw-x64 Compression=lzma/Max SolidCompression=true AppCopyright=Copyright (C) 2015,2017,2020 {#PUBLISHER} PrivilegesRequired=none InternalCompressLevel=Max ShowLanguageDialog=no LanguageDetectionMethod=none WizardImageFile=chordproinst.bmp InfoAfterFile=infoafter.txt [Components] Name: GUI; Description: "ChordPro GUI application"; Types: full compact Name: CLI; Description: "ChordPro command line application"; Types: full [Tasks] Name: desktopicon; Description: "Create desktop icons"; Components: GUI; GroupDescription: "Additional icons:" Name: desktopicon\common; Description: "For all users"; Components: GUI; GroupDescription: "Additional icons:"; Flags: exclusive Name: desktopicon\user; Description: "For the current user only"; Components: GUI; GroupDescription: "Additional icons:"; Flags: exclusive unchecked [Files] Source: chordpro.ico; DestDir: {app}; Components: GUI; Flags: overwritereadonly; Source: wxchordpro.exe; DestDir: {app}\bin; Components: GUI; Flags: ignoreversion recursesubdirs createallsubdirs overwritereadonly 64bit; Source: chordpro.exe; DestDir: {app}\bin; Components: CLI; Flags: ignoreversion recursesubdirs createallsubdirs overwritereadonly 64bit; [Icons] Name: {group}\{#APP}; Filename: {app}\bin\wxchordpro.exe; Components: GUI; IconFilename: "{app}\chordpro.ico"; Name: "{group}\{cm:UninstallProgram,{#APP}}"; Filename: "{uninstallexe}" Name: "{commondesktop}\{#APP}"; Filename: "{app}\bin\wxchordpro.exe"; Tasks: desktopicon\common; IconFilename: "{app}\chordpro.ico"; Name: "{userdesktop}\{#APP}"; Filename: "{app}\bin\wxchordpro.exe"; Tasks: desktopicon\user; IconFilename: "{app}\chordpro.ico"; [Run] Filename: "{app}\bin\wxchordpro.exe"; Description: "Prepare"; Components: GUI; Parameters: "--quit"; StatusMsg: "Preparing... (be patient)..." Filename: "{app}\bin\chordpro.exe"; Description: "Prepare"; Components: CLI; Parameters: "--version"; StatusMsg: "Preparing... (be patient)..." [Messages] BeveledLabel=Perl Powered Software by Squirrel Consultancy App-Music-ChordPro-6.050.7/pp/windows/wxchordpro.pp0000644000400000040000000000264514324567577017707 0ustar jvjv# Packager settings for WxChordPro. # Strawberry Perl + wxWidgets 3.0. @../common/wxchordpro.pp --gui --module=Wx::WebView # Explicitly link the wxmsw libraries. --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxbase30u_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxbase30u_net_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxbase30u_xml_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxmsw30u_adv_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxmsw30u_aui_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxmsw30u_core_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxmsw30u_html_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxmsw30u_media_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxmsw30u_propgrid_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxmsw30u_ribbon_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxmsw30u_richtext_gcc_custom.dll --link=C:/Strawberry/perl/site/lib/Alien/wxWidgets/msw_3_0_2_uni_gcc_3_4/lib/wxmsw30u_webview_gcc_custom.dll App-Music-ChordPro-6.050.7/pp/windows/chordproinst.bmp0000644000400000040000000045565613212166673020366 0ustar jvjvBM[6(:x[۪ӝЦȚÎrĐrÒsÓtÔtƕuǕuǕuȖuȖuɗuʗu˘u˗u̗u͘u͗u͗u͗uΘuϘuКuќvҝwӞxӠxբx֣y֥zצz٩z٪{۬}ۮ}ܰ}ݱ~޳޶ก߹߻޻ݼۺٻ~ֺ~з{ζyʴxƲvñttrqppppppqqrssst²tijuǵw˹y͹zѻ|ս~׾ῄᾃἃߺ޸޶ܳ۲~ڰ~ٮ}٬}ت|֨zէzӥzӣzѡyѠxϞxΝwΛv˛u˘uʗuɖuȕuǖuǕuƕuŔuŔtÓs’ssrrrrrqppponmiΜ轓M9 %,045579:;<=>?@@@A@AA@@@ACFJLOQU V [ \ b dhjnrvy|ƂƆĈĊ !!"##$%$$$$$%%%&&&&&&&&&'&&%%•$Ĕ#Ǔ!ɑ ɏ ɋȇȅǃ~zvrokhdb^ Z X T ROLHFC@@??==;;986552/-+*}'{$v"tronu-șm0 ,155679;<=>@A B C D E F F F G F G G F F F G I K O Q TWZ\`bfhlosvz~ƀǂ Dž"ć"ĉ#$%&&&''()))))***++++++++++++**))”(ē'Ǒ&ȑ&ʏ%ɋ#ɉ"ȇ!ǂ!~{wtpmhfd`]YWTR N L I F F E E C C AA?>=<:85430.+{)y'w$u#u&t&r&'Y?џ?(,012`:pNnJqKrKsLsLtMvMvMwMxNxMyNyNyNyNzNyNzNzNzNyNyNzN|O}OPŁQDŽQȆRʇSʉSˌT͎UΑVϒWЕXҗYҙZԛ[՟[֡\ԡ]ӣ]ӣ]Ф\̣[ȡZßXVTRRPPPPPRSTVXZZ[[\ZZYXWWWWWX¤Yǧ[˨\Щ]Ҫ^ի_֪_֧^֧^֤_Ԣ]Ӡ\Ҝ[њ[ϗYΖY˒WːVʏUɌUljTňSĆRƒQQP~O|NzNyNyNyMwNxMvMvMtMtLsLrLrKpKnJmIlIjIiHgHeGdFcG@ t#u%u%r%|&B"}i)&,~./.xYаѰѰұӲԳճֳִִصصٵٵڶڶڶ۶۶۷۶ܷܶݶݷݸ޸޺ߺ໠ἠ⾡㾡㿡âĢţƤȥɥɥʤʥɣǡĞֿлʷIJ}~~ĸȼʿ–ʿȽŸ·õȹμŜɟˢͥϦͦΧ̦ʥʥȥƤŤãâ࿡߽޼޻ݻݹ۹۷ڶڶٶٶٵص״ֳִճճӲұѰѰЯϯέ̬̫ͬʩ̭yy) t$u%u%t%w&F%iR'&z)},.-eDʦȢɣˤ̦̦ͧΧϧШШѩҪӪӪԫԫիի֫֫֫׫׫ثثج٭گگ۲ݳݴݵ޶޸ຕổ἖⾖˜—۽Իзɲ~yvvrsttuxz~ķɻ̽ÏÏÏ̽ɻŷ´°Ƴ̷ӼÔƗƘǘƚĘØ࿗ྖߺ޺ݸݷ۵ڴڳٱر׮֭֬ԫԫӪӪөҪѨШϨϧ̦ͧͦ˥ʣɢȢǡơşĞĠE's"u%u%u%t%|%v_~j&%x'y*},-Cw˜ÙĚƛƛǜȜʝʞ˞˟̠͠͠ΠϡϡϡСѡҡҡҡҡӢԢգ֥֧רة٫٬ڮۯܰܲ޴ߵ߷๋߸߻ܹٷնϲɮ}ytrnkihffffgknsyĵ~ʺпÇĈňĈξȸŵ~zwuuwzƮ|˲ѷ׻ܽ߿࿌ᾌὌ༌ߺ޸ݶܵ۲ڱٯخ׭֪֪ըӦѤѣТϡΠ̠͠͠͠˞ʞʞɝǜǛƛŚę—~~~~||nWqu%u%u%u%t%1㾴ٱ(%{$v'z)},-a@upqqrrrr‘sÒsœsƔtƕtǕtȕtȕtɖtʖt˖t˗t̗t̖t͗t͗t͖tΖtϗtИtЙuћuѝvӞwԟxաx֣xץyצy٨{٪{۫|ۭ}ܯ~ݱ~ܰ|۳}ز|ԯzάwɨt¤qmifb^X~SwM|qJxnHwmHwmH{pJvMS\enƵvϾ{ĀƁŁĀ}μ{ȷx±somllmpƪsͰwҲz׵|ڷܷ޷ݶݴܲڰ~ڮ}٬|ت{ש{էzԥyӣyҡxѠwОwϝv͛u̙u̘t˗tɖtȖtȕtǕtǔtƔtœtÓsÒs‘rrrrqqpppoonnq}hy- s"u%u%u%u%~&xcU:%%r$y'z)}-1oQebccdddddeefff‹fÊfËfČfƌfŌfƌfnjfȌfȌfȌfɌfʌfˎfˏg̐h͓hϔiЖjјkљkӛlԞlԟm֡mפoץo٧pکpةpתpԩoЧmʥjàfc`[W~OnEma?i^>ka@lc@ndApfAoeAmd@lc@kb@kb@rg@vHVdǴnҾuwyvҾu˹qılhdcccdfƧj̪mѭpծqدrگsڮs٬rتrשq֦pեoԢoӠmўmМkΚk͘k͕j˕iʓhȐhǏgƎfŌfČfËf‹fffffeeddddccb~b}b|az`y_w_u^|g>ru%u%u%u%v%8ۭ(%t%u$y'{)}-4 oQtUtSuTvUvUyUyUzV{W|W~W~WWXXXXXXXXÂX‚XÁXÁXāXŁXƃXDžYdžZɈZʊ\̌\͏]͐]ϒ^Д^З`Ҙ`ӛaԝc֟cՠcԢdѢbϠb˞`Ŝ^\WSKm@j[:m_{m?{n?zm?wj=th;th;thzm>{n?xlAqg@nbm=m=m=m=m=n=q>s>u@wAyA{B}CƀDȃDɅFʇG̉H͌HΎIΐJ̑I˒JǑIHE?k8oY2v_0|c1|f1i3n5p5o6{l5{m:{®quOna3j['vf0~o7{l:vg4}o?Ē@ȒA˒Aʐ@ˏ@ʌ?ȉ>dž=Ń<Ł:}9{9x8v8r5p5n4l4j3h1e0c1c1c1b0`/`/_/_/]/\.[.Z.Z-X-V,T,S*Q*O*N)K)I(G&D%F(9t$u%u%u%u%u%s&Ë|$%t%u%u$y'{)~-07 9;=?@CDEGGIJKLMMNMONOOONNNNPSUX[]`behkn!q"t"w$y#{%|%{&t#}['kLqPyWch"b pY"t`&p&y)~+++++*)+@`ׄvCbPub"|i&|k/k&3Ʀ8š730-,+))***…+ƅ+ł)Ɓ)~'{&w%t$q#n!k heb`\ZXTRPOONMMLKIHGFECB@><:863~0 {. {. x)u%u%u%u%u%u%r&kR&%t%u%v%y'{)~-/13568: = > ? @ A C C E F G G H G I H I II H H G H J NORTWZ\`cfjmpsvxxu_ gFnKyVbhbsZhS dm t"y$&&&&'(()+)ccQr`{g|k-n!,.,)&%}#}"{"}""!"~!|{ xtqnjgda^[XUSPNK I H H G F F D C B @ ? ? = ; 975420~-{*x'v%t$u$u%u%u%u%u%u%r%'qN2&%t%u%v%x'z)~-/24679 ; > ? @ A B D D F GHHIHJIJJJIIHIKMPSUX[]adfjmpsvxwg!hFhFwQaljecN TA\IaNnZhu#|&'''()*+-+6ǺiYta}i!xf'x%--*'%}#{#{#}#~###"} |!xtqokheb_\YVTQOLJIIHGG E D C B @ @ > < : 8 6531.|+y(w&u%u%u%u%u%u%u%u%t%'ZE<&~%u%u%w&y(z)~-/24779 ; > ? @ A B D D F GGHIHJJIJJIIHI KNPSUXZ^`cfimpruwrwP`@rL]ms!om g$m'h&ub*bS$VGiXm!z%''()*+-0ť16ŪjZubzg$zf$',*'%|#{"{#|###""~ {!xurokheb_[YVTQOLJIIGGG E D C B @ @ > < : 8 7521.|+y)x'u%u%u%u%u%u%u%u%u%t%G'<&%u%u%w&y){*~-/24779 ; > ? @ A B D D F GGHIHJJIJJIIHH KNPSUXZ^`cfiloruuc]>iEUgu$z%u t!y#&(/KL[N^Oj z&'()*,.0ĥ3˪3:}IjYwdua#q"))(%|#{"z"|#~##"~"}!{ xtrokhea_[YVSQNLJIIGGG E D C B @ @ > < : 8 7521.|+z)x'u%u%u%u%u%u%u%u%u%p%<>&}%u%u%w&y){*~-/24779 ; > ? @ A B D D F GGHIHJJIJJIIHI KNPRUXZ]`ceilorvstM\:vM^lw$,6~'{#}%&',BP`S%[Ml!~&(**-.0ť3ͬ4Ѱ6.ļiWo\xc$v`&('%}$|"z"|"~##"~"} { xtrokhda^[YVTQNLJIIGGG E D C B @ @ > < : 8 7521.|+z)x'u%u%u%u%u%u%u%u%u%q%1 7&z%t%u%w&y){*~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHI KMPRUWZ\_cfhloqvl Y;e@Scn w!%083.)'((54]P!dUu#(*+-/2ɨ4Ϯ5״7ƨ7r5bgUu`p\#t#''%}$|"z"|"~##"~"} z xtrnkgda^[YVTPNLIIHHGG E D C B @ @ > < : 8 7521.|+z)x'u%u%u%u%u%u%u%u%u%q%'#%{%t%u%w&y){+~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHI KMPRUWZ\`behloqv^!U5nFWdox!$'*Ó+Ö/43+())x)\O|h(*,.0£2ʪ4Ұ6׵89.p8mZmXq^$e&&%}${"{"|"~##"~"| z xtpnkgda^[YVTPNLIIHHGG E D C B @ @ > < : 8 7521.|,z)x'u%u%u%u%u%u%u%u%u%q%'Ĺ#%z%u%u%w&y){*~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHI KMPRUWZ\`beglnquyOY7 vK[enw!%&)Ē*Ŗ,ƚ-Ŝ04.(*(ud%m\'+./1ǧ3ͬ4ӱ6ص7ݺ9ɩ9kZr_jVr^!t\}%&%}${"{"|"~"#~"~#| z wspmkgda^[XUSPNKIIHHGG E D C B @ @ > < : 8 7521.|,z)x'u%u%u%u%u%u%u%u%u%r%'DZ#%x%u%u%w&y){*~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHI KLPRTWZ[`bdgknqvdB_:{M[dnw!%&)Ē*Ƙ-ǚ.Ǟ/Ş01+''q%n]"',/0Ĥ2ʪ4Я6ճ7ڷ8ܹ9߻:|j*mYiUoYoZ#v"%$|#|#{#|"~"#""} z wspmjgca^[XUSPNKIIHHGG E D C B @ @ > < : 8 7521.|,z)x'u%u%u%u%u%u%u%u%u%r%%ޤ#%x%u%u%w&y){+~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHI KLPRTVZ[_aegjmpu_>`;}O[cmu!~$&)Ñ*ŗ,ǚ-ǝ/Ş00-'{&s%tb$&/0Ĥ2ʩ4ϭ5ճ7ض8۸8ܹ9߼91iUkVhSo[%q %${#{#{"|#""!~"~!z vspmjgca^[WUSPNKIIHHGG E D C B @ @ > < : 8 7521.},z)x'u%u%u%u%u%u%u%u%u%s%%ڝ$%x%u%u%w&y)|+~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHH JMORUVY[_bdgjmpu_?a<}OZakt }"%(*Ė,Ƙ-Ɯ/ŝ/0.(w%p%yf%(0ģ2ǧ4έ5ӱ6״7۸8ݺ9޺9޻95hVlWfRnY%n~$$}#{#|"~##"!"}!z vspmigc`][WUSPNKIIHHGG E D C B @ @ > < : 8 7521.},z)x'u%u%u%u%u%u%u%u%u%s%%ڞխ'%w%u%u%w&y){+~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHH JMOQTVY[^aegjlpupJ^:zLW_hqz"%()Ô+×-™,š..-'r"k"}k&+£2ʩ4έ5Ұ6ִ7ٶ8ܹ9ݺ9޺9ݹ84jWlWdPkW#l~$$}#|#|"~"##!~ }!z uspmjgc`][WUSPMKIIHHGG E D D B @ @ > < : 8 7521.|,z)x'u%u%u%u%u%u%u%u%u%s%~%ڞͯ'%v%u%u%w&y){*~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHH JLORTVX\^adgjlotY!X6 tHS\env!~$&(*+,--,}%m!we!o(.ɩ3ϭ5ӱ6״7ٶ8۸9޺9޺9޺9ܸ84gU mWdOkV#k}$~$|"|#}#~""#"~!| z uspmjfca]ZXUSPMKIIHHGG E D C B @ @ > < : 8 7521.|,z)x'u%u%u%u%u%u%u%u%u%s%|%٠Ϯ'%v%u%u%w&y){*~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHH JLORSVX\^acgilosh!Z9a6a)Ybjq z"%&(**++)w$|gsaz+ã2Я6ճ7״7ٶ8۸9޺9޺9޺9߻9ڵ62gTlWcNjV#k|#}$|#|#}#""#"~!| zurpmifb`]ZXUSPMKIIHHGG E D C B @ @ > < : 8 7521.|,z)x'u%u%u%u%u%u%u%u%u%t%|%я}ϯ'%u%u%u%w&y){*~-/24789 ; > ? @ A C D D F GHHIIJJIJJIIHH JLOQSVX[^acgilostiChEƳ[^emt!z#%&()))'o!s_p^1Ϯ5״7ض8۸8ܹ9޺9޺9:߻:ܸ8ٳ62fSlVcMhT#n}$~$|#}"##"#Ā"~!| xurpmieb`]ZXUSPMKIIHHGG E D C B @ @ > < : 8 7521.|,z)x'u%u%u%u%u%u%u%u%u%t%|%ˀlϯ'%u%u%u%w&y){*~-/24789 ; > ? @ A B D D F GGHIHJJIJJIIHH JLOQSVX[^adfhkorv_kK$䵚tV`gnu!z#%&&''v#xcgU}k'Ŧ4״7۸8۸8ݺ9޺9޺9::߻9ܷ8ٳ52eRkUaLjU"q |$~#|#|""##€#Ā"~!|!xuqolifb_\YXUSOMKIIIGGG E D C B @ @ > < : 8 7521.|+z)x'u%u%u%u%u%u%u%u%u%t%|%̃oԼ'%u%u%u%w&y){*~-/24779 ; > ? @ A B D D F GGHIHJJIJJIIHH JLNQSVX[]`cehkoruq kA lS bhn t!x"{$}%|%x$giVbQ2ص7ܹ9ݺ9ݺ9޺9߻::::޺9۶7װ51cPkU`KnW#s }$}#|#~####Á$~!} {!x tqnkifb_]YWTROMJIIIGGG E D C B @ @ > < : 8 7521.|+z)x'u%u%u%u%u%u%u%u%u%t%{%˃oʮ'%t%u%u%w&y){*~-/24779 ; > ? @ A B D D F GHHIHJJIJJIIHH ILNPSUW[]_ceiknqtwepO%[_hlp s"u"s"hkV[Jw-ԯ6ܸ9޺9޺9޺9߻9:::9ܷ8ٳ6ծ40bMjS_Jv]$x!}$}$}#~####ā$!} z w tqnkhfb_\YWTROMJIIIHGG E D C B @ @ > < : 8 7521.|+z)x'u%u%u%u%u%u%u%u%u%t%y%ȃo̯'%s%u%u%w&y(z)~-/24679 ; > ? @ A B D D F GHHIHJIJJJIIHH ILNPSUXZ]_behknqtwz ZmJҴoaejm kcjS[Hvb(̧4ڴ6ܷ7ݸ8޺9޺9޺9޺9޺9޺9ݸ8ٴ6װ5Ҫ3q+cOfPaL~a!y"}${#|####"Ł#!~!z w tqnkgea_\YWTQOMJIIIHG G E D C A @ @ > < : 8 6421.|+y)w'u%u%u%u%u%u%u%u%u%t%y%ȃo̯'%s%u%u%v%x'z)~-/24679 ; > ? @ A B D D F GHHIHJIJJJIIHHI KNPRUXZ]_begjmqswz ~!] e巤~b rWcL]I{d'Ơ3ٯ5ٲ6ڴ7ڶ8۸9۸9۸9۸9۸9ڷ8ڶ7ױ5ӭ4ͧ1n[%hRdOaMh{#}$|#}#~###"Ł#!~!zwtpnkgda_\YWTQOLJIIIHG G E D C A @ @ > < : 8 6421.|+y(w&u%u%u%u%u%u%u%u%u%t%y%ȃo̰'%s%u%u%u$y'{)~-/24679 ; > ? ? B B D D F G HHIHJIJJJIIIHI KMPRTXZ\^begjmpsvy| ˁ"p s@·~pX(^E[GkTw)ˠ2֪3խ4֯4ְ5ֱ5ײ6ֲ6ֳ7ֳ7ֲ6ֱ6հ5Ҭ4Ъ30dQkTbLdP n{#}#|#}"####Ā#!}!y vtpmjgda_\YVSQOLJIIIHG G E D C A @ ? > < : 8 6431.|+y(w&u%u%u%u%u%u%u%u%u%t%y%Ʉpã'%s%u%u%u$y'{)~-/23679 ; > > ? A B D E F F HHIHJIJJJIIIHI KMORTWY\_aefjmprvy{ "˃##qi [vV|\!i&}).ϡ1ҥ1Ѧ1ҩ2Щ3Ъ3ѫ4Э4Э4Э5Э5Э5Ь4Ϫ3̧2ˤ10`MlU_IlU!q!z"|#}#}####Â$ŀ#~!} y vsomjgda^\XUSQOKJIIIHG F E D C A @ > > < : 8 6420.|+y(w&u%u%u%u%u%u%u%u%u%t%x%pZ'%s%u%u%u$y'{)~-/13679 ; = > ? A B D E F F HHIHJIJJJIIIIH KLOQTWY\^adgilorvx{!!Ȃ"Ʌ%ʈ%ɋ'nj(Ǐ*ȓ+ʖ,˙-̜-͟/̟/ʡ0ʢ0ʤ1ʤ1ɤ1ɥ1ɥ1ȥ2ȥ2Ǥ1ɤ1Ǣ1Š0Ɵ/r+`LkS_JrY r!z#z#{#~##"#Á#ƀ"~!|!yuroljfca^\XVSQNKJIIIHG F E D C A @ > = < : 8 6420.|+y(w&u%u%u%u%u%u%u%u%u%u%u%dL'%s%u%u%u$y'{)~-/13679 : < > ? A B D E F F HHIIJIJJJIIIIH KLOQSVY\^`dgilorux {!!~!ȃ#Ȇ%Ɋ'ʍ(ȏ(ɒ*ɔ+ƕ+Ǘ,Ř-ƙ.Ě.›/œ///000000/--o[%eOhP^Jz^t!z#y#{#}#####"~!|!xuroljfc`]ZWVSQNKJIIIHG F E D C A @ > = < : 8 652/.|+y(w&u%u%u%u%u%u%u%u%u%u%u%dL'%t%u%u%u$y'{)~,.13679 : < > ? B B D E F F HHHIJIJIJIIIHH JLOQTVX[^acfilortw z } ~!Ƃ#Ƅ%Ň&Ŋ'ċ(Í(Ž)*+++,,,,,-...--,,+fSiRcM^Jbu!v"w"y"|"}"#"€#~"~ { wtrnlifc`]ZWUSPMKJIHHHG F E D C A @ > = < : 8 6520~-|+y(w&u%u%u%u%u%u%u%u%u%u%u%dL'%t%u%u%u$x'z)},.13679 : < > ? A B D E F F GGHIJJIIJIIIHH JLOQSVX[^`cfiknptvy{ }!"Ă$$%&''(())**++++,,,,++*{+aMlUaJbLes u!v"x!z"|!}#~"~!}"| zwuqnkifc`]ZWUSPMKJIIHHF F E D C A @ > = < : 8 6520~-{+y(w&u%u%u%u%u%u%u%u%u%u%u%dL'%s%u%u%u$x'z)},.13678 : = > ? A B D E F F GHHIIJJIJJIIHH J LORSVXZ]_cehknosvxyz{!~"#$$%%%'''())*********(l(bMmT]HfOfr s s!v!x!y!|!{!{!z z yvsqnkhec_\ZWURPMKJIIHGG F E D C A @ > = < : 76520~-{+y(w&u%u%u%u%u%u%u%u%u%u%t%dLn_'}%s%u%u%u$x'z)},.13579 ; < > @ A B D E F G GHHIIJJIJJIIHH J LORSVXZ]_cehjlorstww x z!z!{"|#}#~$$%&'''()*******)'v_#eOjR]HlShq pq s u v x!y x y xwuromjhec_\YWURPMKIIIHGG F E D C A @ ? = < : 76420~-{+y(w&u%u%u%u%u%u%u%u%u%u%r%dLiZ'}%s%u%u%u%x'z)},.13579 ; = > @ A B D D E G GHHIIJJIJJIIHH I LMPRUWZ]_bdgjlnoqrsttu u v"w!x"z#{#~$%&''')))*++*)))oXiQhO]HqWjoonpq st uuuutsqoljhdb_\YWTQPLKIHIHGG F D D C A @ ? > < : 76420~-{+y(v&u%u%u%u%u%u%u%u%u%u%r%eMk\'|%t%u%u%u%x'z)},.13578 : < > @ A B C D E G GGIIIJJJJJIIIH I LMPRTWY\_bdfiklmopoqpqq rt t!v"y#|#~%&&(()****+**)~)iSnUcK_Hx[ lommnnoqqrrsrpolkigda_\YWTQPMJIHIHG G F D D B A @ ? > ; : 7642/~-{+y(v&u%u%u%u%u%u%u%u%u%u%r%X=k\'y%s%u%u%u%x'z*},.03578 : = > @ A B C D F GG HIIIJIJJJIIIH J KMPRTVY\^adehijklklllmnnq r u!y#|$%&'()**++,,++*y(gPpVaJdL_mnllllllmmnnnnlkjhfca^\YVSQOLJIHIHH F G D D B B @ ? > ; 9 7642/~-{+y(v&u%u%u%u%u%u%u%u%u%u%r%H*k\'x%s%u%u%u%x'z)|+.03578 : = > @ @ B C D E F G HIIIJIJJIIIHHI JMPRTVY[^aceeghhhhhghijkos v!z"}$%&()**+,----++r&gPqVbKhPdo nnkkkijjijjjjjihgfaa][XUTQOLJIHIHH GF D D B A @ ? > ; 9 7642/},{*y(v&u%u%u%u%u%u%u%u%u%u%s%G)dI%y%s%u%u%u%x'z(|*~-/13478;== ? @ B C D E E G H H H I H H H H H H G GI JLPQTVX[^_bcdffeeeeeefikot x"}#%&(())*++,,,,,,i$nUnTcJmU jponljjihggffhghggfeaa][XUTQOLJIIIHH GF D D B A ? ? > ; 9 7642/},{*y(v&u%u%u%u%u%u%u%u%u%u%s%G)\?%w%t%u%x* x* z* ~0 6<AFL$P'Q'S(Y/[/[0]0]/^/^/_0`/a/`.`.`.a-b-`+[%[%X!W VRNLMNQQSUVZ\^_acbbbbabbcehlqu!x!z"|#|${#{#z#{#{$|$%&')*++z`"rVnSfLuY"or p onljigedcccceedcba_][XUSPNKJIIHGH GF D D B A ? > < ; 9 7531/},{*x'v%u%u%u%u%u%u%u%u%u%u%s%G)^A%w%t%r [AbI`EcHfJjLkNmPrRtSuTuSuRuQvQvQwPwOwNwNwMxMwMwLwKvJvIuHuHtGsDrCqBn?k=j:h5g3i3g/g,f)d%d"cbaa`_^^^^_abeikllida_ }^!{]!y]!w]!w]!v]!w^!w]!y_ cjs!s#pW wYlQiO~_!rs!q ppnljgdb`_``babba_^[ZWURPNKJIIHHG GF D D B A ? > < : 8 75200},{*x'u%u%u%u%u%u%u%u%u%u%u%s%G)]@%w%s$u%hOnWkRlRnRnQoRoPqPqOqOrOsPsOtNtMuMuMuMuKuKuJvKvJvIuHtGtFtFsFrDrCqCqCpBpBpBq@s@t@t@v@w?w=x < ; 9 7521.}-{*x'u%u%u%u%u%u%u%u%u%u%u%s%G)^B%v%s$x*hPmUjRmSmRnQoQqQrQqPqPsPsPtPuOuNuNuMvMuMwMuKvKvKvJvIvIuHtGtGsErDrDqBoAoApApArAt@u@w@w@x>w>w=w < ; 9 7521/|,z*x'u%u%u%u%u%u%u%u%u%u%u%t%H*X:%u%s$x)iQnWlSmSnSoRpSqSrRrQsRtQtQvQvPvPvOvNvNwNxNxMwLwLwKvKvJvIuHuHuFsFsErDpCpBqBqAtBuAuAvAvAw@x>v=t;r9p9n6l5j5i3i2e0].sS+iM*lM$iIeFbDbEeGgIiLjMuZ'uIarptGzd3i:w`.u]'nTsVz\vXwXhP \DsSdnt v vtolgb][YXYZ[[\[\ZXVSROMJIIIIHGG E D C B @ ? > < ; 9 7521/|,z*x'v%u%u%u%u%u%u%u%u%u%u%t%E'F%%u%s#y, kSoXmUmToTqTqUrTrSsRsStSuRvQvQwPxPxPwPxOxNxNxMxMxLwKvKwJvIvIuHtFsFsErEqDqBrBtCuBvBvBvAw@u?u=s;o9m8k6i5i3d1\.tR*fJ'eH%eG!fH!fJ"jM#kN#hL!iM!gM"sY,z`0|`+c,k5MdmĶ˸ĻñVe!yYvW|[nR^EdG}Xjt v v snic_[XVVWXYYZYXWVSROMJIIIHHGF E D C B A ? > < : 8 7531/|,z*w'u%u%u%u%u%u%u%u%u%u%u%s%B!G&%t%r"}2oXpYoWpWqVpUqUsVsUuTuTuTvTwSwSwRxQxRxQyQyPyPyOxOxNyMxLxKwKwKwJvHuHtGsFrErDsCtCvDwCwCxBw@t?s=q;m9k7i5f3_/zT-iJ(cE$aC dE!iJ#kL#dG aG"jO*a2o7{;<:860}){$z!y {#B_Ƕb tTrRuUwVmO`EqMesv tpkd_ZWUTUUWWXXXVUSQNLJIIIGGFF E D C B @ ? > = : 8 6420.{,z*w'u%u%u%u%u%u%u%u%u%u%u%s%B!G&%s%r!~3pYqZoWpWqWrWrVtVuVvVvVwUwTwTxSxTyRxRyRyRzQ{QzPyPyOyNyNyMxLxKwKvJvIuHsGsFsEsEuEwEwCvCvCu@t>q=m:k9i8d4[.lM-cD&aA aB!jI#jJ#_C^D#uX0v=;;;:84.*'&z']r:uVsRiH kKuRsQfJjH`qtqmf_ZVTRRSUUVXVVTSQNLJIIHHGGF E D C B @ > = < : 86430.{*z)w'u%u%u%u%u%u%u%u%u%u%u%s%B!H'%s%r!~4q[r[pXqXrWsXuYuXuWvWwWxWxVwUyTyUySzTzSzS{S{R{R{R{PzOzOzNyMxMxLxKvJvJuIsGtEtEwGwFvDwDvCtAr?o=l:h8c4X/eG,`A"`A!fE#kJ$bD"]B"zZ2|?ɎDɏCƐDÏB@>=;;:;:<=>=”7‘1r,y_-uXlQ{TǑwKfHoLtOkLhGZmqmg`[VSPQQRTUUUTSRQNLIIIIHGGF E D C B @ > = ; : 86420.|+z(v%u%u%u%u%u%u%u%u%u%u%u%t%B!E$%r%r <t_r\pYqYrXtYuYuYvXwWwWxXyWyWyVzV{U{U{U{T{S{S{R{R{R{QzO{OyNzNyMxKwKvJvJuItGuFvFwFwEvDtBs@p>n!aA!hF$eE#^A"oO+r=ÊDɎEǍDǏCBA@=<:99:;<>?Ö@řAǙ?i4hMqQaGpU!jp4lNhFsMoNhEVilhaZVSPOOPRSTTSRQQNKIIIIHG GF D D C B @ > = ; : 8641/~.|+z(v%u$u%u%u%u%u%u%u%u%u%u%t%B!2 %q%q =v`s\r[s[tZtYuYvYwZxZxYyYzX{X{W{W{V{V{V|V|U|T}T}S|S|R|Q{QzOzOzNyMxLwKvKvJvIwHvHwGxFuDtCqAo>k;d7U1bC)[= dB#hE%^@!_D&a5}AƉDƋEȎFǍEčDA@?=<:::<<>?ĖAƚAȚBǙ@mV+iO!lR"^H!|]'r't q*kKdAsMuS$gERega[VRPNMNPRRSSRQPNKIIIIHG F F D C B A @ ? = ; : 8641/}-|+z(v%u%u%u%u%u%u%u%u%u%u%u%t%B!0 %q%q >xbt^s\t\u\v[w[w[x[y[z[{[zZ{Y|X|Y|X}W|W~W}W~V}U}T}T}S}S|R|Q{P{O{OzNxMwLvKvJvIxJxHvFtEsCpAm>e9W2aC)[ = ; 9 7632/~-{+y(v&u%u%u%u%u%u%u%u%u%u%u%t%@0 %r%pI-~iv`v_v^v]w]x^z^{^z\{\|\}[}\}[}[~Z~Z~ZY~XWWVV~T~T}S~S}S}S|Q{PzOzOxNxLwKwJwIvHtFqCl@_8bE-[z:w9m4`.lT,bN+jS-t6AəE͜FΝFОFw;_HkP"^FmT)o/|3|3~446q2cgdBnFsJgCjAUXUQMKKJLNOOPQOOLJIIHHHG G F D C B A ? = < ; 9 7531/},{+y(v&u%u%u%u%u%u%u%u%u%u%u%u%;0 %r%pJ-~kwaw`w`w_x_y`z_{_{^}]}]}]}]\\\\[ZYYXWWWV~TT}S}S}S|R|Q{PzOxNyMxKwIuHsFoCe=kJ0Y:"dA&gD'X;!`C+b9s>yBEGňHƋIƌGċGFB@{=r7c2mS-]H'XC!S?VA w=”D˚FϝGОGϞEd3cK!jO"\G uY*s1}5}5~4567c,me>gDqIqH_=tESTOMKIJKLNOPPONLJIIIHH GG E D C B A ? = < ; 8 7531/},z)x'v%u%u%u%u%u%u%u%u%u%u%u%u%;د)%r%qQ6jwbwaw`yaya{az`{`}`}_~_~^~^^]]]\[[ZZYXWWVUT~T~S~S}S|R{QzOyMyLvKuIrEkA|S6[<%c@&jF)[<#\@'\7mp8y[2`J(ZD"[C S>SA#{`3CȘF͜IϝHϝGǗDrY,hN#eM"]G#`,w2}5~56777~7sS'}^lHkHvMnJ ]9~IQOKJIIJLNOOONNLJIIIHHFF E D B B @ @ > < ; 8 6530.},{)x'u%u%u%u%u%u%u%u%u%u%u%u%u%;ү'%s%r V;kxcxcxayaya{a}a}b}a~`~`___^^]]]\[ZZZYXWWUUT~S}S|R|R|QyNxLwKvIpE`<^@*]<$kF*`?&V;%wQ2g;n=vC|EFćIʼnInjJƋIGFAq9sW3\D$\C!_G"ZD"kT/u;B’EɘH͛HНHϝGCiQ&jP$bI aJ%e0y4|5}6878ˆ9Ƌ:d.kK#`6lHrKvLbD#h?OMJJHIJKLNONNNLIIIIHGGF E D B B @ @ > < : 8 6421.|,{)x'v%u%u%u%u%u%u%u%u%u%u%u%u%;ӯ'%s%r V:lzeyeybzc{b}b|b~b~bba```__^^]^\\[[[YXXWVUT~S}S}S{RzQxMvKsIkCoK3Y:#iD)gC)X;#fG-`9ivW2\D$^E#bH$_G&z]4{=@C’FǘG͚IϛHМHAeM%lQ$^G eM'j2z7|7~6899È;Nj:}9gGqM!uNoIuLrL"X;yLQKGHGJJLMNNNMKIIIIIGGF E D B B @ > > < : 8 7430.|,z)x(u%u%u%u%u%u%u%u%u%u%u%u%u%;ӯ'%s$s!\Cm{fze{d{d|d}c}cbcbbb`aaa`^^^^]\\[[ZXXWWVT~T}S{RzQyOvLrH\=V:&dA)lG,\=%Z='|U6e;j?qBwD~GJÈKƊKƋKĊKG{B{[4ZC%`F$bG$]F'~`5w<}>?C‘FȖGʚI̚IΜIv;cL$kP%]E!mS*p3|8|7~7:::ĉ<Ƌ;Ɗ;tS)iGyQ"qKsKtJ`B"gAQ S NHGIKLLNMNMKJIIIHG GF E D B B @ ? < < : 8 6430.|,z)x&u$u%u%u%u%u%u%u%u%u%u%u%u%;Ԯ'z%s$t"cKm}h|g|e|e}f~eeddcddccbbb`__^_^^]\[[ƒZY‚XÁW€VÀVU|R{QxPvMlFcD0\<%lG,eB)Y:$dH1]8f'Z>)xR4a9g=j?qCyFIKĉNʼnMĊLˆJpB^D'_E$dH&YB%vX3n9t;u;z=@CEÒGƖHʙIGz^1fN%aI"^F"|_1v7}9}9:;;=ŋ=Ŋ<Ɖ;t7aBuO"|R"qJsJqI[=pFQ!Q QS SOLKMMML JIIHIGGG F E C B A @ ? = ; : 7 6410~-|+y(w&u$u%u%u%u%u%u%u%u%u%u%u%u%9ݹ&z%s$t$jQo~j}i}h~hhgggggefeeedccbbb`a`__^…]]„[Ä\ÃZÂYÁXÀW~V{RxPnJcC.^=(nH/iE-]='`D0X6bkAqDyG~JLŊOŊOŋNIkN/^C%hK)^D#dL-g7o:r;u}?CEGĒHƔHEnU,fM%^F!^G$e5y7|9~;;;<‰>‰=Ĉ=Æ;y8aD!qK!U$vM!qIrJ `? g@O!P!R!R T"V"X!RLMLL IIIIIH GG E D C B A ? > = ; 9 8 632/}-z+y(w&u$u%u%u%u%u%u%u%u%u%u%u%u%4Ѻܹ&u%s#u%q[nkk~iiihhggggfefeeddccbba``_†_^…^Ä]Ä\Ä[ÃZÂYX~V|TxQ\A[:'gC+rJ1dB*]?)kJ3[9c=f?kBqDxG~JMŊOŊPċPf>[A%fI)eI(\D(vX2l9p;q|?ADFH’HAcK)gM%\E!eM)i4z9{9~;;=>>‰>=<|9aE%oJ!U%zP"rIrIcAb>~M Q"R!S!U"V#X#Z$VOKJ IIIIHH G G E D C B A ? = < ; 9 8 632/~-z+y(w&u$u%u%u%u%u%u%u%u%u%u%u%u%4ѻܹ&s%s#u%r[pmlkjjjjiihhhgggfeeeddcbcba†`†_†_Å^Å]Å\Ą[Ą[ÁY~W{UrOnJ5]>)pJ0qJ1bB*]A.wP3^;d>f@kBpExHKOÉPŋQLlO0_D&mN+cG(aH+_5o;p==<;|9aF&mH W'T%sK rIgC ^={K Q"R"T"V#W#Z$Z#\%[#TLH IHIHG F F E D B B A ? = ; : 9 7532.},z+y(v&u%u%u%u%u%u%u%u%u%u%u%u%u%4λݺ&t%s#v&v`pmnmlkkkkkjiiiihgfffeedddcˆb‡a‡`‡`Æ_Ć_Ć^ą]ń\ÁZY|VhH^?,eC-uN4mH0bA*_C0~T6_g@lBpDxILNÉQÉPoC\B(jK+nO,dH(hM.c8p==;}9w6`E&mI X(V&vL!rI kF!^=yJQ#S#U#V$Y%Z%[%\$\%\$XOIIHHG GF E D B B @ ? > ; : 9 7531/},{)x(v&u%u%u%u%u%u%u%u%u%u%u%u%u%4λ۴&s%s"w(mqonnmllklllkjjijhgggffgfc‰ddÉcˆb‡`ć`ć`Ć_Ň_Ņ^Â[Y{VY@Z<)mH1xP4jF/dC,aE0V8`q=o=p==<~;z:p6_C$oJ"Y(Y)yN#rI!mF![<wH!S$S$U$W%Z&\']']&\%\#Z$Y$UJG GHGF D D B B @ > > < : 9 7420.}+{)x'u$u%u%u%u%u%u%u%u%u%u%u%u%u%4λϜ&s%s"w(nrqpnmnmlmlmllljkjihhgggfe‰d‰dÊdĉdĉcĈaćaŇ`ć_ņ_Ą][xTmL7_>*vN6wP5jG0dE/eF0Y:`=b>f@jCpFvI}MP‡QMcI/eH*vT0oO.nP.lQ1b8p?q?o=p=p < ; : 8 7430.|,z*x&u$u%u%u%u%u%u%u%u%u%u%u%u%u%4λΚ&s%s"x* osrrqoooommnmmmllkjiiihhggËfËeÊdÊdĊdÉcĈbĈaŇ`Ň`ą_\rR[@/fE/}S9vN6kG1eF1hH1Z;a?b?eAjCoEuI|LOQvIZA)mN.{W4qR.rT1lP1`7p?q?o>nw?y?r yJ!T%X'Y([(](^(_(_)_(]&\&[%Z$Z$[%SIF F D C B A @ ? = ; 9 8 6430-|+z*w&u$u%u%u%u%u%u%u%u%u%u%u%u%u%4μϛ&s%s"~2qsssrqpopooonmmmmlljkjj‹iŒhŒhÌgËgÌfċeĊdŊdŊdʼncƈbƈaņ`]fKY=,oJ4V;vN6lI2fH3kI2Z;`@a?dAiCnEtIzMORjDZ@&uS2~Z5tT2xW3mR2|[5p?sAp>n=m = ; 9 8642/.{+z)w&u%u%u%u%u%u%u%u%u%u%u%u%u%u%1ǵϛ&s%s!5sutssqqqqppqpoonmmmllkkjjŒiÌhČhČgČgŋeŋeŋdŊdƉcƉbņ`\|YC\=+vN8WsAq?o>n=m < : 9 7631/~-|*y(v%u%u%u%u%u%u%u%u%u%u%u%u%u%u%|, М&s%s!5twvutrrrrqqqqppoonnnmllkÎkÎjÍičiōhčhŌgŌfŋeƊdƋdƉcňb^cG7dC0}Tn=l=l=n=n!xwwwusrsssrrqqqqononnonŽlÎkÏkÎkĎičhčhōhƌgƌfnjfƊdƋdƉd~]\A2jG3W?[@yR9rM5kL7fG1Zl=l=m < : 9 7531/}-{*y(v%u%u%u%u%u%u%u%u%u%u%u%u%u%u%|, zg&q%r!B%zyxwwwtttssssqrrqppooooÏnÏmÏlďlďlŏlƎjƎiƍhƎgƌgnjfȌfȋduWZ?.oL7[A\B{S;uP8oO9cD1Yc=^9em=l=lm>m=w[9eL*qT.cI(`J.`5h9j9i8h7i8h7g7f5b4|X0eI+rO)\0h5m8k6c1|R)lF#eB#xO*].c1d1f2g2g1h1g1e0c0b.a.`-_-_,^*[)[(Z(X'V%U$T#S#L@:9 8 7420.},z)x'u$u%u%u%u%u%u%u%u%u%u%u%u%u%u%{, {i&q%rK/~|{zzxwwvwuvutuvtsrsrqÑqÑpđpŐoőoƐnŏmƐmƏlǏkǏjƎiȎiȎhˎheNZ?-wR<_EaFX?zS;yT;^C1yT:`BbCcCeCgDkGpJvL}P`A_E*\:h@jAf>ekT4nR.tV0eL+fM/c6i:i:h8h8g7g7e7b4~Z0cJ.rO*X.f6o9o:j7_1xN(hC#fD%U-b1e4h3h4h4i4i3g2f0d0c0b0a.`.^-^,]+[)Z(Y(X&V%U$Q"Q"NB98 643/.{,z)x'u$u%u%u%u%u%u%u%u%u%u%u%u%u%u%{, |j&q%rO4}||{zyxwwwwuuuvuusrsÒrÑqÑqđpđpŒoƑoƒoƑnƐmǐlǏkȏkȎjȎiːj^H^A0{V?aGcG\C{U=~W>_E4tP8_AcDcCeDfDjGnHsKzOhG_E,zW7g@lCmDg?j@pClEbK1sU1k>qBrBpAn?fP3yY2vW1eM.oS0d8i:g9g8g7h8d6a4}Y0dK.rQ,}W.d5m9r=o:i7[/rJ'eB"kH*]/f4i5i4j6k6k5j5i3f1e1c0b0b0a._-^-^,]+Z*Z(X'V&U%S$Q#P"N!B6642/-{+z)x'u$u%u%u%u%u%u%u%u%u%u%u%u%u%u%z, s_&q%rO4~}||{zyyxxxwvvvvvu’ut’sÒrĒrĒqĒqƒqőpǒpƑoǒnǑmǐlȑlȐkȏj̐kvXDaD1~W@cHeJ^D~X?Y?iM:hH3^AcDbCdDfEiGmHqJwMrK`G1uU6gClDpFmDh?oDuHvUcL2pR0fo:g7X.mH&cA#yQ/c3i6l8l8l7m7l7k6j5g3e2d1c1b0b0`0_-_.^,\+[*X(W'U&T%R$P#N"M"C631/-{*y(w'u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%{+ \E&r%qW=~}}|{{{zyyyxxwvvv“v“u“tĒsĒrŔtƓrœqƒqƒqǓpȒoȒoȒnǐlȐlɐl͒moSBaD2~XAcIfKbHYAZAwV@\@/Z?cEbDcDeEhFkHoItMwMnR9lM0oh@oEsIoEkAqETbhR9cI+{[5i=an:c4yQ+fC$bB'\2j7l9n9n:n9o9n8k6j6h5f3d2d1d1c0b0`0`.^-\,[*Y)X(U'T&S$Q$O#M#K"C6 1/},{*y(v&u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%y)ɝ^G&r%q^D~~|{||{zyyyywww“wÓwĔvĔvŔtŔtƔtƔsƔsǔqǔqȓqȒpɓpɒnȒnɑl͓noSBaE2ZCdIgLgL\C\C\CXA1xT:cEdEcEfEgEjGnIrLwNbB_B'ɽuSmDsIwLpFmDsFUsfI[E,dJ+jR5_8f<|^6iO2uV2a8_6{Z2nP-`H,gN1yX4\4_4e8p>xD{Eu@q>k:^2qK)_>"qN/i6o;p;p;qs>s>s>q;p;p;n;j7h6g6g5g5e4d3c2a1a1_/^/]-[,Y+X*V(T'R%P$M#L#K"G!C3 |*{*x(u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%x)^G&r%q bI~}~}}}|{{{•z•zÖyÕyĕxŕwŕwƖvǖwǕuǕtȕtȖtɕtȔrɔrɔqɓpʓpЖr{\I`E3ZDeKiOkOfK_EbF{ZCX?.Z@cFdFdFeFhGiHmJrLuNwY=oS:tkBtIyMSyMrHuHzJyJrEkAmBoAh?w[;sY9`>g@n@l>i;g"pP2oq=m;k:j9i9i9h7f6f5d4d3c2`2_1]/]/[-Y,W+V)T(Q'P'M$K#I"F E B2 w&u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%u%x)›S6|%s%s"iR~~~}—|×}ė|ė{ėzŗyƗyƗxƗxȗxȗxȗwɗuɗuɖuʖuʕsʕrʔqΗspY[B2yXBeLjOnRnReKcIgJtVA^C1]CeHeGeGfGhHjHmJpLvO|]At\EôoJrIxL}PTWWWUYe_yItEsEtDsCuDzINRSNK~H{FwDsAj<\4jH)[?%[7vAzDzEzDyCxCvAu@s?q?na8pL,\?$oM1rC}G}G|E{EzEyEwCuAs@q?o>nn>l>kmO=aJhPnSrWtXnRgLjObJ[C3qR;cHgIgIhIhIiIjImKqMuOcEp]rLrIxM~PRTUVVVVTTRRQOM}KzHvEsDmAb:wR0_A&^B)gBKƒKJ€K~J~H|HzExEvDtCqAo?n?n?m>l=k=>~3u%u%u%u%u%u%u%u%u%u%u%u%u%w%oS5w%s$w'ydž˜ÙÚęĚŚŚƚǚǚ~ț~Ț}ɚ}ʚ}ʛ|ʚzʙz˙yʙy˙x˙w̘v˘u͗uaNdJ9_HgOmSrWvZtXkPiNlPpS@\C1\CfIgIhIhJiIjJlKnLsNvP`ArпzWpFyN}ORTUVUTSRPON~M|JxIuFpCl@a;tR1]A&[A)iC…OŅNĄNăMÁL€K~J|IzHxFwEtDrAqAoAn@n?l>k>k=>}2u%u%u%u%u%u%u%u%u%u%u%u%w%pH(s%t#y, pŸššÛĚŚśƛƚƛǜǜɜɜ~ɛ}ɛ}ʛ}ʜ|ʛ{˚z˚z˙y̙x̙x̙w̘vv]bH8}]IgPmSpVwZx\qUiOnQiO[D4fJ6aFgJhJhJiJiJkJnKpLsNvQdEyhݵlAwL|PQRSRQQP~O|MxJwItGqEoCi@]9qP0[@&_E.pGÆQƇPƆOƆOŅOăOÁL~J|I{IyGwFuDrBqBpAoAoAn?l?k>k=j=|1u#u%u%u%u%u%u%u%u%u%u%w%s^D$r%t#y,qŸššÛěśśƛƜƛǜȜɜɜʜɛ~˛}ʛ}˜}˜|˛{˚z̚y̙x̙x̙wk`J;}bPiRjRpVtZy^y\mRlQqSbKS=-tT=eIhJiJiJiJjJlJnKpLsOxRmLmXϥ\wK{O|O|P|P|O{OxKvJsHrGpEmCiAbtgPoVtZx\|`vYnSpSsTmTBZA/}[BhJiKiKiKjJlKlJnMqNsOySuO~eMquLwNW\[X~W|WvOlFg@ak=j=hi=gh=g;f;d9a8_7^6]6[4Y2W2T/R/P-M,J)H(E&E'D&D&B$B$A"A"@!>z, t$u%u%u%u%u%u%w%lV֤>s%s#|1ţġáàߟžĞĞŞşƞǟǟǟȟɟɟʟʟ˟ʟ˟̞̟̞͞͞͞~͝}͝}Μ|Ν|əx~`MhNgg=e;d;b:_9^8[5Z4X3U2T2Q0N-L+J*H*H*F'F(E'D&C%C%B$B#B$=v(u$u%u%u%u%w&\D̛:s%t"? ƥţţġááààžžÞßğşşşǠǠǠȠɡɡʡʡʡ̡̠̠̠̠͟͟͟͠ΟΞΞΝ}Ϟ}əztYI{dUwcoUu[{_cfdvZsVwYy^eO?YB1{[ChLkMkMkLlMmMmMnMpNrNtOrYBcJ2kP6]F0lTf=d;c;a:_9]7[5Y4V3T2R1O/L.J+H+H*H*F(F(E'E'D&C%C$B#D%;u%u$u%u%u%w&\D̜:s%t"> ǦŤŤţŢġááàߞßĠŠŠƠǠǡȡɢɡɡʡʢʢˢ̢̢̡͢͡͡͡͡ΠΠΠΟΟΞ~ӡhcM>ƴmUqYw]~begdvZsWz\y_bM=[C2}]DhLkMkMlNmMmMnMnMoMqNbM:eL4fM4[G3tX=oKwQ}TVY\_ǒa˕bΗdϗcΕcΔa͓`̒_ː^ʏ]ʎ[ɍ[ȋYljXŇWćVąUÄUSR~P|O{NyMxMwKwKvJvJsIsHrFpEpEnCmCkAj@i@f>e=cd>b=_;^9\8Z7X5U4S2P1N0L.K.J-I,I,H+H*G)F(E'E'D&D&C%B$5u%u%u%w&\Eɔ9s%s"K/ţȨǦƦƥŤŤŤţġáġšġŢƢǣǣȣɤȤɣʣˤˣˣˣ̣ͣͤ͢͢΢΢ϢϣϡϡϠϠϠРПpjTFúmWqYx^cfijcuXvZ}^}gZF7`G4`FjLkNmNmNmNmNnOcH]G4kR8_G1^J7bDqNwR{T~V[]`ƒbʕc˕d͖dΗe͔d͔a̒`ˑ`ː^Ɏ_ɍ\Nj\Ɗ[ʼnYŇXąWÄV‚USS~R|PzOzOyOxNwLvJuJuItIrHqGpFnEnDkCiAh@f?d?b>b<_;\9Z7X7V5S3Q1N0M0L/K.K.I,I,H+H*G)G)F(D&D&C%C%C$|/t$u%v%Q5ͺ4s%t"K.ŤɩȧȧƦƥƥŤŤţġġšŢǢǢǣǣȣɤʤʤˤˤˤ̤̤ͤͣͣͤΣϤΣϣϣϣТϡСРРПϞkWtymUt[{`dhikbuYwZezeXE5`H5`FjMlNmOlNmNmNt[DcK4mR:]F2bL8eFqOvRzTV[]aĐcȔc˕e͕e͕dΕc͔c̓b̒aʐ`ʏ`ɍ^ȍ]NJ\ƉZňYćXÄW‚VTS~S}R{Q{PzOyOxNwNvKvJtJsIrHpHoFmDlDjBiBg@e?c>b=`<^;[9Y7W6U5R4O0M1M0M0L/K.K.I,H,H+H*G)F(E'D&D&E'B$x*u%u%N2̹4s%t"Q5ȨʪȨȨȧǧƦƥƥŤŤŢţƣǤǣȤǣȤʥʤʥ˥ˤ˥̥̥ͥͥΣΤΤϤϤϤϤУУТТРСРРvpVFs^oWw]}bfhklcvYwYj|hZG8]F4}^EiMlOmOmNmNjT?jP8nS;]G3hP:gHqNtPyT~V[]`aƒc˔e̔e̔d̔c̔c͓c̓bʑbʏ`Ɏ_ȍ^Nj]Ɖ[ňZćYĆYƒWUSS}S|S|R{QzPyOxNwMvLtJsIrIqIpHoFlEkCiChBf@c?b>`=^<]:Y8X8T5S4Q3N2N1N1M0L/L/K.J-H,H+H+H*G)F(E'E'G)8t#u%N2κ4t%s#Y?ʭʬʪɩȨȨȧƦƦƥƥŤƤǤǤȤȥɥȥʥʦʥ˦˦̦ͦͦͦͦΥΥΤϥϥϤФФФѤУѣѢѢСРycwaTŏjTs[z`egilmcvZxYhq]I:[D2|\DiMlOmOkNfP=sV>lR:^I5pV>hJpOsPwSzVY\^`Ñcǒdʔf˕e̓d͕e̓d̒cʑcʏaɎ_ȍ^nj^Ɗ]ʼn[Ň[ÅYƒXWU~T~T}S}S|S{QzPyPxNwMuLtKrJrJpIoHmFlEiDiBgBeAb>`=_<\;Z:Y9V7S5Q3P4O3O2N1N1M0L/L/J-J-H+H+H+H*F(E'G*9t#u%O3ͺ4t%s#Y=ʭʭʫʫɩȨȨȨǦƦƥƥƤǥȦǥɦɦɦ˦ʦ˦˦̧̦ͦͧͦΦΦΦϥϦХЦХХФѤФѣУѢТѡ̝~bOiQv]|cfikmmdvZwZbt^J:ZD2|]EhLmOgLjR=y\ClQ:_I6vYAkLoOqQuSyT|W[]_aĐcȓdʔf̔e˒d̔e˓dʑcʏbɏaȍ`Nj^Ɗ]ʼn\Ĉ[ąZ„XWVVU~T}S|S|SzRzPxOxOuMuLsKrJqIoImGmFjEhCgCfBdAb?_=];[;Y:W8U6R5Q5P4O3O3N2N1M0L/L/K.J.I,H+H+H*G)H*<t$u%N1л5t%s"]C̮˭ʬʬʬʪɩɩȨǧǦƦƥǦȦȦʧɦʧ˧˧˧˧̧ͧͨͨϧΧϧϦϧЧϦЦЦЦѥХѥѤѣѣѣѢԤwiQBxqYw^~dikmmncw[y\ds]J:ZD3z\DhM|aIqV?aFlR;bL9z]DjMnOqPrQuSzV~W[^`bÐdǓdʓe˓fʒe̓eʑdɐcȏaȎaǍ`Ƌ^ʼn]Ĉ]ć\„ZXXWWU}T}T|S{SzRzPxPwNuMtLtLrKpIoHmGlFjFhDfBdBb@`>^=[u%u%@!嵩0t%s#hOγ˭˭ʬʬʫɪɩȨȨȧǦǦȦȧȧʧʧʧ˧˨˨̨̨ͧͨΨΩΨϨϧϧЧШЧЧѦѦѦѥѥѤѣѣѤѢצkt_R͘xbs[zaeimnoncx[y[kraM=VB1vYBrXDw[CbGjQ;gP<aGkNnOoOqPtRwTzVY\_bdŐdɒeɒfʒf̓fʒeʐcɏbȎaȌaƋ`ʼn_ň]Æ\„[ZYXWW~U~U}S|S{R{RyQxOuNuMtMsLqKoImHlGjFiEfCdBcAa@_?\=Y;X:U8T8S7S7R6P4P4O3O3N2M0M0L/L/K.J-I,H+J-? u$u%=ᯢ. t%s"gNеͯˮ˭˭˭ʬʫʪɩȨȩȨȧȦȧʩ˩˩ʩ̨̨ͨͩͩΨΨΩϩϩЩЩШѧѨѨѧѦѦҦҦҦҥҥҤңңҢu`~¹kTu]{bglnponex\xZlz^iR@T@1eO=cJaHkS=kS?fJnOmOmOoPrQtRxT|XZ^_adőfɓgɒfʑfʒfʑeɏcȎbǍbNjaƋaʼn_Ç]…\\ZZXXWW~V}T|T{SyRyQwPvNtMsMrLpKoJlGkGiFgEgDcBaA^?]?[=Y;U9T9T8T8T8R6Q5P4P4O3O2N2M0M0L/L/K.J-K.? u$u%>ᰣ. t%t#iQѶͰͰ̮˭˭˭˭ʫʫɪɩȨȨʩɨʩ˩˩̪̩ͪͪͩΪΩϪϩϩЪϩЩЩѨѩѨѨҨѧѧҧҦҦҥҥҤңҤȜfTıfNw_}chloqpnfz]z\`uXjTBgQ?gM}`GoU?mUAhLmOmOnOoOpPqQtSxU|Y[]adÏeǑfǑgȑf˒fʑfɐeɎdǍbƌbƋaĉ_Ĉ_†^\\[ZZXW~W~V|U{TzRxRxQwPuNtNrMpLpKmIkHjGhFfFdCbB_A]@\>Y߰. t%u%ycҷααͰ̯̮˭˭˭ʬʫʫɪɩʪʪ˪˪̪̪ͫͫΫͪΪϪϫϫϪϪЪЩѪѪѪҩҩҨҩҨҧҧҧӦҦҦӤӤԤzzaPlUy`dimprpog{^y\x[nShOkP}`GpWBqYCkNmPnPmPnPoQpPrRvTyW}Z\`bdÎeƐfɒhȑgʑeɑeȏeȎdƍcŋ`ʼn`Ĉ`†_…^]\[[ZYW~W}W|V|TySxRwQvPuPsNqMoLnKmIkHhGgFeEcC`B_A\?Y=X=X=W߰. t%u$ycҹϲβͰͰ̯̯˭˭˭ʬʬʫʪʩʪʫ˪̪̫ͫͫͫάϬϫϫϫЫЫЪѪѪѪѪҪѪҩҩҩҨҨҦҧҦӦҦӤӥզlwaSrZy`fjnpqqqj~ay\vYtXlQcJoWCqWAjOmPmPmPmOnPoPpQrRvV{Y[^abeďeǑgȑhʒgɑfȏeȎeǍdŋbĉaÉ`‡_†_^^][ZZYW}W}V|V{UzSwQvQuPsPrOpLoLmKlIjIgGeFdDbC_B]@Z?X>X=WY>X=WX=X=V;U:U:T9T9T7T8R6Q5P4R6J,v'u$z/ ͗|)u%v' xսѷѶѶдϳϳϳϲαͰͰ̯˭̮̭ͮͮήίϯϮϮЯЯЮЮѮѮҮүҮҭҭӮӭӭӬӬӬԫԫӪӪӪԪԩӨӨӧԧԦԦ̠hVûוt^v_~ejnqsrsrodvZz]}anXGQ>0qVBhNlRlQmRmQlPnRqTsUwX|Z^adghŏhȐjǐiȐiƎgƎgŌfŋeĊdÊdcˆc```^^][[~Z~Y|X{WyUxTvSuSsQqPpNnNmMkLgIfHdGaF_E^D]C\B\B[A[AZ@Y>Y>X=X=V;U:U:T9T8S7S7R6R6M0w(t$x, ǿ̘z)u%w) սҸѷѷѶегϳϳϳβΰͰͰͯͯͮͯίϯϯЯЯЯЯЯѯѯүҮҮүӯҮӮӮӮԮӬӭԬԬԫԫԪԪԪԩԩԨԧԧԧ֨ȞfSŘycx`fjnrssrsn~bx]}`ihTCTA1uZEhOlQmQmQmQmQoSqUtVyY~\acfgÏiƐjǐjǏhǏhƎhƌgŌgċeÊd‰d‰dcb`__^]]\~Y}Y{XzVxVwTuTtSrQpOnNlNkMiLgJdHbF`E_E_E^D\B\B\B[AZ@Z?Y>Y>X=WY=WX=X=WX=X=WX=X<\Bz- t#z&}jw%t$5Ѷú־־ֽռռԻԺӹҸҸҸѷѶѵдгддеѵҴҴҵӴӴӴӴӴԴԴԳԳճԳճղղղֱֱֲֲկհ֭֭֭֯֯֯խ֭֫ժժժթթثwshξzbkosuvvvvp~bx]z_lVUC6pVCjRnUmTmTmSpVrWtYy]~`dgilÐnƑoƑoǒoƑnŐlĎkĎlÎjŒiihgfedcca__}]|\{\z[yZvXuWsVqToRmRjPhNgNfNfLeLdKdKcJbIaHaH`F`F_E^D]C\B\B[A[AZ@Y?Y=]Bz- t#y&nYw&s"?ӻĺ׿׿־־ֽռռԺԺӹҸҸѷѷѶѶѵѵѵҶӵҵӵӵӵӵԵԴԵԴյմմմմֱֲֲֲֲֲֳֳױְְְ֭֭֭֮֮֯լի֪֪ժթ֫{ui˻|cmpsvxwvulz`y_y`v_NXE6bMmUoVmTmUoUqWrXx\{^chilÏnÑoƒpǒpƒpƑmďmďkÎkjŒiiggfeddaa`_}]|]z[y[wZuYsXqUpUmTlRiPhPgOfNfNeLeLeLdKcJcJaHaG`F`F_E]C]C\B\B[A[AZ?^C{. t#y&mXw&s">ԼŻ׿־־־־ռռԻԺӹҸҸҸѷѶѶѶѵҶҶӶӵҵԶԶնԵԵյմմմմֲֳֳֳִִײֱֲֲײְװְ֪֭֭֭֮֮֯֬֫֫ժթժzzm}hnquwxwusgx^y_u[`M@iQ?hRoWnVmUnVoVsYvZ{^bfiloÑpƒpǓqǒpƑoŐnďmĎlÎkjjihfeedcb``~_}^z[y[wZvYtXrWpUnUlRkRiQhPhPfNfNeMeLdKcJcJbIaI`G`F`F`F_E]C\B\B[AZ@_E{. t#y&mXw&s"C$׿Ƽ׿׿־־־ֽռԻԻԺӹҸҸѷҷҷҷҷҶӷԷԷԶԷննԶնԵնֳִִִִֵֵֵֶײ׳ײײײְװװװׯ֮׮֭֭֭֬֫֫ժթթrm^òzblpruwxwup}cx`z`hT\I;|_KlUoWnVnVoWrXtYy^}aeiloĒrƒqǔrƓqŒpŒođnĐmmlkjigffeedba`~_|^y\y[wZuYsXqWpVmTkSkSjRiQhPgOfNfNeMdKdKcJcJcJaH`G`G`F^D^D]C\B\B`F}2s"y&mXw&r Q5ŻƼùù־־־սսԻԻԻӹҸҸҸӸҸӸӷӷԷԷԸԷշշնշֶնִִֵֵֶֶֶֶ״״ײ׳ײ׳ײְױװׯװ׮׮֭֭֮֬֫֫ժԨӨ}iyaRnv_~flpruwwwtky`x`u]iTDiQ@iSoXoWnVoWqWsZv]}bfikoÑqƓsŒrǔrŒqőpőpĐnÏnmkkjihgffddba~`|_{_y\w[uZtXrWpVmUlTkSkSkSjRiQgOgOgNfMfMdKcJcJcJaHaH`F`F`F_E^D]C`G6s"y'{nnYw&r Q4ƽǾĺù¸¸׿־־־ֽֽԻԻԻӸҸӸԹӹԹԹԸոԸոոոշֶַַַַ׷׶׷׶׶׵׵׵״״׳ײ׳ײײױױװװׯ׮׮׭֭֭֮֬Өҧ{ioXIm[yhhqrttvvreu^w_fS_J<~bMoXpYoWpXpXsZv]{aehloqŔsƔsȔtƔrœqŒpđpÐooŽnljjjigfeddcb|`{^z^x]uZsYrXpWnVmVmUlUkSjRkSiQhPgOgOfNfMeLdKdKcJbIaHaH`F`F`E^DaH6s"z'|ocLv&r R6ȾȾżĺĺù¸¸׿־־־ռռԻԺԺӹԹԹԺԹչչչչֹֹոոָָ׸ַ׷׷׷׷ط׶׵صصص״׳ײ׳׳زײױױװׯׯ׮׮׮֭ԫԩϤhYiTEƻiorrsuukw_u^t]gRClTDkVqYoXnWpYrZu]yaehlprŔtƓtȖuƔsƔsŒrđqÑpoonlkjjigfeedb}b|_z_y^w\t[sYqXoWnWnWmVmTkSkSkSjRiQhPgOgOfMeLeLdKdKbIbIaH`G`F_EcJ8s"z'|o\Cv&pbIǾƽżŻĺùù¸׿׿־־ֽռջԻԻԺԺԺպպֺֺֺֺֺֹֺ׹׸׹׹׹׸׸ظظطضضضصصصسشسسسززذװװׯׯ׮׮ԬҪÞt^PmWHžÿҨpkoqrsq~fu^v_fS_JfL>fL>hM@gK?iM?iL?iL?kMAkNAlMAnOApQBpPBqQDrQCrQBqPBoN?oN?nN>nM>nM>nL=lJ1]=0Z;.W;/M-89j5&y1u$u&жȿǾǾǾƽżŻĺĺĹĹĸ÷öööõõ³ۿۿܾ۽۾۽ۼۼۼۼۻںںڹڸڸڸ׵tgfUJl[OiWLp_Uyiwvtsvy~ßq\Orwdp_~fV}eVt^QwaQxek~i~i~jmrv{u}hp\y`NfPAVB8L;0H7.F6-G7.I8/I8.K:/K:0M<1N;0N;0O<2O;2Q<2P<2R=3S=2S=3T?4U>4U>4X@6Y@6Y@6[A6\B7[A7^C7^C8^C7]C7[@5[@5[@4[?4Z?4Z?3Z>2W=1W<0W;0U:/S8-Q7,P5+N4)K2'H0&G0%@'- +j.r(u#v(úȿȿǾǾƽƼĻźźźŹĸķ÷ĶĶõô³³²³±ܾܾܾܿܽܽۼۼۼۻۻڻںڹڸԴ~mahXMm[OkZOo`Uwhwwttux|ʥuhTHɻs`r`fX|cTwaSq\MxdkkkknsxzqycfTnVHVD8>0'/#'%&)*,!0#1&3'7*#:,%;,%<-&<-'>/'?/'?/'A1)C1)B1)D1*C1)E2*H4,I4,I4,J6-J5,J5,J5,J5,J4+I3*I3*I4*I4*I3)H1(G0'C.%A-$@,#=)!;( 7%6#1 .* f2r)u#v(ȿǾƽƽƼŻŻƻƺŹŸĸĸķĶõõô²³²²ܾܾܿܽܽۼܼܼۻۻںںڹԴve[kZOn]Rn]Qo`Uteyxtttx{ġ˧weXzoޜvqafWycTycUmZLvdmmllpsyyns_zaP]H9~v}zurnja\X]WRSLHKE@=6193.,$ ,#*!,#)'*'(*+- - . 1#3$3$4%4%5%4$2#2#2"1"1"2"0 / 0!. 0"0".!2$7-(:1-@83HB@KEBRNI[[W~_Tûu(t#x)ȿǾǾǽƼǻƼƺƺŹƹŸķŶĶĶĵó´ó²±ܾܼܼܼܿܿܽܽۼۻڻԵm^Um[Qp^Sp_SrbWpbxyuttv{ ˦gVLûsb~gXwaSycTkYLtbnnmpqvzxnp^w^OT@3|{rnjkeae`\XRMKD@JC?C;8:2.=40;2.C;6JB=H@;G>:E<8D<7D<7KD?MEAJC?QJFVQM[VRYTPd`]jhenljomkrrorrostqrsqnnljmle^Y϶y&t#z- ȿȿȾȽǽǽƻƻǺƺƹƹŸŷŷĶŵõôóó²±ܾܼܿܽܽܽۼۼշgYPo^Sq`Uq_TudZ~k_xzwutvy~Ţʧk]zpxhhXv`Rv`RkYNraopoqsw|zos_w`PVC6sh_¾}}}zzzwvuspqnmljihfefegb]x϶y&t#z, ȿɿȾȽȾǽǼǼǺǺƹƸŷŷĶĶŶĶĴôóò±±ܾܾܾܾܼܿܽܽնcVNp`TtbWsbVucYyh\w{wutvy|áȥ~cQF~phYt_Qu_QlZNo_orprux~~sucybRYE9eWN¾}~}{zywwvtsrpppmlkjihfeggdc_XBʰy&s"}1ɿɿɾȾȾȽǼǻǺǹǺƹƸŷŷŶŶĵĴĴóò±°ָܾܾܿܿܽܽdWOraVveYsbWveYueZr{yvttw|Ť¢xdWtjhYs^Qr]Pm[Pl\psstvzuxe}dT`K=_PF¾}}|zzywvvssspppmlljiigegfd^Y< z'qH*ɿɿȿɾɾȼȼǻǺǺǹƸŸƷŶŶŶŵĴijIJñ°ܾܾܿܽԷeXOrbWyg\udYxg[tdY}n{zxutvzţ}meSFi[q]Pp\PkZPl[qtuuy|x{ihXePCWG<½~~}{zywwvttsqppmlljihgegghXP|="Ժz'qI*ʿʿɾʾɼȻȻȻȺǹƸƷƷƶƶŵŵĴijIJññݿܾܿܿոeXPtdY|j_wf[zh]teZvh{{wuuvx|â}hWMmaj\p\Po[Ok[Oj[ruvwz~ž{ljZjUHQ@6½}~~zzywvvstsqppmmljjhfdhghVM{9Ӹz'qI,ʿʿɾʾɽȻɼȻȺȺǹǸƷƶƶŶĵĴijijòñ°ܿغeYPtcYmbwf[{i^teZncz|xvuuvz~uaVn\Pj\o\PjXLhXNk]rvwx|Ġnl\oYLK9.½~~}{{zxwvssrpppmmljjifehhfYRz3ȩ|p{'naHʿ˿ʾɽɼɼɼȻȺȹǸƷǷƶƶŶĴĴijijò°°ػpcZrbYqgxh]{i^wg\yh]w}zwutux{}j^hVL{gYn\PgVKfVMl]rxx{~šrqar\PG5+½~~~z{zwwwtsspppmlljjhfehhe[U|0{o{'ngNʿʿʾʽʾɽɻȻȺȺǹǷƷƶƷƶŵĴijijò°°ܿ|mco`Wti{j_{j`}laqbYp}}xtstuy|vidSIwdWn\PeUIeVMnatxy}Ƣstct_RM<2½}}}z{ywvwttspppmlkiiifdhggXO{1ym{'ngO˿ʾ˾˾ʽɽȻɻɺȹǸǷƷǷƷƵŵĴijijò°°zol^Uvjoc|kapepaXsf|~zvustvy|~paRHtaVm[PcSHcUKqbty{~ ɥvueu`TSA6½}~|zzywwvtsspppmllihhfdhhiSIy6âgZ{(k{g˿˿˿ʾʽɼɻɻȺȹǷǷǸƷǶƶĴĴijòñ°yi[Svjuj}larfxj`ue[x~|xutstvyz|saSIq_Tm[OaRHdULtetz}Ģʦ¡zxgxcUWD:{u½~~}zzywwvttrqppmmljihedijiRGz4ɬbU{(jq˿˿˿˿˾ɽɼɻɻȺȹǷǸǸǷƶĵŴijijò°j]Utiymodrgtii[Szn}yvusstvwytcTLo^RjZO_PFfXNwhw{Ť˨Ǥ{yizdWZF<{qj½~~}zzywvvssrpppmlliiigdikiQE~2ϴcV{(jp˿˿˿˾ʽʼɻɻȺȹǸǷǸǷƶƶŵijijòi\Uqg}rshrgxmse\uf[z|xusssuuvtbTLo^SjYO^OFj[Q|mx~âȦ̪ȥ{k|fZ_LAl`Z½~~}z{zxwvttrqppmlljjigeiijL?~. ̱VCw'i˿˿˽ʽʼɻɺȹǹǸǸǷǷƶŵĴijǰk^W}mctynshxmwmgYRwl}~zvtsssstrbVLn^ThXN\OGp`U~o{ťʨͪɧ|m}g[`NDk`Y¾~~}{{zwwvttspppmlljiigefelI9:зO9v&gţ˿̿˽ʼʼʻʻȹǸǸǸǷǷƶŵи{mevh_~ssvkxm~rvh`o`Wv~{xvtrrrs~qbVMo_UfVL[OGzh]t|ãȧʪέ˩pi]dRH\OG½~~}zzzwwwttsqoommkiijhefepE2D'S=v&gá˿̾˾ʽʼɻʻɺȹȹǸǷǷǷ׿{rqd\|qx{qwls~sh\U|mby|zwtssrqymeXOrbWbTJ]QIodwĥʩ̪άͪqk_fTIXKC}}|zzywwvtsspppmlkiijhdhio>)E)B*u%kʭ̿˾˾ʼʼɻɺɺǸȹǸǷŶocZyoyw|q~rytjeYRtiy{xutsr~qrgeXOrbXaRJdWNxj{Ǩʪάϭάsk`fUJZNF¾}}|zzywvvssrpppmlljijieebq7F)7t#pԻ̿̿˾˽ʼʻɻɻȸǹȹɺlaZsjy{w~sz{viafZRuiwywuts}qte\j[RqaW_RJm_VqĦʪ̬ήѰЮuociWM[OF¾~~}zzywwvttrqppmlljijig_Zr4X?:"t$pӹ̿̿˾ʽʼɻʻɺȹȹɳvjb{mew~}xvuibaUMzk`|puut{ooegZRqaXl]T_RJwh^yƨ˭ϰϯѰЯvpciWMWKC½}~~zzywvvstsqppmmljhlkh[Ts3r^x,s#s"¹̿̿˾ʼʼɻɺɺõwoth_|~y{ysWMFeXOxi`qgpese[h\Tn`Vvf]i[RdWOsh~çɪ̮ϰѱѲϯvpdjXNXKC½~~}{{zxwvssrpppmmljhmmhXNy.sq!r!y* ̿̿˽ʼʻʻ;maYx}~la[TKDWMGZPIl`Xyi`zj`vg]gXQj]VsǪʭϰѱѲҲѱzqfl[RZMF½~~}z{zwwwtsrpppmlkjgmniQE})q"r!x* ̿̿˿˾ʼʼ̽ımb[sk~uvynrhqgte\gZRte]~ŪɬΰвѲѲҲԳ|shn\T[NG}}}zzywvwtsspppmlljhjikM>}(nr z- ̿˾˽ʽʼslwkdw|r}rsjpcZh[SulèǬ̯ϱѳѴӴҳӳ}sin\T[NG¾}}|zzywvvssrpppmlmkhignH87Ůmq7̿˿˾kaZxpfn`Xk_X}ǫ˰βгҵӵӵҴӴ~sio]U[OH~}|zzywwvtsrppomlnmhifpD0E(¼nq8̿Ÿoe^ļwzkbi\U{meƬɯβѶҶӶԷӵҵԵ~tio]UZNH~~}zzywvvssrppollnmhkjunt#r ǻ[RM^VQ{ĹʾʾɽȼǻƺƺŹ÷÷¶ؿ־ֽջxpiZTqie}}|yzywvvssrpopnsrogVθE/v'iŻɽʿʾɾɾȼǼƻƺƺĸ÷·׿׾ԻvohYStnj}}|zzywwvtsrposrwpjvhzlz'q>ʿɾɾȽƻƻźĹø¶׿ԽwphZTxrn~~|zzywvvssqoput|phzx'u%oͯʿʿɾɽǽƼƻźĹø·ԼwoeWR|xt~~}{zzwwvtrrostrphR7v&pW>ɿɿȾǽǼƻźĹĹ·ӺvoeWR|xt~}}z{zwwvtrqouur|x'u&r!zȾȽǼƼŻĹùø¸¸һunh\W}{w~~}zzzwvvssrpxxuD&u%s#w' ɿȾǽǽƼŻĺĺ¹ĺҼslh]X|zw}}|zzywvvstsq||yϻ|'t%s"s"[@Ǿɿsnk`\|zx}|zzywvvsutr}}z|'u%u&ps!Y=ʿɿȽȽȽǼǼƻźŹŹĸ÷¶׿׿׾׾ռջջԺԹҸҷҷѶѶѷƭxxg_dXR}}z~~{zywvvswvt|¤1v%u&u%ojps"v&y, y, y, y, y, y+ y+ y+ y+ y+ y+ y+ y+ y+ y+ y+ y, y, y+ y+ y, y+ y+ y+ y+ y+ y+ y+ y+ y+ y+ y+ x+ x+ x+ x+ y+ y+ x+ y+ y+ y+ y+ y+ y* y* z+ {, {- {- {- }- }. }/ ~/ ~/ ~. ~0 ~0 0 1 ~1 ~1 0 1 1 1 0 1 ~0 0 ~/ ~/ ~. }/ }/ }/ }.|- {, {- {, y*y)x)x*x*x*y*r'e#UAC#|{yvvsyyv~fN}- z'v&w&t$s#s!r r r r r r r r r r r r r r r r r r r r r r r r r r r s s s s s!s!s!s!s!s!s!s!s!s!s!s!s!s!s!s!s!t"u#v#v#v$w$w%w&x&x&x&x&y&y&y&y'y'y'y'y&y&x&x&x&x&x&w&w%w$v$v$u$u#t!s!s!s"s"s"s"s"l_P; E(eeaICA{zvӽuiC.innnmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnoooqqqrrrrrsssssssrrrrrqqqpoonmmmmmmmmngZM;M."]MC* 621ֿտԿӾĽļApp-Music-ChordPro-6.050.7/pp/windows/innosetup.iss0000644000400000040000000000614314544302032017661 0ustar jvjv# define APP "ChordPro" # define PUBLISHER "ChordPro.ORG" ; These are updated by the vfix program. # define V_MAJ 0 # define V_MIN 85 # define V_AUX 0 # define BuildNum 27 [Setup] ArchitecturesInstallIn64BitMode=x64 arm64 ia64 ArchitecturesAllowed=x64 arm64 ia64 AppID={{F8D1018C-AAE3-45E6-9447-5997F512F932} AppName={#APP} AppVersion={#V_MAJ}.{#V_MIN}.{#V_AUX}.{#BuildNum}.0 AppVerName={#APP} {#V_MAJ}.{#V_MIN} AppPublisher={#PUBLISHER} AppPublisherURL=https://www.chordpro.org DefaultDirName={commonpf}\{#PUBLISHER}\{#APP} DefaultGroupName=\{#PUBLISHER}\{#APP} OutputDir=. OutputBaseFilename={#APP}-Installer-{#V_MAJ}-{#V_MIN}-{#V_AUX}-{#BuildNum}-msw-x64 Compression=lzma/Max SolidCompression=true AppCopyright=Copyright (C) 2015,2017,2020 {#PUBLISHER} PrivilegesRequired=none InternalCompressLevel=Max ShowLanguageDialog=no LanguageDetectionMethod=none WizardImageFile=chordproinst.bmp InfoAfterFile=infoafter.txt ChangesAssociations=yes [Components] Name: GUI; Description: "ChordPro GUI application"; Types: full compact Name: CLI; Description: "ChordPro command line application"; Types: full [Tasks] Name: desktopicon; Description: "Create desktop icons"; Components: GUI; GroupDescription: "Additional icons:" Name: desktopicon\common; Description: "For all users"; Components: GUI; GroupDescription: "Additional icons:"; Flags: exclusive Name: desktopicon\user; Description: "For the current user only"; Components: GUI; GroupDescription: "Additional icons:"; Flags: exclusive unchecked [Files] Source: "build\*"; DestDir: {app}; Flags: recursesubdirs createallsubdirs overwritereadonly ignoreversion; [Icons] Name: {group}\{#APP}; Filename: {app}\wxchordpro.exe; Components: GUI; IconFilename: "{app}\chordpro.ico"; Name: "{group}\{cm:UninstallProgram,{#APP}}"; Filename: "{uninstallexe}" Name: "{commondesktop}\{#APP}"; Filename: "{app}\wxchordpro.exe"; Tasks: desktopicon\common; IconFilename: "{app}\chordpro.ico"; Name: "{userdesktop}\{#APP}"; Filename: "{app}\wxchordpro.exe"; Tasks: desktopicon\user; IconFilename: "{app}\chordpro.ico"; [Registry] Root: HKA; Subkey: "Software\Classes\.cho\OpenWithProgids"; ValueType: string; ValueName: ""; ValueData: "org.chordpro.chordpro"; Flags: uninsdeletevalue Root: HKA; Subkey: "Software\Classes\.crd\OpenWithProgids"; ValueType: string; ValueName: ""; ValueData: "org.chordpro.chordpro"; Flags: uninsdeletevalue Root: HKA; Subkey: "Software\Classes\org.chordpro.chordpro"; ValueType: string; ValueName: ""; ValueData: "ChordPro File"; Flags: uninsdeletekey Root: HKA; Subkey: "Software\Classes\org.chordpro.chordpro\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: """{app}\chordpro-doc.ico""" Root: HKA; Subkey: "Software\Classes\org.chordpro.chordpro\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\wxchordpro.exe"" ""%1""" Root: HKA; Subkey: "Software\Classes\Applications\wxchordpro.exe\SupportedTypes"; ValueType: string; ValueName: ".cho"; ValueData: "" Root: HKA; Subkey: "Software\Classes\Applications\wxchordpro.exe\SupportedTypes"; ValueType: string; ValueName: ".crd"; ValueData: "" [Messages] BeveledLabel=Perl Powered Software by Squirrel Consultancy App-Music-ChordPro-6.050.7/pp/windows/infoafter.txt0000644000400000040000000000033014120712212017616 0ustar jvjvThank you for installing ChordPro! Consult https://www.chordpro.org for information and documentation. Please join the user forum for support and the latest updates. https://groups.io/g/ChordPro See you there! App-Music-ChordPro-6.050.7/pp/windows/chordpro.pp0000644000400000040000000000013314140764754017306 0ustar jvjv@../common/chordpro.pp # HarfBuzz library. --link=C:/Strawberry/c/bin/libharfbuzz-0__.dll App-Music-ChordPro-6.050.7/pp/linux/0000755000400000040000000000000014567360037014574 5ustar jvjvApp-Music-ChordPro-6.050.7/pp/linux/wxchordpro.pp0000644000400000040000000000115114544302032017314 0ustar jvjv# Packager settings for WxChordPro. @../common/wxchordpro.pp --module=Wx::WebView # Explicitly link the wxGTK3 libraries. --link=libwx_baseu-3.2.so.0 --link=libwx_baseu_net-3.2.so.0 --link=libwx_baseu_xml-3.2.so.0 --link=libwx_gtk3u_adv-3.2.so.0 --link=libwx_gtk3u_aui-3.2.so.0 --link=libwx_gtk3u_core-3.2.so.0 --link=libwx_gtk3u_html-3.2.so.0 --link=libwx_gtk3u_media-3.2.so.0 --link=libwx_gtk3u_propgrid-3.2.so.0 --link=libwx_gtk3u_ribbon-3.2.so.0 --link=libwx_gtk3u_richtext-3.2.so.0 # And more... --link=libpng16.so.16 --link=libjpeg.so.62 --link=libSDL-1.2.so.0 --link=libgconf-2.so.4 --link=libORBit-2.so.0 App-Music-ChordPro-6.050.7/pp/linux/chordpro.pp0000644000400000040000000000004414120712212016730 0ustar jvjv@../common/chordpro.pp -l harfbuzz App-Music-ChordPro-6.050.7/pp/linux/Makefile0000644000400000040000000000507714544302032016227 0ustar jvjv#! /bin/make -f # GNUmakefile for Linux + wxGTK3 .PHONY :: chordpro wxchordpro ROOT := ../.. COMMON := ../common SCRIPT := ${ROOT}/script DEST := build VERSION := $(shell perl ../../lib/ChordPro/Version.pm) PP := pp -u default : appimage # PAR Packer. chordpro : chordpro.pp ${COMMON}/chordpro.pp ${PP} --output=chordpro @chordpro.pp ${SCRIPT}/chordpro.pl wxchordpro : chordpro.pp ${COMMON}/chordpro.pp ${COMMON}/wxchordpro.pp ${PP} --output=wxchordpro @wxchordpro.pp ${SCRIPT}/wxchordpro.pl clean :: rm -f *.pp.deps *~ chordpro wxchordpro # Portable Perl Launcher PERLLIB := /usr/lib64 PERLINC := ${PERLLIB}/perl5/CORE PERLSO := $(shell perl -wle 'printf("libperl.so.%d.%d\n",$$1,$$2) if $$] =~ /^(\d+)\.(\d\d\d)/') .PHONY :: ppl ppl :: clean wxchordpro unpack copy_coredll loaders .PHONY :: unpack unpack : perl ../pp2ppl.pl --dest=${DEST} wxchordpro --quiet cp -p "${ROOT}/script/chordpro.pl" "${DEST}/script/chordpro.pl" cp -p "${ROOT}/lib/ChordPro/Config.pm" "${DEST}/lib/ChordPro/Config.pm" cp "${DEST}/res/icons/chordpro.ico" ${DEST} mv "${DEST}/res" "${DEST}/lib/ChordPro/" # Copy code DLLs. # We assume that the target system has: # linux-vdso.so.1 # libdl.so.2 # libc.so.6 # libm.so.6 # libcrypt.so.2 # /lib64/ld-linux-x86-64.so.2 copy_coredll : cp -pL ${PERLLIB}/libperl.so ${DEST}/${PERLSO} patchelf --set-soname ${PERLSO} ${DEST}/${PERLSO} cp -pL ${PERLLIB}/libpthread.so.0 ${DEST}/ find . -type f -name '*.so' -exec patchelf --set-rpath '$$ORIGIN' {} \; find . -type f -name '*.so.*' -exec patchelf --set-rpath '$$ORIGIN' {} \; PREFIX := '"script/"' CCOPTS := $(shell perl -MExtUtils::Embed -e ccopts) -DSCRIPTPREFIX=${PREFIX} LDOPTS := -Wl,--rpath='$${ORIGIN}' ${DEST}/${PERLSO} -lpthread LDOPTS := -Wl,--rpath='$$ORIGIN' ${DEST}/${PERLSO} -lpthread .PHONY :: loaders loaders : "${DEST}/wxchordpro" "${DEST}/chordpro" # For testing... .PHONY :: _loaders _loaders : "${DEST}/chordpro" cp "${DEST}/chordpro" "${DEST}/perl" "${DEST}/wxchordpro" : ppl.c ${CC} ${CCOPTS} -o "${DEST}/wxchordpro" -L${DEST} ${LDOPTS} ppl.c "${DEST}/chordpro" : ppl.c ${CC} ${CCOPTS} -o "${DEST}/chordpro" -L${DEST} ${LDOPTS} ppl.c clean :: rm -fr build # Build AppImage. APPIMAGETOOL := appimagetool appimage : ppl cp AppRun ${DEST} cd ${DEST}; ln lib/ChordPro/res/linux/chordpro.desktop . cd ${DEST}; ln lib/ChordPro/res/icons/chordpro.png . ${APPIMAGETOOL} -n ${DEST} ChordPro-${VERSION}.AppImage # Truly dynamic version of loader. Rename to {wx}chordpro if you dare. "${DEST}/poc" : poc.c ${CC} ${CCOPTS} -o "${DEST}/poc" poc.c -ldl App-Music-ChordPro-6.050.7/pp/linux/AppRun0000755000400000040000000000016514531066067015726 0ustar jvjv#!/bin/sh SELF=$(readlink -f "$0") HERE=${SELF%/*} export APPIMAGE_PACKAGED=1.00 exec ${HERE}/wxchordpro ${1+"$@"} App-Music-ChordPro-6.050.7/pp/linux/ppl.c0000644000400000040000000000773214120712212015521 0ustar jvjv/* Portable Perl Loader (Linux version) */ #include #include #include #include #include #include #include #include "XSUB.h" static pTHX; /* Set up DynaLoader so modules can load modules. */ void xs_init(pTHX); /* Main. */ static char selfpath[PATH_MAX]; /* /foo/bar */ int main( int argc, char **argv, char **env ) { // Make ourselves known. putenv( "PPL_PACKAGED=1.00" ); /* Assuming the program binary /foo/bar/blech */ char scriptname[PATH_MAX]; /* blech */ memset (selfpath, 0, PATH_MAX); memset (scriptname, 0, PATH_MAX); if ( readlink ("/proc/self/exe", selfpath, PATH_MAX-1 ) > 0 ) { char *p = rindex( selfpath, '/' ); if ( p ) { p++; strcpy( scriptname, p ); *p = 0; } else strcpy( scriptname, selfpath ); #ifdef DEBUG fprintf( stderr, "selfpath: %s\n", selfpath ); fprintf( stderr, "scriptname: %s\n", scriptname ); #endif } else { strncpy( selfpath, argv[0], PATH_MAX-1 ); char *p = rindex( selfpath, '/' ); if ( p ) { p++; strcpy( scriptname, p ); *p = 0; } else { p = getcwd( selfpath, PATH_MAX-1 ); strcat( selfpath, "/" ); strncpy( scriptname, argv[0], PATH_MAX-1 ); } #ifdef DEBUG fprintf( stderr, "cwdpath: %s\n", selfpath ); fprintf( stderr, "scriptname: %s\n", scriptname ); #endif } /* Start perl environment. */ PERL_SYS_INIT3( &argc, &argv, &env ); /* Create a perl interpreter. */ my_perl = perl_alloc(); perl_construct(my_perl); /* Strip unwanted environment variables. */ static char **ourenv = NULL; if ( env ) { int envc = 0; while ( env[envc] ) envc++; ourenv = (char **)calloc( envc+1, sizeof(char**) ); int j = 0; for ( int i=0; inew; unless ( $zip->read($exe) == AZ_OK ) { die("$exe [zip] open error\n"); } foreach my $member ( $zip->members ) { next if $member->isDirectory; # Get file name. my $x = $member->fileName; # shlibs to root. $x =~ s;^shlib/.*?([^/]+)$;$1;; my $skip = -f catfile( $dest, $x ); if ( $verbose ) { print STDERR ( $skip ? "Skipping \"" : "Extracting \"", $member->fileName); print STDERR ("\" to \"$x") unless $x eq $member->fileName; print STDERR ("\"\n"); } next if $skip; my $file = catfile( $dest, $x ); $zip->extractMember( $member, $file ) == AZ_OK or die("Zip extract $x error\n"); my @st = stat($file); warn("STAT $file: $!\n") unless @st; my $perm = $st[2] & 0777; chmod( $perm | 0220, $file ); } } sub extract_embedded { # Code stolen from extract_embedded.pl, which is code stolen from # one of Roderich Schupp's mails to the PAR mailing list. He # attributes this to: code stolen from PAR script/parl.pl. my ( $exe, $extract ) = @_; open( my $fh, '<:raw', $exe) or die("$exe: $!\n"); # Search for the "\nPAR.pm\n" signature backward from the end of the file. my $buf; my $size = -s $exe; my $offset = 512; my $idx = -1; while ( 1 ) { $offset = $size if $offset > $size; seek( $fh, -$offset, 2 ) or die("$exe: Seek failed: $!\n"); my $nread = read( $fh, $buf, $offset ); die("$exe: Read failed: $!\n") unless $nread == $offset; $idx = rindex( $buf, "\nPAR.pm\n" ); last if $idx >= 0 || $offset == $size || $offset > 128 * 1024; $offset *= 2; } die("$exe: No PAR signature found\n") unless $idx >= 0; # Seek 4 bytes backward from the signature to get the offset # of the first embedded FILE, then seek to it. $offset -= $idx - 4; seek( $fh, -$offset, 2 ); read( $fh, $buf, 4 ); seek( $fh, -$offset - unpack("N", $buf), 2 ); printf STDERR ( "$exe: Embedded files start at offset %d\n", $exe, tell($fh) ) if $trace; read( $fh, $buf, 4 ); while ( $buf eq "FILE" ) { read( $fh, $buf, 4 ); read( $fh, $buf, unpack("N", $buf) ); ( my $fullname = $buf ) =~ s|^([a-f\d]{8})/||; # strip CRC read( $fh, $buf, 4 ); read( $fh, $buf, unpack("N", $buf) ); my $file = catdir( $extract, split(/\//, $fullname) ); if ( -f $file ) { warn("Skipping: \"$fullname\"\n") if $verbose; next; } my $dir = dirname($file); unless ( -d $dir ) { # print STDERR "Creating directory $dir...\n"; mkpath($dir); } print STDERR ("Extracting \"$file\"\n" ) if $verbose; open( my $out, '>:raw', $file ) or die("Error creating \"$file\": $!\n"); print $out $buf; close($out) or die("Error closing \"$file\": $!\n"); } continue { read( $fh, $buf, 4 ); } close $fh; } ################ Subroutines ################ sub app_options { my $help = 0; # handled locally my $ident = 0; # handled locally my $man = 0; # handled locally my $pod2usage = sub { # Load Pod::Usage only if needed. require Pod::Usage; Pod::Usage->import; &pod2usage; }; # Process options. if ( @ARGV > 0 ) { GetOptions( 'dest=s' => \$dest, 'ident' => \$ident, 'verbose+' => \$verbose, 'quiet' => sub { $verbose = 0 }, 'trace' => \$trace, 'help|?' => \$help, 'man' => \$man, 'debug' => \$debug) or $pod2usage->(2); } if ( $ident or $help or $man ) { print STDERR ("This is $my_package [$my_name $my_version]\n"); } if ( $man or $help ) { $pod2usage->(1) if $help; $pod2usage->(VERBOSE => 2) if $man; } unless ( $dest && @ARGV == 1 ) { $pod2usage->(2); } } __END__ ################ Documentation ################ =head1 NAME pp2ppl - Create portable tree out of PP package. =head1 SYNOPSIS sample [options] package Options: --dest=XXX destination folder --ident shows identification --help shows a brief help message and exits --man shows full documentation and exits --verbose provides more verbose information --quiet runs as silently as possible =head1 OPTIONS =over 8 =item B<--dest=>I The folder where the tree will be unpacked. =item B<--help> Prints a brief help message and exits. =item B<--man> Prints the manual page and exits. =item B<--ident> Prints program identification. =item B<--verbose> Provides more verbose information. This option may be repeated to increase verbosity. =item B<--quiet> Suppresses all non-essential information. =item I The package to process. This must be an archive file created by L. =back =head1 DESCRIPTION B will read the archive file and unpack it into the destination directory. First the embedded files are extracted to $dest/lib. Then the remainder of the PAR is processed as a ZIP archive: =over 4 =item * C files are extracted to $dest/lib; =item * C files are extracted to $dest; =item * The rest is extracted I. =back The resultant file tree is suitable to build a ppl (Portable Perl Loader) kit. =cut App-Music-ChordPro-6.050.7/script/0000755000400000040000000000000014567360037014322 5ustar jvjvApp-Music-ChordPro-6.050.7/script/ppchordpro.PL0000644000400000040000000000020112736264363016731 0ustar jvjv#!/usr/bin/perl my $src = shift; open( my $fd, ">", $src); open( my $fi, "<", "$src.pl" ); print { $fd } ( <$fi> ); close($fd); App-Music-ChordPro-6.050.7/script/chordpro.pl0000644000400000040000000000245114562504546016501 0ustar jvjv#!/usr/bin/perl # ChordPro -- Successor of Chord/Chordii # Author : Johan Vromans # Created On : Fri Jul 9 14:32:34 2010 # Last Modified On: Mon Feb 12 22:11:02 2024 # Update Count : 282 # Status : Unknown, Use with caution! ################ Common stuff ################ =head1 NAME chordpro - A lyrics and chords formatting program =head1 SYNOPSYS chordpro [ options ] file [ file ... ] =head1 DESCRIPTION B will read one or more text files containing the lyrics of one or many songs plus chord information. B will then generate a photo-ready, professional looking, impress-your-friends sheet-music suitable for printing on your nearest printer. When invoked as B, or with first argument B<--a2crd>, the input will be interpreted as a 'chords over lyrics' file, converted to ChordPro and written to standard output. For command line usage summary, use chordpro --manual Visit the web site L for complete documentation. =cut use strict; use warnings; use FindBin; use lib "$FindBin::Bin/../lib"; use ChordPro; use ChordPro::Paths; CP->pathprepend( "$FindBin::Bin", "$FindBin::Bin/.." ); run(); ################ Subroutines ################ # Synchronous system call. Used in Util module. sub ::sys { system(@_) } 1; App-Music-ChordPro-6.050.7/script/wxchordpro.pl0000644000400000040000000000672014562504642017060 0ustar jvjv#!/usr/bin/perl # WxChordPro -- Successor of Chord/Chordii # Author : Johan Vromans # Created On : Fri Jul 9 14:32:34 2010 # Last Modified On: Mon Feb 12 22:12:02 2024 # Update Count : 283 # Status : Unknown, Use with caution! ################ Common stuff ################ use Wx 0.9912 qw[:allclasses]; use strict; use warnings; use utf8; package main; binmode(STDERR, ':utf8'); binmode(STDOUT, ':utf8'); use FindBin; use lib "$FindBin::Bin/../lib"; use ChordPro; use ChordPro::Paths; CP->pathprepend( "$FindBin::Bin", "$FindBin::Bin/.." ); # Package name. my $my_package = 'ChordPro'; # Program name and version. my $my_name = 'WxChordPro'; my $my_version = $ChordPro::VERSION; # We need Wx::App for the mainloop. # ChordPro::Wx::Main is the main entry of the program. use base qw(Wx::App ChordPro::Wx::Main); my $options = app_options(); sub OnInit { my ( $self ) = shift; $self->SetAppName("ChordPro"); $self->SetVendorName("ChordPro.ORG"); Wx::InitAllImageHandlers(); my $main = ChordPro::Wx::Main->new(); exit unless $main->init($options); $self->SetTopWindow($main); $main->Show(1); if ( $options->{maximize} ) { $main->Maximize(1); } # elsif ( $options->{geometry} # && $options->{geometry} =~ /^(?:(\d+)x(\d+))?(?:([+-]\d+)([+-]\d+))?$/ ) { # $main->SetSize( $1, $2 ) # if defined($1) && defined($2); # $main->Move( $3+0, $4+0 ) # if defined($3) && defined($4); # } return 1; } # No localisation yet. # my $locale = Wx::Locale->new("English", "en", "en_US"); # $locale->AddCatalog("wxchordpro"); my $m = main->new(); $m->MainLoop(); ################ Subroutines ################ use Wx qw( wxEXEC_SYNC ); # Not yet defined in this version of wxPerl. use constant wxEXEC_HIDE_CONSOLE => 32; # Synchronous system call. Used in Util module. sub ::sys { Wx::ExecuteArgs( \@_, wxEXEC_SYNC | wxEXEC_HIDE_CONSOLE ); } ################ Subroutines ################ use Getopt::Long 2.13; sub app_options { my $options = {}; # Process options, if any. # Make sure defaults are set before returning! return unless @ARGV > 0; if ( !GetOptions( $options, 'ident', 'log', 'verbose|v+', 'version|V', 'maximize', # 'geometry=s', 'quit', 'trace', 'help|?', 'debug', ) or $options->{help} ) { app_usage(2); } # This is to allow installers to fake an initial run. exit if $options->{quit}; if ( $options->{version} ) { app_ident(); exit(0); } app_ident() if $options->{ident}; return $options; } sub app_ident { print STDERR ("This is $my_package [$my_name $my_version]\n"); print STDERR ( ::runtimeinfo("short"), "\n"); } sub app_usage { my ($exit) = @_; app_ident(); print STDERR < is a GUI wrapper for the ChordPro program. It allows opening of files, make changes, and preview (optionally print) the formatted result. Visit the web site L for complete documentation. =cut App-Music-ChordPro-6.050.7/LICENSE0000644000400000040000000004652314120712212014012 0ustar jvjvLICENSING ========= This program is free software; you can redistribute it and/or modify it under the terms of either: a) the GNU General Public License as published by the Free Software Foundation; either version 1 (included), or (at your option) any later version, or b) the "Artistic License", included in this document. This program 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 either the GNU General Public License or the Artistic License for more details. ============================================================================= The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End ============================================================================= GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! ============================================================================= App-Music-ChordPro-6.050.7/README.md0000644000400000040000000000356714544300524014276 0ustar jvjv# [ChordPro](https://www.chordpro.org) *A lyrics and chords formatting program* ![Version](https://img.shields.io/github/v/release/ChordPro/chordpro) ![GitHub issues](https://img.shields.io/github/issues/chordpro/chordpro) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com) [![Built by musicians](https://img.shields.io/badge/built%20by-musicians%20𝄞-d15d27.svg?&labelColor=e36d25)](https://forthebadge.com) ![Language Perl](https://img.shields.io/badge/Language-Perl-blue) [![User Forum](https://img.shields.io/badge/forum-groups.io%2Fg%2FChordPro-8fff00)](https://groups.io/g/ChordPro) ChordPro generates professional-looking sheet music from a text file containing the lyrics of one or many songs with chord information. Check for installation instructions and execute `chordpro --help` for an overview about the available options. ## Support For general discussion, please see [the user group](https://groups.io/g/ChordPro/topics). Bugs and feature requests go to [the GitHub issue tracker](https://github.com/ChordPro/chordpro/issues). ## Development Status This program implements ChordPro language version 6. It is the successor of [*Chord* and *Chord*ii](https://chordpro.org/chordpro/chordpro-history/) with additional features such as native PDF generation, Unicode input and fully customizable layout, fonts and sizes. Prominent features of ChordPro 6 are Pango style text markup, annotations, and a more powerful way of assigning typefaces to layout items. For up-to-date information, see [The ChordPro implementation](https://chordpro.org/chordpro/chordpro-reference-implementation/). ## License Copyright © 2010,2018,2023 The ChordPro Team This program is free software. You can redistribute it and/or modify it under the terms of the Artistic License 2.0. App-Music-ChordPro-6.050.7/Changes0000644000400000040000000011552414567357655014335 0ustar jvjv0.000 !Functionality - ABC support will default to QuickJS, either embedded via JavaScript::QuickJS or via an external interpreter. To use another tool, set delegates.abc.program in the config. - Substitute alert image for failed delegates. !Bugfixes - (Song) Add col alias for columns. !Internal - Modify delegate API. 6.050.6 2024-02-25 - !Internal - Remove ABC kit from kit. It is not license clean. - Add cmdline.js for external QuickJS. 6.050.5 2024-02-23 - !Internal - Packaging changes and small typo's. 6.050.3 2024-02-22 - !Internal - Packaging changes. 6.050.2 2024-02-21 - !Functionality - Upgraded ABC files to 1.22.13 and repackaged. 6.050.1 2024-02-19 - !Functionality - Upgrade to SVGPDF 0.086. - New sample song: Molly Malone. - !Internal - Repackaged ABC files. 6.050 2024-02-09 - !Highlights - Customize the tables of content by providing a template. The template is processed as a song before the tables and can be used to set title, subtitle, columns, maybe even an introduction text. Since the template it iself a song, it can be associated with its own config file for unlimited customization. [Read more...](https://chordpro.org/chordpro/chordpro-configuration-generic/#customizing-the-table-of-contents) - New delegated environment: textblock. The text between start_of_textblock and end_of_textblock is formatted normally, but the result is an image that can be placed anywhere. Several attributes are available to control the appearance of the text, e.g. centered and flush right. [Read more...](https://chordpro.org/chordpro/directives-env_textblock/) - Delegate type may now also be 'omit' to omit the content of the section, and 'none' to treat the section a generic. [Read more...](https://chordpro.org/chordpro/directives-delegates/) - !Functionality - (Experimental) Add environment textblock. - (Experimental) Add ToC templates. - Wrap toc lines. - (PDF) Allow strwidth and strheight to return both values at once. - (Config) allow delegate types 'none' and 'omit'; remove 'omit' attribute. - !Bug fixes - Fix issue #265. - Fix spread indent with labels. - Fix issue #343. 6.042 2024-01-09 - !This is a bugfix release. - !Important - To trace problems, the ChordPro 'about' information is included in the PDF. This should not reveal sensitive information, but in case this bothers you, you can disable this by setting debug.runtimeinfo to 0 in the config. - !Functionality - Report XDG_CONFIG_HOME in runtime info. - Include 'about' info as PDF metadata. - !Bug fixes - Post-release typo fixes. - (Stringdiagram) Fix font size of base fret numeral (issue 337). - (Stringdiagram) Fix fret number colours. - Fix handling of XDG_CONFIG_HOME. - Fix 'spread' images. - Fix problem finding notes:german et al. - (MMA) Fix test fail with perl >= 5.39.6. 6.040 2023-12-26 - !Highlights - Images can be [placed everywhere](https://chordpro.org/chordpro/directives-image/). They can be placed relative to the paper, the page, the column, and the lyrics. - Images can be [embedded](https://chordpro.org/chordpro/directives-image/#inline-images) in text (lyrics) lines, either as part of the text similar to a glyph, or somewhere else on the page relative to a particular place in the text. The latter is most interesting for annotations. - Delegates are images too. Annotate your lyrics with SVG images and musical notes using ABC or Lilypond. - Chord and keyboard diagrams are images too. And you can use string and keyboard diagrams simultaneously. - Resources like configs, tasks and images are now more logically searched using [resource libraries](https://chordpro.org/chordpro/resources/). - !ChordPro functionality - Rework paths handling for consistent resource handling; eliminate App::Packager. - Inline images. - {image} can have label and align properties. - {define}d chords overrules suppress list. - (ABC) Use QuickJS XS (JavaScript::QuickJS) as preferred. - (ABC) ABC embedding no longer uses nodeJS (npx). - (ABC) Make split work (and enable by default). - (ABC) Images are left aligned by default. - (Lilypond) Images are left aligned by default. - Improve runtime info. - Suppress songs that do not have content. - Suppress table of content entry for a song w/ {ns toc=no}. - (PDF) Image scale strategy change for spread images. - (PDF/Writer) Add generalized add_object for objects and images. - (PDF) Prevent case problems when looking up fonts for SVG. - (PDF) Add aliases for web standard fonts like serif, sans, ... - (PDF) Ignore leading empty and ignores (was: leading empty only). - (Windows) ChordPro now installs as a 64-bit application in \Program Files instead of \Program Files (x86). You are adsvised to remove the old 32bits install first. - !Bug fixes - Prevent warning when parsing {key} and trancode to nashville/roman. - Fix chord inversion (issue 321). - Fix comment lines disturbing a consecutive series of {chord}s. - Fix typo in Wx Preferencesdialog, causing it to crash. - Fix problem with PDF/SVG caching fonts. - Fix comment labels for delegates (issue 329.3). - (Wx) Filter configs on prp ans json. - Fix memorize/recall/transpose issue 333. - Fix issue 334. - !Internal - (ABC) ABC embedding use tohtml instead of toxhtml. - (PDF) Enhance assets (wip), labels; move grid to separate module. - Experimental ##include facility. 6.030 2023-09-18 - !Highlights - [ABC](https://www.chordpro.org/chordpro/directives-env_abc/) and [Lilypond](https://www.chordpro.org/chordpro/directives-env_ly/) embedding now use SVG (vector graphics) instead of pixmap images. This is very crisp at all sizes, and greatly reduces the size of the resultant PDF document. + No additional tools are required for embedding, except the `abc2svg` and `lilypond` tools. The installer kits for MS Windows and MacOS already include the `abc2svg` tool. - The ChordPro GUI (`wxchordpro`) has been extended with a Tasks menu that can be used to quickly select presets for a specific task. For example, to produce a PDF with only lyrics, or with keyboard diagrams instead of string diagrams. User presets can be added by placing small config files in a `tasks` folder under the `CHORDPRO_LIB`. - !ChordPro functionality - Directives and config to change the [chorus appearance](https://www.chordpro.org/chordpro/directives-props_chorus_legacy/). - Add support for SVG images. Allow `pdf.fonts.XXX` as fonts in SVG. - Handle ABC and Lilypond embedding via SVG images. No more need for ImageMagick. Add 'staffsep' option for ABC embedding. - Add 'omit' to delegates config. - Infer chord inversions for keyboard. - Use SVGPDF text callback to substitute missing flat/sharp glyphs. - Trap missing font sizes (should not happen). - !Breaking changes - The image scale strategy was changed to be more intuitive. See [this forum message](https://groups.io/g/ChordPro/message/1846) for details. - !Experimental - A primitive but effective docker based web app. - !BugFixes - Fix issue #297. - Fix issue #298. - Fix issue message/1780. - Fix issue #300. - Fix issue #301. - Fix issue #309. - Fix issue #311. 6.020 2023-07-21 - !ChordPro functionality - New directive diagrams obsoletes grid/no_grid with more possibilities, Based on a suggestion and concept implementation from Jörg Bublath. - Images without directory can be looked up in CHORDPO_LIB. - Turn pseudo-chords like | and spaces into annotations. - !BugFixes - Fix a number of problems with define/copy/copyall. - Fix issue #293. - !Internal - Bump minimal perl version to 5.26 and add Object::Pad to dependencies. - Change namespace App::Music::ChordPro to ChordPro. - Put VERSION on single line for stupid tools. - Upgrade ChordPro::Chords::Appearance to class. - Enhance Parser to use signatures to catch call errors. - Enhance Config to use signatures to catch call errors. - Enhance Chords to use signatures to catch call errors. - Enhance Utils to use signatures to catch call errors. 6.010 2023-06-05 - !ChordPro functionality - Allow line continuation for input lines using a backslash. - Allow define chord "|" in config. - Allow graceful handling of rootless chords. - Allow simple markup in chords. Yes, this includes grid chords. - Add flags for preprocessor pattern replacements. - Allow image scale as a percentage. - Experimental: Absolute placement for images. - Experimental: Diagram control in {define}. - Remove diagrams.auto from config. - !BugFixes - Add Pod::Usage to required modules. Some distros seem to have removed it from the core. - Add jpeg library to MacOS kit. - Add some missing files for docs building. - Fix root relocation when transcoding to a movable system. - Fix missing parser in {define XX} without frets etc. - Fix forum 1696 (chord recall with trans{code,pose}). - Fix issue #269 (problem with page labels). - Fix issue #270 (PDF outlines issues, 'letter' setting). - Fix issue #273 (PDF CreationDate string is not conformant, but PDF::API2 2.042+ rejects conformant strings). 6.000 2022-12-28 - !ChordPro functionality - Chord finger settings can be suppressed with pdf.diagrams.fingers. - (Experimental) Add line selection to preprocessor. - Remove * for user defined chords. - !BugFixes - Fix issue 259. - Fix issue 260 (chords not being handled correctly in comments). - Fix issue 260 (suppress diagrams and grids when lyrics-only). - Fix issue 261 (truesf not functional beyond chord root). 5.990 2022-11-03 - !BugFixes - Fix erroneous error message when pdf.formats.default.title (cs) is []. - Fix compatibility problems with PDF::Builder. - Fix page labels for PDF::API2 >= 2.041. - Eliminate potential splice warning with pagealign-songs. - Fix resize preferences window. - Fix problem with copied chords not being registered as agnostic. 5.989 2022-10-21 - !ChordPro functionality - Verify configs (and fix if possible). - Enhance ToC sorting, unlimited keys, numeric and reverse sorting. - Warn if X: is missing in ABC content. - Add volta for grid. - Add display for {define}. - Replace TAB characters by a single space on input. - Support building installer with MacOS homebrew perl. - Allow empty chord defines (just the name) to make the chord known. - Allow front-matter and back-matter in the config and filelist. - (Experimental) Allow PDF filenames in the filelist. - (Experimental) Allow multiple chords in a grid cell. - !Miscellaneous - Luke Pinner contributed a nice search feature for our doc pages. - !BugFixes - Fix problem that toc columns overlapped lefter columns. - Fix some problems with {define}. - Fix some more problems with {define}. - Fix automatic replacement of apostroph (prime) by friendly quote. - Fix crash when auto-adding an unknown chord. - Only print user chords when diagrams.show=user. - Make base optional in json chords (part of fix 234). - Modern3 style now has keyboard diagrams at the right. See issue 236. - (PDF) Print chorus tag if there's no chorus to rechorus. - Fix regression with pagealign = 2. - Fix issue 222. - Fix issue 223. - Fix issue 224. - Fix issue 226. - Fix issue 234. - Fix issue 237. - Fix issue 239. - Fix issue 250. - Fix issue 251. - Fix issue 253. - Fix issue 255. 5.988 2022-05-17 - !ChordPro functionality - Automatically use real sharps and flats in chord names. Fallback to the ChordProSymbols font if the font doesn't have the appropriate symbols. - Add settings.truesf (default: false) to enable/disable this. - Allow settings.* to be used in %{} substitutions. - Add meta chords and numchords (list/number of chords used). - Add config pdf.spacing.diagramchords. - Allow meta values for directive selectors. - Re-enable agnostic chord lookup. - (Wx)(MacOS) Improve prefences dialog. - Several ABC fixes/improvements. - (PDF) Add support for background document. - Markdown export (EXPERIMENTAL). Thanks to Johannes Rumpf. - LaTeX export (EXPERIMENTAL). Thanks to Johannes Rumpf. - !BugFixes - Fix issue #208. - (Wx) Fix sharps/flats mixup in PreferencesDialog. 5.987 2022-02-08 - !ChordPro functionality - Conditional directives can be negated with a trailing ! - (Wx)(MacOS) Improve prefences dialog. - !BugFixes - Add File::HomeDir to dependencies. - Fix issue #204. 5.986 2022-02-02 - !ChordPro functionality - (Config) Theme colors foreground-medium and foreground-light. - Show cell bars in grid lines (Config: pdf.grids) - Suppress warnings for empty chords (i.e., [ ] spacers). - Add error message when font name is not a built-in. - Allow custom PDF meta data. Requires PDF::API2 2.042. - Allow meta split on separator. - Add settings.inline-annotations. - Add settings.chords-canonical to use canonical representation for chords. - Rework chords lookup to always use chordsinfo. - Use chord objects for show and display. - (GUI) Allow selection of custom library. - (PDF) Allow multi-line headings and footers. - (PDF) Configurable PDF library. - (PDF) Provide meta %{pages} for headings. - Allow (uppercase) letters for chord fingers. - (Wx) Overhaul preferences management. - Support center and scale for lilypond. - Add config settings.choruslabels for MSPro convenience. - !BugFixes - Remove obsolete __EMBEDDING__. - Bump requirement Text::Layout to 0.024. - Fix issue #182. - Fix warning if diagrams.show=user and no chords. - Fix labels for grid with pdf.labels.comment. - Fix key_actual and key_from to reflect all keys. - Fix tilde expansion for font files. - (PDF) Suppress borders around ToC entries, just in case. - Fix sorttitles field name in CSV export. - Fix issue #194. - Fix issue #195. - Add workaround for incompatible pagel labels change in PDF::API 2.042. - Fix placement of finger dots. - Fix handling of NC chords. - Fix config loss problem with '{+pdf...}'. 5.985 2021-09-28 - !ChordPro functionality - Change config preset lookup algorithm and document it. - (docs) First 'hints and tips' contributed by xeruf. - (PDF) Suppress empty chorus recall tag. - !BugFixes - Remove obsolete code from Makefile.PL and GNUmakefile. - Fix issue #175. 5.983 2021-09-24 - !ChordPro Syntax - (docs) Add transpose directive. - !ChordPro functionality - (Songbook) Allow whitespace between curly and directive in a2crd detection - (GUI) Add --maximize command line option. - Allow song-specific configs to have includes. - Add --no-songconfig to ignore song specific configs. - Add --reference to defeat configs and other fun. - !BugFixes - Prevent wrapping loop with long comment with chords. - Fix Can't locate object method "reset_parsers" (Songbook). - Fix colour default with {xxxcolor}. - Prevent havoc when pdf.chorus.recall.type has illegal values. - (ChordPro/MSPro) Fix missing meta in substitutions. - Fix incomplete chord warning for {chord x}. - Fix issue #132. - Fix issue #163. - Fix issue #165. - Fix issue #178 (non-ascii filenames on Windows). 5.982 2021-08-31 - !ChordPro functionality - Add --strict option to enforce conformance to ChordPro standard. Enabled by default, may be disabled with --no-strict. - Add/update misc. files for desktop systems. - !BugFixes - Incorporate Data::Properties instead of using the CPAN version, since this version is incompatible. 5.981 2021-08-23 - !ChordPro Syntax - Add Cheat Sheet with ChordPro syntax and availability. - !ChordPro functionality - Document properties for the PDF can be supplied in the config file. - !BugFixes - Fix issue #159. 5.980 2021-08-14 - !ChordPro functionality - Bump version to 5.xxx moving towards 6.0. - Setting config value pdf.pagealign-songs to a value greater than 1 will force the resultant PDF to have an even number of pages. - Add warning when chord diagram exceeds the diagram size. - Do not complain about unknown meta data. - Add numbercolor property for chordfingers chord. - Add baselabeloffset property for chords. - Config settings that have corresponding command line options are always overridden when the command line option is used. - Add preprocessing for directives. - Preprocessing 'all' may result in multiple lines. - Experimental 'choruslike' property for pdf.chorus.recall. - Add clo --noa2crd to suppress autoconversion. - Improve page labeling and aligning. - Config pdf.csv.songsonly controls whether matter pages are included in the CSV. - Add warning if no songs were found in the input. - Simplify README.md. - Add ChordPro history doc. - !BugFixes - Fix crash when no chords. - Fix case insensitive matching of directive selectors. - Fix issue #145. - Allow {define} and {chord} to take multi-digit fret/finger positions. - Fix background drawing for finger positions. - Fix "Modification of a read-only value attempted" crash when instrument or user get nulled. - Fix %{pageno} vs. %{page} confusion. - Do not tamper with ABC content. E.g., adding K: has side-effects. - Fix issue #148. - Fix issue #149. - Fix issue #158. 0.979 2021-07-09 - !ChordPro syntax - {define ...} can take key definitions for keyboards. - All directives can be selected out by appending -XXX, where XXX is the type of instrument or a user name. - !ChordPro functionality - (musejazz) Change font to MuseJazzText.otf as downloadable from GitHub. - Improve error messages for font files not found. - Default CHORDPRO_LIB to ~/.config/chordpro, if present. - Experimental: Allow delegates to specify image type. - Allow variable expansion on all input lines. - Experimental support for preprocessing. - Experimental support for song-specific configs. - Support for keyboard diagrams. - Experimental support for metadata in filelist. - Add --print-delta-config option. - Suppress outline title if there is only one outline. - Allow meta data definitions in config. - Remove support for legacy configs. - Suppress a directive if its argument is empty as result from %{} expansion. - Add directive suppression with instrument/user selectors. - !BugFixes - Fix crash when abc section is the very first thing in a song. - Fix decapo setting from config file (issue #140). 0.978 2021-03-05 - !ChordPro functionality - Allow array element addressing in --define. - Retain line numbers for backend diagnostics. - Experimental support for ABC. - Experimental support for MacOS. - New icons. - Windows: Installer associates ChordPro with .cho files. - Linux: Support for desktop and app icons. - Linux: Support building an AppImage. - Restore section label as comment (config: pdf.labels.comment). - Add experimental MMA backend. - Add metadata "today". - !Bugfixes - Fix chords transpose in comments with output ChordPro. - Fix detection of grid params in start_of_grid. - Fix problem with path name in start menu after windows install. 0.977 2020-08-26 - !ChordPro functionality - Add CSV columns for sorttitle artist composer collection key year. - !Bugfixes - Raise requirement for Text::Layout to 0.019. - Use only 'name' for chords in the built-in config. 'description' cannot be overridden by the user with 'name'. - Fix page numbers in CSV. - Several fixes for font descriptions and sizes. 0.976 2020-08-16 - !Bugfixes - Fix markup defragmentation (#111) - Fix page numbers in CSV. - Fix problem that --no-toc was not honoured. - Fix a2crd crash (#115) 0.975 2020-08-13 - !ChordPro syntax - Support Pango style markup language. - Add basic support for annotations. - Add directives start/end_of_verse/bridge and short forms. - !ChordPro functionality - Add PDF outlines (bookmarks). - Revamp table of contens (finally). - Remove section handling (we now have labels). - Allow ~ expansion in file names. - Allow relaxed parsing of chords (root name + arbitrary). - Allow parsing of notes (chords with only a lc root name). - (Wx) Show filename in window title. - (Wx) Show asterisk if file is modified. - !ChordPro configuration - Add split marker to be inserted between text phrases when the chord is wider than the phrase. - Add settings.suppress-empty-lyrics to suppress blank lyrics lines. - Add display property to chords to control the way they are displayed. - Add guitar-br.json with Brandt-Roemer compliant chord symbols. - Add meta variable songindex. - Add metadata sorttitle. - Add config settings for HTML backend. - Add font properties in fontconfig settings. - Add settings for chordnames and notenames. - !Bugfixes - Fix interpretation of directives and markup in tab sections. - Fix bug where some command line arguments did not properly support utf8. - Fix handling of {chorus: label}. - Fix problem where using a {chord} directive before any song lines would crash. - Fix problem where the tag of a grid was ignored. - Do not indent chorus labels when chorus indenting (issue #81). - !Miscellaneous - Use Text::Layout to support Pango style markup language. - Use File::LoadLines. - Upgrade requirement for PDF::API2 to 2.035, Font::TTF to 1.05, Text::Layout to 0.014. - Packaged version no longer loads default config from chordpro.json. It is now really built-in. - Change CHANGES to Changes. 0.974 2019-10-05 - Restructure chord definitions. Default is now an orthogonal set of basic chords. Additional legacy weirdo's are available in "guitar-legacy.json". - Allow chord definitions with multiple names. See the docs. - Add chord types add2 and add4. - Fix erroneous transposing of transcoded chords. - Fix erroneous recall of chorus from previous song. - Fix missing fret positions in {chord}. 0.973 2019-03-13 - Integrate a2crd into chordpro. Use "chordpro --a2crd" to invoke. - Add --decapo option to eliminate {capo} settings by transposing. - Implement image assets. Requires IO::String and Image::Info. - Implement a basic form of line wrapping (regular chords+lyrics). - Implement a basic form of line wrapping (comments and non-chords lyrics). - Implement user library (env. var. CHORDPRO_LIB). Experimental. - Allow a2crd as a filter. - Use label instead of section name, if provided. - Make instrument and tuning accessible as meta data. - Fix undefined if no output file was supplied. - Fix section change detection. - Fix crash when chordless songline and not suppress-empty-chords. - Fix misplacement of diagrams when columns and too many {chord} directives. - Fix printing of auto-added chords. - Fix issue #63: Labels are lost when a new song is encountered. - Fix crash when --dump-chords and no meta. - (PDF) Fix label width. 0.972 2018-11-06 - Add a2crd script. - Minimize all configs to only override what is necessary. - Split german notes into scandinavian (..., A, Bb/A#, H, C) and german (..., A, Ais/B, H, C). - Use String::Interpolate::Named. - (Experimental) Allow %{..} interpolations on the output file name, e.g. --output="%{artist|%{} - }%{title}.pdf". Command line only. - Fix memorize problem with the first chord. - Upgrade WxChordPro to 0.972. 0.97.1 2018-10-24 - Upgrade WxChordPro to 0.970 to fix problem with custom config. 0.97 2018-10-23 - Instrument defintions are now in separate config files. There are no 'built-in' tunings and chords, just defaults. Available instrument configs are guitar, guitar-ly, mandolin-ly, and ukulele-ly. Default is guitar. - Chords parsing has been completely overhauled. - Config file handling has been completely overhauled. - Alternative note naming systems, e.g. Latin (Do Re Mi ...) and Solfege are now supported. - Experimental: Chords can be transcoded between note naming systems. - Chords can be shown under the lyrics, controlled by config item settings.chords-under. - Nashville and Roman chord systems need to be explicitly enabled. - Allow meta substitutions in title and subtitle. - Fix {transpose}, --transpose and {key} interaction. - Experimental: Chords can be recalled from previous sections using [^] or plain ^. Requires config setting settings.memorize. - Upgrade WxChordPro to 0.960_059. - Add config settings for ChordPro backend. - Add slash as grid symbol. - Allow labels for grids. - Show durations as hh:mm. - Fix grey background of comment_italic. - Add font "label" for section labels. Defaults to text font. - Fix section labels when the first line is not a song line. - {chorus} Do not print Chorus tag when quoting. - {chorus} Allow label. - Allow empty comment directives. - Do not print background for empty strings. 0.96 2018-07-11 - (pp/linux/GNUmakefile) Verify we're running the right perl. - Upgrade to App::Packager 1.43. - Fix transpose of Asus and Esus chords. - Fix issue #47 by Increasing base fret limit to 23. - Fix error handling with illegal chord definitions. - (wxChordPro) Fix file saving logic. - Experimental: Markup for song sections. - Experimental: All fonts can have background and frame. 0.95 2018-06-04 - Add (derived) meta item _key to reflect the actual song key, taking capo setting into account. - Allow {comment} without background colour. - Make {comment_box} box the same colour as its text. - Warn if multiple {capo} settings. - Fix problem that chords in grids were not transposed. - Add value "auto" for pdf.labels.width to automatically reserve margin space when labels are used. - Fix problem that titles-directive-ignore was ignored. - (PDF) Fix problem that toc entries were not clickable. - Fix issue #41 - Error in transposition of a recalled chorus. - Fix issue #42 - Defining Chords Fails for Songbooks. Song chord definitions were lost in multi-song songbooks except for the last (or only) song. - Fix schema validation for configs. 0.94 2018-01-23 - Allow \ { } and | to be escaped with \ in replacement strings. - Fix problem that in-song chords caused CANNOT HAPPEN error. - Add --filelist option to read song file names from files. - Fix inconsistent handling of --lyrics-only in backends. - Add html to list of recognized output types (even though experimental). Note that the HTML backend is not yet included. - Fix Chord/Chordii regression: Base frets in chord diagrams should be arabic numbers, not roman. - Pass unknown directives through to backends. - Fix labels handling for ChordPro output. - Fix problem that bass notes in chords were not transposed. 0.930.1 - (pp/windows) Add PDF::API2::Bundle to make sure all PDF::API2 and TTF::Font modules are included. 0.93 2017-12-07 - Fix transposition of chord diagrams. 0.92 2017-12-07 - Add configurable sort methods for table of contents. Config option: toc.order, values "page" or "alpha". Default is "page". Config option: toc.title, default "Table of Contents". Supersedes pdf.formats.default.toc-title. - Fix JSON problem with loading UTF8 config files. - Fix the need for a bogus file argument when dumping chords. - Experimental support for indenting and margin labels. - Obsolete pdf.diagramscolumn in favour of pdf.diagrams.show. This can be top, bottom, right of the first page, and below, following the last song line. - Provide song source for unknown chords message. - Handle UTF-8 encoded filenames correctly. - Implement in-line printing of chords, config: settings.inline-chords. Add style 'inline'. - Fix problem with font restore after {textfont} cs. - Fix problem that trailing empty lines were discarded. - Fix final line discard if input is not newline terminated. - Fix issue#31 (textsize directive with percentage raises error). - Fix problem where first empty line was inadvertently ignored. 0.910.1 2017-11-09 - Add style 'modern3'. 0.91 2017-11-09 - Add printing of bars in chord diagrams. - Allow PDF config "fontdir" to take an array of paths. Also, allow the path elements to be a colon-(Windows: semicolon)-separated list of paths. - Add PDF config "diagramscolumn". This will have the song chord diagrams printed on the first page, in a side column. Experimental. - Fix problem with misnumbered fingers in non-builtin chords. - Fix problem with restoring defaults for {textsize} and friends. 0.90 2017-10-17 - Fix dependencies in Makefile.PL. - Do not mark config defined chords as being user defined. - Fix some problems with '{chord}' chords. 0.89 2017-09-22 - Add {transpose} directive. - Transpositions and metadata substitutions are now handled at parse time. - Update built-in documentation. - Upgrade WxChordPro to 0.89. - Fix problem with locating manual page. - Normalize CHANGES according to CPAN:Changes::Spec. 0.88 2017-09-11 - Put the Table of Contents (if any) at the beginning. - Fix a bug that caused no TOC to be produced with multiple song input. - Add --csv command line option to request writing the CSV. - Add --cover command line option to prepend cover pages. - Improve JSON config validation. 0.87 2017-09-04 - Fix problem where songlines without chords yielded empty lines in the ChordPro backend after transposition. - Allow "-" as filename for standard input. - Handle Byte Order Mark in input files. - (ChordPro) Do not use {meta} for known meta keys. - (Windows) Handle version number setting in iss file. 0.860.1 2017-08-18 - Fix test failures with PDF::Builder 3.004, issue https://rt.cpan.org/Ticket/Display.html?id=122815 . 0.86 2017-08-16 - Fix problems with disappearing page titles. - Fix some packing issues. 0.85 2017-08-15 - Rename config pdf.fonts.diagram_capo to pdf.fonts.diagram_base. - Fix some (well, several) layout issues with odd/even page printing. - Fix missing fingers in config defined chords. - Allow PDF::Builder to be used instead of PDF::API2. - Improve define/chord parsing and diagnostics. - (WxChordPro) Update to 0.84. 0.84 2017-07-31 - Emergency fix for PDF font problem. 0.83 2017-07-31 - Supply default '1+4x4+1' for first start_of_grid. - Supply default straight font for grid lines. - Allow empty lines in grids. - Improve WxChordPro integration. - Change old terminology "chordgrid" to "diagrams". - (WxChordPro) Update to 0.83. 0.82 2017-07-21 - Add Version.pm. 0.81 2017-07-16 - Fixed problem where wxChordPro couldn't preview. - Restructured the files for packaging support. 0.80 2017-07-13 - (PDF) Improve terminology in warning about unkown chords. - Prevent undefined warnings when a song has no chords. - (pp) Allow resource updating. - Prevent undefined warnings when the system provides no configs. - Add missing POD resources for packaged binaries. - Supply usage info and exit when run without action/file arguments. 0.79 2017-07-12 - Mostly packing/packaging fixes. - (WxChordPro) Update to 0.79. 0.78 2017-07-12 - Mostly packaging fixes. - (WxChordPro) Update to 0.78. 0.77 2017-06-26 - Finalize design and implementation of chord grids. - Add support for chord fingerings, as suggested by Christian - Erickson (author of the Songsheet Generator). - Fix meaning of clo -G (was negated). - Add song examples. - (WxChordPro) Update to 0.76. 0.76 2017-05-16 - Allow text properties to stack/unstack. - Suppress empty text line if there's only [Chords]. - Enhance parameter substitution in titles/comments. - Allow {chord NAME} to designate known chords. - Some more fix problems with dot-less @INC in newer perls. - Add schema to verify (and edit) json config files. 0.75 2017-04-13 - Experimental support for Nashville Numbering System and Roman - Numbered Chords. - (Config) Add more meta data: lyricist, arranger, copyright, year, duration. - (PDF) Improve grids drawing: add config for line thickness, add space for the crosses/circles. - (PDF) Allow PDF to be written to standard output. Output file will now be named after the input file if there's only one. - Keep track of #-comments in ChordPro input and reproduce in - ChordPro output. - (PDF) Fonts are now looked up in a font path consisting of the fontdir config setting, the application's fonts resource - directory, and the value of environment variable FONTDIR. - (Packager) Use App::Packager from CPAN. - (WxChordPro) Update to 0.74. 0.74 2017-04-02 - Fix problems with dot-less @INC in newer perls. 0.73 2017-04-04 - (WxChordPro) Update to 0.710.3. 0.72 2017-01-18 - (WxChordPro) Update to 0.710.2. - Fix style_chordii sample config. 0.71 2017-01-17 - Produce CSV with PDF and toc. - Implement {chord...} directive. 0.70 2016-11-10 - (ChordPro) Fix require of Common. - (Config) Comment example chord definition. - (ChordPro) Add rechorus handling. - (ChordPro) Fix --toc/--notoc command line option. - (PDF) Fix background colour in indented chorus. - (PDF) Fix wrong headspace on continuation pages. 0.69 2016-09-29 - Add parser tests. - Prevent nasty errors when transposing unknown chords. - (PDF) Fix comment decorations that were off due to substituting metadata. - (ChordPro) Add msp as output variant. 0.68 2016-08-23 - Extend chorus recall. Chorus may be quoted, and/or referred with a tag text. - Handle {pagesize} in legacy config. - Minor adjustments to the default configuration to match the documentation. 0.67 2016-08-23 - Overhaul of chord definitions and transpositions. - Chords may now be parenthesised. - {defined: name ...} is now preferred. - "base-fret NN" may be omitted. - All strings may be omitted to define an unknown chord. 0.66 2016-08-22 - Uploaded to GitHub. - Added support for {meta} directives. - Make the list of known metatada configurable. - Allow using metadata in titles and comments. - Remove meta-mapping (no longer needed). - Change the way unknown chords are dealt with, for - Chord/Chordii compatibility. - Add res/config/style_chordii.json with as much Chord/Chordii compatibility as can be reasonably achieved. 0.65 2016-07-15 - Add --define to set config items from the command line. - Smooth some config trickeries. - Add meta-map config to treat metadata items differently. - Normalize directives parsing to be (more) Chord/Chordii compatible. - Handle defining chords with flexible number of strings. 0.64 2016-07-10 - Add support for Chord/Chordii legacy config. - Add --no-legacy-config to suppress legacy config. - Add --no-default-configs (-X) to suppress all default configs. - Do not make "no easy chords" default. - More pp stuff. 0.63 2016-07-06 - Add support for {grid} and friends. - More pp stuff. 0.62 2016-07-03 - Improve support for PAR packaging. - Add Undo/Redo (MSW only?). - Better viewer launching. - Use separate PODs for --manual and --help-config. - Add wxchordpro to the kit. 0.61 2016-06-28 - Improve packaging. - Add support for PAR packaging. 0.60 2016-06-23 - Bring chorus layout attributes under a single topic. - Add chordgrid and chordgrid_capo chords. 0.59 2016-06-23 - We have a Ukulele. - And a GUI. 0.58 2016-06-20 - Handle --chord-grid-size. - Add chord definitions in configuration. - Add chords sorting. - Add user defined chords and tunings. - Handle --no-easy-chord-grids and --chord-grids-sorted. 0.57 2016-06-19 - Move transpose code to Chords module. - Default grid font to comment, not font. - Register user defined fonts. - First shot at printing chord grids. - Second shot at printing chord grids. - Support -D, but use backend to generate the grids. 0.56 2016-06-13 - Handle {titles} directive. - Add support for head-first-only. Titles are now top-printed. - Move low-level primitives to PRWriter module. - Add font and spacing for 'empty' lines. 0.55 2016-06-10 - Detailed page headers/footers control. - Require perl version v5.10. 0.54 2016-06-08 - Fix bug #115156: Will not build on Mac OSX. - Fix bug #115159: IO::File is not loaded automatically in older perls ( < 5.12.6 ). 0.53.1 2016-06-08 - Improve Makefile.PL to get indexing right. 0.53 2016-06-07 - Add built-in chords and the --dump-chords-text facility. - (PDF) Turn missing images into a comment. 0.52.6 2016-06-07 - Fix POD problem in Config.pm. 0.52.5 2016-06-07 - Improve Makefile.PL to get indexing right. 0.52.4 2016-06-06 - Improve Makefile.PL. 0.52.3 2016-06-06 - Move configuration pod to Config.pod. Will it be indexed? - Add --print-default-config and --print-final-config options. - Fix problems with songline colours. - Fix headings. - Add head-first-only setting. - Fix page footers. 0.52.2 2016-06-06 - Minor documentation changes. 0.52.1 2016-06-05 - Some fixes for tests on Windows. 0.52 2016-06-05 - Move runnable code from chordpro script to ChordPro.pm module. The script is now a simple wrapper. - Add documentation. 0.51.3 2016-06-05 - Eliminate Clone as a dependency. - Eliminate IO::String as an explicit dependency. It's implied by Font::TTF. 0.51.2 2016-06-04 - Better Makefile.PL (no_index of namespace). 0.51.1 2016-06-04 - Better Makefile.PL. 0.51 2016-06-04 - First alpha version released. App-Music-ChordPro-6.050.7/README.ABC0000644000400000040000000000032214561675731014263 0ustar jvjvTo render ABC, ChordPro makes use of QuickJS embedding. You can install this easily with cpan JavaScript::QuickJS The Windows and MacOS installers for ChordPro include all tools to provide ABC embedding. App-Music-ChordPro-6.050.7/README.LilyPond0000644000400000040000000000040714561676311015427 0ustar jvjvTo embed LilyPond, ChordPro makes use of the lilypond tool to convert the embedded source to an image, and then embeds the image. For this, it requires the LilyPond typesetter to be installed. You can install LilyPond using the package manager for your system. App-Music-ChordPro-6.050.7/MANIFEST0000644000400000040000000002460514567360040014150 0ustar jvjvChanges LICENSE MANIFEST Makefile.PL README.ABC README.LilyPond README.md lib/ChordPro.pm lib/ChordPro/A2Crd.pm lib/ChordPro/Chords.pm lib/ChordPro/Chords/Appearance.pm lib/ChordPro/Chords/Parser.pm lib/ChordPro/Config.pm lib/ChordPro/Config/Properties.pm lib/ChordPro/Delegate/ABC.pm lib/ChordPro/Delegate/Lilypond.pm lib/ChordPro/Delegate/SVG.pm lib/ChordPro/Delegate/TextBlock.pm lib/ChordPro/Dumper.pm lib/ChordPro/Output/ChordPro.pm lib/ChordPro/Output/Common.pm lib/ChordPro/Output/Debug.pm lib/ChordPro/Output/HTML.pm lib/ChordPro/Output/LaTeX.pm lib/ChordPro/Output/MMA.pm lib/ChordPro/Output/Markdown.pm lib/ChordPro/Output/PDF.pm lib/ChordPro/Output/PDF/Grid.pm lib/ChordPro/Output/PDF/KeyboardDiagram.pm lib/ChordPro/Output/PDF/StringDiagram.pm lib/ChordPro/Output/PDF/Writer.pm lib/ChordPro/Output/Text.pm lib/ChordPro/Paths.pm lib/ChordPro/Song.pm lib/ChordPro/Songbook.pm lib/ChordPro/Testing.pm lib/ChordPro/Utils.pm lib/ChordPro/Version.pm lib/ChordPro/Wx.pm lib/ChordPro/Wx/Main.pm lib/ChordPro/Wx/Main_wxg.pm lib/ChordPro/Wx/PreferencesDialog.pm lib/ChordPro/Wx/PreferencesDialog_wxg.pm lib/ChordPro/Wx/RenderDialog.pm lib/ChordPro/Wx/RenderDialog_wxg.pm lib/ChordPro/lib/SVGPDF.pm lib/ChordPro/lib/SVGPDF/CSS.pm lib/ChordPro/lib/SVGPDF/Circle.pm lib/ChordPro/lib/SVGPDF/Contrib/Bogen.pm lib/ChordPro/lib/SVGPDF/Contrib/PathExtract.pm lib/ChordPro/lib/SVGPDF/Defs.pm lib/ChordPro/lib/SVGPDF/Element.pm lib/ChordPro/lib/SVGPDF/Ellipse.pm lib/ChordPro/lib/SVGPDF/FontManager.pm lib/ChordPro/lib/SVGPDF/G.pm lib/ChordPro/lib/SVGPDF/Image.pm lib/ChordPro/lib/SVGPDF/Line.pm lib/ChordPro/lib/SVGPDF/PAST.pm lib/ChordPro/lib/SVGPDF/Parser.pm lib/ChordPro/lib/SVGPDF/Path.pm lib/ChordPro/lib/SVGPDF/Polygon.pm lib/ChordPro/lib/SVGPDF/Polyline.pm lib/ChordPro/lib/SVGPDF/Rect.pm lib/ChordPro/lib/SVGPDF/Style.pm lib/ChordPro/lib/SVGPDF/Svg.pm lib/ChordPro/lib/SVGPDF/Text.pm lib/ChordPro/lib/SVGPDF/Tspan.pm lib/ChordPro/lib/SVGPDF/Use.pm lib/ChordPro/res/abc/chordproabc.js lib/ChordPro/res/abc/cmd.js lib/ChordPro/res/config/brandtroemer.json lib/ChordPro/res/config/chordii.json lib/ChordPro/res/config/chordpro.json lib/ChordPro/res/config/config.schema lib/ChordPro/res/config/guitar-br.json lib/ChordPro/res/config/guitar-ly.json lib/ChordPro/res/config/guitar.json lib/ChordPro/res/config/guitar_latex.json lib/ChordPro/res/config/inline.json lib/ChordPro/res/config/keyboard.json lib/ChordPro/res/config/mandolin-ly.json lib/ChordPro/res/config/modern1.json lib/ChordPro/res/config/modern2.json lib/ChordPro/res/config/modern3.json lib/ChordPro/res/config/nashville.json lib/ChordPro/res/config/notes/common.json lib/ChordPro/res/config/notes/dutch.json lib/ChordPro/res/config/notes/german.json lib/ChordPro/res/config/notes/latin.json lib/ChordPro/res/config/notes/nashville.json lib/ChordPro/res/config/notes/roman.json lib/ChordPro/res/config/notes/scandinavian.json lib/ChordPro/res/config/notes/solfege.json lib/ChordPro/res/config/roman.json lib/ChordPro/res/config/songbook_latex.json lib/ChordPro/res/config/ukulele-ly.json lib/ChordPro/res/config/ukulele.json lib/ChordPro/res/examples/bgdemo.pdf lib/ChordPro/res/examples/mollymalone.cho lib/ChordPro/res/examples/swinglow.cho lib/ChordPro/res/fonts/ChordProSymbols.ttf lib/ChordPro/res/icons/chordpro-doc.icns lib/ChordPro/res/icons/chordpro-doc.ico lib/ChordPro/res/icons/chordpro-doc.png lib/ChordPro/res/icons/chordpro-doc.svg lib/ChordPro/res/icons/chordpro.icns lib/ChordPro/res/icons/chordpro.ico lib/ChordPro/res/icons/chordpro.png lib/ChordPro/res/icons/chordpro.svg lib/ChordPro/res/linux/chordpro.desktop lib/ChordPro/res/linux/chordpro.metainfo.xml lib/ChordPro/res/linux/chordpro.xml lib/ChordPro/res/linux/setup_desktop.sh lib/ChordPro/res/pod/A2Crd.pod lib/ChordPro/res/pod/ChordPro.pod lib/ChordPro/res/pod/Config.pod lib/ChordPro/res/templates/comment.tt lib/ChordPro/res/templates/guitar_comment.tt lib/ChordPro/res/templates/guitar_image.tt lib/ChordPro/res/templates/guitar_songbook.tt lib/ChordPro/res/templates/image.tt lib/ChordPro/res/templates/songbook.tt pp/README pp/common/ChordPro_Bundle.pm pp/common/PAR/Filter/Null.pm pp/common/PDF_API2_Bundle.pm pp/common/Text_Layout_Bundle.pm pp/common/chordpro.pp pp/common/wxchordpro.pp pp/linux/AppRun pp/linux/Makefile pp/linux/chordpro.pp pp/linux/ppl.c pp/linux/wxchordpro.pp pp/macos/Info.plist pp/macos/Makefile pp/macos/PkgInfo pp/macos/README.html pp/macos/chordpro.pp pp/macos/create-dmg pp/macos/dndhandler.cpp pp/macos/ppl.c pp/macos/reloc.pl pp/macos/wxchordpro_pp.pl pp/pp2ppl.pl pp/windows/Makefile pp/windows/chordpro.pp pp/windows/chordpro.rc pp/windows/chordproinst.bmp pp/windows/infoafter.txt pp/windows/innosetup.iss pp/windows/innosetup_par.iss pp/windows/ppl.c pp/windows/vfix.pl pp/windows/wxchordpro.pp pp/windows/wxchordpro.rc script/chordpro.pl script/ppchordpro.PL script/wxchordpro.pl t/00_basic.pl t/01_prereq.t t/02_load.t t/100_basic.t t/101_empty.t t/101_directives.t t/102_new_song.t t/103_title.t t/104_subtitles.t t/105_chords.t t/107_chords_latin.t t/107_chords_latin_dash.t t/107_chords_latin_o.t t/108_chords_solfege.t t/108_chords_solfege_dash.t t/108_chords_solfege_o.t t/109_chords_nashville.t t/110_chords_roman.t t/111_chords_markup.t t/112_comment.t t/113_comment.t t/114_songline.t t/115_songline.t t/116_chorus.t t/117_rechorus.t t/118_tab.t t/119_verse.t t/120_meta.t t/122_memorize.t t/123_memtrans.t t/124_memtrans.t t/130_image.jpg t/130_image.t t/131_image.t t/132_image.t t/140_chords.t t/141_chords.t t/142_chords.t t/143_chords.t t/150_fonts.t t/151_fonts.t t/152_fonts.t t/15_subst.t t/160_diagrams.t t/161_titles.t t/162_newpage.t t/163_columns.t t/164_pagesize.t t/169_custom.t t/170_transpose.t t/171_transpose.t t/172_transpose.t t/173_transpose.t t/174_transpose.t t/175_transpose.t t/177_transcode.t t/178_transcode.t t/180_grids.t t/190_outlines.t t/191_outlines.t t/192_outlines.t t/193_outlines.t t/194_outlines.t t/200_markup.t t/20_basic01_crd.t t/210_configs.t t/211_config.t t/21_basic02_crd.t t/30_basic01_cho.t t/310_basic.t t/311_keys.t t/312_context.t t/313_null.t t/314_data.t t/315_escape.t t/316_array.t t/317_empty.t t/31_basic02_cho.t t/320_subst.t t/321_subst.t t/322_subst.t t/323_null.t t/380_roundtrip.t t/400_kv.t t/40_basic01_html.t t/50_encodings.t t/60_transpose.cho t/60_transpose.t t/70_a2crd.t t/71_cho.t t/72_mma.t t/73_md.t t/74_latex.t t/80_pages.t t/81_pages.t t/82_pages.t t/83_pages.t t/84_pages.t t/85_pages.t t/86_pages.t t/87_pages.t t/a2crd/t001.cho t/a2crd/t001.crd t/a2crd/t002.cho t/a2crd/t002.crd t/a2crd/t003.cho t/a2crd/t003.crd t/a2crd/t004.cho t/a2crd/t004.crd t/a2crd/t005.cho t/a2crd/t005.crd t/a2crd/t101.cho t/a2crd/t101.crd t/a2crd/t102.cho t/a2crd/t102.crd t/a2crd/t103.cho t/a2crd/t103.crd t/a2crd/t104.cho t/a2crd/t104.crd t/a2crd/t105.cho t/a2crd/t105.crd t/a2crd/t106.cho t/a2crd/t106.crd t/a2crd/t107.cho t/a2crd/t107.crd t/a2crd/t108.cho t/a2crd/t108.crd t/a2crd/t109.cho t/a2crd/t109.crd t/a2crd/t110.cho t/a2crd/t110.crd t/a2crd/t111.cho t/a2crd/t111.crd t/a2crd/t113.cho t/a2crd/t113.crd t/a2crd/t114.cho t/a2crd/t114.crd t/a2crd/t115.cho t/a2crd/t115.crd t/basic01.cho t/basic02.cho t/cho/cho001.cho t/cho/cho001.ref t/cho/cho002.cho t/cho/cho002.ref t/cho/cho003.cho t/cho/cho003.ref t/cho/cho004.cho t/cho/cho004.ref t/cho/cho005.cho t/cho/cho005.ref t/cho/cho006.cho t/cho/cho006.ref t/cho/cho007.cho t/cho/cho007.ref t/cho/cho008.cho t/cho/cho008.ref t/cho/cho009.cho t/cho/cho009.ref t/cho/cho010.cho t/cho/cho010.ref t/cho/cho011.cho t/cho/cho011.ref t/cho/cho020.cho t/cho/cho020.ref t/cho/cho021.cho t/cho/cho021.ref t/cho/cho022.cho t/cho/cho022.ref t/cho/cho023.cho t/cho/cho023.ref t/cho/cho024.cho t/cho/cho024.ref t/cho/cho025.cho t/cho/cho025.ref t/latex/30_cho_1.cho t/latex/30_cho_1.tex t/latex/30_cho_2.cho t/latex/30_cho_2.tex t/latex/debug/array t/latex/debug/hash t/latex/debug/struct t/latex/t_comment.tt t/latex/t_config.json t/latex/t_image.tt t/latex/t_songbook.tt t/latex/table/cell t/latex/table/edge t/latex/table/head t/latex/table/row t/md/30_cho_1.cho t/md/30_cho_1.md t/md/30_cho_2.cho t/md/30_cho_2.md t/md/30_cho_3.cho t/md/30_cho_3.md t/md/a34.cho t/md/a34.md t/md/a44.cho t/md/a44.md t/md/cho001.cho t/md/cho001.md t/md/cho002.cho t/md/cho002.md t/md/cho003.cho t/md/cho003.md t/md/cho004.cho t/md/cho004.md t/md/cho005.cho t/md/cho005.md t/mma/a34.cho t/mma/a34.mma t/mma/a44.cho t/mma/a44.mma t/mma/a68.cho t/mma/a68.mma t/mma/deCoda34.cho t/mma/deCoda34.mma t/mma/deCoda44.cho t/mma/deCoda44.mma t/mma/deCoda44a.cho t/mma/deCoda44a.mma t/mma/deCoda68.cho t/mma/deCoda68.mma t/ref/20_crd_1.crd t/ref/20_crd_2.crd t/ref/20_crd_3.crd t/ref/21_crd_1.crd t/ref/21_crd_2.crd t/ref/21_crd_3.crd t/ref/30_cho_1.cho t/ref/30_cho_2.cho t/ref/30_cho_3.cho t/ref/31_cho_1.cho t/ref/31_cho_2.cho t/ref/31_cho_3.cho t/ref/40_html_1.html t/ref/40_html_2.html t/ref/40_html_3.html t/ref/60_crd_1.crd t/ref/60_crd_2.crd t/ref/60_crd_3.crd t/ref/80_pages.csv t/ref/81_pages.csv t/ref/82_pages.csv t/ref/83_pages.csv t/ref/84_pages.csv t/ref/85_pages.csv t/ref/86_pages.csv t/ref/87_pages.csv lib/ChordPro/res/abc/abc2svg/abc2svg-1.js lib/ChordPro/res/abc/abc2svg/ambitus-1.js lib/ChordPro/res/abc/abc2svg/break-1.js lib/ChordPro/res/abc/abc2svg/capo-1.js lib/ChordPro/res/abc/abc2svg/chordnames-1.js lib/ChordPro/res/abc/abc2svg/clair-1.js lib/ChordPro/res/abc/abc2svg/clip-1.js lib/ChordPro/res/abc/abc2svg/cmdline.js lib/ChordPro/res/abc/abc2svg/combine-1.js lib/ChordPro/res/abc/abc2svg/COPYING.LESSER lib/ChordPro/res/abc/abc2svg/diag-1.js lib/ChordPro/res/abc/abc2svg/equalbars-1.js lib/ChordPro/res/abc/abc2svg/gamelan-1.js lib/ChordPro/res/abc/abc2svg/grid-1.js lib/ChordPro/res/abc/abc2svg/grid2-1.js lib/ChordPro/res/abc/abc2svg/grid3-1.js lib/ChordPro/res/abc/abc2svg/jazzchord-1.js lib/ChordPro/res/abc/abc2svg/jianpu-1.js lib/ChordPro/res/abc/abc2svg/mdnn-1.js lib/ChordPro/res/abc/abc2svg/MIDI-1.js lib/ChordPro/res/abc/abc2svg/nns-1.js lib/ChordPro/res/abc/abc2svg/page-1.js lib/ChordPro/res/abc/abc2svg/pedline-1.js lib/ChordPro/res/abc/abc2svg/perc-1.js lib/ChordPro/res/abc/abc2svg/psvg-1.js lib/ChordPro/res/abc/abc2svg/README.FIRST lib/ChordPro/res/abc/abc2svg/README.md lib/ChordPro/res/abc/abc2svg/roman-1.js lib/ChordPro/res/abc/abc2svg/soloffs-1.js lib/ChordPro/res/abc/abc2svg/sth-1.js lib/ChordPro/res/abc/abc2svg/strtab-1.js lib/ChordPro/res/abc/abc2svg/temper-1.js lib/ChordPro/res/abc/abc2svg/tohtml.js lib/ChordPro/res/abc/abc2svg/tropt-1.js META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) App-Music-ChordPro-6.050.7/Makefile.PL0000644000400000040000000000700614566644603014776 0ustar jvjv#! perl # Ease the life of the CPAN testers. exit 0 if $ENV{AUTOMATED_TESTING} && $] < 5.026000; use ExtUtils::MakeMaker 7.24; # comes with 5.26 my $master = 'lib/ChordPro/Version.pm'; my $version = MM->parse_version($master); $master =~ s;/Version;;; my @bins = qw( chordpro wxchordpro ); my @pdfapi = ( 'PDF::API2' => 2.043 ); # default if ( my $a = $ENV{CHORDPRO_PDF_API} ) { if ( $a =~ /PDF::Builder/ ) { @pdfapi = ( 'PDF::Builder' => 3.025 ); } elsif ( $a =~ /PDF::API2/ ) { } else { @pdfapi = ( $a => 0 ); } } WriteMakefile ( NAME => 'App::Music::ChordPro', ABSTRACT => "A lyrics and chords formatting program", VERSION => $version, AUTHOR => 'Johan Vromans ', LICENSE => "perl_5", MIN_PERL_VERSION => 5.026000, INSTALLDIRS => 'site', EXE_FILES => [ map { "script/$_" } @bins ], PL_FILES => { 'script/ppchordpro.PL' => [ map { "script/$_" } @bins ] }, MAN3PODS => { 'lib/ChordPro.pm' => 'blib/man3/ChordPro.3pm', 'lib/ChordPro/A2Crd.pm' => 'blib/man3/ChordPro::A2Crd.3pm', 'lib/ChordPro/Config.pm' => 'blib/man3/ChordPro::Config.3pm', 'lib/ChordPro/Wx.pm' => 'blib/man3/ChordPro::Wx.3pm', }, # Always require PDF::API2 or PDF::Builder and Text::Layout. PREREQ_PM => { @pdfapi, 'Text::Layout' => 0.032, 'JSON::PP' => 2.27203, 'String::Interpolate::Named' => 1.030, 'File::LoadLines' => 1.044, 'File::HomeDir' => 1.004, 'Image::Info' => 1.41, 'List::Util' => 1.46, 'Data::Printer' => 1.001001, 'Storable' => 3.08, 'Object::Pad' => 0.78, 'JavaScript::QuickJS' => 0.18, # Some distro's have removed this from the core. 'Pod::Usage' => 2.03, # These are only used by the LaTeX backend # 'Template' => 3.010, # 'LaTeX::Encode' => 0.092.0, }, CONFIGURE_REQUIRES => { 'ExtUtils::MakeMaker' => 7.24, }, # BUILD_REQUIRES => { # }, TEST_REQUIRES => { 'Test::More' => 0, 'JSON::PP' => 2.27203, 'String::Interpolate::Named' => 1.01, 'Storable' => 3.08, 'Object::Pad' => 0.78, @pdfapi, }, META_MERGE => { 'meta-spec' => { version => 2, }, resources => { license => [ "https://dev.perl.org/licenses/" ], homepage => 'https://chordpro.org', repository => { url => 'git://github.com/ChordPro/chordpro.git', web => 'https://github.com/ChordPro/chordpro', type => 'git', }, bugtracker => { web => "https://github.com/ChordPro/chordpro/issues", mailto => "info@chordpro.org", }, }, provides => { chordpro => { file => "script/chordpro", version => $version }, "App::Music::ChordPro" => { file => "lib/ChordPro.pm", version => $version }, }, no_index => { file => [ "lib/ChordPro/Songbook.pm", "lib/ChordPro/Chords.pm", "lib/ChordPro/Testing.pm", "lib/ChordPro/Utils.pm", "lib/ChordPro/Version.pm", "lib/ChordPro/Wx.pm", ], directory => [ "lib/ChordPro/Chords", "lib/ChordPro/Config", "lib/ChordPro/Delegate", "lib/ChordPro/Output", "lib/ChordPro/Wx", "lib/ChordPro/lib", "lib/ChordPro/res", "pp", ], }, }, ); # Note about the no_index: CPAN and MetaCPAN have differing opinions # on how no_index must be interpreted, in particular in combination # with provides. App-Music-ChordPro-6.050.7/META.yml0000644000400000040000000000331214567360037014266 0ustar jvjv--- abstract: 'A lyrics and chords formatting program' author: - 'Johan Vromans ' build_requires: ExtUtils::MakeMaker: '0' JSON::PP: '2.27203' Object::Pad: '0.78' PDF::API2: '2.043' Storable: '3.08' String::Interpolate::Named: '1.01' Test::More: '0' configure_requires: ExtUtils::MakeMaker: '7.24' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: App-Music-ChordPro no_index: directory: - t - inc - lib/ChordPro/Chords - lib/ChordPro/Config - lib/ChordPro/Delegate - lib/ChordPro/Output - lib/ChordPro/Wx - lib/ChordPro/lib - lib/ChordPro/res - pp file: - lib/ChordPro/Songbook.pm - lib/ChordPro/Chords.pm - lib/ChordPro/Testing.pm - lib/ChordPro/Utils.pm - lib/ChordPro/Version.pm - lib/ChordPro/Wx.pm provides: App::Music::ChordPro: file: lib/ChordPro.pm version: v6.50.7 chordpro: file: script/chordpro version: v6.50.7 requires: Data::Printer: '1.001001' File::HomeDir: '1.004' File::LoadLines: '1.044' Image::Info: '1.41' JSON::PP: '2.27203' JavaScript::QuickJS: '0.18' List::Util: '1.46' Object::Pad: '0.78' PDF::API2: '2.043' Pod::Usage: '2.03' Storable: '3.08' String::Interpolate::Named: '1.03' Text::Layout: '0.032' perl: '5.026' resources: bugtracker: https://github.com/ChordPro/chordpro/issues homepage: https://chordpro.org license: https://dev.perl.org/licenses/ repository: git://github.com/ChordPro/chordpro.git version: v6.050.7 x_serialization_backend: 'CPAN::Meta::YAML version 0.018' App-Music-ChordPro-6.050.7/META.json0000644000400000040000000000547014567360040014437 0ustar jvjv{ "abstract" : "A lyrics and chords formatting program", "author" : [ "Johan Vromans " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "App-Music-ChordPro", "no_index" : { "directory" : [ "t", "inc", "lib/ChordPro/Chords", "lib/ChordPro/Config", "lib/ChordPro/Delegate", "lib/ChordPro/Output", "lib/ChordPro/Wx", "lib/ChordPro/lib", "lib/ChordPro/res", "pp" ], "file" : [ "lib/ChordPro/Songbook.pm", "lib/ChordPro/Chords.pm", "lib/ChordPro/Testing.pm", "lib/ChordPro/Utils.pm", "lib/ChordPro/Version.pm", "lib/ChordPro/Wx.pm" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "7.24" } }, "runtime" : { "requires" : { "Data::Printer" : "1.001001", "File::HomeDir" : "1.004", "File::LoadLines" : "1.044", "Image::Info" : "1.41", "JSON::PP" : "2.27203", "JavaScript::QuickJS" : "0.18", "List::Util" : "1.46", "Object::Pad" : "0.78", "PDF::API2" : "2.043", "Pod::Usage" : "2.03", "Storable" : "3.08", "String::Interpolate::Named" : "1.03", "Text::Layout" : "0.032", "perl" : "5.026" } }, "test" : { "requires" : { "JSON::PP" : "2.27203", "Object::Pad" : "0.78", "PDF::API2" : "2.043", "Storable" : "3.08", "String::Interpolate::Named" : "1.01", "Test::More" : "0" } } }, "provides" : { "App::Music::ChordPro" : { "file" : "lib/ChordPro.pm", "version" : "v6.50.7" }, "chordpro" : { "file" : "script/chordpro", "version" : "v6.50.7" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "info.org", "web" : "https://github.com/ChordPro/chordpro/issues" }, "homepage" : "https://chordpro.org", "license" : [ "https://dev.perl.org/licenses/" ], "repository" : { "type" : "git", "url" : "git://github.com/ChordPro/chordpro.git", "web" : "https://github.com/ChordPro/chordpro" } }, "version" : "v6.050.7", "x_serialization_backend" : "JSON::PP version 4.16" }

Consult www.chordpro.org for information and documentation.