Index: README.md ================================================================== --- README.md +++ README.md @@ -14,11 +14,11 @@ [abcm2ps](https://github.com/lewdlime/abcm2ps) C code which requires compilation on every operating system. The **abc2svg** scripts can run in any system with no compilation on any platform that contains an internet browser. This includes MS-Windows, Apple, and Unix-like systems (Linux, BSD...) as well as portable devices -such as cell phones and iPads. +such as cell phones and tablets. A description of the ABC parameters that relate to both abcm2ps and abc2svg can be found [here][1]. [1]: http://moinejf.free.fr/abcm2ps-doc/index.html "abc2svg documentation" @@ -42,20 +42,20 @@ the javascript code by right clicking on one of the bookmarklets below and selecting copy url. Then, paste this code into the URL of your new bookmark in your library. -To use a abc2svg bookmarklet, first load an ABC file into your +To use a abc2svg bookmarklet, first, load an ABC file into your browser either from a web site of from a file on your system. Once you see the textual abc code, click on the bookmarklet that you created. After a slight delay depending upon the complexity of the abc code, it should be replaced by a music representation or a list of the contents of the ABC file. Here are two bookmarklets that you can try. This -first abc2svg bookmarklet renders all the music it finds in the page currently displayed. Once the music is displayed, clicking inside a tune starts playing it from the beginning. Clicking on a particular note or rest starts playing from that point. @@ -64,11 +64,11 @@ (If the 'Print' button does not appear on your browser menu, try right clicking on the web page.) Alternatively, if your source contains many tunes, you can use this -second bookmarklet. The browser will list the titles of the tunes. Clicking on a title displays the tune. Playing and printing work in the same manner as above. The generated pages contain a yellow menu in the top right corner which permits @@ -107,10 +107,23 @@ - load the ABC file using the browse button, - click the include file name button, - and load the include file using the same previous browse button. Only one included file is allowed. + +If you have installed abc2svg in a site of yours, you may put a file +**pref.js** with the abc2svg scripts. This file may contain various +javascript functions and also ABC parameters. +These parameters must be defined inside the following sequence: + + if (typeof abc2svg == "undefined") + var abc2svg = {} + if (!abc2svg.a_inc) + abc2svg.a_inc = {} + abc2svg.a_inc["default.abc"] = ` + .. put here the ABC parameters .. + ` If you have a US keyboard, its behaviour can be changed for easier music entering by one of these bookmarklets: keyboard 1 @@ -134,15 +147,15 @@ [4]: http://moinejf.free.fr/abcm2ps-doc/tabac.html "J'ai du bon tabac" As it is apparent, HTML and ABC can be mixed in the same html file. Both are rendered in the order you defined them. -You may also have noticed this CSS about SVG elements: +You may also have noticed this style about SVG elements: `        svg { display: block }` -This permits to have the music on separate vertical areas. -Without this CSS, the music is in-lined as in [this other example][5]. +It puts the lines of music on vertical areas. +Without this style, the music is in-lined as in [this other example][5]. [5]: http://moinejf.free.fr/abcm2ps-doc/dansou-i.html "Tune index example" Global ABC parameters may be added as `parameter=value` in the query string of the URL of the page. The [following example][16] calls the same @@ -151,22 +164,22 @@ [16]: http://moinejf.free.fr/abcm2ps-doc/tabac.html?pagescale=1.2 "J'ai du bon tabac" In the above examples, all the ABC music is generated (displayed and ready to be played) by means of the script **abcweb-1.js**. If there is a large collection of tunes, it may be preferable to -link to the other script, **abcweb1-1.js**, which permits you -to select particular tune(s). Without an explicit +link to the other script, **abcweb1-1.js**, which offers a tune selection. +Without an explicit selection (see below), a list of the tunes is displayed, and, after a tune has been selected, the whole page is replaced by the music. (The HTML code, if any, is not displayed.) -A menu on the top right corner of the screen -permits to get the tune list again. Here is [a real example][6]. +You can go back to the list of the tunes thanks to the menu +on the top right corner of the screen. Here is [a real example][6]. [6]: http://moinejf.free.fr/abc/boyvin-2-2.html "J. Boyvin organ tunes" As you may notice in the menu, the edition of the ABC content is proposed. -This permits, for example, to transpose the tunes. +This permits you, for example, to transpose the tunes. This edition is done inside the browser, so, your changes will be lost after leaving the page. When accessing such pages, an external selection of the tunes can be accomplished by ending the URL with the character '`#`' @@ -189,10 +202,18 @@ The **abc2svg** package on my server is still being tuned which could change its behaviour at any unknown time; so you may prefer to install and run it from your own system. There are many ways to install abc2svg: + +- [tarball][16] or [.zip archive][17] from my site + Both files contain the scripts that are generated from the source + when abc2svg is stable enough. The scripts are ready + to be used from the root directory. + +[16]: http://moinejf.free.fr/abc2svg.tar.bz2 "tarball" +[17]: http://moinejf.free.fr/abc2svg.zip "ZIP file" - [Guido Gonzato's page](http://abcplus.sourceforge.net/#abc2svg) Guido maintains a ZIP archive of the abc2vg scripts after each release of a new version (many thanks, Guido!). You can just download and unzip this archive. **abc2svg** should @@ -200,11 +221,11 @@ In the same page, you can also find some binaries of QuickJS, a fast javascript interpreter (see below). - tarball From the timeline in the [chisel repository][13], you can get a tarball - any version of the abc2svg source and install it in your system. + of any version of the abc2svg source and install it in your system. The abc2svg scripts must then be built from the raw source files described in the section 'Build' below. The disadvantage of this approach is that if you want to use an other or newer version you need to download a new tarball. @@ -218,24 +239,18 @@ and get the abc2svg source files containing the last changes between the official versions. Updating your files is done by `        fossil pull` `        fossil update` Building the scripts is done in the same way as with a tarball. - The repository is presently over 34Mb. + The repository is presently over 50Mb. For those unfamiliar with [fossil][14], it is an integrated software management system similar to [git](https://git-scm.com/). Chisel acts like a repository similar to [github](https://github.com/). [14]: https://fossil-scm.org/home/doc/trunk/www/index.wiki -- npm - The scripts may be installed from the `npm` repository by: - `        npm install abc2svg` - They are ready to run (web and shell with nodejs). - I upload a new npm version about once a month. - Using bookmarklets with a local installation does not work directly because of a cross-domain security hole, but this is possible by running a local HTTP server (you will also have to change the location of the scripts in the bookmarklet code). @@ -260,18 +275,17 @@ As you have seen, printing the music can be done easily with any web browser. You can automate the process of creating music sheets with **abc2svg** using shell scripts running a Javascript interpreter. -The interfaces to the various scripts are different. Below you will -find various scripts that I had to built. +The interfaces to the various interpreters are different. Below you will +find the scripts I had to built. - `abcqjs` with `qjs` [QuickJS by Fabrice Bellard and Charlie Gordon][10] -- `abcmjs` with `js78`, `js68`, `js60`, `js52` or `js24` (Mozilla JavaScript shell) +- `abcmjs` with `js78`, `js60`, `js52` or `js24` (Mozilla JavaScript shell) - `abcjsc` with `jsc` (webkit2gtk) - `abcnode` with `node` (nodeJS without module) -- `abc2svg` with `node` (nodeJS with modules) [10]: https://bellard.org/quickjs/ Each script gets the abc2svg options and ABC files from the command line and sends the generated file to `stdout` and possible errors to `stderr`. @@ -336,19 +350,14 @@ #### 2.3 PDF generation `abctopdf` is a shell script which converts ABC to PDF using one of the previous shell scripts and, either a chrome/chromium compatible web browser (settable by the environment variable 'BROWSER'), -or the program [weasyprint](https://weasyprint.org/) or -the program `rsvg-convert`. - -With `rsvg-convert`, the used music font must be installed and defined by -`%%musicfont `. - -Note also that, with `weasyprint` or `rsvg-convert`, the paper size is -forced to A4. Instructions for changing this size may be found in the -script source. +or the program [weasyprint](https://weasyprint.org/). + +Note also that, with `weasyprint`, the paper size is forced to A4. +Instructions for changing this size may be found in the script source. The output PDF document may be specified by the command line argument `-o` (default `abc.pdf`). Example: @@ -424,25 +433,27 @@ music line / text block. If you want to move these images to some other files, each one must contain the full CSS and defs. For that, insert `        %%fullsvg x` in the ABC file before rendering (see the - [fullsvg documentation](http://moinejf.free.fr/abcm2ps-doc/fullsvg.xhtml) + [fullsvg documentation](http://moinejf.free.fr/abcm2ps-doc/fullsvg.html) for more information). - Playing uses the HTML5 audio and/or the midi APIs. For audio, by default, abc2svg uses a sound font (format SF2) which is split into one file per instrument. This sound font is stored in the subdirectory `Scc1t2/`. Each instrument file is a base64 encoded javascript array. - Other sound fonts may be used. Some of them are stored in the subdirectory + + Other sound fonts may be used. Some of them are stored in the subdirectory `sf2/` (`AWE_ROM_gm` and `2MBGMGS`). Two formats are supported: raw SF2 and SF2 wrapped into javascript (the raw SF2 files can be loaded only when they are in the same HTTP domain). The shell script `sf.sh` (in `sf2/`) may be used to create the javacript files - from raw SF2 files. - The sound font to be used for playing may be defined in the ABC code + from raw SF2 files. + + The sound font to be used for playing may be defined in the ABC code by the command `%%soundfont`. E.g.: `        %%soundfont http://moinejf.free.fr/js/sf2/AWE_ROM_gm.js` - The names of the abc2svg scripts have a suffix which is the version of the core interface (actually '`-1`'). @@ -455,26 +466,61 @@ This script replaces the ABC or MEI sequences found in the (X)HTML file by SVG images of the music. The ABC sequences are searched: - first inside <script> elements with a type ["text/vnd.abc"](https://www.iana.org/assignments/media-types/text/vnd.abc) - (the script tag is removed from the page), + (the script tag is replaced by a <div>), - or inside (X)HTML elements with a class `abc` (lower case letters), - otherwise on `X:` or `%abc-` at start of line up to a XML tag at start of line. When a ABC sequence is not included in a <script> and when it contains the characters '<', '>' or '&', it must be enclosed in a XML comment or in a CDATA - (%<![CDATA[ .. %]]> - the comment or CDATA must be in a ABC comment). - See the - [%%beginml documentation](http://moinejf.free.fr/abcm2ps-doc/beginml.xhtml) + (%<![CDATA[ .. %]]> - the comment or CDATA must be in a ABC comment). + + When using <script>, it is possible to set abc2svg parameters via CSS. + For that, the <style> in the HTML <head> may contain custom + properties (properties starting with '--') and these properties are converted + to abc2svg parameters (starting with '%%') before the ABC sequence. + For instance, in the (<head>) <style> element, you can put: + `        .parm {--pagewidth:30cm; --bgcolor : yellow}` + + and in some <script ..vnd.abc..>, you set the class: + `         ABC edit with abc2svg - - +
@@ -143,11 +142,11 @@
') + abc2svg.page.user_out('') page.h = 0 }, // close_page() // handle the output flow of the abc2svg generator img_in: function(p) { @@ -331,11 +323,11 @@ page = this.page // copy a block function blkcpy(page) { while (page.blk.length) - abc2svg.page.img_out(page, page.blk.shift()) + abc2svg.page.user_out(page.blk.shift()) page.blk = null // direct output } // blkcpy() // img_in() switch (p.slice(0, 4)) { @@ -378,11 +370,11 @@ // if no overflow yet, keep the block if (page.blk) page.blk.push(p) else - abc2svg.page.img_out(page, p) + abc2svg.page.user_out(p) page.h += h break case "'; g = 2 } - svgbuf += s.replace(/<|>|&| /g, function(c){ + svgbuf += s.replace(/<|>|&/g, function(c){ switch (c) { case '<': return "<" case '>': return ">" case '&': return "&" - case ' ': return '  ' // space + nbspace } }) if (span) svgbuf += ""; gcur.cx = x + strw(s) Index: modules/sth.js ================================================================== --- modules/sth.js +++ modules/sth.js @@ -32,13 +32,13 @@ 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) + if (s.sth != undefined) s.ys = s.sth - if (s2.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 Index: modules/strtab.js ================================================================== --- modules/strtab.js +++ modules/strtab.js @@ -24,21 +24,124 @@ // is the list of the strings as ABC notes // diafret indicates the instrument has diatonic frets // The fret may be forced by a decoration format // "!" digit "s!" // where 'digit' is the string range in the string list (last string is '1') +// +// The command %%minfret permits to force upper frets. +// Syntax: +// %%minfret [ : ]* +// Each command replaces the previous value. // // This module accepts Willem Vree's tablature syntax: // https://wim.vree.org/svgParse/abc2xml.html#tab abc2svg.strtab = { // draw the tablature draw_symbols: function(of, p_v) { - var s, m, not, stb, x, y, + var s, m, not, stb, x, y, g, C = abc2svg.C, abc = this + + // draw the note heads + function draw_heads(stb, s) { + var m, not, x, y + + for (m = 0; m <= s.nhd; m++) { + not = s.notes[m] + if (not.nb < 0) + continue + x = s.x - 3 + if (not.nb >= 10) + x -= 3 + y = 3 * (not.pit - 18) + abc.out_svg('' + not.nb + '\n') + } + } // draw_heads() + + // draw the stems, flags and beams + function draw_stems(stb, s) { + if (!s.tabst) + return // don't draw any stem + var s1, s2, nfl, l, + y = stb + 3 * (s.notes[0].pit - 19) // -18) - 3 + * s.p_v.staffscale, + h = (11 + 3 * (s.notes[0].pit - 18)) * s.p_v.staffscale + + abc.out_svg('\n') + + // draw the flag(s) + if (s.nflags <= 0 + || !s.beam_end) // inside beam + return + y -= h + if (s.beam_st) { // no beam + abc.out_svg('' + + String.fromCharCode(0xe23f + 2 * s.nflags) + + '\n') + return + } + + // draw the beam(s) + s2 = s + nfl = s.nflags + while (1) { + if (s.nflags > nfl) + nfl = s.nflags + if (s.beam_st) + break + s = s.prev + } + s1 = s + l = (s2.x - s1.x).toFixed(1) + abc.out_svg('\n') + if (nfl == 1) + return +//fixme: do more levels + y += 5 + while (1) { + while (s.nflags < 2) { + s = s.next + if (s == s2) + break + } + if (s == s2) + break + s1 = s + while (s.next.nflags >= 2) { + s = s.next + if (s == s2) + break + } + l = (s.x - s1.x).toFixed(1) + abc.out_svg('\n') + if (s == s2) + break + s = s.next + if (s == s2) + break + } + } // draw_stems() if (!p_v.tab) { of(p_v) return } @@ -50,11 +153,11 @@ abc.bgn = 1 else abc.bgn++ abc.bgt = m abc.defs_add('\ -\n\ +\n\ \n\ \n\ ') abc.add_style('\n.bg' + abc.bgn + '{filter:url(#bg' + abc.bgn + ')}') } @@ -65,38 +168,44 @@ case C.KEY: case C.METER: case C.REST: s.invis = true break - case C.NOTE: - if (!s.stemless) - s.ys = -10 - break } } of(p_v) abc.glout() // output the stems - // draw the note heads stb = abc.get_staff_tb()[p_v.st].y + + // draw the stems and beams + abc.set_sscale(-1) // no scale + for (s = p_v.sym; s; s = s.next) { + switch (s.type) { + case C.GRACE: + for (g = s.extra; g; g = g.next) + draw_stems(stb, g) + break + case C.NOTE: + draw_stems(stb, s) + break + } + } + + // draw the note heads + abc.set_scale(p_v.sym) // (for draw_all_ties) abc.out_svg('\n') for (s = p_v.sym; s; s = s.next) { - if (s.type == C.NOTE) { - for (m = 0; m <= s.nhd; m++) { - not = s.notes[m] - if (not.nb < 0) - continue - x = s.x - 3 - if (not.nb >= 10) - x -= 3 - y = 3 * (not.pit - 18) - abc.out_svg('' + not.nb + '\n') - } + switch (s.type) { + case C.GRACE: + for (g = s.extra; g; g = g.next) + draw_heads(stb, g) + break + case C.NOTE: + draw_heads(stb, s) + break } } abc.out_svg('\n') }, // draw_symbols() @@ -103,38 +212,33 @@ // set a format parameter set_fmt: function(of, cmd, parm) { if (cmd == "strtab") { if (!parm) return + var p_v = this.get_curvoice() + if (!p_v) { + this.parse.tab = parm + return + } this.set_v_param("clef", "tab") if (parm.indexOf("diafret") >= 0) { this.set_v_param("diafret", true) parm = parm.replace(/\s*diafret\s*/, "") } this.set_v_param("strings", parm) return + } + if (cmd == "minfret") { + this.set_v_param("minfret", parm) + return } of(cmd, parm) }, // set_fmt() - // adjust the horizontal offset of the stems - set_width: function(of, s) { - var m, not, - abc = this, - C = abc2svg.C, - o = s.stem < 0 ? 3.5 : -2.5 - - of(s) - if (s.p_v && s.p_v.tab && s.type == C.NOTE && !s.stemless) { - for (m = 0; m <= s.nhd; m++) - s.notes[m].shhd = o - } - }, // set_width() - // change the notes when the global generation settings are done set_stems: function(of) { - var p_v, i, m, nt, n, bi, bn, strss, + var p_v, i, m, nt, n, bi, bn, strss, g, C = abc2svg.C, abc = this, s = abc.get_tsfirst(), // first symbol strs = [], // notes per staff - index = staff lstr = [] // lowest string per staff - index staff @@ -152,21 +256,96 @@ nt.nb = -1 nt.pit = 18 } nt.acc = 0 nt.invis = true - strss[i] = s.time + s.dur - if (s.nflags >= -1 && !s.stemless) { + if (!s.grace) + strss[i] = s.time + s.dur + if (s.dur <= C.BLEN / 2 && !s.stemless) { if (!lstr[st]) lstr[st] = [ 10 ] if (lstr[st][0] > i) { lstr[st][0] = i lstr[st][1] = s } s.stemless = true } } // set_pit() + + function set_notes(p_v, s) { + var i, bi, bn, nt, m, n, ns + + s.stem = -1 // down stems + + // handle the fret numbers as chord decoration + if (!s.nhd && s.a_dd) { + i = s.a_dd.length + while (--i >= 0) { + bi = strnum(s.a_dd[i].name) + if (bi >= 0) { + nt = s.notes[0] + set_pit(p_v, s, nt, bi) + break + } + } + } + delete s.a_dd + + if (s.sls) { // set the slurs above the staff + for (i = 0; i < s.sls.length; i++) { + s.sls[i].ty &= ~0x07 + s.sls[i].ty |= C.SL_ABOVE + } + } + + ls: for (m = 0; m <= s.nhd; m++) { + nt = s.notes[m] + if (nt.sls) { // if start of slur + for (i = 0; i < nt.sls.length; i++) { + nt.sls[i].ty &= ~0x07 + nt.sls[i].ty |= C.SL_ABOVE + } + } + if (nt.nb != undefined) + continue + if (nt.a_dd) { + i = nt.a_dd.length + while (--i >= 0) { + bi = strnum(nt.a_dd[i].name) + if (bi >= 0) { + set_pit(p_v, s, nt, bi) + delete nt.a_dd + continue ls + } + } + delete nt.a_dd + } + + // search the best string + bn = 100 + bi = -1 + ns = i = p_v.tab.length + while (--i >= 0) { + if (strss[i] && strss[i] > s.time) + continue + n = (p_v.diafret ? + nt.pit : nt.midi) - + p_v.tab[i] + if (n >= 0 && n < bn + && (!p_v.minfret + || !p_v.minfret[ns - i] + || n >= p_v.minfret[ns - i])) { + bi = i + bn = n + } + } + set_pit(p_v, s, nt, bi) + } + + s.y = 3 * (nt.pit - 18) + s.ymn = 0 // don't get space below the tablature + } // set_notes() // get the string number from the decoration // format is either !s! or !! function strnum(n) { n = n.match(/^([1-9])s?$/) @@ -182,13 +361,26 @@ if (m) { for (i = 0; i < p_v[n].tab.length; i++) p_v[n].tab[i] += m } } + + of() // do the normal work // loop on the notes of the voices with a tablature for ( ; s; s = s.ts_next) { + + // let a stem on the lowest string + if (s.seqst || (s.ts_prev && s.ts_prev.type == C.GRACE)) { + for (i = 0; i < lstr.length; i++) { + if (lstr[i]) { + lstr[i][1].tabst = 1 + lstr[i] = null + } + } + } + p_v = s.p_v if (!p_v.tab) continue strss = strs[s.st] if (!strss) @@ -197,87 +389,36 @@ case C.KEY: case C.REST: case C.TIME: s.invis = true default: - continue - case C.NOTE: - break - } - - s.stem = -1 // down stems - - // handle the fret numbers as chord decoration - if (!s.nhd && s.a_dd) { - i = s.a_dd.length - while (--i >= 0) { - bi = strnum(s.a_dd[i].name) - if (bi >= 0) { - nt = s.notes[0] - set_pit(p_v, s, nt, bi) - break - } - } delete s.a_dd - } - - ls: for (m = 0; m <= s.nhd; m++) { - nt = s.notes[m] - if (nt.nb != undefined) - continue - if (nt.a_dd) { - i = nt.a_dd.length - while (--i >= 0) { - bi = strnum(nt.a_dd[i].name) - if (bi >= 0) { - set_pit(p_v, s, nt, bi) - delete nt.a_dd - continue ls - } - } - delete nt.a_dd - } - - // search the best string - bn = 100 - bi = -1 - i = p_v.tab.length - while (--i >= 0) { - if (strss[i] && strss[i] > s.time) - continue - n = (p_v.diafret ? - nt.pit : nt.midi) - - p_v.tab[i] - if (n >= 0 && n < bn) { - bi = i - bn = n - } - } - set_pit(p_v, s, nt, bi) - } - - // put the stems on the lowest strings - if (!s.ts_next || s.ts_next.time != s.time) { - for (i = 0; i < lstr.length; i++) { - if (lstr[i]) { - delete lstr[i][1].stemless - lstr[i] = null - } - } - } - } - of() + continue + case C.GRACE: + if (p_v.pos.gst == C.SL_HIDDEN) + s.sappo = 0 + for (g = s.extra; g; g = g.next) { + if (p_v.pos.gst == C.SL_HIDDEN) + g.stemless = true + set_notes(p_v, g) + } + break + case C.NOTE: + set_notes(p_v, s) + break + } + } }, // set_stems() // get the parameters of the current voice set_vp: function(of, a) { var i, e, g, tab, strs, ok, p_v = this.get_curvoice() // convert a list of ABC notes into a list of MIDI pitches function abc2tab(p) { - var i, c, + var i, c, a, t = [] if (p_v.diafret) { for (i = 0; i < p.length; i++) { c = p[i] @@ -299,14 +440,24 @@ t.push(c) } } else { for (i = 0; i < p.length; i++) { c = p[i] + switch (c) { + case '^': + case '_': + a = c == '^' ? 1 : -1 + c = p[++i] + break + default: + a = 0 + break + } c = "CCDDEFFGGAABccddeffggaab".indexOf(c) if (c < 0) return // null - c += 60 + c += 60 + a while (1) { if (p[i + 1] == "'") { c += 12 i++ } else if (p[i + 1] == ",") { @@ -342,18 +493,47 @@ while (1) { str = a.shift() if (!str) break p = "CCDDEFFGGAAB".indexOf(str[0]) - o = Number(str[1]) - if (p < 0 || isNaN(o)) - return // null + if (p < 0) + return // undefined + o = str[1] + switch (o) { + case '#': + case 'b': + p += o == '#' ? 1 : -1 + o = Number(str[2]) + break + default: + o = Number(str[1]) + break + } + if (isNaN(o)) + return // undefined t.push((o + 1) * 12 + p) // C4 = 60 } } return t } // str2tab() + + // convert the list of ':' + function minfret(a) { + var sf, + sfa = a.split(' ') + + p_v.minfret = {} + while (1) { + sf = sfa.shift() + if (!sf) + break + sf = sf.split(':') + if (sf.length != 2) + break //fixme: error + p_v.minfret[sf[0]] = sf[1] + } + } //minfret() for (i = 0; i < a.length; i++) { switch (a[i]) { case "clef=": e = a[i + 1] @@ -370,26 +550,38 @@ strs = a[++i] ok = true break case "nostems": p_v.pos.stm = abc2svg.C.SL_HIDDEN + p_v.pos.gst = abc2svg.C.SL_HIDDEN break case "capo=": p_v.capo = Number(a[++i]) break case "diafret=": i++ case "diafret": p_v.diafret = true break + case "minfret=": + minfret(a[++i]) + break } } // define the elements of the tablature if (ok) { + if (!strs && this.parse.tab) { // if a global definition + strs = this.parse.tab + if (strs.indexOf("diafret") >= 0) { + p_v.diafret = true + strs = strs.replace(/\s*diafret\s*/, "") + } + } if (strs) { - if (strs[1] >= '1' && strs[1] <= '9') + e = strs.slice(-1) + if (e >= '1' && e <= '9') tab = str2tab(strs.split(',')) // W.V.'s syntax else tab = abc2tab(strs) // ABC syntax if (!tab) { this.syntax(1, "Bad strings in tablature") @@ -417,18 +609,18 @@ if (tab.length == 3) { a.push('"tab3"') if (!g.tab3) // SMuFL: -none- g.tab3 = 'TAB' } else if (tab.length == 4) { a.push('"tab4"') if (!g.tab4) // SMuFL: \ue06e g.tab4 = 'TAB' } else if (tab.length == 5) { a.push('"tab5"') if (!g.tab5) // SMuFL: -none- @@ -454,11 +646,10 @@ set_hooks: function(abc) { abc.draw_symbols = abc2svg.strtab.draw_symbols.bind(abc, abc.draw_symbols) abc.set_format = abc2svg.strtab.set_fmt.bind(abc, abc.set_format); abc.set_stems = abc2svg.strtab.set_stems.bind(abc, abc.set_stems) - abc.set_width = abc2svg.strtab.set_width.bind(abc, abc.set_width) abc.set_vp = abc2svg.strtab.set_vp.bind(abc, abc.set_vp) // define specific decorations used to force the string number var decos = abc.get_decos() decos["1s"] = "0 nul 0 0 0" @@ -468,11 +659,11 @@ decos["5s"] = "0 nul 0 0 0" decos["6s"] = "0 nul 0 0 0" if (!user.nul) user.nul = function() {} - abc.add_style("\n.bn{font:bold 8px sans-serif}") + abc.add_style("\n.bn{font:bold 7px sans-serif}") } // set_hooks() } // strtab abc2svg.modules.hooks.push(abc2svg.strtab.set_hooks) abc2svg.modules.strtab.loaded = true ADDED modules/tropt.js Index: modules/tropt.js ================================================================== --- /dev/null +++ modules/tropt.js @@ -0,0 +1,154 @@ +// tropt.js - module to optimize the notes after transposition +// +// Copyright (C) 2022 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 "%%tropt" appears in a ABC source. + +abc2svg.tropt = { + + // function called before start of the generation + set_pitch: function(of, last_s) { + if (last_s) { // if not first time + of(last_s) + return + } + var v, p_v, s, m, nt, p, a, np, na, + C = abc2svg.C, + vo_tb = this.get_voice_tb(), + nv = vo_tb.length + + // check if some next note has the same pitch without any accidental + function ok(s, p) { + var nt, m + + while (s) { + if (s.bar_type) + return 1 //true + if (s.type == C.NOTE) { + for (m = 0; m <= s.nhd; m++) { + nt = s.notes[m] + if (nt.pit == p) + return nt.acc + } + } + s = s.next + } + return 1 //true + } // ok() + + for (v = 0; v < nv; v++) { + p_v = vo_tb[v] + if (!p_v.tropt || !p_v.key.k_none) + continue + for (s = p_v.sym; s; s = s.next) { + if (s.type != C.NOTE) + continue + for (m = 0; m <= s.nhd; m++) { + nt = s.notes[m] + if (nt.tie_s) { // if end of tie + nt.pit = nt.tie_s.pit + continue + } + p = nt.pit % 7 // A..G + a = nt.acc + na = 3 + switch (a) { + case -1: + switch (p) { + case 2: // C + case 5: // F + break + default: + continue + } + np = nt.pit - 1 + break + case -2: + switch (p) { + case 2: // C + case 5: // F + na = -1 + break + } + np = nt.pit - 1 + break + case 1: + switch (p) { + case 1: // B + case 4: // E + break + default: + continue + } + np = nt.pit + 1 + break + case 2: + switch (p) { + case 1: // B + case 4: // E + na = 1 + break + } + np = nt.pit + 1 + break + default: + continue + } + if (ok(s, np)) { + nt.pit = np + nt.acc = na + } + } + } + } + of(last_s) + }, // set_pitch() + + // set the tropt parameter + do_pscom: function(of, text) { + if (text.indexOf("tropt ") == 0) + this.set_v_param("tropt", text.split(/[ \t]/)[1]) + else + of(text) + }, // do_pscom() + + // set the tropt 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] == "tropt=") { + curvoice.tropt = this.get_bool(a[i + 1]) + break + } + } + of(a) + }, // set_vp() + + set_hooks: function(abc) { + abc.do_pscom = abc2svg.tropt.do_pscom.bind(abc, abc.do_pscom) + abc.set_pitch = abc2svg.tropt.set_pitch.bind(abc, abc.set_pitch) + abc.set_vp = abc2svg.tropt.set_vp.bind(abc, abc.set_vp) + } +} // tropt + +abc2svg.modules.hooks.push(abc2svg.tropt.set_hooks) + +// the module is loaded +abc2svg.modules.tropt.loaded = 1 //true Index: package.json ================================================================== --- package.json +++ package.json @@ -1,8 +1,8 @@ { "name": "abc2svg", - "version": "1.21.6", + "version": "1.22.1", "description": "ABC music notation translator", "files": [ "COPYING", "COPYING.LESSER", "MIDI-1.js", @@ -13,10 +13,14 @@ "abc2svg.ttf", "abcdoc-1.js", "abcemb-1.js", "abcemb1-1.js", "abcemb2-1.js", + "abcjsc", + "abcqjs", + "abcmjs", + "abcnode", "abctopdf", "abcweb-1.js", "abcweb1-1.js", "abcweb2-1.js", "ambitus-1.js", @@ -41,10 +45,11 @@ "err-de.js", "err-en.js", "err-fr.js", "err-it.js", "err-pt_BR.js", + "gamelan-1.js", "grid-1.js", "grid2-1.js", "grid3-1.js", "json-1.js", "jazzchord-1.js", @@ -76,10 +81,14 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "bin": { "abc2odt": "abc2odt", "abc2svg": "abc2svg", + "abcjsc": "abcjsc", + "abcqjs": "abcqjs", + "abcmjs": "abcmjs", + "abcnode": "abcnode", "abctopdf": "abctopdf" }, "repository": { "type": "fossil", "url": "https://chiselapp.com/user/moinejf/repository/abc2svg" Index: toabc.js ================================================================== --- toabc.js +++ toabc.js @@ -1,8 +1,8 @@ // abc2svg - toabc.js - convert ABC to ABC // -// Copyright (C) 2016-2021 Jean-Francois Moine +// Copyright (C) 2016-2022 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 General Public License as published by @@ -227,11 +227,13 @@ ln += (s.clef_oct_transp ? '_' : '-') + '8' return "clef=" + ln } // clef_dump() function deco_dump(a_dd) { - var i, n, dd + var i, n, dd, + ln = "" + //fixme: check if user deco for (i = 0; i < a_dd.length; i++) { dd = a_dd[i] n = dd.name if (dd.ty) { @@ -244,22 +246,25 @@ line += deco_l[n] else line += '!' + n + '!' } } + return ln } // deco_dump function dur_dump(dur, grace) { var d = 0, - l = grace ? C.BLEN / 4 : ulen + l = grace ? C.BLEN / 4 : ulen, + ln = "" + if (dur == l) - return + return "" while (1) { if (dur % l == 0) { dur /= l if (dur != 1) - line += dur.toString() + ln += dur.toString() break } dur *= 2 if (d > 6) { abc2svg.print("% *** bad duration") @@ -266,12 +271,16 @@ break } d++ } if (d > 0) - line += "//////".slice(0, d) + ln += "//////".slice(0, d) + return ln } // dur_dump() + + var ft_w = ['', 'Thin', 'ExtraLight', 'Light', 'Regular', + 'Medium', 'Demi', 'Bold', 'ExtraBold', 'Black'] function font_def(fn, p) { var c, f, i = p.indexOf('$') @@ -280,11 +289,13 @@ if (i >= 0) { c = p[i + 1] if (c >= '1' && c <= '9') { c = "u" + c f = cfmt[c + "font"] // user font - f.fid = abc.get_font(c).fid // dump it! +// f.fid = abc.get_font(c).fid // dump it! + f.fid = 100 + +c + f.used = 1 } } font_dump() // dump the new fonts } // font_def() @@ -302,19 +313,39 @@ if (old_font[f.fid]) continue // already out old_font[f.fid] = true def = f.name || "" if (f.weight) - def += f.weight + def += ft_w[(f.weight / 100) | 0] if (f.style) - def += f.style + def += f.style[0].toUpperCase() + f.style.slice(1) if (!def) def = "*" + def += ' ' + (f.size || "*") + if (f.box) + def += ' box' + if (f.pad) + def += ' padding=' + f.pad.toFixed(1) + if (f.class) + def += ' class=' + f.class + if (f.wadj) { + def += ' wadj=' + switch (f.wadj) { + case 'spacing': + def += 'space' + break + case 'spacingAndGlyphs': + def += 'glyph' + break + default: + def += 'none' + break + } + } if (k[0] == "u") k = "setfont-" + k[1] - abc2svg.print('%%' + k + ' ' + - def + ' ' + (f.size || "*")) + abc2svg.print('%%' + k + ' ' + def) } } // font_dump() function gch_dump(a_gch) { var i, j, gch @@ -361,11 +392,12 @@ abc2svg.print(l + ':' + info[l].replace(/\n/g, '\n' + l + ':')) } } // header_dump() function key_dump(s, clef) { - var ln + var ln, i, a + if (s.k_bagpipe) { bagpipe = true // for grace notes ln = "K:H" + s.k_bagpipe } else if (s.k_drum) { ln = "K:P" @@ -373,10 +405,18 @@ ln = "K:none" } else { ln = "K:" ln += k_tb[s.k_sf + 7 + mode_tb[s.k_mode][0]] ln += mode_tb[s.k_mode][1] + } + a = s.k_a_acc // accidental list + if (a) { + if (s.k_exp) + ln += ' exp' + ln += ' ' + for (i = 0; i < a.length; i++) + ln += note_dump({}, a[i]) } if (clef && clef.clef_type != 'a') ln += ' ' + clef_dump(clef) //fixme: to continue @@ -450,86 +490,92 @@ } return ln } // meter_dump() function note_dump(s, note, tie_ch) { - var p, j, sl, s2, a, d + var p, j, sl, s2, a, d, + ln = "" + if (note.sls) { for (j = 0; j < note.sls.length; j++) { sl = note.sls[j]; - slti_dump(sl.ty, '('); + ln += slti_dump(sl.ty, '(') s2 = sl.note.s if (s2.sl2) s2.sl2++ else s2.sl2 = 1 } } if (note.a_dd) - deco_dump(note.a_dd) + ln += deco_dump(note.a_dd) if (note.color) - line += "!" + note.color + "!" + ln += "!" + note.color + "!" a = note.acc if (typeof a == "object") { d = a[1] a = a[0] if (a > 0) { - line += '^' + ln += '^' } else { - line += '_' + ln += '_' a = -a } - dur_dump(C.BLEN / 4 * a / d, true) + ln += dur_dump(C.BLEN / 4 * a / d, true) } else { switch (a) { - case -2: line += '__'; break - case -1: line += '_'; break - case 1: line += '^'; break - case 2: line += '^^'; break - case 3: line += '='; break + case -2: ln += '__'; break + case -1: ln += '_'; break + case 1: ln += '^'; break + case 2: ln += '^^'; break + case 3: ln += '='; break } } p = note.pit if (p >= 23) { - line += "abcdefg"[p % 7] + ln += "abcdefg"[p % 7] if (p >= 30) { - line += "'" + ln += "'" if (p >= 37) - line += "'" + ln += "'" } } else { p += 7; // for very low notes - line += "ABCDEFG"[p % 7] + ln += "ABCDEFG"[p % 7] if (p < 23) { - line += "," + ln += "," if (p < 16) { - line += "," + ln += "," if (p < 9) - line += "," + ln += "," } } } if (!tie_ch && note.tie_ty) - slti_dump(note.tie_ty, '-') + ln += slti_dump(note.tie_ty, '-') while (note.sl2) { - line += ')'; + ln += ')' note.sl2-- } + return ln } // note_dump() function slti_dump(fl, ty) { + var ln = "" + if (fl & C.SL_DOTTED) - line += "."; - line += ty + ln += '.' + ln += ty switch (fl & 0x07) { case C.SL_ABOVE: - line += "'" + ln += "'" break case C.SL_BELOW: - line += ',' + ln += ',' break } + return ln } // slti_dump() //fixme: missing: '+' (master voice) and '*' (floating voice) function staves_dump(s) { var v, p_v, staff, ln, @@ -692,11 +738,11 @@ if (s.tp) tuplet_dump(s) if (s.sls) { for (i = 0; i < s.sls.length; i++) { sl = s.sls[i]; - slti_dump(sl.ty, '('); + line += slti_dump(sl.ty, '(') if (sl.is_note) { if (sl.note.sl2) sl.note.sl2++ else sl.note.sl2 = 1 @@ -709,11 +755,11 @@ } } if (s.a_gch) gch_dump(s.a_gch) if (s.a_dd) - deco_dump(s.a_dd) + line += deco_dump(s.a_dd) if (s.color) line += "!" + s.color + "!" switch (s.type) { case C.BAR: if (s.beam_on) @@ -770,21 +816,21 @@ } } } tie_ch = s.ti1 if (s.notes.length == 1) { - note_dump(s, s.notes[0], tie_ch) + line += note_dump(s, s.notes[0], tie_ch) } else { for (i = 0; i < s.notes.length; i++) { if (!s.notes[i].tie_ty) { tie_ch = false break } } line += '[' for (i = 0; i < s.notes.length; i++) - note_dump(s, s.notes[i], tie_ch); + line += note_dump(s, s.notes[i], tie_ch) line += ']' } if (s.grace) { if (bagpipe) { tmp = s.dur * 8 @@ -791,19 +837,19 @@ } else { tmp = s.dur * 2 if (s.prev || s.next) tmp *= 2 } - dur_dump(tmp, true) + line += dur_dump(tmp, true) } else { tmp = s.notes[0].dur // head duration if (s.trem2) tmp /= 2; - dur_dump(tmp) + line += dur_dump(tmp) } if (tie_ch) - slti_dump(s.notes[0].tie_ty, '-'); + line += slti_dump(s.notes[0].tie_ty, '-') tmp = s.sl2 while (tmp) { line += ')'; tmp-- } @@ -822,12 +868,11 @@ } } tempo_dump(s) break case C.REST: - line += s.invis ? 'x' : 'z'; - dur_dump(s.dur_orig) + line += (s.invis ? 'x' : 'z') + dur_dump(s.dur_orig) break case C.SPACE: line += 'y' if (s.width != 10) line += s.width @@ -898,11 +943,11 @@ vo[v] = "" vti[v] = 0 } if (info.Q) { - abc2svg.print("Q: " + info.Q) + abc2svg.print("Q:" + info.Q) for (s = tsfirst; s; s = s.ts_next) { if (s.time) break if (s.type == C.TEMPO) s.del = true @@ -936,12 +981,11 @@ // (all voices are synchronized on %%score) if (s.type != C.STAVES && s.time > vti[v]) { //fixme: put 'X' if more than one measure if (s.time > vti[v] + 2) { - line += 'x'; - dur_dump(s.time - vti[v]); + line += 'x' + dur_dump(s.time - vti[v]) } vti[v] = s.time } sym_dump(s) if (s.dur) Index: tohtml.js ================================================================== --- tohtml.js +++ tohtml.js @@ -1,8 +1,8 @@ // abc2svg - tohtml.js - HTML+SVG generation // -// Copyright (C) 2014-2021 Jean-Francois Moine +// Copyright (C) 2014-2022 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 General Public License as published by @@ -15,11 +15,11 @@ // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with abc2svg. If not, see . - var init_done, pw, ml, mr, pkf, lkf + var init_done, pw, ml, mr, pkf, lkf, fn // replace <>& by XML character references function clean_txt(txt) { return txt.replace(/<|>|&.*?;|&/g, function(c) { switch (c) { @@ -147,11 +147,11 @@ } return psty } // entry point from cmdline -abc2svg.abc_init = function() { +abc2svg.abc_init = function(args) { var cfmt = abc.cfmt() // output a header or footer function gen_hf(type, str) { var i, page, @@ -180,10 +180,15 @@ user.page_format = true // output the html header user.img_out = function(str) { + if (init_done) { + abc2svg.print(str) + return + } + var header = cfmt.header, footer = cfmt.footer, topmargin = cfmt.topmargin || "1cm", botmargin = cfmt.botmargin || "1cm", media_s = '@media print {\n\ @@ -249,11 +254,11 @@ abc2svg.print('@page{size:' + (cfmt.pagewidth / 96).toFixed(2) + 'in ' + (cfmt.pageheight / 96).toFixed(2) + 'in;margin:0}') abc2svg.print('\n\ - ' + abc.parse.fname.replace(/.*\//, '') +<title>' + fn.replace(/.*\//,'') + '\n\ ') if (header || footer) { if (header) gen_hf("header", header) @@ -271,13 +276,22 @@ init_done = 1 } // output the first generated string abc2svg.print(str) + } + + // get the main ABC source file name + for (var i = 0; i < args.length; i++) { + var a = args[i] - // change the output function - user.img_out = abc2svg.print + if (a[0] == '-') { + i++ + continue + } + fn = a + break } } abc2svg.abc_end = function() { var font_style = abc.get_font_style() Index: tonotes.js ================================================================== --- tonotes.js +++ tonotes.js @@ -1,8 +1,8 @@ // abc2svg - tonotes.js - convert ABC to a list of MIDI notes // -// Copyright (C) 2021 Jean-Francois Moine +// 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 General Public License as published by @@ -35,12 +35,13 @@ // don't load tohtml.js abc2svg.abc_init = function(args) { } -// but load the sound generator +// but load the sound generator and the chord generator abc2svg.loadjs('util/sndgen.js') +abc2svg.loadjs('util/chord.js') abc2svg.abc_end = function() { function pit(v) { if (v - (v | 0)) return v.toFixed(2) @@ -53,18 +54,33 @@ conf: { // configuration speed: 1 }, tgen: 3600, // generate by (for) 1 hour get_time: function() { return -.3}, // (move the time origin) - midi_ctrl: function(po, s, t) { }, + midi_ctrl: function(po, s, t) { + abc2svg.print(' v:' + s.v + + ' MIDI control ' + s.ctrl + ' ' + s.val) + }, // midi_ctrl() + midi_prog: function(po, s) { + if (s.instr) + abc2svg.print(' v:' + s.v + + ' MIDI program ' + + (s.chn + 1) + ' ' + + s.instr) + else + abc2svg.print(' v:' + s.v + + ' MIDI channel ' + (s.chn + 1)) + }, // midi_prog() note_run: function(po, s, k, t, d) { - abc2svg.print(' '+ (t * 100).toFixed(0) + - '\t' + s.instr + + abc2svg.print(' ' + (t * 100).toFixed(0) + + '\t' + po.c_i[po.v_c[s.v]] + '\t' + pit(k) + '\t' + (d * 100).toFixed(0) + '\t' + vn[s.p_v.id]) - } // note_run() + }, // note_run() + v_c: [], // voice to channel + c_i: [] // channel to instrument }, tunes = abc.tunes.slice(0) // get a copy of the generated tunes // define the voice numbers function dvn(v_tb) { Index: toxhtml.js ================================================================== --- toxhtml.js +++ toxhtml.js @@ -239,11 +239,11 @@ (cfmt.pageheight / 96).toFixed(2) + 'in;margin:0}') else abc2svg.print('@page{margin:' + topmargin + ' 0 ' + botmargin + ' 0}') abc2svg.print('\n\ - ' + abc.parse.fname.replace(/.*\//, '') + '\n\ +' + abc.parse.fname.replace(/.*\//, '') + '\n\ \n\ ') if (header) gen_hf("header", header) if (footer) Index: util/abcdoc.js ================================================================== --- util/abcdoc.js +++ util/abcdoc.js @@ -71,19 +71,21 @@ }) } // function to load javascript files abc2svg.loadjs = function(fn, relay, onerror) { - var s = document.createElement('script'); + var s = document.createElement('script') if (/:\/\//.test(fn)) s.src = fn // absolute URL else - s.src = jsdir + fn; - if (relay) - s.onload = relay; - s.onerror = onerror || function() { - alert('error loading ' + fn) + s.src = jsdir + fn + s.onload = relay + s.onerror = function() { + if (onerror) + onerror(fn) + else + alert('error loading ' + fn) } document.head.appendChild(s) } // search the ABC tunes and add their rendering as SVG images @@ -96,14 +98,10 @@ if (page.indexOf('\n' + re.lastIndex = ss // new HTML start + } + out += src.slice(ss) // copy the last HTML sequence + if (abc2svg.page && html) + out += '\ +
\
+Printing may be bad because the file contains pure HTML and %%pageheight\
+
\n' + document.body.innerHTML = out + } // move_music() + + // save the music sequences and replace them by empty
's + function save_music() { + var i, k, div, c, s, sa + + // save MEI sequences + function save_spe(ty) { + var s, div + while (1) { + s = document.getElementsByTagName(ty)[0] + if (!s) + break + div = document.createElement('div') + abc2svg.music.push({ + n: 'mus' + abc2svg.music.length, + t: s.outerHTML, + d: div + }) + s.parentNode.replaceChild(div, s) + } + } // save_spe() + + // get the global parameters + abc2svg.music = [{ + t: "", // global parameters + n: "mus0" + }] + + // get the parameters from the query string of the URL + k = location.search + if (k) { + k = k.substr(1).split("&") + for (i = 0; i < k.length; i++) + abc2svg.music[0].t += "%%" + + decodeURIComponent(k[i].replace('=', ' ')) + + '\n' + } + + // search the ', i) - ss = j + 10 - break - default: // < .. class="abc" - j = i - - i = res[2].index(' ', i) // get the tag - t = res[2].slice(1, i) - - i = page.indexOf('>', j) - i = page.indexOf('\n', i) + 1 - new_page += page.slice(j, i) // keep the element - j = page.indexOf('', i) - ss = j + t.length + 4 - break - } - } else { // %abc or X: - - // get the end of the ABC sequence - // including the %%beginxxx/%%endxxx sequences - re_stop.lastIndex = i - while (1) { - res = re_stop.exec(page) - if (!res || res[0] == "\n<") - break - k = page.indexOf(res[0].replace("begin", "end"), - re_stop.lastIndex) - if (k < 0) - break - re_stop.lastIndex = k - } - if (!res || k < 0) - j = page.length - else - j = re_stop.lastIndex - 1 - ss = j - } - if (j < 0) { -//fixme error -console.log('error i/j '+i+' '+j) - return - } - - re.lastIndex = ss // new HTML start -// src = page.slice(i, j) // music sequence -// if (abc2svg.modules.load(src, render1)) -// render1() -// } // extract() - - // render one text music sequence - // @page is the content of the page - // @ss and @se are the start and end indexes of the music sequence - // i,j are the start and stop of the music inside the page source -// function render1() { - abc.tosvg(app, page, i, j) - abc2svg.abc_end() // close the page if %%pageheight - - if (errtxt) { - new_page += '
'
-					+ errtxt + "
\n" - errtxt = "" - } - - // try the next sequence - extract() - } // render1() - - // get the ABC insertion mode - if (/]*type="text\/vnd.abc"/.test(page)) - re = ']*type="text\/vnd.abc"' - else if (/<[^>]* class="[^"]*abc[^"]*/.test(page)) // " - re = '<[^>]* class="[^"]*abc[^"]*' - else - re = '%abc-\\d|X:\\s*\\d' - - re = new RegExp('(^|\n)(' + re + ')', 'g') - - // prepare the generation of the music sequences - abc = new abc2svg.Abc(user) - if (typeof follow == "function") // initialize the play follow function - follow(abc, user, playconf) - abc.tosvg(app, glopar) - - extract() // start -} // render() - - // load the %%abc-include files - function include() { - var i, j, fn, r, - k = 0 - - while (1) { - i = page.indexOf('%%abc-include ', k) - if (i < 0) { - render() - return - } - i += 14 - j = page.indexOf('\n', i) - fn = page.slice(i, j).trim() - if (!a_inc[fn]) - break - k = j - } - - // %%abc-include found: load the file - r = new XMLHttpRequest() - r.open('GET', fn, true) // (async) - r.onload = function() { - if (r.status === 200) { - a_inc[fn] = r.responseText - if (abc2svg.modules.load(a_inc[fn], include)) - include() - } else { - a_inc[fn] = '%\n' - alert('Error getting ' + fn + '\n' + r.statusText) - include() - } - } - r.onerror = function () { - a_inc[fn] = '%\n' - alert('Error getting ' + fn + '\n' + r.statusText) - include() - } - r.send() - } // include() - // --- dom_loaded() main code --- // load the abc2svg core if not done by