abc2svg
Check-in Differences
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From afc1222e0282b772 To d6636870ae43e9ac

2023-01-07
14:29
modules: nns: fix: add 'roman=2' to %%nns and fix some chords - reported by Robert Bley-Vroman ... (check-in: 8365017fa6 user: jef tags: trunk)
07:38
New release v1.22.6 ... (check-in: d6636870ae user: jef tags: trunk, v1.22.6)
07:35
source: copyright update ... (check-in: d49a8df607 user: jef tags: trunk)
2022-02-16
15:53
core: fix: bad setting of voice parameters in the tune header with possible crash - reported by Hudson Lacerda ... (check-in: 8a9b5595a4 user: jef tags: trunk)
2022-02-12
17:42
New release v1.21.6 ... (check-in: afc1222e02 user: jef tags: trunk, v1.21.6)
15:42
core: add !caesura!, !tick! and !short! ... (check-in: 21e5a0d63b user: jef tags: trunk)

Changes to README.md.
︙︙
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

The **abc2svg** core is based on the
[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.

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"

### 1. Web browser usage







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

The **abc2svg** core is based on the
[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 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"

### 1. Web browser usage
︙︙
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
search on the internet the name of the browser and keywords such as
'bookmarklet' and 'javascript in url'). To edit the URL, extract
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
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
<a href="javascript:(function(){var%20s,n=2,d=document,b=d.body;d.head.innerHTML='&lt;style&gt;svg{display:block};@media print{body{margin:0;padding:0;border:0};.nop{display:none}}&lt;/style&gt;\n';b.innerHTML='\n%25abc-2.2%25%3c!--\n'+b.textContent+'%25--%3e\n';function%20f(u){s=d.createElement('script');s.src='http://moinejf.free.fr/js/'+u;s.onload=function(){if(--n==0)dom_loaded()};d.head.appendChild(s)};f('abcweb-1.js');f('snd-1.js')})()"
title="Copy me">first abc2svg bookmarklet</a>
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.  
To print or convert the music to a PDF file,
simply click on the 'Print' button of the web browser.
(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
<a href="javascript:(function(){var%20s,n=2,d=document,b=d.body;d.head.innerHTML='&lt;style&gt;svg{display:block};@media print{body{margin:0;padding:0;border:0};.nop{display:none}}&lt;/style&gt;\n';b.innerHTML='\n%25abc-2.2%25%3c!--\n'+b.textContent+'%25--%3e\n';function%20f(u){s=d.createElement('script');s.src='http://moinejf.free.fr/js/'+u;s.onload=function(){if(--n==0)dom_loaded()};d.head.appendChild(s)};f('abcweb1-1.js');f('snd-1.js')})()"
title="Copy me">second bookmarklet</a>.
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
you to return to the tune list or to modify the music. With this last option,
you can adjust the page size before printing, correct the ABC syntax or







|








|












|







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
search on the internet the name of the browser and keywords such as
'bookmarklet' and 'javascript in url'). To edit the URL, extract
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
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
<a href="javascript:(function(){var%20s,n=2,d=document,b=d.body;d.head.innerHTML='%3cstyle%3esvg{display:block};@media print{body{margin:0;padding:0;border:0};.nop{display:none}}%3c/style%3e\n';b.innerHTML='%3cscript type=%22text/vnd.abc%22%3e\n'+b.textContent+'%3c/script%3e\n';function%20f(u){s=d.createElement('script');s.src='http://moinejf.free.fr/js/'+u;s.onload=function(){if(--n==0)dom_loaded()};d.head.appendChild(s)};f('abcweb-1.js');f('snd-1.js')})()"
title="Copy me">first abc2svg bookmarklet</a>
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.  
To print or convert the music to a PDF file,
simply click on the 'Print' button of the web browser.
(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
<a href="javascript:(function(){var%20s,n=2,d=document,b=d.body;d.head.innerHTML='%3cstyle%3esvg{display:block};@media print{body{margin:0;padding:0;border:0};.nop{display:none}}%3c/style%3e\n';b.innerHTML='%3cscript type=%22text/vnd.abc%22%3e\n'+b.textContent+'%3c/script%3e\n';function%20f(u){s=d.createElement('script');s.src='http://moinejf.free.fr/js/'+u;s.onload=function(){if(--n==0)dom_loaded()};d.head.appendChild(s)};f('abcweb1-1.js');f('snd-1.js')})()"
title="Copy me">second bookmarklet</a>.
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
you to return to the tune list or to modify the music. With this last option,
you can adjust the page size before printing, correct the ABC syntax or
︙︙
105
106
107
108
109
110
111













112
113
114
115
116
117
118
If your ABC files contain `%%abc-include`, then you must:

  - 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 a US keyboard, its behaviour can be changed for easier music
entering by one of these bookmarklets:
<a href="javascript:(function(){if(typeof%20abc2svg.loadjs=='function'){abc2svg.loadjs('abckbd-1.js')}else{alert('use%20with%20abc2svg%20editor')}})()"
title="Copy me">keyboard 1</a>
and
<a href="javascript:(function(){if(typeof%20abc2svg.loadjs=='function'){abc2svg.loadjs('abckbd2-1.js')}else{alert('use%20with%20abc2svg%20editor')}})()"







>
>
>
>
>
>
>
>
>
>
>
>
>







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
If your ABC files contain `%%abc-include`, then you must:

  - 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:
<a href="javascript:(function(){if(typeof%20abc2svg.loadjs=='function'){abc2svg.loadjs('abckbd-1.js')}else{alert('use%20with%20abc2svg%20editor')}})()"
title="Copy me">keyboard 1</a>
and
<a href="javascript:(function(){if(typeof%20abc2svg.loadjs=='function'){abc2svg.loadjs('abckbd2-1.js')}else{alert('use%20with%20abc2svg%20editor')}})()"
︙︙
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170
171
172
173
174
(Note that, in the example, the paths are relative - see why below.)

[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:  
`        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].

[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
"J'ai du bon tabac" with `pagescale=1.2` (giving `%%pagescale 1.2`).

[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
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].

[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 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 '`#`'
followed by a [regular expression][7]
to be applied to the tune headers.







|


|
|












|
|



>
|
<




|







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

176
177
178
179
180
181
182
183
184
185
186
187
(Note that, in the example, the paths are relative - see why below.)

[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 style about SVG elements:  
`        svg { display: block }`

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
"J'ai du bon tabac" with `pagescale=1.2` (giving `%%pagescale 1.2`).

[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 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.)
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 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 '`#`'
followed by a [regular expression][7]
to be applied to the tune headers.
︙︙
187
188
189
190
191
192
193








194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#### 1.4 Installing abc2svg on your system or on your server

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:









- [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
  run immediately in your machine without connecting to my site.  
  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.  
  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.

[13]: https://chiselapp.com/user/moinejf/repository/abc2svg/timeline

- fossil clone  
  If you can get the fossil program (one binary) for your system,
  you may clone the chisel repository by  
  `        fossil clone https://chiselapp.com/user/moinejf/repository/abc2svg abc2svg.fossil`  
  `        fossil open abc2svg.fossil`  
  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.

  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).

If you have write access on a remote server, you may put there
the abc2svg scripts. There is no automatic process to do that.







>
>
>
>
>
>
>
>











|

















|







<
<
<
<
<
<







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251






252
253
254
255
256
257
258
#### 1.4 Installing abc2svg on your system or on your server

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
  run immediately in your machine without connecting to my site.  
  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
  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.

[13]: https://chiselapp.com/user/moinejf/repository/abc2svg/timeline

- fossil clone  
  If you can get the fossil program (one binary) for your system,
  you may clone the chisel repository by  
  `        fossil clone https://chiselapp.com/user/moinejf/repository/abc2svg abc2svg.fossil`  
  `        fossil open abc2svg.fossil`  
  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 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







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).

If you have write access on a remote server, you may put there
the abc2svg scripts. There is no automatic process to do that.
︙︙
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

#### 2.1 abc2svg shell scripts

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.

- `abcqjs` with `qjs` [QuickJS by Fabrice Bellard and Charlie Gordon][10]
- `abcmjs` with `js78`, `js68`, `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`.  
The general syntax of the command line is:  
`        script [script.js] [options] ABC_file [[options] ABC_file]* [options]`







|
|


|


<







273
274
275
276
277
278
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293

#### 2.1 abc2svg shell scripts

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 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`, `js60`, `js52` or `js24` (Mozilla JavaScript shell)
- `abcjsc` with `jsc` (webkit2gtk)
- `abcnode` with `node` (nodeJS without module)


[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`.  
The general syntax of the command line is:  
`        script [script.js] [options] ABC_file [[options] ABC_file]* [options]`
︙︙
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  This script just outputs the abc2svg parameters.

#### 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 <fontname>`.

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.

The output PDF document may be specified by the command line argument `-o`
(default `abc.pdf`).

Example:  
`        abctopdf my_file.abc -o my_file.pdf`








|
<

<
<
<
|
|
<







348
349
350
351
352
353
354
355

356



357
358

359
360
361
362
363
364
365
  This script just outputs the abc2svg parameters.

#### 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/).





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:  
`        abctopdf my_file.abc -o my_file.pdf`

︙︙
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436

437
438
439
440
441
442

443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467













468
469
470
471
472
473
474
475






















476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511

- The music is displayed as SVG images. There is one image per
  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)
  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
  `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
  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`').

#### 4.3 More about the web scripts

Here are the scripts which are used in a web context:

- `abcweb-1.js`  
  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 &lt;script&gt; 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),
  - 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 &lt;script&gt; and when it contains
  the characters '<', '>' or '&',
  it must be enclosed in a XML comment or in a CDATA
  (%&lt;![CDATA[ .. %]]&gt; - the comment or CDATA must be in a ABC comment).  













  See the
  [%%beginml documentation](http://moinejf.free.fr/abcm2ps-doc/beginml.xhtml)
  for an example, and here is [how to put inline music in HTML][15].   
  Playing and highlighting the played notes may be offered loading
  the script `snd-1.js` (see below).

[15]: http://moinejf.free.fr/abcm2ps-doc/inline.html "abc2svg - inline music"























- `abcweb1-1.js`  
  The page body is analyzed as a ABC file and its content is
  replaced by music as SVG images.  
  If the page contains reserved XML characters ('&lt;', '&gt;' and '&amp;'),
  the ABC code must be enclosed in a
  `         <script type="text/vnd.abc"> .. </script>`
  sequence.

	When there are many tunes in the file, the script displays a list
  of the tunes. The list step may be bypassed when the URL of the file
  contains a regular expression in the 'hash' value ('#' followed by
  a string at the end of the URL).
  This string does a
  [%%select](http://moinejf.free.fr/abcm2ps-doc/select.xhtml).

	When one or many tunes are displayed, a menu in the top/right corner
  offers to go back to the tune list or to modify the ABC source.
  
- `snd-1.js`  
  This script may be used with `abcweb-1.js` or `abcweb1-1.js`
  to play the rendered ABC music.  

- `abcdoc-1.js`  
  This script is mainly used for ABC documentation.
  It lets the ABC source sequences in the page before the SVG images.  
  See the source of
  [abcm2ps/abc2svg features](http://moinejf.free.fr/abcm2ps-doc/features.html)
  for an example.

### 5. MEI support

As an experimental feature, an extended core `mei2svg-1.js` may be generated.
This one may handle both the ABC and
[MEI](https://music-encoding.org/ "Music Encoding Initiative")
notations.








|







>
|




|
>
|
















|






|
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

















|




<
<
<
<
<
<
<







431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543







544
545
546
547
548
549
550

- The music is displayed as SVG images. There is one image per
  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.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
  `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
  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`').

#### 4.3 More about the web scripts

Here are the scripts which are used in a web context:

- `abcweb-1.js`  
  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 &lt;script&gt; elements with a type
    ["text/vnd.abc"](https://www.iana.org/assignments/media-types/text/vnd.abc)
    (the script tag is replaced by a &lt;div&gt;),
  - 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 &lt;script&gt; and when it contains
  the characters '<', '>' or '&',
  it must be enclosed in a XML comment or in a CDATA
  (%&lt;![CDATA[ .. %]]&gt; - the comment or CDATA must be in a ABC comment).

	When using &lt;script&gt;, it is possible to set abc2svg parameters via CSS.
  For that, the &lt;style&gt; in the HTML &lt;head&gt; 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 (&lt;head&gt;) &lt;style&gt; element, you can put:  
  `        .parm {--pagewidth:30cm;  --bgcolor : yellow}`

	and in some &lt;script ..vnd.abc..&gt;, you set the class:  
  `        <script class="parm" type="text/vnd.abc">`

	This defines the page width and the background color of the generated music.

	See the
  [%%beginml documentation](http://moinejf.free.fr/abcm2ps-doc/beginml.html)
  for an example, and here is [how to put inline music in HTML][15].   
  Playing and highlighting the played notes may be offered loading
  the script `snd-1.js` (see below).

[15]: http://moinejf.free.fr/abcm2ps-doc/inline.html "abc2svg - inline music"

    This script also accepts a parameter `with_source`.
    When this parameter is set, the music source is included before the
    SVG images of the music. An argument `nohead` prevents displaying
    the source of the first music sequence. The music source is displayed
    in a &lt;pre&gt; element of class `source`. The SVG's are included
    in a &lt;div&gt; of the same class `source`.
    The source may be displayed either above (default) or
    at the left side of the music (using a style as
    `.source{display: inline-block; vertical-align: top}`).
    See the source of
    [abcm2ps/abc2svg features](http://moinejf.free.fr/abcm2ps-doc/features.html)
    for an example.

    The music source may be editable.
    To change it, the script contains two functions:  
    - `abc2svg.get_music` returns the source of the music sequence
       (in &lt;script&gt; type "text/vnd.abc", class="abc" or inlined ABC)
       Its argument is the HTML &lt;div&gt; element that contains the music.  
    - `abc2svg.set_music` has two arguments, the HTML &lt;div&gt; element
       and the new source of the music.
       It generates and replaces the music in the &lt;div&gt;.

- `abcweb1-1.js`  
  The page body is analyzed as a ABC file and its content is
  replaced by music as SVG images.  
  If the page contains reserved XML characters ('&lt;', '&gt;' and '&amp;'),
  the ABC code must be enclosed in a
  `         <script type="text/vnd.abc"> .. </script>`
  sequence.

	When there are many tunes in the file, the script displays a list
  of the tunes. The list step may be bypassed when the URL of the file
  contains a regular expression in the 'hash' value ('#' followed by
  a string at the end of the URL).
  This string does a
  [%%select](http://moinejf.free.fr/abcm2ps-doc/select.xhtml).

	When one or many tunes are displayed, a menu in the top/right corner
  offers to go back to the tune list or to modify the ABC source.

- `snd-1.js`  
  This script may be used with `abcweb-1.js` or `abcweb1-1.js`
  to play the rendered ABC music.  








### 5. MEI support

As an experimental feature, an extended core `mei2svg-1.js` may be generated.
This one may handle both the ABC and
[MEI](https://music-encoding.org/ "Music Encoding Initiative")
notations.

︙︙
Deleted abc2svg.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/usr/bin/env node

// abc2svg with native nodeJS

// define the system functions
    var	sep = require('path').sep

// load the abc2svg core
var m = require("abc2svg/abc2svg-1");
global.abc2svg = m.abc2svg;
global.Abc = m.Abc;

// interpreter specific functions
abc2svg.print = function(str) {
	process.stdout.write(str + '\n')
}
abc2svg.printErr = function(str) {
	process.stderr.write(str + '\n')
}
abc2svg.quit = function() {
	process.exit(1)
}
abc2svg.readFile = function(fname) {
    var	fs = require("fs")

	try {
		return fs.readFileSync(fname).toString()
	} catch(e) {
	}
	return null
}

abc2svg.get_mtime = function(fn) {
    var	fs = require("fs")

	return fs.statSync(fn).mtime
}

abc2svg.loadjs = function(fn, relay, onerror) {
	try {
		var geval = eval;
		geval(abc2svg.readFile(__dirname + sep + fn))
		if (relay)
			relay()
	} catch(e) {
		if (onerror)
			onerror()
		else
			abc2svg.printErr('Cannot read file ' + fn)
	}
}

// load the command line
m = require("abc2svg/cmdline");
global.user = m.user;
global.abc_cmd = m.abc_cmd

// set the ABC path
var path = process.env.ABCPATH
if (path)
	abc2svg.path = path.split(':')

// main
var args = process.argv
args.shift();
args.shift()

abc_cmd("abc2svg", args, "nodeJS")
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








































































































































Changes to abc2svg.ttf.

cannot compute difference between binary files

Deleted abcjs24.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env js24
// abc2svg with js24 (Mozilla JavaScript shell - Spidermonkey
//		packages: debian: libmozjs-24-bin, voidlinux: mozjs24)

// interpreter specific functions
var abc2svg = {
    print: function(str) {
	print(str)
    },
    printErr: function(str) {
	printErr(str)
    },
    quit: function() {
	quit(1)
    },
    readFile: function(fname) {
	return read(fname)
    },
    loadjs: function(fn, relay, onerror) {
	try {
		loadRelativeToScript(fn)
		if (relay)
			relay()
	} catch(e) {
		if (onerror)
			onerror()
		else
			printErr('Cannot read file ' + fn)
	}
    } // loadjs()
} // abc2svg

loadRelativeToScript("abc2svg-1.js")
loadRelativeToScript("cmdline.js")

// main

	abc_cmd("abcjs24", scriptArgs, "js24 (SpiderMonkey)")
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































Deleted abcjs52.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env js52
// abc2svg with js52 (Mozilla JavaScript shell - Spidermonkey
//		packages: debian: libmozjs-52-dev, voidlinux: mozjs52)

// interpreter specific functions
var abc2svg = {
    print: function(str) {
	print(str)
    },
    printErr: function(str) {
	printErr(str)
    },
    quit: function() {
	quit(1)
    },
    readFile: function(fname) {
	return read(fname)
    },
    loadjs: function(fn, relay, onerror) {
	try {
		loadRelativeToScript(fn)
		if (relay)
			relay()
	} catch(e) {
		if (onerror)
			onerror()
		else
			printErr('Cannot read file ' + fn)
	}
    } // loadjs()
} // abc2svg

loadRelativeToScript("abc2svg-1.js")
loadRelativeToScript("cmdline.js")

// main

	abc_cmd("abcjs52", scriptArgs, "js52 (SpiderMonkey)")
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































Deleted abcjs60.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env js60
// abc2svg with js60 (Mozilla JavaScript shell - Spidermonkey
//		packages: debian: libmozjs-60-dev, voidlinux: mozjs60)

// interpreter specific functions
var abc2svg = {
    print: function(str) {
	print(str)
    },
    printErr: function(str) {
	printErr(str)
    },
    quit: function() {
	quit(1)
    },
    readFile: function(fname) {
	return read(fname)
    },
    loadjs: function(fn, relay, onerror) {
	try {
		loadRelativeToScript(fn)
		if (relay)
			relay()
	} catch(e) {
		if (onerror)
			onerror()
		else
			printErr('Cannot read file ' + fn)
	}
    } // loadjs()
} // abc2svg

loadRelativeToScript("abc2svg-1.js")
loadRelativeToScript("cmdline.js")

// main

	abc_cmd("abcjs60", scriptArgs, "js60 (SpiderMonkey)")
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












































































Changes to abcnode.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

29
30
31
32
33
34
35
:;# abc2svg with raw nodeJS

# search where are the abc2svg scripts
p=$0
if [ `dirname $p` = . ]; then
	p=`which $p`
fi
if [ -L $p ]; then
	p=`realpath $p`
fi
p=`dirname $p`

cat ${p}/abc2svg-1.js ${p}/cmdline.js > /tmp/toto.js
case $1 in
	*.js)
		cat $1 >> /tmp/toto.js
		if grep 'abc2svg.abc_init =' "$1" > /dev/null; then
			x="no"
		fi
		shift
		;;
esac
if [ "x$x" != 'xno' ]; then
	cat ${p}/tohtml.js >> /tmp/toto.js
fi

cat <<EOF >> /tmp/toto.js
    var	path='${p}/'


// interpreter specific functions
abc2svg.print = function(str) {
	process.stdout.write(str + '\n')
}
abc2svg.printErr = function(str) {
	process.stderr.write(str + '\n')












<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
|
>







1
2
3
4
5
6
7
8
9
10
11
12













13

14
15
16
17
18
19
20
21
22
:;# abc2svg with raw nodeJS

# search where are the abc2svg scripts
p=$0
if [ `dirname $p` = . ]; then
	p=`which $p`
fi
if [ -L $p ]; then
	p=`realpath $p`
fi
p=`dirname $p`














node - "$@" <<EOF

    var	path='${p}/',
	abc2svg = {}

// interpreter specific functions
abc2svg.print = function(str) {
	process.stdout.write(str + '\n')
}
abc2svg.printErr = function(str) {
	process.stderr.write(str + '\n')
︙︙
63
64
65
66
67
68
69
70
71
72
73
74
75
76

77
78
79


80


81
82



83
84
85
86
		if (onerror)
			onerror()
		else
			abc2svg.printErr('Cannot read file ' + fn)
	}
}

// (this file is wrapped)
global.Abc = Abc
global.abc2svg = abc2svg

// main
    var	args = process.argv,
	i = '${ABCPATH}'

	if (i)
		abc2svg.path = i.split(':')



	args.shift()


	args.shift()
	global.user = user;		// (for modules/page.js)



	abc_cmd("abcnode", args, "nodeJS")
EOF

node /tmp/toto.js "$@"







<
<
<
<



>



>
>
|
>
>
|

>
>
>


<
<
50
51
52
53
54
55
56




57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75


		if (onerror)
			onerror()
		else
			abc2svg.printErr('Cannot read file ' + fn)
	}
}





// main
    var	args = process.argv,
	i = '${ABCPATH}'

	if (i)
		abc2svg.path = i.split(':')

	abc2svg.loadjs("abc2svg-1.js")
	abc2svg.loadjs("cmdline.js")

	global.Abc = Abc
	global.abc2svg = abc2svg

	global.user = user;		// (for modules/page.js)

	args.shift()			// (remove 'node')
	args.shift()			// (remove the '-')
	abc_cmd("abcnode", args, "nodeJS")
EOF


Changes to abctopdf.
︙︙
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
	if [ $bdir != '.' ]; then
		bdir="$bdir/"
	else
		bdir=''
	fi
fi
# (sorted from fastest to slowest in ARM machines)
for c in qjs jsc js24 js52 js60 d8 abc2svg node end; do
	if [ $c = 'end' ]; then
		echo "$0: could not find a javascript interpreter - abort"
		exit 1
	fi
	if command -v $c >/dev/null 2>&1; then
		case $c in
		(qjs) c=abcqjs;;
		(jsc) c=abcjsc;;
		(js24) c=abcjs24;;
		(js52) c=abcjs52;;
		(js60) c=abcjs60;;

		(d8) c=abcv8;;
		(abc2svg) c=abc2svg;;
		(node) c=abcnode;;
		esac
		if command -v $c >/dev/null 2>&1; then
			abcscr=$c
			break
		fi
		if command -v $bdir$c >/dev/null 2>&1; then







|








|
|
|
>

<







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

58
59
60
61
62
63
64
	if [ $bdir != '.' ]; then
		bdir="$bdir/"
	else
		bdir=''
	fi
fi
# (sorted from fastest to slowest in ARM machines)
for c in qjs jsc js78 js60 js52 js24 d8 node end; do
	if [ $c = 'end' ]; then
		echo "$0: could not find a javascript interpreter - abort"
		exit 1
	fi
	if command -v $c >/dev/null 2>&1; then
		case $c in
		(qjs) c=abcqjs;;
		(jsc) c=abcjsc;;
		(js24) c=abcmjs;;
		(js52) c=abcmjs;;
		(js60) c=abcmjs;;
		(js78) c=abcmjs;;
		(d8) c=abcv8;;

		(node) c=abcnode;;
		esac
		if command -v $c >/dev/null 2>&1; then
			abcscr=$c
			break
		fi
		if command -v $bdir$c >/dev/null 2>&1; then
︙︙
Changes to build.
1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
:;# simple build of abc2svg as a shell script

MAJOR=1

# use jsmin if available
if command -v jsmin >/dev/null 2>&1; then
# binary program
gen() {
 cat "$@" | jsmin

}
elif command -v qjs >/dev/null 2>&1; then
# script using QuickJS
if [ ! -f jsmin-min.js ] || [ jsmin-min.js -ot jsmin.js ]; then
	qjs --std jsmin.js < jsmin.js > jsmin-min.js
fi
gen() {
 cat "$@" | qjs --std jsmin-min.js
}
else
# no minify
gen() {
 cat "$@"
}
fi

out=abc2svg-$MAJOR.js
VERSION=`grep '"version"' package.json|cut -d'"' -f4`
DATE=`date -r package.json '+%Y-%m-%d'`
cp core/license $out
gen core/abc2svg.js core/deco.js core/draw.js\
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js\
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js\
 core/modules.js >> $out
 echo "abc2svg.version=\"$VERSION\";abc2svg.vdate=\"$DATE\"" >> $out










>

















|
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
:;# simple build of abc2svg as a shell script

MAJOR=1

# use jsmin if available
if command -v jsmin >/dev/null 2>&1; then
# binary program
gen() {
 cat "$@" | jsmin
 echo ""
}
elif command -v qjs >/dev/null 2>&1; then
# script using QuickJS
if [ ! -f jsmin-min.js ] || [ jsmin-min.js -ot jsmin.js ]; then
	qjs --std jsmin.js < jsmin.js > jsmin-min.js
fi
gen() {
 cat "$@" | qjs --std jsmin-min.js
}
else
# no minify
gen() {
 cat "$@"
}
fi

out=abc2svg-$MAJOR.js
VERSION='??????'
DATE=`date -Idate`
cp core/license $out
gen core/abc2svg.js core/deco.js core/draw.js\
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js\
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js\
 core/modules.js >> $out
 echo "abc2svg.version=\"$VERSION\";abc2svg.vdate=\"$DATE\"" >> $out

︙︙
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
gen modules/perc.js > perc-$MAJOR.js
gen modules/wps.js modules/psvg.js > psvg-$MAJOR.js
gen modules/roman.js > roman-$MAJOR.js
gen modules/soloffs.js > soloffs-$MAJOR.js
gen modules/sth.js > sth-$MAJOR.js
gen modules/strtab.js > strtab-$MAJOR.js
gen modules/temper.js > temper-$MAJOR.js


cp editor/edit.css edit-$MAJOR.css

gen util/abcdoc.js > abcdoc-$MAJOR.js
gen util/abcemb.js > abcemb-$MAJOR.js
gen util/abcemb1.js > abcemb1-$MAJOR.js
gen util/abcemb2.js > abcemb2-$MAJOR.js
gen util/abcweb.js > abcweb-$MAJOR.js
gen util/abcweb1.js > abcweb1-$MAJOR.js
gen util/abcweb.js > abcweb2-$MAJOR.js
gen util/chord.js > chord-$MAJOR.js
gen util/json.js > json-$MAJOR.js
gen util/toaudio.js > toaudio-$MAJOR.js
gen util/play.js util/toaudio.js util/toaudio5.js\
 util/sf2-parser.js util/tomidi5.js util/follow.js > play-$MAJOR.js
gen util/play.js util/sndgen.js util/sndaud.js\
 util/sf2-parser.js util/sndmid.js util/follow.js > snd-$MAJOR.js

sed "s+@MAJOR@+$MAJOR+" editor/edit.xhtml > edit-$MAJOR.xhtml
sed "s+@MAJOR@+$MAJOR+" editor/abckbd.js > abckbd-$MAJOR.js
sed "s+@MAJOR@+$MAJOR+" editor/abckbd2.js > abckbd2-$MAJOR.js
sed "s+@MAJOR@+$MAJOR+" editor/edit.js > edit-$MAJOR.js

touch follow-$MAJOR.js







>




<
<
<


<
<
<
<
<
<
|
|





<
<
65
66
67
68
69
70
71
72
73
74
75
76



77
78






79
80
81
82
83
84
85


gen modules/perc.js > perc-$MAJOR.js
gen modules/wps.js modules/psvg.js > psvg-$MAJOR.js
gen modules/roman.js > roman-$MAJOR.js
gen modules/soloffs.js > soloffs-$MAJOR.js
gen modules/sth.js > sth-$MAJOR.js
gen modules/strtab.js > strtab-$MAJOR.js
gen modules/temper.js > temper-$MAJOR.js
gen modules/tropt.js > tropt-$MAJOR.js

cp editor/edit.css edit-$MAJOR.css

gen util/abcdoc.js > abcdoc-$MAJOR.js



gen util/abcweb.js > abcweb-$MAJOR.js
gen util/abcweb1.js > abcweb1-$MAJOR.js






gen util/play.js util/sndgen.js util/sndaud.js util/sf2-parser.js\
 util/sndmid.js util/follow.js util/chord.js > snd-$MAJOR.js

sed "s+@MAJOR@+$MAJOR+" editor/edit.xhtml > edit-$MAJOR.xhtml
sed "s+@MAJOR@+$MAJOR+" editor/abckbd.js > abckbd-$MAJOR.js
sed "s+@MAJOR@+$MAJOR+" editor/abckbd2.js > abckbd2-$MAJOR.js
sed "s+@MAJOR@+$MAJOR+" editor/edit.js > edit-$MAJOR.js


Changes to build.ninja.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

34
35

36
37
38
39
40

41



42

43
44
45
46
47
48
49
50
51
52
53

54

55



56
57
58
59
60
61
62
# rules for ninja (ninja-build) or samurai

# minifying with 'jsmin' or 'uglifyjs' may be disabled by running
#	NOMIN=1 ninja -v

MAJOR=1

# ninja tool
tool=samu

rule cp
  command = cp $in $out

rule cpm
  command = sed "s+@MAJOR@+$MAJOR+" $in > $out

rule font
  command = fontforge -c 'open("font/abc2svg.sfd").generate("abc2svg.ttf")';$
   fontforge -c 'open("font/abc2svg.sfd").generate("abc2svg.woff")';$
   echo '// abc2svg music font' > font.js;$
   echo "var musicfont = 'url(\"data:application/octet-stream;base64,\\" >> font.js;$
   base64 abc2svg.ttf | sed 's/$$/\\/' >> font.js;$
   echo "\") format(\"truetype\")'" >> font.js

rule jsc
  command = VERSION=`grep '"version"' package.json|cut -d'"' -f4`;$
   if [ -e .fslckout ] && command -v fossil >/dev/null 2>&1; then $
	c=`fossil info | grep checkout:`;$
	t=`fossil info | grep tags: | awk '{print $$NF}'`;$
	if [ "$${t#v}" != "$$VERSION" ]; then $
		VERSION="$${VERSION}-$$(echo $$c | cut -d' ' -f2 | cut -c1-10)";$
	fi;$
	DATE=`echo $$c | cut -d' ' -f3`;$

   else $
	DATE=`date -r package.json '+%Y-%m-%d'`;$

   fi;$
   cp core/license $out;$
   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$

      else $



	cat $in | uglifyjs >> $out;$

     fi;$
   else $
	cat $in >> $out;$
   fi;$
   echo "abc2svg.version=\"$$VERSION\";abc2svg.vdate=\"$$DATE\"" >> $out

rule jscu
  command = cp core/license $out;$
   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$

      else $

	uglifyjs $in >> $out;$



      fi;$
   else $
	cat $in >> $out;$
   fi

rule edit
  command = if [ "x$$NOMIN" = "x" ]; then $







<
<
<















<
|
|
<
<
<
|
<
>

<
>





>

>
>
>
|
>




|






>

>
|
>
>
>







1
2
3
4
5
6
7



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

23
24



25

26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# rules for ninja (ninja-build) or samurai

# minifying with 'jsmin' or 'uglifyjs' may be disabled by running
#	NOMIN=1 ninja -v

MAJOR=1




rule cp
  command = cp $in $out

rule cpm
  command = sed "s+@MAJOR@+$MAJOR+" $in > $out

rule font
  command = fontforge -c 'open("font/abc2svg.sfd").generate("abc2svg.ttf")';$
   fontforge -c 'open("font/abc2svg.sfd").generate("abc2svg.woff")';$
   echo '// abc2svg music font' > font.js;$
   echo "var musicfont = 'url(\"data:application/octet-stream;base64,\\" >> font.js;$
   base64 abc2svg.ttf | sed 's/$$/\\/' >> font.js;$
   echo "\") format(\"truetype\")'" >> font.js

rule jsc

  command = if [ -e .fslckout ] && command -v fossil >/dev/null 2>&1; then $
	v=`fossil describe`;$



	d=`date -Idate`;$

	v="abc2svg.version=\"$$v\";abc2svg.vdate=\"$$d\"";$
   else $

	v=`grep abc2svg.version $out`;$
   fi;$
   cp core/license $out;$
   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$
	echo "" >> $out;$
      else $
	if command -v qjs >/dev/null 2>&1; then $
	  cat $in | ./jsmin >> $out;$
	else $
	  cat $in | uglifyjs >> $out;$
	fi;$
     fi;$
   else $
	cat $in >> $out;$
   fi;$
   echo $$v >> $out

rule jscu
  command = cp core/license $out;$
   if [ "x$$NOMIN" = "x" ]; then $
      if command -v jsmin >/dev/null 2>&1; then $
	cat $in | jsmin >> $out;$
	echo "" >> $out;$
      else $
	if command -v qjs >/dev/null 2>&1; then $
	  cat $in | ./jsmin >> $out;$
	else $
	  cat $in | uglifyjs >> $out;$
	fi;$
      fi;$
   else $
	cat $in >> $out;$
   fi

rule edit
  command = if [ "x$$NOMIN" = "x" ]; then $
︙︙
85
86
87
88
89
90
91
92

93
94
95
96
97
98
99

build font: font | font/abc2svg.sfd
build sf: sf | Scc1t2/

build abc2svg-$MAJOR.js: jsc core/abc2svg.js core/deco.js core/draw.js $
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js $
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js $
 core/modules.js | package.json

build mei2svg-$MAJOR.js: jsc core/abc2svg.js core/deco.js core/draw.js $
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js $
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/mei.js core/tail.js $
 core/modules.js | package.json

build ambitus-$MAJOR.js: jscu modules/ambitus.js
build break-$MAJOR.js: jscu modules/break.js







|
>







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

build font: font | font/abc2svg.sfd
build sf: sf | Scc1t2/

build abc2svg-$MAJOR.js: jsc core/abc2svg.js core/deco.js core/draw.js $
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js $
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/tail.js $
 core/modules.js
#| package.json
build mei2svg-$MAJOR.js: jsc core/abc2svg.js core/deco.js core/draw.js $
 font.js core/format.js core/front.js core/music.js core/parse.js core/subs.js $
 core/svg.js core/tune.js core/lyrics.js core/gchord.js core/mei.js core/tail.js $
 core/modules.js | package.json

build ambitus-$MAJOR.js: jscu modules/ambitus.js
build break-$MAJOR.js: jscu modules/break.js
︙︙
118
119
120
121
122
123
124

125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168



169




170











171
172
173
174
175
176
177
178
build perc-$MAJOR.js: jscu modules/perc.js
build psvg-$MAJOR.js: jscu modules/wps.js modules/psvg.js
build roman-$MAJOR.js: jscu modules/roman.js
build soloffs-$MAJOR.js: jscu modules/soloffs.js
build sth-$MAJOR.js: jscu modules/sth.js
build strtab-$MAJOR.js: jscu modules/strtab.js
build temper-$MAJOR.js: jscu modules/temper.js


build edit-$MAJOR.css: cp editor/edit.css

build abcdoc-$MAJOR.js: jscu util/abcdoc.js
build abcemb-$MAJOR.js: jscu util/abcemb.js
build abcemb1-$MAJOR.js: jscu util/abcemb1.js
build abcemb2-$MAJOR.js: jscu util/abcemb2.js
build abcweb-$MAJOR.js: jscu util/abcweb.js
build abcweb1-$MAJOR.js: jscu util/abcweb1.js
build abcweb2-$MAJOR.js: jscu util/abcweb.js
build chord-$MAJOR.js: jscu util/chord.js
build json-$MAJOR.js: jscu util/json.js
build toaudio-$MAJOR.js: jscu util/toaudio.js
build play-$MAJOR.js: jscu util/play.js util/toaudio.js $
 util/toaudio5.js util/sf2-parser.js util/tomidi5.js util/follow.js
build snd-$MAJOR.js: jscu util/play.js util/sndgen.js $
 util/sndaud.js util/sf2-parser.js util/sndmid.js util/follow.js

build edit-$MAJOR.xhtml: cpm editor/edit.xhtml
build abckbd-$MAJOR.js: edit editor/abckbd.js
build abckbd2-$MAJOR.js: edit editor/abckbd2.js
build edit-$MAJOR.js: edit editor/edit.js

build all: phony abc2svg-$MAJOR.js abcdoc-$MAJOR.js $
 abcemb-$MAJOR.js abcemb1-$MAJOR.js abcemb2-$MAJOR.js $
 abcweb-$MAJOR.js abcweb1-$MAJOR.js abcweb2-$MAJOR.js chord-$MAJOR.js $
 json-$MAJOR.js toaudio-$MAJOR.js snd-$MAJOR.js play-$MAJOR.js psvg-$MAJOR.js $
 ambitus-$MAJOR.js break-$MAJOR.js capo-$MAJOR.js $
 chordnames-$MAJOR.js clair-$MAJOR.js clip-$MAJOR.js $
 combine-$MAJOR.js diag-$MAJOR.js equalbars-$MAJOR.js gamelan-$MAJOR.js $
 grid-$MAJOR.js grid2-$MAJOR.js grid3-$MAJOR.js jianpu-$MAJOR.js $
 jazzchord-$MAJOR.js  mdnn-$MAJOR.js MIDI-$MAJOR.js $
 nns-$MAJOR.js page-$MAJOR.js pedline-$MAJOR.js perc-$MAJOR.js $
 roman-$MAJOR.js soloffs-$MAJOR.js sth-$MAJOR.js strtab-$MAJOR.js $
 temper-$MAJOR.js edit-$MAJOR.css edit-$MAJOR.js edit-$MAJOR.xhtml $
 abckbd-$MAJOR.js abckbd2-$MAJOR.js follow-$MAJOR.js
# not mei2svg-$MAJOR.js

default all

# npm/fossil new release
rule version
  command = npm version $out;$
	tag=`ag -Q '"version"' package.json|cut -d'"' -f4`;$



	fossil commit -m "New release v$$tag" -tag v$$tag;$




	$tool -v;$











	echo 'do "npm publish --otp <authy_code>"'
build minor: version
build patch: version

# temporary
rule nul
  command = touch $out
build follow-$MAJOR.js: nul







>




<
<
<


<
<

<
<
<
|
|







<
|
|





|

|
|




|

|
|
>
>
>
|
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
|


<
<
<
<
<
122
123
124
125
126
127
128
129
130
131
132
133



134
135


136



137
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187





build perc-$MAJOR.js: jscu modules/perc.js
build psvg-$MAJOR.js: jscu modules/wps.js modules/psvg.js
build roman-$MAJOR.js: jscu modules/roman.js
build soloffs-$MAJOR.js: jscu modules/soloffs.js
build sth-$MAJOR.js: jscu modules/sth.js
build strtab-$MAJOR.js: jscu modules/strtab.js
build temper-$MAJOR.js: jscu modules/temper.js
build tropt-$MAJOR.js: jscu modules/tropt.js

build edit-$MAJOR.css: cp editor/edit.css

build abcdoc-$MAJOR.js: jscu util/abcdoc.js



build abcweb-$MAJOR.js: jscu util/abcweb.js
build abcweb1-$MAJOR.js: jscu util/abcweb1.js


build json-$MAJOR.js: jscu util/json.js



build snd-$MAJOR.js: jscu util/play.js util/sndgen.js util/sndaud.js $
 util/sf2-parser.js util/sndmid.js util/follow.js util/chord.js

build edit-$MAJOR.xhtml: cpm editor/edit.xhtml
build abckbd-$MAJOR.js: edit editor/abckbd.js
build abckbd2-$MAJOR.js: edit editor/abckbd2.js
build edit-$MAJOR.js: edit editor/edit.js

build all: phony abc2svg-$MAJOR.js abcdoc-$MAJOR.js $

 abcweb-$MAJOR.js abcweb1-$MAJOR.js $
 snd-$MAJOR.js psvg-$MAJOR.js $
 ambitus-$MAJOR.js break-$MAJOR.js capo-$MAJOR.js $
 chordnames-$MAJOR.js clair-$MAJOR.js clip-$MAJOR.js $
 combine-$MAJOR.js diag-$MAJOR.js equalbars-$MAJOR.js gamelan-$MAJOR.js $
 grid-$MAJOR.js grid2-$MAJOR.js grid3-$MAJOR.js jianpu-$MAJOR.js $
 jazzchord-$MAJOR.js  mdnn-$MAJOR.js MIDI-$MAJOR.js $
 nns-$MAJOR.js page-$MAJOR.js pedline-$MAJOR.js perc-$MAJOR.js psvg-$MAJOR.js $
 roman-$MAJOR.js soloffs-$MAJOR.js sth-$MAJOR.js strtab-$MAJOR.js $
 temper-$MAJOR.js tropt-$MAJOR.js edit-$MAJOR.css edit-$MAJOR.js edit-$MAJOR.xhtml $
 abckbd-$MAJOR.js abckbd2-$MAJOR.js
# not mei2svg-$MAJOR.js

default all

# fossil new release
rule version
  command = v=`fossil changes|wc -l`;$
	if [ $$v -ne 0 ]; then $
		echo "!! There are changes !!";$
		exit 1;$
	fi;$
	v=`fossil describe`;$
	if [ "$$out"="patch" ]; then $
		v=`echo $$v|awk -F. -v OFS=. '{$$3 += 1; print}'`;$
	else $
		v=`echo $$v|awk -F. -v OFS=. '{$$2 += 1; $$3=0; print}'`;$
	fi;$
	fossil commit --allow-empty -m "New release $$v" -tag $$v;$
	n=`tail -1 abc2svg-1.js|wc -c`;$
	truncate -s -$$n abc2svg-1.js;$
	d=`date -Idate`;$
	echo "abc2svg.version=\"$$v\";abc2svg.vdate=\"$$d\"" >> abc2svg-1.js;$
	r="abc2svg-$$v";$
	ln -s . $$r;$
	sed "s#^#$$r/#" dist_bin | pax -wj -x tar -f abc2svg.tar.bz2;$
	rm -f abc2svg.zip;$
	sed "s#^#$$r/#" dist_bin | zip -r abc2svg.zip -@;$
	rm $$r;$
	echo "Don't forget to upload abc2svg.tar.bz2 and abc2svg.zip"
build minor: version
build patch: version





Changes to core/abc2svg.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - abc2svg.js
//
// Copyright (C) 2014-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// 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.
︙︙
25
26
27
28
29
30
31

32
33
34
35
36
37
38
abc2svg.C = {
	BLEN: 1536,

	// symbol types
	BAR: 0,
	CLEF: 1,
	CUSTOS: 2,

	GRACE: 4,
	KEY: 5,
	METER: 6,
	MREST: 7,
	NOTE: 8,
	PART: 9,
	REST: 10,







>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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,
︙︙
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
	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', '', '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







|







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
	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
︙︙
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//		       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, 0, -2, -1, 0, 1, 2, 0,
//		E		 F		     G
	-2, -1, 0, 1, 2, -2, -1, 0, 1, 2, 0, -2, -1, 0, 1, 2, 0,
//		A		    B
	-2, -1, 0, 1, 2, 0, -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.b40k =  new Int8Array(		// base-40 interval to possible transposition
//		        C		  D
		[36, 1, 2, 3, 8, 2, 2, 7, 8,13,14, 2,
//		    37
//	       E	      F		        G
	 8,13,14,19,20,13,14,19,20,25, 2,19,24,25,30,31, 2,
//					    20
//	       A		 B
	25,30,31,36,37, 2,31,36,37,38, 3 ])
//				    2
abc2svg.b40sf = new Int8Array(		// base-40 interval to key signature
//		        C		   D
		[-2,-7, 0, 7, 2, 88, 0,-5, 2,-3, 4, 88,
//	       E	      F		        G
	 2,-3, 4,-1, 6,-3, 4,-1, 6, 1,88,-1,-6, 1,-4, 3,88,
//	       A		 B







|
|
|
|
|
|















|







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//		       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.b40k =  new Int8Array(		// base-40 interval to possible transposition
//		        C		  D
		[36, 1, 2, 3, 8, 2, 2, 7, 8,13,14, 2,
//		    37
//	       E	      F		        G
	 8,13,14,19,20,13,14,19,20,25, 2,19,24,25,30,31, 2,
//					    20
//	       A		 B
	25,30,31,36,37, 2,31,36,37, 2, 3 ])
//				    2
abc2svg.b40sf = new Int8Array(		// base-40 interval to key signature
//		        C		   D
		[-2,-7, 0, 7, 2, 88, 0,-5, 2,-3, 4, 88,
//	       E	      F		        G
	 2,-3, 4,-1, 6,-3, 4,-1, 6, 1,88,-1,-6, 1,-4, 3,88,
//	       A		 B
︙︙
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
	"aug7": "+7",
	"7+5": "+7",
	"7#5": "+7",
	"sus": "sus4",
	"7sus": "7sus4"
} // ch_alias

// extract one of the chord symbols
// With chords as "xxx(yyy)" or "[yyy];xxx"
// (!sel - default) returns "xxx" and (sel) returns "yyy"
abc2svg.cs_sel0 = /[\[(].*[\])]/g
abc2svg.cs_sel1 = /.*[\[(]|[\])].*/g
abc2svg.cs_filter = function(a_cs, sel) {
    var	i, cs,
	tcs = ""

	for (i = 0; i < a_cs.length; i++) {
		cs = a_cs[i]
		if (cs.type == 'g')
			tcs += cs.text
	}
	return tcs.replace(sel ? abc2svg.cs_sel1 : abc2svg.cs_sel0, '')
} // cs_filter()

// font weight
// reference:
//	https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
abc2svg.ft_w = {
	thin: 100,
	extralight: 200,
	light: 300,







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







175
176
177
178
179
180
181

















182
183
184
185
186
187
188
	"aug7": "+7",
	"7+5": "+7",
	"7#5": "+7",
	"sus": "sus4",
	"7sus": "7sus4"
} // ch_alias


















// font weight
// reference:
//	https://developer.mozilla.org/en-US/docs/Web/CSS/font-weight
abc2svg.ft_w = {
	thin: 100,
	extralight: 200,
	light: 300,
︙︙
Changes to core/deco.js.
︙︙
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
	gmark: "3 grm 6 5 5",
	wedge: "0 wedge 8 3 3",	// (staccatissimo or spiccato)
	longphrase: "3 lphr 0 1 1",
	mediumphrase: "3 mphr 0 1 1",
	shortphrase: "3 sphr 0 1 1",
	turnx: "3 turnx 5,5 6 6",
	invertedturn: "3 turn 5,5 6 6",
	invertedturnx: "3 turnx 5,5 6 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 7 3 3",







<







71
72
73
74
75
76
77

78
79
80
81
82
83
84
	gmark: "3 grm 6 5 5",
	wedge: "0 wedge 8 3 3",	// (staccatissimo or spiccato)
	longphrase: "3 lphr 0 1 1",
	mediumphrase: "3 mphr 0 1 1",
	shortphrase: "3 sphr 0 1 1",
	turnx: "3 turnx 5,5 6 6",
	invertedturn: "3 turn 5,5 6 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 7 3 3",
︙︙
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
	dacoda: "5 dacs 16 20 20 Da Coda",
	"D.C.": "5 dcap 16 10 10",
	"D.S.": "5 dsgn 16 10 10",
	"D.C.alcoda": "5 dacs 16 38 38 D.C. al Coda",
	"D.S.alcoda": "5 dacs 16 38 38 D.S. al Coda",
	"D.C.alfine": "5 dacs 16 38 38 D.C. al Fine",
	"D.S.alfine": "5 dacs 16 38 38 D.S. al Fine",
	fermata: "5 hld 12 7 7",
	fine: "5 dacs 16 10 10 Fine",
	invertedfermata: "7 hld 12 7 7",
	segno: "5 sgno 22 8 8",
	f: "6 f 18 4 4",
	ff: "6 ff 18 6 6",
	fff: "6 fff 18 9 9",
	ffff: "6 ffff 18 12 12",
	mf: "6 mf 18 9 9",
	mp: "6 mp 18 10 10",
	p: "6 p 18 5 5",
	pp: "6 pp 18 9 9",
	ppp: "6 ppp 18 13 13",
	pppp: "6 pppp 18 17 17",
	pralltriller: "3 umrd 4,4 5 5",
	sfz: "6 sfz 18 8 8",
	ped: "4 ped 18 6 10",
	"ped-up": "4 pedoff 12 4 4",
	"ped(": "4 lped 20 5 5",
	"ped)": "4 lped 20 5 5",
	"crescendo(": "6 cresc 18 0 0",
	"crescendo)": "6 cresc 18 0 0",
	"<(": "6 cresc 18 0 0",
	"<)": "6 cresc 18 0 0",
	"diminuendo(": "6 dim 18 0 0",
	"diminuendo)": "6 dim 18 0 0",
	">(": "6 dim 18 0 0",







|

|













|
|
|
|







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
	dacoda: "5 dacs 16 20 20 Da Coda",
	"D.C.": "5 dcap 16 10 10",
	"D.S.": "5 dsgn 16 10 10",
	"D.C.alcoda": "5 dacs 16 38 38 D.C. al Coda",
	"D.S.alcoda": "5 dacs 16 38 38 D.S. al Coda",
	"D.C.alfine": "5 dacs 16 38 38 D.C. al Fine",
	"D.S.alfine": "5 dacs 16 38 38 D.S. al Fine",
	fermata: "3 hld 12 7 7",
	fine: "5 dacs 16 10 10 Fine",
	invertedfermata: "4 hld 12 7 7",
	segno: "5 sgno 22 8 8",
	f: "6 f 18 4 4",
	ff: "6 ff 18 6 6",
	fff: "6 fff 18 9 9",
	ffff: "6 ffff 18 12 12",
	mf: "6 mf 18 9 9",
	mp: "6 mp 18 10 10",
	p: "6 p 18 5 5",
	pp: "6 pp 18 9 9",
	ppp: "6 ppp 18 13 13",
	pppp: "6 pppp 18 17 17",
	pralltriller: "3 umrd 4,4 5 5",
	sfz: "6 sfz 18 8 8",
	ped: "7 ped 18 6 10",
	"ped-up": "7 pedoff 12 4 4",
	"ped(": "7 lped 20 5 5",
	"ped)": "7 lped 20 5 5",
	"crescendo(": "6 cresc 18 0 0",
	"crescendo)": "6 cresc 18 0 0",
	"<(": "6 cresc 18 0 0",
	"<)": "6 cresc 18 0 0",
	"diminuendo(": "6 dim 18 0 0",
	"diminuendo)": "6 dim 18 0 0",
	">(": "6 dim 18 0 0",
︙︙
262
263
264
265
266
267
268
269
270
271

272
273
274
275
276
277
278
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

} // up3()

// - of the dynamic and volume marks
function up6(s, pos) {
	switch (pos & 0x07) {
	case C.SL_ABOVE:
		return true







|
|
|
>







261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
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
︙︙
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467









468
469
470
471
472
473
474
	else if (dd.ty == '_')
		up = 0
	else if (s.multi)
		up = s.multi > 0
	else
		up = s.stem < 0
	y = up ? s.ymx : s.ymn
	if (y > -6 && y < 30) {
		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.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 decoration */
	 && s.nflags >= -1) {			/* on stem */
		if (up) {
			if (s.stem > 0)
				de.x += 3.5	// stem_xoff
		} else {
			if (s.stem < 0)
				de.x -= 3.5
		}









	}
}

/* 6: dynamic marks */
function d_pf(de) {
	var	dd2, x2, x, up,
		s = de.s,







|
















|
|







>
>
>
>
>
>
>
>
>







435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
	else if (dd.ty == '_')
		up = 0
	else if (s.multi)
		up = s.multi > 0
	else
		up = s.stem < 0
	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.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
			}
		}
	    }
	}
}

/* 6: dynamic marks */
function d_pf(de) {
	var	dd2, x2, x, up,
		s = de.s,
︙︙
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
				if (x2 > x)
					x = x2
			}
		}
	}

	de.x = x;
	de.y = y_get(s.st, up, x, de.val)
	if (!up)
		de.y -= dd.h
	else
		de.y += dd.hd
	/* (y_set is done later in draw_deco_staff) */
}








|







514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
				if (x2 > x)
					x = x2
			}
		}
	}

	de.x = x;
	de.y = y_get(s.st, up, x - dd.wl, de.val)
	if (!up)
		de.y -= dd.h
	else
		de.y += dd.hd
	/* (y_set is done later in draw_deco_staff) */
}

︙︙
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
			 || dd.glyph == "accent"
			 || dd.glyph == "roll")
				de.inv = 1
			s.ymn = y - dd.hd
		}
	}

	if (dd.func != 6) {			// if not d_pf
		if (y >= stafft)
			y_set(s.st, 1, x - dd.wl, w, y + dd.h)
		else if (y <= staffb)
			y_set(s.st, 0, x - dd.wl, w, y - dd.hd)
	}

	de.x = x;
	de.y = y
}

// add a decoration
/* syntax:







<




<







730
731
732
733
734
735
736

737
738
739
740

741
742
743
744
745
746
747
			 || dd.glyph == "accent"
			 || dd.glyph == "roll")
				de.inv = 1
			s.ymn = y - dd.hd
		}
	}


		if (y >= stafft)
			y_set(s.st, 1, x - dd.wl, w, y + dd.h)
		else if (y <= staffb)
			y_set(s.st, 0, x - dd.wl, w, y - dd.hd)


	de.x = x;
	de.y = y
}

// add a decoration
/* syntax:
︙︙
897
898
899
900
901
902
903
904
905
906
907
908


909
910
911
912
913
914
915
	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) {
				error(1, s, "Bad position in !$1!", nm)
				return
			}
			ty = p[0]


		}
		dd = deco_def(nm, nm.replace(ty, ''))
	} else {
		dd = deco_def(nm)
	}
	if (!dd)
		return







|
<
<
<
|
>
>







904
905
906
907
908
909
910
911



912
913
914
915
916
917
918
919
920
921
	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
︙︙
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
				ix: a_de.length,
				defl: {},
				x: x,
				y: y,
				pos: pos
//				dy: 0
			}
			if (dd.name.indexOf("invert") == 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) {







|







1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
				ix: a_de.length,
				defl: {},
				x: x,
				y: y,
				pos: pos
//				dy: 0
			}
			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) {
︙︙
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
					create_dh(s, m)
			}
		}
	} // create_all()

	// link the long decorations
	function ll_deco() {
		var	i, j, de, de2, 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







|







1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
					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
︙︙
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608


1609
1610
1611

1612
1613



1614
1615
1616
1617
1618
1619
1620
				if (de.m != undefined)
					de2.m = de.m;
				a_de.push(de2)
			}
			de2.start = de;
			de2.defl.nost = de.defl.nost

			// handle same numbered decoration at a same time
			j = dd.name.slice(-1)
			if (j >= '0' && j <= '9'


			 && i > 0
			 && a_de[i - 1].s.time == de.s.time
			 && a_de[i - 1].dd.name.slice(0, -2) ==

					dd.name.slice(0, -2))
				de2.prev = a_de[i - 1]



		}

		// 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







|
|
|
>
>
|
|
<
>
|
|
>
>
>







1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618

1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
				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
︙︙
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
	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 + 25	// 20 (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)







|







1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
	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)
︙︙
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
			}
			y2 = y_get(p_voice.st, true, s1.x, s.x - s1.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, s1.x, 3) + 20
				if (y < y2)
					y = y2
			}
			if (s.rbstop == 2) {
				y2 = y_get(p_voice.st, true, s.x - 3, 3) + 20
				if (y < y2)
					y = y2
			}
			if (s1.text) {
				wh = strwh(s1.text);
				y2 = y_get(p_voice.st, true, s1.x + 4, wh[0]) +
						wh[1]







|




|







1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
			}
			y2 = y_get(p_voice.st, true, s1.x, s.x - s1.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, s1.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, s1.x + 4, wh[0]) +
						wh[1]
︙︙
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
					w = 12
				else if (s.bar_type[0] != ':')
//				      || s.bar_type == "]")
					w = 0		/* explicit repeat end */
				else
					w = 8
			} else {
				w = s.rbstop ? 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 - 3,
					s1.text);
			xypath(x, y2);
			if (s1.rbstart == 2)
				output += 'm0 20v-20';
			output+= 'h' + w.toFixed(1)
			if (s.rbstop == 2)
				output += 'v20';
			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,







|











|



|


|




|







1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
					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.prev
		}
	} // draw_repbra()

	/* create the decorations tied to the staves */
	for (i = 0; i <= nstaff; i++)
		minmax[i] = {
			ymin: 0,
︙︙
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
	}

	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)
			continue
		draw_repbra(p_voice)
	}
}

/* -- draw the measure bar numbers -- */
/* (scaled delayed output) */







|







1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
	}

	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) */
︙︙
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
	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, 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:







|







1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
	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:
︙︙
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
		     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.x - s.wl + 2	// if clef, shift a bit
			y = y_get(st, true, x, w)
			if (y < staff_tb[st].topbar + 6)
				y = staff_tb[st].topbar + 6;
			y += 2 + gene.curfont.pad
			xy_str(x, y, bar_num.toString())
			y += gene.curfont.size + gene.curfont.pad
			y_set(st, true, x, w, y);
//			s.ymx = y
		     }
		}
	}








|
|


|
|







1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
		     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.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
		     }
		}
	}

︙︙
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
					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, 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)







|







2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
					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)
︙︙
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
				s.ymn = (dosh & 1) ? h : y;
				s.ymx = s.ymn + 14;
				anno_start(s)
			}
			writempo(s, s.x - 16, (dosh & 1) ? h : y);
			anno_stop(s);
			dosh >>= 1
			while (s.ts_next && s.ts_next.type == C.TEMPO)
				s = s.ts_next
		}
	}

	/* then, put the parts */
/*fixme: should reduce vertical space if parts don't overlap tempo...*/
	ymin = staff_tb[st].topbar + 6
	for (s = tsfirst; s; s = s.ts_next) {







<
<







2072
2073
2074
2075
2076
2077
2078


2079
2080
2081
2082
2083
2084
2085
				s.ymn = (dosh & 1) ? h : y;
				s.ymx = s.ymn + 14;
				anno_start(s)
			}
			writempo(s, s.x - 16, (dosh & 1) ? h : y);
			anno_stop(s);
			dosh >>= 1


		}
	}

	/* then, put the parts */
/*fixme: should reduce vertical space if parts don't overlap tempo...*/
	ymin = staff_tb[st].topbar + 6
	for (s = tsfirst; s; s = s.ts_next) {
︙︙
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
				s2.v = s.v
				s2.st = s2.wl = 0
				s2.wr = strwh(s2.text)[0]
				s2.ymn = -ht
				s2.ymx = s2.ymn + h
				anno_start(s2)
			}
			xy_str(s2.x, 2 - ht, s2.text)
			anno_stop(s2)
		}
	}
	return dy /= staff_tb[0].staffscale
}







|





2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
				s2.v = s.v
				s2.st = s2.wl = 0
				s2.wr = strwh(s2.text)[0]
				s2.ymn = -ht
				s2.ymx = s2.ymn + h
				anno_start(s2)
			}
			xy_str(s2.x, 2 - ht + gene.curfont.size * .22, s2.text)
			anno_stop(s2)
		}
	}
	return dy /= staff_tb[0].staffscale
}
Changes to core/draw.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - draw.js - draw functions
//
// 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.


|







1
2
3
4
5
6
7
8
9
10
// 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.
︙︙
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
	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	= 8,	/* 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








|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
	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

︙︙
241
242
243
244
245
246
247


248
249
250
251
252
253
254
255
	/* have room for the symbols in the staff */
	max_stem_err = 0;		/* check stem lengths */
	s = s1
	if (two_dir) {				/* 2 directions */
/*fixme: more to do*/
		ys = ((s1.grace ? 3.5 : BEAM_SHIFT) * (nflags - 1) +
			BEAM_DEPTH) * .5


		if (s1.stem != s2.stem && s1.nflags < s2.nflags)
			b += ys * s2.stem
		else
			b += ys * s1.stem
	} else if (!s1.grace) {		/* normal notes */
		var beam_h = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
//--fixme: added for abc2svg
		while (s.ts_prev







>
>
|







241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
	/* have room for the symbols in the staff */
	max_stem_err = 0;		/* check stem lengths */
	s = s1
	if (two_dir) {				/* 2 directions */
/*fixme: more to do*/
		ys = ((s1.grace ? 3.5 : BEAM_SHIFT) * (nflags - 1) +
			BEAM_DEPTH) * .5
		if (s1.nflags == s2.nflags)
			;
		else if (s1.stem != s2.stem && s1.nflags < s2.nflags)
			b += ys * s2.stem
		else
			b += ys * s1.stem
	} else if (!s1.grace) {		/* normal notes */
		var beam_h = BEAM_DEPTH + BEAM_SHIFT * (nflags - 1)
//--fixme: added for abc2svg
		while (s.ts_prev
︙︙
339
340
341
342
343
344
345


346
347
348
349
350
351
352

	/* have room for the gracenotes, bars and clefs */
/*fixme: test*/
    if (!two_staves && !two_dir)
	for (s = s1.next; ; s = s.next) {
		switch (s.type) {
		case C.REST:		/* cannot move rests in multi-voices */


			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*/







>
>







341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356

	/* have room for the gracenotes, bars and clefs */
/*fixme: test*/
    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*/
︙︙
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477





478
479

480
481
482
483
484
485
486
487
488
489
490
491
492
493
	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,
		k, k1, k2, x1,
		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) {





				x1 += 5;
				x2 -= 6

			}
		}

		y1 = bm.a * x1 + bm.b - dy;
		x2 -= x1;
		x2 /= stv_g.scale;
		dy2 = bm.a * x2
		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()








|


















>
>
>
>
>
|
|
>






|







456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
	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, bd,
		k, k1, k2, x1,
		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()

︙︙
501
502
503
504
505
506
507

508
509
510
511
512
513
514
		bh = BEAM_DEPTH
	} else {
		bshift = 3.5;
		bstub = 3.2;
		shift = .29;
		bh = 1.8
	}


/*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)







>







511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
		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)
︙︙
580
581
582
583
584
585
586

587
588
589
590
591
592
593
					break
				s = k
			}
			k2 = s
			while (k2.type != C.NOTE)
				k2 = k2.prev;
			x1 = k1.xs

			if (k1 == k2) {
				if (k1 == s1) {
					x1 += bstub
				} else if (k1 == s2) {
					x1 -= bstub
				} else if (k1.beam_br1
				        || (k1.beam_br2







>







591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
					break
				s = k
			}
			k2 = s
			while (k2.type != C.NOTE)
				k2 = k2.prev;
			x1 = k1.xs
			bd = beam_dir
			if (k1 == k2) {
				if (k1 == s1) {
					x1 += bstub
				} else if (k1 == s2) {
					x1 -= bstub
				} else if (k1.beam_br1
				        || (k1.beam_br2
︙︙
608
609
610
611
612
613
614




615

616
617
618
619
620
621
622
623
624
						 || (k1.nflags == k.nflags
						  && k1.dots < k.dots))
							x1 += bstub
						else
							x1 -= bstub
					}
				}




			}

			draw_beam(x1, k2.xs,
				  shift * beam_dir,
				  bh, bm, i)
			if (s == s2)
				break
		}
	}
	if (s1.tmp)
		unlksym(s1)







>
>
>
>
|
>

|







620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
						 || (k1.nflags == k.nflags
						  && k1.dots < k.dots))
							x1 += bstub
						else
							x1 -= bstub
					}
				}
				if (k1.stem != beam_dir) {
					bd = k1.stem
					k1.ys = bm.a * k1.xs + bm.b
						- staff_tb[k1.st].y - bh
				}
			}
			draw_beam(x1, k2.xs,
				  shift * bd,
				  bh, bm, i)
			if (s == s2)
				break
		}
	}
	if (s1.tmp)
		unlksym(s1)
︙︙
862
863
864
865
866
867
868
869

870










































871
872
873
874
875
876
877
878



879
880
881
882
883
884
885
886
	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












































	if (clef_ix & 1)
		clef_ix += 7;
	clef_ix /= 2
	while (clef_ix < 0)
		clef_ix += 7;
	clef_ix %= 7

	/* normal accidentals */



	if (!s.k_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) {







|
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








>
>
>
|







879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
	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) {
︙︙
920
921
922
923
924
925
926




927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
				x += 2
				for (; i < old_sf; i++) {
					xygl(x, staffb + shift, "acc3");
					shift += p_seq[i];
					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 (s.k_a_acc.length) {

		/* explicit accidentals */
		var	acc,
			last_acc = s.k_a_acc[0].acc,
			last_shift = 100,
			s2 = {
				st: st,
				nhd: 0,
				notes: [{}]
			}

		for (i = 0; i < s.k_a_acc.length; i++) {
			acc = s.k_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







>
>
>
>




















|



|







|
|







983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
				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
︙︙
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108








1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
	set_color();
	anno_a.push(s)
}

// -- draw a multi-measure rest --
// (the staves are defined)
function draw_mrest(s) {
    var	x1, x2,
	p_st = staff_tb[s.st],
	y = p_st.y + (p_st.topbar + p_st.botbar) / 2,
	p = s.nmes.toString()

	if (!s.next) {
		error(1, s, "Lack of bar after multi-measure rest")
		return
	}
	set_scale(s)








	x1 = s.prev.x + 20
	x2 = s.next.x - 20
	s.x = (x1 + x2) / 2
	anno_start(s)
	out_XYAB('<path d="mX Y', x1 + .6, y - 2.7)
	output += 'v2.7h-1.4v-10.8h1.4v2.7h'
		+ (x2 - x1 - 2.8).toFixed(1)
		+ 'v-2.7h1.4v10.8h-1.4v-2.7z"/>\n'
	out_XYAB('<text x ="X" y="Y" text-anchor="middle">A</text>\n',
		s.x, y + 22, m_gl(p))
	anno_a.push(s)
} // draw_mrest()

function grace_slur(s) {







|









>
>
>
>
>
>
>
>
|





|







1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
	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()

	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)
	out_XYAB('<path d="mX Y', x1 + .6, y - 2.7)
	output += 'v2.7h-1.4v-10.8h1.4v2.7h'
		+ ((x2 - x1 - 2.8) / stv_g.scale).toFixed(1)
		+ 'v-2.7h1.4v10.8h-1.4v-2.7z"/>\n'
	out_XYAB('<text x ="X" y="Y" text-anchor="middle">A</text>\n',
		s.x, y + 22, m_gl(p))
	anno_a.push(s)
} // draw_mrest()

function grace_slur(s) {
︙︙
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685

1686
1687
1688
1689
1690
1691
1692
}

// 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
		not1,		// note if start on a note head
		sl) {		// ending variables: type, note, end on a note
    var	i,
	k, g, x1, y1, x2, y2, height, addy,
	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,

	k1 = path[0],
	k2 = path[i2]

/*fixme: if two staves, may have upper or lower slur*/

	var	nn = 1,
		upstaff = k1.st,







<
|








>







1744
1745
1746
1747
1748
1749
1750

1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
}

// 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
    var	i,
	k, g, x1, y1, x2, y2, height, addy,
	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 start on a note
	k1 = path[0],
	k2 = path[i2]

/*fixme: if two staves, may have upper or lower slur*/

	var	nn = 1,
		upstaff = k1.st,
︙︙
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
				} else {
					y1 = k1.y - 8
				}
			}
		}
	}

	if (sl.is_note) {				// end on a note
		y2 = 3 * (sl.note.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







|
|







1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
				} 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
︙︙
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
		y = a * k.x + addy
		if (k.ymx < y)
			k.ymx = y
		else if (k.ymn > y)
			k.ymn = y
		if (i == i2 - 1) {
			dx = x2
			if (k2.sl1)
				dx -= 5;
			if (i)
				y -= height / 3
		} else {
			dx = path[i + 1].x
		}
		if (i != 0)







|







2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
		y = a * k.x + addy
		if (k.ymx < y)
			k.ymx = y
		else if (k.ymn > y)
			k.ymn = y
		if (i == i2 - 1) {
			dx = x2
			if (sl.nte)
				dx -= 5;
			if (i)
				y -= height / 3
		} else {
			dx = path[i + 1].x
		}
		if (i != 0)
︙︙
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022







































2023
2024
2025










2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040

/* -- 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,		// stop note
			snote) {	// optional start note
	    var	k, v, i, dir,
		path = [],
		enote = sl.note,
		s2 = enote.s			// end of slur

		if (last && s2.time > last.time)
			return			// will be drawn next time








































		// if the slur continues on the next music line,
		// stop it on the invisible bar at end of current line
		if (tsnext && s2.time >= tsnext.time) {










			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
			sl.note = {
				s: s2
			}
		}

		// set the slur position
		switch (sl.ty & 0x07) {
		case C.SL_ABOVE: dir = 1; break
		case C.SL_BELOW: dir = -1; break
		default:







|
<
|

|
<




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|

>
>
>
>
>
>
>
>
>
>





<
<
|







2081
2082
2083
2084
2085
2086
2087
2088

2089
2090
2091

2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152


2153
2154
2155
2156
2157
2158
2159
2160

/* -- 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
			s3 = s.ts_prev
			for (s = s3; s; s = s.ts_prev) {
				if (s.dur) {
					if (s.v == s2.v) {
						s3 = s
						break
					}
					if (s.st == s2.st) {
						s3 = s
						continue
					} else if (!s3) {
						s3 = s
					}
				} else if (!s3) {
					s3 = s
				}
			}
			s = s3
			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 (tsnext && s2.time >= tsnext.time) {
		    if (s2.grace && s2.time == tsnext.time) {
			for (s3 = tsnext; s3 && s3.time == s2.time; s3 = s3.ts_next) {
				if (s3.type == C.GRACE)
//fixme: check if s2 in the grace notes
					break
			}
			if (s3.type == C.GRACE)
				s3 = null
		    }
		    if (!s2.grace || s2.time == tsnext.time || !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:
︙︙
2073
2074
2075
2076
2077
2078
2079
2080

2081
2082
2083
2084
2085
2086
2087
				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) {







|
>







2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
				s = s.next
			else
				s = s.ts_next
		}

		if (!s2.grace) {		// if end on a normal note
			while (s) {
				if (s.v == v
				 && !s.invis)
					path.push(s)
				if (s == s2)
					break
				s = s.ts_next
			}
		} else if (s.grace) {		// if start/end in the same sequence
			while (1) {
︙︙
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
				s = s.next
			}
		}

		// if some nested slurs/tuplets, draw them
		for (i = 1; i < path.length - 1; i++) {
			s = path[i]
			if (s.sls || s.sl1)
				draw_slurs(s, last)
			if (s.tp)
				draw_tuplet(s)
		}
		draw_slur(path, snote, 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







|




|







2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
				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
︙︙
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189


2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
			for (i = 0; i < sls.length; i++) {
				if (!draw_sls(s, sls[i]))
					nsls.push(sls[i])
			}
			if (nsls.length)
				s.sls = nsls
		}
		if (s.sl1) {			// slurs from the note heads
			for (m = 0; m <= s.nhd; m++) {
				note = s.notes[m]
				if (note.sls) {
					sls = note.sls
					note.sls = null
					nsls = []
					for (i = 0; i < sls.length; i++) {
						if (!draw_sls(s, sls[i], note))
							nsls.push(sls[i])
					}
					if (nsls.length)
						note.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, upstaff, nb_only,
	x1, x2, y1, y2, xm, ym, a, s0, yy, yx, dy, a, dir, r, w,
	tp = s1.tp.shift()		// tuplet parameters

	if (!s1.tp.length)
		delete s1.tp		// last tuplet

	// treat the slurs and the nested tuplets
	upstaff = s1.st
	set_dscale(s1.st)
	for (s2 = s1; s2; s2 = s2.next) {
		switch (s2.type) {
		case C.GRACE:


			for (g = s2.extra; g; g = g.next) {
				if (g.sls || g.sl1)
					draw_slurs(g)
			}
			// fall thru
		default:
			continue
		case C.NOTE:
		case C.REST:
			break
		}
		if (s2.sls || s2.sl1)
			draw_slurs(s2)
		if (s2.st < upstaff)
			upstaff = s2.st
		if (s2.tp)
			draw_tuplet(s2)
		if (s2.tpe)
			break







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<











|











>
>

|









|







2265
2266
2267
2268
2269
2270
2271
















2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
			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, upstaff, 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
	upstaff = s1.st
	set_dscale(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 < upstaff)
			upstaff = s2.st
		if (s2.tp)
			draw_tuplet(s2)
		if (s2.tpe)
			break
︙︙
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
		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.type != C.NOTE)
			s3 = s3.next
		dir = s3.stem > 0 ? C.SL_ABOVE : C.SL_BELOW
	}

	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], null, {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) {







|

|







|







2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
		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
	}

	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) {
︙︙
2396
2397
2398
2399
2400
2401
2402

2403
2404
2405
2406
2407
2408
2409
			a = s0
	} else {
		if (a > 0)
			a = 0
		else if (a < s0)
			a = s0
	}

	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 */







>







2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
			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 */
︙︙
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
	/* shift the slurs / decorations */
	ym += 6
	for (s3 = s1; ; s3 = s3.next) {
		if (s3.st == upstaff) {
			yy = ym + (s3.x - xm) * a
			if (s3.ymx < yy)
				s3.ymx = yy
			w = s3.next.x - s3.x - 10
			y_set(upstaff, true, s3.x - w, w * 2, yy)
		}
		if (s3 == s2)
			break
	}

    // below
    } else {	/* lower voice of the staff: the bracket is below the staff */







<
|







2535
2536
2537
2538
2539
2540
2541

2542
2543
2544
2545
2546
2547
2548
2549
	/* shift the slurs / decorations */
	ym += 6
	for (s3 = s1; ; s3 = s3.next) {
		if (s3.st == upstaff) {
			yy = ym + (s3.x - xm) * a
			if (s3.ymx < yy)
				s3.ymx = yy

			y_set(upstaff, true, s3.x - 3, 6, yy)
		}
		if (s3 == s2)
			break
	}

    // below
    } else {	/* lower voice of the staff: the bracket is below the staff */
︙︙
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
	/* shift the slurs / decorations */
	ym -= 2
	for (s3 = s1; ; s3 = s3.next) {
		if (s3.st == upstaff) {
			yy = ym + (s3.x - xm) * a
			if (s3.ymn > yy)
				s3.ymn = yy;
			w = s3.next.x - s3.x - 10
			if (s3.next)
				y_set(upstaff, false, s3.x - w, w * 2, yy)
		}
		if (s3 == s2)
			break
	}
    } /* lower voice */

	if (tp.f[2] == 1) {			/* if 'which' == none */







<
<
|







2613
2614
2615
2616
2617
2618
2619


2620
2621
2622
2623
2624
2625
2626
2627
	/* shift the slurs / decorations */
	ym -= 2
	for (s3 = s1; ; s3 = s3.next) {
		if (s3.st == upstaff) {
			yy = ym + (s3.x - xm) * a
			if (s3.ymn > yy)
				s3.ymn = yy;


			y_set(upstaff, false, s3.x - 3, 6, yy)
		}
		if (s3 == s2)
			break
	}
    } /* lower voice */

	if (tp.f[2] == 1) {			/* if 'which' == none */
︙︙
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726

/* -- draw the symbols near the notes -- */
/* (the staves are not yet defined) */
/* order:
 * - scaled
 *   - beams
 *   - decorations near the notes
 *   - measure bar numbers
 *   - decorations tied to the notes
 *   - tuplets and slurs
 * - not scaled
 *   - measure numbers
 *   - lyrics
 *   - staff decorations
 *   - chord symbols
 *   - repeat brackets
 * 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, slur,
	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,







<











|







2805
2806
2807
2808
2809
2810
2811

2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830

/* -- 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
 * 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,
︙︙
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
		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:
				slur = 0
				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 (g.sls || g.sl1)
						slur++
				}
				if (!s.p_v.ckey.k_bagpipe	// no slur when bagpipe
				 && s.fmt.graceslurs
				 && !s.gr_shift			// tied to previous note
				 && !slur			// explicit slur
				 && !s.ti1			// some tie
				 && s.next
				 && s.next.type == C.NOTE)
					grace_slur(s)
				break
			}
		}







<






<
<




|







2872
2873
2874
2875
2876
2877
2878

2879
2880
2881
2882
2883
2884


2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
		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
			}
		}
︙︙
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
			}
			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 = s.y + 8
			if (s.ymx < y)
				s.ymx = y;
			y_set(s.st, true, s.x, 0, y)
		}
		if (s.notes[0].acc) {
			y = s.y
			if (s.notes[0].acc == 1		// sharp
			 || s.notes[0].acc == 3)	// natural
				y -= 7
			else
				y -= 5
			if (s.ymn > y)
				s.ymn = y;
			y_set(s.st, false, s.x, 0, y)
		}
	}

	draw_deco_note()

	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v];







|
|
|
|


<
|
|
<
<
|
<
<
|







2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981

2982
2983


2984


2985
2986
2987
2988
2989
2990
2991
2992
			}
			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];
︙︙
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993

2994
2995
2996
2997
2998
2999
3000

	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, name_type, h, h2,
	staff_d = []

	for (st = stl.length; st >= 0; st--) {
		if (stl[st])
			break
	}
	if (st < 0)
		return

	// check if full or sub names
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (!p_voice.sym || !cur_sy.voices[v])
			continue
		st = cur_sy.voices[v].st
		if (!stl[st])
			continue
		if (p_voice.new_name) {
			name_type = 2
			break
		}
		if (p_voice.snm)
			name_type = 1
	}
	if (!name_type)
		return
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (!p_voice.sym || !cur_sy.voices[v])
			continue
		st = cur_sy.voices[v].st
		if (!stl[st])
			continue
		if (p_voice.new_name)
			delete p_voice.new_name;
		p = name_type == 2 ? p_voice.nm : p_voice.snm
		if (!p)
			continue

		if (!staff_d[st])
			staff_d[st] = p
		else
			staff_d[st] += "\n" + p
	}
	if (!staff_d.length)
		return







|









<


|




<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

<
<
|


>







3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060

3061
3062
3063
3064
3065
3066
3067














3068
3069


3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080

	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 = []

	for (st = stl.length; st >= 0; st--) {
		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
︙︙
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080

3081



3082
3083
3084
3085
3086
3087
3088
				y = val
		}
//	}

	/* draw the parts and tempo indications if any */
	y += draw_partempo(st, y)

	if (!gene.st_print[st]) {
		p_staff.y = -y
		return y
	}

	/* set the vertical offset of the 1st staff */
	y *= p_staff.staffscale;
	staffsep = fmt.staffsep * .5 +
			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 */

	prev_staff = st



	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 || cfmt.sysstaffsep;
		maxsep = sy_staff_prev.maxsep || cfmt.maxsysstaffsep;







<
<
<
<
<











>
|
>
>
>







3138
3139
3140
3141
3142
3143
3144





3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
				y = val
		}
//	}

	/* draw the parts and tempo indications if any */
	y += draw_partempo(st, y)






	/* set the vertical offset of the 1st staff */
	y *= p_staff.staffscale;
	staffsep = fmt.staffsep * .5 +
			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 || cfmt.sysstaffsep;
		maxsep = sy_staff_prev.maxsep || cfmt.maxsysstaffsep;
︙︙
3110
3111
3112
3113
3114
3115
3116

3117
3118
3119
3120
3121
3122
3123
3124
			dy = staffsep;
		maxsep += p_staff.topbar * p_staff.staffscale
		if (dy > maxsep)
			dy = maxsep;
		y += dy;
		p_staff.y = -y;


		prev_staff = st;
		while (1) {
			sy_staff_prev = sy.staves[prev_staff]
			if (sy_staff_prev)
				break
			sy = sy.next
		}
	}







>
|







3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
			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
		}
	}
︙︙
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
	// 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,
		staves_bar, bar_force,
		xstaff = [],
		stl = [],		// all staves in the line
		bar_bot = [],
		bar_height = [],
		ba = [],		// bars [symbol, bottom, height]
		sb = "",
		thb = ""







<







3243
3244
3245
3246
3247
3248
3249

3250
3251
3252
3253
3254
3255
3256
	// 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 = ""
︙︙
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207

3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
			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, ws, i, dy, ty,
			y = 0,
			ln = "",
			stafflines = staff_tb[st].stafflines,
			l = stafflines.length


		if (!/[\[|]/.test(stafflines))
			return				// no line
		w = x2 - x1;
		set_sscale(st);
		ws = w / stv_g.scale

		// check if default staff
		if (cache && cache.st_l == stafflines
		 && cache.st_ws == (ws | 0)) {
			xygl(x1, staff_tb[st].y, 'stdef' + cfmt.fullsvg)
			return
		}
		for (i = 0; i < l; i++, y -= 6) {
			if (stafflines[i] == '.')
				continue
			dy = 0
			for (; i < l; i++, y -= 6, dy -= 6) {
				switch (stafflines[i]) {
				case '.':
				case '-':
					continue
				case ty:
					ln += 'm-' + ws.toFixed(1) +
						' ' + dy +
						'h' + ws.toFixed(1);
					dy = 0
					continue
				}
				if (ty != undefined)
					ln += '"/>\n';
				ty = stafflines[i]
				ln += '<path class="' +
					(ty == '[' ? 'slthW' : 'slW') +
					'" d="m0 ' + y + 'h' + ws.toFixed(1);
				dy = 0
			}
			ln += '"/>'
		}
		y = staff_tb[st].y
		if (!cache
		 && w > get_lwidth() - 10) {
			cache = {
				st_l: stafflines,
				st_ws: ws | 0
			}
			i = 'stdef' + cfmt.fullsvg;
			if (ln.indexOf('<path', 1) < 0)
				glyphs[i] = ln.replace('path', 'path id="' + i + '"')
			else
				glyphs[i] = '<g id="' + i + '">\n' + ln + '\n</g>';
			xygl(x1, y, i)







|



|
>




|
<



|



|



|





|

|








|









|







3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292

3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
			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
		 && 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 += '<path class="' +
					(ty == '[' ? 'slthW' : 'slW') +
					'" d="m0 ' + y + 'h' + w.toFixed(1);
				dy = 0
			}
			ln += '"/>'
		}
		y = staff_tb[st].y
		if (!cache
		 && w > get_lwidth() - 10) {
			cache = {
				st_l: stafflines,
				st_w: w | 0
			}
			i = 'stdef' + cfmt.fullsvg;
			if (ln.indexOf('<path', 1) < 0)
				glyphs[i] = ln.replace('path', 'path id="' + i + '"')
			else
				glyphs[i] = '<g id="' + i + '">\n' + ln + '\n</g>';
			xygl(x1, y, i)
︙︙
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470



3471


3472
3473
3474
3475
3476
3477

3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
		}
	} // hl_rest()

	// ---- draw_systems() ----

	/* draw the staff, skipping the staff breaks */
	for (st = 0; st <= nstaff; st++) {
		xstaff[st] = !cur_sy.st_print[st] ? -1 : 0;
		stl[st] = cur_sy.st_print[st]	// staff in the line
	}
	bar_set();
	draw_lstaff(0)
	for (s = tsfirst; s; s = s.ts_next) {
		if (bar_force && s.time != bar_force) {
			bar_force = 0
			for (st = 0; st <= nstaff; st++) {
				if (!cur_sy.st_print[st])
					xstaff[st] = -1
			}
			bar_set()
		}
		switch (s.type) {
		case C.STAVES:
			staves_bar = s.ts_prev.type == C.BAR ? s.ts_prev.x : 0
		    if (!staves_bar) {
			for (s2 = s.ts_next; s2; s2 = s2.ts_next) {
				if (s2.time != s.time)
					break
				switch (s2.type) {
				case C.BAR:
				case C.CLEF:
				case C.KEY:
				case C.METER:
					staves_bar = s2.x
					continue
				}
				break
			}
			if (!s2)
				staves_bar = realwidth;
		    }
			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] = staves_bar ?


							staves_bar : (s.x - s.wl - 2)
						stl[st] = true
					}
					continue
				}
				if (sy.st_print[st]	// if not staff stop

				 && sy.staves[st].stafflines ==
						cur_sy.staves[st].stafflines)
					continue
				if (staves_bar) {
					x2 = staves_bar;
					bar_force = s.time
				} else {
					x2 = s.x - s.wl - 2;
					xstaff[st] = -1
				}
				draw_staff(st, x, x2)
				if (sy.st_print[st])
					xstaff[st] = x2
			}
			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])







|
|




<
<
<
<
<
<
<
<


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





>
>
>
|
>
>
|





>



|
|
<

|



<
|







3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516








3517
3518


















3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541

3542
3543
3544
3545
3546

3547
3548
3549
3550
3551
3552
3553
3554
		}
	} // hl_rest()

	// ---- draw_systems() ----

	/* draw the staff, skipping the staff breaks */
	for (st = 0; st <= nstaff; st++) {
		stl[st] = gene.st_print[st] || cur_sy.st_print[st] // staff in the 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]) {
						if (s.ts_prev.bar_type)
							xstaff[st] = s.ts_prev.x
						else if (s.ts_next.bar_type)
							xstaff[st] = s.x
						else
							xstaff[st] = s.ts_prev.x
								+ s.ts_prev.wr
						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])
︙︙
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
//				s.invis = true
//			break
		}
	}

	// draw the end of the staves
	for (st = 0; st <= nstaff; st++) {
		if (bar_force && !cur_sy.st_print[st])
			continue
		x = xstaff[st]
		if (x < 0 || x >= realwidth)
			continue
		draw_staff(st, x, realwidth)
	}

	// the ledger lines







<
<







3618
3619
3620
3621
3622
3623
3624


3625
3626
3627
3628
3629
3630
3631
//				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
︙︙
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
				draw_note(s, !bm.s2);
				anno_a.push(s)
			}
			if (s == bm.s2)
				bm.s2 = null
			break
		case C.REST:
			if (!staff_tb[st].topbar)
				break
			draw_rest(s);
			break
		case C.BAR:
			break			/* drawn in draw_systems */
		case C.CLEF:
			if (s.time >= staff_tb[st].clef.time)







|







3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
				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)
︙︙
Changes to core/format.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - format.js - formatting functions
//
// Copyright (C) 2014-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - format.js - formatting functions
//
// 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.
︙︙
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101
102
103
104
105
	musicspace: 6,
//	notespacingfactor: "1.3, 38",
	partsfont: {name: txt_ff, size: 15},
	parskipfac: .4,
	partsspace: 8,
//	pageheight: 29.7 * CM,
	pagewidth: 21 * CM,

	printmargin: 0,
	rightmargin: 1.4 * CM,
	rbmax: 4,
	rbmin: 2,
	repeatfont: {name: txt_ff, size: 13},
	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]),







>




|







87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
	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]),
︙︙
126
127
128
129
130
131
132
133

134
135
136
137
138
139
140
	tupletfont: {name: txt_ff, style: "italic", size: 12},
	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

}

// parameters that are used in the symbols
var sfmt = {
bardef: true,
barsperstaff: true,
beamslope: true,







|
>







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
	tupletfont: {name: txt_ff, style: "italic", size: 12},
	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,
︙︙
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
		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.name]

	if (!scale)
		scale = 1.1;
	font.swfac = font.size * scale
}

// %%xxxfont fontname|* [encoding] [size|*]







|







183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
		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|*]
︙︙
210
211
212
213
214
215
216


217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
		font = {
			pad: 0
		}
	} else {
		font = {
			name: font.name,
			size: font.size,


			box: font.box,
			pad: font.pad || 0
		}
	}
	cfmt[xxxfont] = font;

	// fill the values
	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 = font.size * .4 - 3
		}
		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])







>
>














|







212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
		font = {
			pad: 0
		}
	} else {
		font = {
			name: font.name,
			size: font.size,
			weight: font.weight,
			style: font.style,
			box: font.box,
			pad: font.pad || 0
		}
	}
	cfmt[xxxfont] = font;

	// fill the values
	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])
︙︙
267
268
269
270
271
272
273


274
275
276
277
278
279
280
	a = p.match(/\s+([0-9.]+|\*)$/)
	if (a) {
		if (a[1] != "*")
			font.size = +a[1]
		p = p.replace(a[0], "")
	}



	// accept url(...) as the font name
	if (p[0] == 'u' && p.slice(0, 4) == "url(") {
		n = p.indexOf(')', 1)
		if (n < 0) {
			syntax(1, "No end of url in font family")
			return
		}







>
>







271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
	a = p.match(/\s+([0-9.]+|\*)$/)
	if (a) {
		if (a[1] != "*")
			font.size = +a[1]
		p = p.replace(a[0], "")
	}

	font.fname = p			// full name for scale factor

	// accept url(...) as the font name
	if (p[0] == 'u' && p.slice(0, 4) == "url(") {
		n = p.indexOf(')', 1)
		if (n < 0) {
			syntax(1, "No end of url in font family")
			return
		}
︙︙
296
297
298
299
300
301
302




303
304
305
306
307
308
309
		}

		// 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()]







>
>
>
>







302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
		}

		// extract the font attributes
		a = p.match(/[- ]?[nN]ormal/)
		if (a) {
			font.normal = true
			p = p.replace(a[0], '')
			if (font.weight)
				font.weight = null
			if (font.style)
				font.style = null
		}

		// font weight
		a = p.match(abc2svg.ft_re)
		if (a) {
			font.weight = abc2svg.ft_w[a[0].replace(/[ -]/, '')
					.toLowerCase()]
︙︙
335
336
337
338
339
340
341


342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
		default:
//hack: the font "Figurato" is used for figured bass
			if (p.indexOf("Fig") > 0)
				font.figb = true
			break
		}
	}


	if (font.size)
		set_font_fac(font)
	else
		font.swfac = 0

	font.name = p
}

// 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







>
>




<
<




|







345
346
347
348
349
350
351
352
353
354
355
356
357


358
359
360
361
362
363
364
365
366
367
368
369
		default:
//hack: the font "Figurato" is used for figured bass
			if (p.indexOf("Fig") > 0)
				font.figb = true
			break
		}
	}
	font.name = p

	if (font.size)
		set_font_fac(font)
	else
		font.swfac = 0


}

// 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
︙︙
445
446
447
448
449
450
451


452
453
454
455
456
457
458
459
		}
	}
}

// set a voice specific parameter
function set_v_param(k, v) {
	k = [k + '=', v]


	if (curvoice)
		set_kv_parm(k)
	else
		memo_kv_parm('*', k)
}

function set_page() {
	if (!img.chg)







>
>
|







455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
		}
	}
}

// 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)
︙︙
508
509
510
511
512
513
514

515

516
517
518
519
520
521
522
			break
		}
		cfmt[cmd] = v
		break
	case "abc-version":
	case "bgcolor":
	case "fgcolor":

	case "titleformat":

		cfmt[cmd] = param
		break
	case "beamslope":
	case "breaklimit":			// float values
	case "lineskipfac":
	case "maxshrink":
	case "pagescale":







>

>







520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
			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":
︙︙
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
	case "flatbeams":
	case "graceslurs":
	case "graceword":
	case "hyphencont":
	case "keywarn":
	case "linewarn":
	case "quiet":
	case "singleline":
	case "squarebreve":
	case "splittune":
	case "straightflags":
	case "stretchstaff":
	case "timewarn":
	case "titlecaps":
	case "titleleft":







<







572
573
574
575
576
577
578

579
580
581
582
583
584
585
	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":
︙︙
671
672
673
674
675
676
677
678
679
680
681





682
683
684
685
686
687
688
689
			}
		if (parse.ufmt)
			cfmt[cmd] = new Float32Array(3)
		for (i = 0; i < 3; i++)
			cfmt[cmd][i] = +v[i]
		break
	case "tuplets":
		cfmt[cmd] = param.split(/\s+/);
		v = cfmt[cmd][3]
		if (v			// if 'where'
		 && (posval[v]))	// translate the keyword





			cfmt[cmd][3] = posval[v]
		break
	case "infoname":
		set_infoname(param)
		break
	case "notespacingfactor":
		v = param.match(/([.\d]+)[,\s]*(\d+)?/)
		if (v) {







|
|
|
|
>
>
>
>
>
|







684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
			}
		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) {
︙︙
723
724
725
726
727
728
729




















730
731
732
733
734
735
736
		cfmt.sound = "play"		// without clef
		break
	case "pos":
		cmd = param.match(/(\w*)\s+(.*)/)
		if (!cmd || !cmd[2]) {
			syntax(1, "Error in %%pos")
			break




















		}
		set_pos(cmd[1], cmd[2])
		break
	case "sounding-score":
		if (cfmt.sound != "play")
			cfmt.sound = "sounding"
		break







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
		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
		}
		set_pos(cmd[1], cmd[2])
		break
	case "sounding-score":
		if (cfmt.sound != "play")
			cfmt.sound = "sounding"
		break
︙︙
752
753
754
755
756
757
758

759
760
761
762
763
764
765
766
767
768
769
770
771
		cfmt.rightmargin = v;
		img.chg = true
		break
	case "textoption":
		cfmt[cmd] = get_textopt(param)
		break
	case "dynalign":

	case "stretchlast":
	case "titletrim":
		v = +param
		if (isNaN(v))
			v = get_bool(param) ? 0 : 1
		if (cmd[0] == 's') {		// stretchlast
			if (v < 0 || v > 1) {
				syntax(1, errs.bad_val, '%%' + cmd)
				break
			}
		}
		cfmt[cmd] = v
		break







>





|







790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
		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
︙︙
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
		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{fill:currentColor;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[1]);
		return gene.curfont
	}

	if (!font.name || !font.size) {		// if incomplete user font
		font2 = Object.create(gene.deffont)
		if (font.name)
			font2.name = font.name







|










|







886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
		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
︙︙
Changes to core/front.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - front.js - ABC parsing front-end
//
// Copyright (C) 2014-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - front.js - ABC parsing front-end
//
// 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.
︙︙
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
		if (flag && src.indexOf('\\') >= 0)
			return cnv_escape(src, flag)
		return src
	} // uncomment()

	function end_tune() {
		generate()
		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)
		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.part
		init_tune()
		img.chg = true;
		set_page();
	} // end_tune()

	// get %%voice
	function do_voice(select, in_tune) {







<
<
<
<
<
<










|







269
270
271
272
273
274
275






276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
		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.ctrl
		init_tune()
		img.chg = true;
		set_page();
	} // end_tune()

	// get %%voice
	function do_voice(select, in_tune) {
︙︙
422
423
424
425
426
427
428

429
430
431
432
433
434
435
		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()

				if (parse.select) {	// skip to next tune
					eol = file.indexOf('\nX:', parse.eol)
					if (eol < 0)
						eol = eof
					parse.eol = eol
				}
			}







>







416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
		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
				}
			}
︙︙
488
489
490
491
492
493
494

495
496
497
498
499
500
501
					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(/^%%/gm,''))
				parse.eol = file.indexOf('\n', i + 6)
				if (parse.eol < 0)
					parse.eol = eof
				continue
			}
			switch (a[1]) {







>







483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
					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]) {
︙︙
Changes to core/gchord.js.
︙︙
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// -- 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, type,
	i, j, istart, iend,
		ann_font = get_font("annotation"),
		h_ann = ann_font.size,
		line = parse.line

	function get_float() {
		var txt = ''







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// -- 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 = ''
︙︙
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
		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] != '\\')

				break
			i = j + 1
		}
		text = cnv_escape(line.buffer.slice(line.index, j))
		line.index = j
		iend = parse.bol + line.index + 1
	}







|
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
		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
	}
︙︙
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
				y_abs = 0
			} else {
				y_abs = get_float()
				if (c != ' ')
					i--
			}
			gch.x = x_abs;
			y_abs -= h_ann * .3	// center vertically
			gch.y = y_abs
			break
		case '^':
			gch.pos = C.SL_ABOVE
			// fall thru
		case '_':
			if (c == '_')







<







84
85
86
87
88
89
90

91
92
93
94
95
96
97
				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 == '_')
︙︙
126
127
128
129
130
131
132
133
134
135


136
137
138
139
140
141
142
		while (1) {
			c = text[i]
			if (!c)
				break
			switch (c) {
			case '\\':
				c = text[++i]
				if (!c || c == 'n')
					break
				gch.text += '\\'


			default:
				gch.text += c;
				i++
				continue
			case '&':			/* skip "&xxx;" */
				while (1) {
					gch.text += c;







|


>
>







126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
		while (1) {
			c = text[i]
			if (!c)
				break
			switch (c) {
			case '\\':
				c = text[++i]
				if (c == 'n')
					break
				gch.text += '\\'
				if (!c)
					break
			default:
				gch.text += c;
				i++
				continue
			case '&':			/* skip "&xxx;" */
				while (1) {
					gch.text += c;
︙︙
170
171
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
227
228
229
230
231
232








233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
	}
}

// transpose a chord symbol
var	note_names = "CDEFGAB",
	acc_name = ["bb", "b", "", "#", "##"]

	function gch_tr1(p, transp) {
	    var	i, o, n, a, ip, b40,

		csa = p.split('/')

		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
			a = 0
			while (p[ip] == '#' || p[ip] == '\u266f') {
				a++;
				ip++
			}
			while (p[ip] == 'b' || p[ip] == '\u266d') {
				a--;
				ip++
			}
			n = note_names.indexOf(p[o]) + 16
			b40 = (abc2svg.pab40(n, a) + transp + 200) % 40
			b40 = abc2svg.b40k[b40]
			csa[i] = p.slice(0, o) +
					note_names[abc2svg.b40_p[b40]] +
					acc_name[abc2svg.b40_a[b40] + 2] +
					p.slice(ip)
		}
		return csa.join('/')
	} // gch_tr1

function gch_transp(s) {
    var	gch,
	i = s.a_gch.length

	while (--i >= 0) {
		gch = s.a_gch[i]
		if (gch.type == 'g')
			gch.text = gch_tr1(gch.text, curvoice.vtransp)
	}
}

// parser: add the parsed list of chord symbols and annotations
//	to the symbol (note, rest or bar)

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') {
				syntax(1,
				       "There cannot be chord symbols on measure bars")
				a_gch.splice(i)
			}
		}
	}









	if (s.a_gch)
		s.a_gch = s.a_gch.concat(a_gch)
	else
		s.a_gch = a_gch
	a_gch = null

	if (curvoice.vtransp)
		gch_transp(s)
} // csan_add

// generator: build the chord symbols / annotations
// (possible hook)
Abc.prototype.gch_build = function(s) {

	/* split the chord symbols / annotations







|

>


















|









<
<
<
<
<
<
<
<
<
<
<


>







|





>
>
>
>
>
>
>
>






<
<
<







172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209











210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239



240
241
242
243
244
245
246
	}
}

// transpose a chord symbol
var	note_names = "CDEFGAB",
	acc_name = ["bb", "b", "", "#", "##"]

	function gch_tr1(p) {
	    var	i, o, n, a, ip, b40,
		tr = curvoice.tr_sco,
		csa = p.split('/')

		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
			a = 0
			while (p[ip] == '#' || p[ip] == '\u266f') {
				a++;
				ip++
			}
			while (p[ip] == 'b' || p[ip] == '\u266d') {
				a--;
				ip++
			}
			n = note_names.indexOf(p[o]) + 16
			b40 = (abc2svg.pab40(n, a) + tr + 200) % 40
			b40 = abc2svg.b40k[b40]
			csa[i] = p.slice(0, o) +
					note_names[abc2svg.b40_p[b40]] +
					acc_name[abc2svg.b40_a[b40] + 2] +
					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)
		}
	}

	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
︙︙
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
					return "&#x1d12b;"
				});
		} else {
			if (gch.type == '@'
			 && !user.anno_start && !user.anno_stop) {
				set_font(gch.font)
				gch.text = str2svg(gch.text)
				gch.text.wh = [0, 0]
				continue		/* no width */
			}
		}

		/* set the offsets and widths */
		set_font(gch.font);
		gch.text = str2svg(gch.text)
		wh = gch.text.wh
		wh[1] += gch.font.pad * 2
		switch (gch.type) {
		case '@':
			break
		case '^':			/* above */
		case '_':			/* below */
			xspc = wh[0] * GCHPRE
			if (xspc > 8)







<








<







267
268
269
270
271
272
273

274
275
276
277
278
279
280
281

282
283
284
285
286
287
288
					return "&#x1d12b;"
				});
		} 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
		case '^':			/* above */
		case '_':			/* below */
			xspc = wh[0] * GCHPRE
			if (xspc > 8)
︙︙
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409








410
411
412
413
414
415
416
	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 = 0

	if (an.font.figb) {
		h *= 2.4
		dy = an.font.size * 1.3
	}

	switch (an.type) {
	case '_':			// below
		y -= h + pad
		y_set(s.st, 0, x, w, y - pad)
		break
	case '^':			// above
		y = y_get(s.st, 1, x, w)
		y += pad
		y_set(s.st, 1, x, w, y + h + 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
		if (y > 24)
			y_set(s.st, 1, x, w, y + h + pad)
		if (y < 0)
			y_set(s.st, 0, x, w, y - pad)
		break
	default:			// chord symbol
		if (y >= 0) {
			y += pad
			y_set(s.st, 1, x, w, y + h + pad)
		} else {
			y -= h + pad
			y_set(s.st, 0, x, w, y - 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 * .8
			if (y2 > staff_tb[s.st].ann_top)
				staff_tb[s.st].ann_top = y2
		} else {
			y2 = y
			if (y2 < staff_tb[s.st].ann_bot)
				staff_tb[s.st].ann_bot = y2
		}
		break
	}








	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)







|



|





<


<

<




















<
<
<
<


|

<
|

<
<






|

|



|





>
>
>
>
>
>
>
>







332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

349
350

351

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371




372
373
374
375

376
377


378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
	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 > 24)
			y_set(s.st, 1, x, w, y + h + pad + 2)
		else if (y < 0)
			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)
︙︙
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
			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







|







432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
			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) + 2	// 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
︙︙
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
			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)	// 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







|







461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
			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
︙︙
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
		if (i > n_an)
			n_an = i
		while (--i >= 0) {
			if (an[i].type == 'g') {
				an = an[i]
				w = an.text.wh[0]
				if (an.pos == C.SL_ABOVE) {
					y = y_get(s.st, true, s.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, s.x, w)
					if (y < minmax[s.st].ydn)
						minmax[s.st].ydn = y
				}
				break
			}
		}
	}







|



|







489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
		if (i > n_an)
			n_an = i
		while (--i >= 0) {
			if (an[i].type == 'g') {
				an = an[i]
				w = an.text.wh[0]
				if (an.pos == C.SL_ABOVE) {
					y = y_get(s.st, true, s.x, w) + 2
					if (y > minmax[s.st].yup)
						minmax[s.st].yup = y
				} else if (an.pos == C.SL_BELOW) {
					y = y_get(s.st, false, s.x, w) - 2
					if (y < minmax[s.st].ydn)
						minmax[s.st].ydn = y
				}
				break
			}
		}
	}
︙︙
Changes to core/license.
1
2
3
// abc2svg - ABC to SVG translator
// @source: https://chiselapp.com/user/moinejf/repository/abc2svg
// Copyright (C) 2014-2021 Jean-Francois Moine - LGPL3+


|
1
2
3
// abc2svg - ABC to SVG translator
// @source: https://chiselapp.com/user/moinejf/repository/abc2svg
// Copyright (C) 2014-2023 Jean-Francois Moine - LGPL3+
Changes to core/lyrics.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - lyrics.js - lyrics
//
// Copyright (C) 2014-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - lyrics.js - lyrics
//
// 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.
︙︙
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
				case '*':
				case '|':
					i--
				case ' ':
				case '\t':
					break
				case '~':
					word += ' ';	// (&nbsp;)
					i++
					continue
				case '-':
					ln = 1		// start of line
					break
				case '\\':
					if (!p[++i])







|







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
				case '*':
				case '|':
					i--
				case ' ':
				case '\t':
					break
				case '~':
					word += ' '
					i++
					continue
				case '-':
					ln = 1		// start of line
					break
				case '\\':
					if (!p[++i])
︙︙
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
			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(' ');	// (&nbsp;)
				set_font(ly.font)
				if (j > 0)
					sz = strwh(p.slice(0, j))[0]
				else
					sz = w * .2
			}
			w -= sz







|







313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
			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
			}
			w -= sz
︙︙
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
	}
} // 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, font, 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







|







382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
	}
} // 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
︙︙
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
					lflag = false;
					lastx = s.x + s.wr
				}
			}
			continue
		}
		if (ly.font != gene.curfont)		/* font change */
			gene.curfont = font = ly.font;
		p = ly.t;
		w = p.wh[0]
		shift = ly.shift
		if (hyflag) {
			if (ly.ln == 2 && p == "_") {	// '_'
				p = "-"
			} else if (ly.ln != 2) {	// not '-'







|







412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
					lflag = false;
					lastx = s.x + s.wr
				}
			}
			continue
		}
		if (ly.font != gene.curfont)		/* font change */
			gene.curfont = ly.font
		p = ly.t;
		w = p.wh[0]
		shift = ly.shift
		if (hyflag) {
			if (ly.ln == 2 && p == "_") {	// '_'
				p = "-"
			} else if (ly.ln != 2) {	// not '-'
︙︙
446
447
448
449
450
451
452

453
454
455


456
457
458
459
460
461
462
			continue
		}
		x0 = s.x - shift;
		if (ly.ln)			// '-' at end
			hyflag = true
		if (user.anno_start || user.anno_stop) {
			s2 = {

				st: s.st,
				istart: ly.istart,
				iend: ly.iend,


				x: x0,
				y: y,
				ymn: y,
				ymx: y + gene.curfont.size,
				wl: 0,
				wr: w
			}







>



>
>







446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
			continue
		}
		x0 = s.x - shift;
		if (ly.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
			}
︙︙
Changes to core/mei.js.
︙︙
696
697
698
699
700
701
702
703

704
705
706
707
708
709
710
					curr.beam = 2
					break
				}
				sym_link(s)
			}
		}
		if (id) {
			note.s = s

			do_delayed(id, curr.chord ? note : s)
		}
		note.dur = dur
		if (acc_ty[tag.accid])
			note.acc = acc_ty[tag.accid]

//fixme: may have notes of the chord on different staves







|
>







696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
					curr.beam = 2
					break
				}
				sym_link(s)
			}
		}
		if (id) {
			note.s = s			// chord
			note.m = s.nhd			// note index in the chord
			do_delayed(id, curr.chord ? note : s)
		}
		note.dur = dur
		if (acc_ty[tag.accid])
			note.acc = acc_ty[tag.accid]

//fixme: may have notes of the chord on different staves
︙︙
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029




1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
		case "below": ty = C.SL_BELOW; break
		}
		switch (tag.lform) {
		case "dashed":
		case "dotted": ty |= C.SL_DOTTED; break
		}

		sl = { ty: ty }
		if (s2.s) {
			sl.is_note = true	// to note
			sl.note = s2
		} else {
			s2.notes[0].s = s2
			sl.note = s2.notes[0]	// to chord
		}





		if (!s1.sls)
			s1.sls = []
		s1.sls.push(sl)
		if (s1.s)			// if from note of chord
			s1.s.sl1 = true
//fixme: ko if grace note
	}, // slur()

	sourceDesc: function(tag) {
		return true	// skip
	},








|
<
<
<
|
|
|

>
>
>
>




<
<







1016
1017
1018
1019
1020
1021
1022
1023



1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035


1036
1037
1038
1039
1040
1041
1042
		case "below": ty = C.SL_BELOW; break
		}
		switch (tag.lform) {
		case "dashed":
		case "dotted": ty |= C.SL_DOTTED; break
		}

		sl = {



			ty: ty,
			ss: s1.s || s1,
			se: s2.s || s2
		}
		if (s1.s)
			sl.nts = s1
		if (s2.s)
			sl.nte = s2	// to note

		if (!s1.sls)
			s1.sls = []
		s1.sls.push(sl)


//fixme: ko if grace note
	}, // slur()

	sourceDesc: function(tag) {
		return true	// skip
	},

︙︙
Changes to core/modules.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// abc2svg - modules.js - module handling
//
// Copyright (C) 2018-2020 Jean-Francois Moine
//
// This file is part of abc2svg-core.
//
// abc2svg-core is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg-core is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg-core.  If not, see <http://www.gnu.org/licenses/>.

// empty function
if (!abc2svg.loadjs) {
    abc2svg.loadjs = function(fn, onsuccess, onerror) {
	if (onerror)
		onerror()
    }
}

abc2svg.modules = {
	ambitus: {},
	begingrid: { fn: 'grid3' },
	beginps: { fn: 'psvg' },


|




















|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 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 <http://www.gnu.org/licenses/>.

// empty function
if (!abc2svg.loadjs) {
    abc2svg.loadjs = function(fn, onsuccess, onerror) {
	if (onerror)
		onerror(fn)
    }
}

abc2svg.modules = {
	ambitus: {},
	begingrid: { fn: 'grid3' },
	beginps: { fn: 'psvg' },
︙︙
49
50
51
52
53
54
55

56
57
58
59
60
61
62
	pedline: {},
	percmap: { fn: 'perc' },
	roman: {},
	soloffs: {},
	sth: {},
	strtab: {},
	temperament: { fn: 'temper' },


	nreq: 0,
	hooks: [],
	g_hooks: [],

	// scan the file and find the required modules
	// @file: ABC file







>







49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
	pedline: {},
	percmap: { fn: 'perc' },
	roman: {},
	soloffs: {},
	sth: {},
	strtab: {},
	temperament: { fn: 'temper' },
	tropt: {},

	nreq: 0,
	hooks: [],
	g_hooks: [],

	// scan the file and find the required modules
	// @file: ABC file
︙︙
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
		} // get_errmsg()

		// call back functions for loadjs()
		function load_end() {
			if (--abc2svg.modules.nreq == 0)
				abc2svg.modules.cbf()
		}
		function load_ko(fn) {
			abc2svg.modules.errmsg('Error loading the module ' + fn)
			load_end()
		}

		// test if some keyword in the file
	    var	m, i, fn,
		nreq_i = this.nreq,
		ls = file.match(/(^|\n)(%%|I:).+?\b/g)

		if (!ls)







<
<
<
<







80
81
82
83
84
85
86




87
88
89
90
91
92
93
		} // 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)
︙︙
109
110
111
112
113
114
115
116




117
118
119
120

			// load the module
			if (m.fn)
				fn = m.fn
			this.nreq++
			abc2svg.loadjs(fn + "-1.js",
					load_end,
					function() {load_ko(fn)})




		}
		return this.nreq == nreq_i
	}
} // modules







|
>
>
>
>




106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

			// 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
Changes to core/music.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - music.js - music generation
//
// 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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - music.js - music generation
//
// 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.
︙︙
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

	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 (p1 != ps)
			dx1 += 7	// (there is a shift of a head when unison)
		notes[i2].shac = dx2 = dx1
	}
	dx2 += 7

	// shift the remaining accidentals
	for (i = i1; --i > i2; ) {		// from top to bottom
		acc = notes[i].acc







<
|







236
237
238
239
240
241
242

243
244
245
246
247
248
249
250

	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

		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
︙︙
310
311
312
313
314
315
316
317

318

319
320
321
322
323
324
325
			continue
		}

		dx_head = dx_tb[s.head]

		// build a pseudo chord and shift the accidentals
		notes = []
		for ( ; s != s2; s = s.ts_next)

			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







|
>
|
>







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
			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
︙︙
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
	}

	switch (s.type) {
	case C.NOTE:
	case C.REST:

		/* set the note widths */
		s.wr = wlnote = 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







|







691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
	}

	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
︙︙
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825

826
827

828
829
830
831
832
833
834
						wlw = 14
				}
				break
			case C.CLEF:		/* extra space at start of line */
				if (s2.second
				 || s2.clef_small)
					break
				wlw += 8
				break
			case C.KEY:
/*			case C.METER:	*/
				wlw += 4
				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 (s2 && s2.type == C.GRACE)
			s.wl = wlnote - 4.5

		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)







<
|

|
|











|
|
>
|

>







802
803
804
805
806
807
808

809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
						wlw = 14
				}
				break
			case C.CLEF:		/* extra space at start of line */
				if (s2.second
				 || s2.clef_small)
					break

				// fall thru
			case C.KEY:
			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)
︙︙
864
865
866
867
868
869
870
871
872
873
874


875
876
877
878
879
880
881
			 && s.next.type != C.METER)
				s.wr = 7
			else
				s.wr = 5
//			s.notes[0].shhd = (w - 5) * -.5

			/* if preceeded by a grace note sequence, adjust */
			for (s2 = s.prev; s2; s2 = s2.prev) {
				if (w_tb[s2.type]) {
					if (s2.type == C.GRACE)
						s.wl -= 6


					if (s2.type == C.STBRK)
						s.wl -= 12
					break
				}
			}

		if (s.a_dd)







|
<
|
|
>
>







866
867
868
869
870
871
872
873

874
875
876
877
878
879
880
881
882
883
884
			 && s.next.type != C.METER)
				s.wr = 7
			else
				s.wr = 5
//			s.notes[0].shhd = (w - 5) * -.5

			/* 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)
︙︙
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935


936




937
938
939
940
941
942
943
944
945
946
947
948


949


950
951
952
953



954
955
956
957
958
959
960
	case C.CLEF:
// (there may be invisible clefs in empty staves)
		if (s.invis) {
			s.wl = s.wr = 1		// (!! not 0 !!)
			return
		}
		s.wl = s.clef_small ? 11 : 12
		s.wr = s.clef_small ? 10 : 12
		if (s.prev && s.prev.type == C.STBRK) {
			s.wl -= 6
			delete s.next.clef_small
		}
		if (s.next && s.next.type == C.BAR)
			s.wr -= 4
		return
	case C.KEY:
		if (s.invis) {				// if no accidental
			s.wl = s.wr = 0			// no width
			return
		}
		s.wl = 3;
		esp = 4
		if (!s.k_a_acc) {
			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() */
			}
		} else {


			n1 = n2 = s.k_a_acc.length




			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
			}
		}


		s.wr = 5.5 * n1 + esp


		return
	case C.METER:
		wlw = 0;
		s.x_meter = []



		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;







|




<
<






|
|
<


















|
>
>
|
>
>
>
>












>
>

>
>


<

>
>
>







897
898
899
900
901
902
903
904
905
906
907
908


909
910
911
912
913
914
915
916

917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961

962
963
964
965
966
967
968
969
970
971
972
	case C.CLEF:
// (there may be invisible clefs in empty staves)
		if (s.invis) {
			s.wl = s.wr = 1		// (!! not 0 !!)
			return
		}
		s.wl = s.clef_small ? 11 : 12
		s.wr = s.clef_small ? 8 : 13
		if (s.prev && s.prev.type == C.STBRK) {
			s.wl -= 6
			delete s.next.clef_small
		}


		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;
︙︙
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
						break
					}
				}
				s.x_meter[i] = wlw + w / 2
				wlw += w
			}
		}
		s.wl = 0;
		s.wr = wlw + 6
		return
	case C.MREST:
		s.wl = 6;
		s.wr = 66
		return
	case C.GRACE:
		if (s.invis)







|
|







997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
						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)
︙︙
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120

	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 - 7
		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







|







1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132

	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
︙︙
1225
1226
1227
1228
1229
1230
1231
1232

1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251

1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263

1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275



1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302

1303

1304
1305



1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323



1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
		prev: s,
		fmt: s.fmt
	}
} // _bar()

// create an invisible bar for end of music lines
function add_end_bar(s) {
    var b = _bar(s)


	b.wl = 0
	b.wr = 0
	b.ts_prev = s
	b.next = s.next
	b.ts_next = s.ts_next
	b.shrink = 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 = set_space(b, s.time + s.dur * .4)
	return b
}

/* -- set the width and space of all symbols -- */
// this function is called once for the whole tune

function set_allsymwidth() {
    var	val, st, s_chs, stup,
	s = tsfirst,
	s2 = s,
	xa = 0,
	xl = [],
	wr = [],
	maxx = xa,
	tim = s.time

	/* loop on all symbols */
	while (1) {

		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;
			val = xl[st] + wr[st] + s.wl
			if (val > maxx)
				maxx = val



			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 (s2.dur && s2.dur != s2.notes[0].dur) {
			if (!stup) {
				stup = s2
				while (stup.prev
				 && stup.prev.dur
				 && stup.prev.dur != stup.prev.notes[0].dur)
					stup = stup.prev
				while (!stup.seqst)
					stup = stup.ts_prev
			}
		} else if (stup && stup.v == s2.v) {
			set_sp_tup(stup, s2)
			stup = null
		}


		if (!s2.shrink && !s2.space && s2.type == C.CLEF) {

			delete s2.seqst;		/* no space */
			s2.time = tim



		}
		tim = s2.time
		if (!s)
			break

		// update the min left space per staff
		for (st = 0; st < wr.length; st++)
			wr[st] = 0;
		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)
	}




	// let the chord symbols at the same offset
	// and adjust the spacing due to the lyrics
	if (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.sl1
	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







|
>






|






|





>
|
|










>












>
>
>












|
|

<
<
<
<
<
<
<





>
|
>


>
>
>


















>
>
>


|















|







1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308







1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
		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;
			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.space && s2.type == C.CLEF
		     && !s2.ts_prev.bar_type) {
			delete s2.seqst;		/* no space */
			s2.time = tim
		    } else if (s2.dur) {
			s2.shrink = 10			// cannot be null
		    }
		}
		tim = s2.time
		if (!s)
			break

		// update the min left space per staff
		for (st = 0; st < wr.length; st++)
			wr[st] = 0;
		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
︙︙
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
/* -- 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, noseq

	    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







|







1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
/* -- 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
︙︙
1621
1622
1623
1624
1625
1626
1627
1628






1629

1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653

1654
1655
1656
1657
1658
1659
1660

1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
		}
	    } // new_typ()

		s = so				// start of sequence
		while (s.ts_prev
		 && s.ts_prev.time == so.time) {
			s = s.ts_prev
			if (s.bar_type)






				s1 = s		// first previous bar

			if (s.type == C.GRACE)
				so = s		// keep the grace notes in the next line
		}
		if (!s1)
			return so

		// search the insertion point in the new line
		for (s = so; ; s = s.ts_next) {
			switch (s.type) {
//			default:
//				if (w_tb[s.type])
//					break
			case C.KEY:
			case C.METER:
			case C.CLEF:
				continue
			}
			break
		}
		if (s.bar_type)
			noseq = 1

		while (s1 != so) {
		    if (s1.bar_type

		     && (s1.bar_type.slice(-1) == ':'
		      || s1.text)
		     && s1.bar_type != ':') {
			new_type(s1)		// return t1 and t2
			s2 = clone(s1)
			s1.bar_type = t1
			delete s1.rbstop

			delete s1.a_gch
			s2.bar_type = t2
			lkvsym(s2, s1.next)	// voice
			while (1) {
				if (s.type != C.BAR
//fixme: pb if inverted voices
				 || s.v > s2.v)
					break
				s = s.ts_next
			}
			lktsym(s2, s)		// time
			if (!noseq) {
				noseq = 1
				s2.seqst = true
				s2.shrink = s2.wl + s2.prev.wr
				s2.space = 0
			}
			if (s == so)
				so = s2
			if (s1.text) {
				s2.invis = true
				delete s1.text
				delete s1.rbstart
			}







|
>
>
>
>
>
>

>
|
|

















<
<



>






|
>
|










|
<
|
<
<
<







1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674


1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698

1699



1700
1701
1702
1703
1704
1705
1706
		}
	    } // new_typ()

		s = so				// start of sequence
		while (s.ts_prev
		 && s.ts_prev.time == so.time) {
			s = s.ts_prev
			if (s.bar_type) {

				// if repeat variant, move the new line
				if (s.bar_type[0] == '['
				 && s.text
				 && s.ts_next == so)
					so = s
				s1 = s		// first previous bar
			}
//			if (s.type == C.GRACE)
//				so = s		// keep the grace notes in the next line
		}
		if (!s1)
			return so

		// search the insertion point in the new line
		for (s = so; ; s = s.ts_next) {
			switch (s.type) {
//			default:
//				if (w_tb[s.type])
//					break
			case C.KEY:
			case C.METER:
			case C.CLEF:
				continue
			}
			break
		}



		while (s1 != so) {
		    if (s1.bar_type
		     && s1.next
		     && (s1.bar_type.slice(-1) == ':'
		      || s1.text)
		     && s1.bar_type != ':') {
			new_type(s1)		// return t1 and t2
			s2 = clone(s1)
			s1.bar_type = t1
			delete s1.a_gch
			if (t1 == '|')
				delete s1.rbstop // (needed for play variant)
			s2.bar_type = t2
			lkvsym(s2, s1.next)	// voice
			while (1) {
				if (s.type != C.BAR
//fixme: pb if inverted voices
				 || s.v > s2.v)
					break
				s = s.ts_next
			}
			lktsym(s2, s)		// time
			if (s1.seqst)

				s2.seqst = 1



			if (s == so)
				so = s2
			if (s1.text) {
				s2.invis = true
				delete s1.text
				delete s1.rbstart
			}
︙︙
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
		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

		// advance in the next line
		for (s2 = s; s2; s2 = s2.ts_next) {
			switch (s2.type) {
			case C.KEY:
				if (!s.fmt.keywarn
				 || s2.invis)







|







1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
		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

		// advance in the next line
		for (s2 = s; s2; s2 = s2.ts_next) {
			switch (s2.type) {
			case C.KEY:
				if (!s.fmt.keywarn
				 || s2.invis)
︙︙
1724
1725
1726
1727
1728
1729
1730


1731
1732
1733



1734
1735
1736
1737
1738
1739
1740
					s1 = s.ts_prev
				}
				// fall thru
			case C.CLEF:
				if (!s2.prev)		// start of voice
					continue
				if (s2.type == C.CLEF) {


					for (s1 = s.ts_prev; s1; s1 = s1.ts_prev) {
						switch (s1.type) {
						case C.BAR:



						case C.KEY:
						case C.METER:
							continue
						}
						break
					}
				}







>
>



>
>
>







1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
					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
					}
				}
︙︙
1750
1751
1752
1753
1754
1755
1756

1757


1758
1759
1760



1761
1762
1763
1764
1765
1766
1767
1768
					if (s1.v == s2.v)
						break
				}
				lkvsym(s3, s1)		// voice link

				// care with spacing
				if (s3.seqst) {

					s3.shrink = s3.wl


					if (s3.prev)	// if not start of voice
						s3.shrink += s3.prev.wr
					s3.space = set_space(s3, s3.ts_prev.time)



					s3.next.shrink = s3.wr + s3.next.wl
				}
				delete s3.part
				continue
			}
			if (w_tb[s2.type])
				break		// symbol with a width
		}







>

>
>
|
|
|
>
>
>
|







1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
					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
					while (!s4.seqst)
						s4 = s4.ts_prev
					s3.shrink += s4.wr
					s3.space = 0
					s4 = s3.ts_next
					while (!s4.seqst)
						s4 = s4.ts_next
					s4.shrink = s3.wr + s4.wl
				}
				delete s3.part
				continue
			}
			if (w_tb[s2.type])
				break		// symbol with a width
		}
︙︙
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
}

/* -- 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, x, xmin, xmid, xmax, wwidth, shrink, space,
	nlines,
	last = next ? next.ts_prev : null,
	ws = get_width(s, next)		// 2 widths: nice and shrinked

	// 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[1] + indent < lwidth) {
		if (next)
			next = set_nl(next)
		return next || last
	}

	/* loop on cutting the tune into music lines */
	wwidth = ws[0] + indent







|


|









|







1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
}

/* -- 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
︙︙
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
				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 (next)
				next = set_nl(next)
			return next
		}

		/* try to cut on a measure bar */
		s3 = null
		for ( ; s != next; s = s.ts_next) {
			x = s.x
			if (!x)







|
|
|







1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
				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)
︙︙
1960
1961
1962
1963
1964
1965
1966

1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986

1987




1988


1989

1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
		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)

			for ( ; s != next; s = s.ts_next) {
				if (s.dur) {
					if (!s.beam_end)
						beam |= 1 << s.v
					else
						beam &= ~(1 << s.v)
				}
				x = s.x
				if (!x)
					continue
				if (x + s.wr >= xmax)
					break
				if (beam || s.in_tuplet)
					continue
				if (s3 && s.x >= xmid) {
					if (xmid - s3.x > s.x - xmid
					 || (s.dur
					  && (s.time + s.dur - bar_time)
							% (C.BLEN / 4) == 0))
						s3 = s

					break




				}


				s3 = s

			}
		}

		// 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 && s.x >= xmid) {
					if (xmid - s3.x > s.x - xmid)
						s3 = s
					break
				}
				s3 = s
			}
		}
		s = s3







>

|
<
<
<
<
<
|
<
<
|
|
|
<
<
|
|
<


>
|
>
>
>
>
|
>
>
|
>












|
|







1993
1994
1995
1996
1997
1998
1999
2000
2001
2002





2003


2004
2005
2006


2007
2008

2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
		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
︙︙
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047


2048
2049
2050
2051
2052
2053

2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
		wwidth -= s.x - first.x;
		indent = 0
	}
	return s
}

/* -- cut the tune into music lines -- */
function cut_tune(lwidth, indent) {
    var	s2, i,
//fixme: not usable yet
//		pg_sav = {
//			leftmargin: cfmt.leftmargin,
//			rightmargin: cfmt.rightmargin,
//			pagewidth: cfmt.pagewidth,
//			scale: cfmt.scale
//		},


	s = tsfirst

	// take care of the voice subnames
	if (indent) {
		i = set_indent()
		lwidth -= i;

		indent -= i;
	}

	/* adjust the line width according to the starting clef
	 * and key signature */
/*fixme: may change in the tune*/
	i = get_ck_width();
	lwidth -= i[0];
	indent += i[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) {







|
|
<
|
|
|
|
|
|
>
>


<
<
<
|
>
|
|
<
|
<
<
<
|
|







2064
2065
2066
2067
2068
2069
2070
2071
2072

2073
2074
2075
2076
2077
2078
2079
2080
2081
2082



2083
2084
2085
2086

2087



2088
2089
2090
2091
2092
2093
2094
2095
2096
		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) {
︙︙
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094

2095
2096
2097


2098







2099



2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110


2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126


2127
2128
2129
2130
2131
2132
2133
			i = s.fmt.barsperstaff
		}
	}

	/* cut at explicit end of line, checking the line width */
	s2 = s
	for ( ; s; s = s.ts_next) {
//fixme: not usable yet
//		if (s.type == C.BLOCK) {
//			switch (s.subtype) {
//			case "leftmargin":
//			case "rightmargin":
//			case "pagescale":
//			case "pagewidth":
//			case "scale":
//			case "staffwidth":

//				set_format(s.subtype, s.param)
//				break
//			}


//			continue







//		}



		if (!s.ts_next) {
			s = null
		} else if (!s.soln) {
			continue
		} else {
			s.soln = false
			if (s.time == s2.time)
				continue	// empty music line!
			while (!s.seqst)
				s = s.ts_prev
		}


		s2 = set_lines(s2, s, lwidth, indent)
		if (!s2)
			break

//--fixme
//		s = s2.ts_prev;		// don't miss an eoln
		s = s2
		indent = 0
	}

//fixme: not usable yet
//	// 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


}

/* -- 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







<
|
|
|
|
|
|
|
|
>
|
|
<
>
>
|
>
>
>
>
>
>
>
|
>
>
>











>
>




|
|
|



<
|
|
|
|
|
>
>







2106
2107
2108
2109
2110
2111
2112

2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123

2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160

2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
			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":
				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
			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
︙︙
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
		}

		/* 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
			 && s2.v == s.v) {
				s3 = s2.bar_type[0] != ':' ? s2 : s2.next
				break
			}
			if (s2.type != C.NOTE)
				continue

			/* have a 2nd choice on beam start */







|
<







2542
2543
2544
2545
2546
2547
2548
2549

2550
2551
2552
2553
2554
2555
2556
		}

		/* 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 */
︙︙
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
		}
		staff_tb[st] = {
			output: "",
			sc_out: ""
		}
	}

	// set the starting clefs of the staves
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (!sy.voices[v])
			continue
		st = sy.voices[v].st
		if (!sy.voices[v].second) {		// main voices
			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;

			if (!p_voice.clef.clef_auto)
				delete staff_clef[st].autoclef
		}
	}
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v]
		if (!sy.voices[v]
		 || sy.voices[v].second)		// main voices
			continue
		st = sy.voices[v].st;
		s = p_voice.clef
		if (staff_clef[st].autoclef) {
			s.clef_type = set_auto_clef(st,
						tsfirst,
						s.clef_type);
			s.clef_line = s.clef_type == 't' ? 2 : 4
		}
		staff_clef[st].clef = staff_tb[st].clef = s
	}
	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)








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







2598
2599
2600
2601
2602
2603
2604


































2605
2606
2607
2608
2609
2610
2611
		}
		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)

︙︙
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
					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.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







|







2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
					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
︙︙
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
}

/* upper and lower space needed by rests */
var rest_sp = [
	[18, 18],
	[12, 18],
	[12, 12],
	[0, 12],
	[6, 8],
	[10, 10],			/* crotchet */
	[6, 4],
	[10, 0],
	[10, 4],
	[10, 10]
]







|







2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
}

/* upper and lower space needed by rests */
var rest_sp = [
	[18, 18],
	[12, 18],
	[12, 12],
	[6, 12],
	[6, 8],
	[10, 10],			/* crotchet */
	[6, 4],
	[10, 0],
	[10, 4],
	[10, 10]
]
︙︙
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257

3258
3259
3260
3261
3262
3263

3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
	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, fmt,
	shlp, p_st, top,
	nv = voice_tb.length


	/* initialize the voices */
	for (v = 0; v < nv; v++) {
		if (!cur_sy.voices[v])
			continue
		p_voice = voice_tb[v];

		p_voice.second = cur_sy.voices[v].second;
		p_voice.last_sym = p_voice.sym;

		/* move the voice to a printed staff */
		st = cur_sy.voices[v].st
		while (st <= nstaff && !cur_sy.st_print[st])
			st++
		if (st > nstaff)
			continue		// no printed staff
		p_voice.st = st
		if (!p_voice.second) {
			staff_tb[st].key = p_voice.ckey
			staff_tb[st].meter = p_voice.meter
		}
	}

	// move the first clefs, key signatures and time signatures
	// to the staves
	s = tsfirst
	fmt = s.fmt
	while (s) {
		switch (s.type) {
		case C.CLEF:
		case C.KEY:
		case C.METER:
			switch (s.type) {
			case C.CLEF:
				staff_tb[s.st].clef = s







|

|
>






>



<
<
<
<
<
<
<
<
<
<
<
<
<


|
<
<







3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274













3275
3276
3277


3278
3279
3280
3281
3282
3283
3284
	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
︙︙
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312

3313
3314
3315
3316
3317
3318
3319
				break
			}
			unlksym(s)
			// fall thru
		case C.TEMPO:
		case C.BLOCK:
		case C.REMARK:
			s = s.ts_next
			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++) {







<



>







3295
3296
3297
3298
3299
3300
3301

3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
				break
			}
			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++) {
︙︙
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374

3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391

3392
3393
3394
3395
3396
3397
3398
			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
			if (last_s.type == C.CLEF)
				delete last_s.seqst
		}
		delete s.clef_small;
		s.second = cur_sy.voices[v].second
// (fixme: needed for sample5 X:3 Fugue & staffnonote.xhtml)
		if (!cur_sy.st_print[st])
			s.invis = true
		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

			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)

			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++) {







|

<
|
<
|



















>

















>







3336
3337
3338
3339
3340
3341
3342
3343
3344

3345

3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
			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.xhtml)
		if (!cur_sy.st_print[st])
			s.invis = true
		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++) {
︙︙
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468

3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
	}

	// compute the spacing of the added symbols
	self.set_pitch(last_s);

	s = tsfirst
	s.seqst = true
	shlp = 0
	while (1) {
		s2 = s;
		shl = shrmx = 0
		do {
			self.set_width(s);
			shr = s.wl + shlp
			if (shr > shrmx)
				shrmx = shr;
			if (s.wr > shl)
				shl = s.wr	// left width for next symbol
			s = s.ts_next
		} while (s != last_s && !s.seqst);
		s2.shrink = shrmx;
		s2.space = 0
		if (s == last_s)
			break
		shlp = shl
	}


	// update the spacing before the first old time sequence
	shr = 0
	if (last_s.type == C.BAR && s2.type == C.CLEF)
		shl += 4		// (see set_width)
	do {
		self.set_width(s)
		if (s.a_ly)
			ly_set(s)
		if (shr < s.wl)
			shr = s.wl;
		s = s.ts_next
	} while (s && !s.seqst);
	last_s.shrink = shl + shr
	last_s.space = 0
} // init_music_line()

// check if the tune ends on a measure bar
function check_end_bar() {
    var	s2,
	s = tsfirst
	while (s.ts_next)







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
>
|
<
|
|
<
<
|
<
<
<
<
|
<
<
<







3436
3437
3438
3439
3440
3441
3442


















3443
3444
3445

3446
3447


3448




3449



3450
3451
3452
3453
3454
3455
3456
	}

	// 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)
		;

	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)
︙︙
3501
3502
3503
3504
3505
3506
3507
















3508
3509
3510
3511
3512
3513
3514
// 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 */

















	for (s = p_voice.sym; s; s = s.next) {
		if (s.type == C.NOTE) {
			pitch = s.notes[0].pit
			break
		}
	}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
// 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
		}
	}
︙︙
3527
3528
3529
3530
3531
3532
3533
3534
3535








3536
3537
3538
3539
3540
3541
3542
				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)








			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]







|

>
>
>
>
>
>
>
>







3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
				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]
︙︙
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
					s.nflags += s.ntrem
				else
					s.nflags = s.ntrem
			}
			if (s.next && s.next.trem2)
				break
			if (s.trem2) {
				s.prev.trem2 = true;
				s.prev.nflags = --s.nflags;
				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
				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







<
<
<
<
<
<
<
<
<
<
|
<
<









|







3540
3541
3542
3543
3544
3545
3546










3547


3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
					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
︙︙
3707
3708
3709
3710
3711
3712
3713

3714
3715
3716



3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748


3749








3750


3751
3752






3753


3754
3755
3756
3757
3758
3759
3760
3761
3762




3763



3764

3765



3766














3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796

3797
3798
3799
3800
3801
3802
3803
		set_ottava();

	// set the clefs and adjust the pitches of all symbol
	set_clefs();
	self.set_pitch(null)
}


/* -- return the left indentation of the staves -- */
function set_indent(first) {
	var	st, v, w, p_voice, p, i, j, font,



		nv = voice_tb.length,
		maxw = 0

	for (v = 0; v < nv; v++) {
		p_voice = voice_tb[v]
		if (!cur_sy.voices[v])
			continue
		st = cur_sy.voices[v].st
//		if (!cur_sy.st_print[st])
//			continue
		p = ((first || p_voice.new_name) && p_voice.nm) ?
			p_voice.nm : p_voice.snm
		if (!p)
			continue
		if (!font) {
			set_font("voice");
			font = gene.curfont
		}
		i = 0
		while (1) {
			j = p.indexOf("\n", i)
			if (j < 0)
				w = strwh(p.slice(i))
			else
				w = strwh(p.slice(i, j))
			w = w[0]
			if (w > maxw)
				maxw = w
			if (j < 0)
				break
			i = j + 1
		}


	}








	if (font)


		maxw += 4 * cwidf(' ');







	w = .5				// (width of left bar)


	for (st = 0; st <= cur_sy.nstaff; st++) {
		if (cur_sy.staves[st].flags
				& (OPEN_BRACE2 | OPEN_BRACKET2)) {
			w = 12
			break
		}
		if (cur_sy.staves[st].flags & (OPEN_BRACE | OPEN_BRACKET))
			w = 6
	}




	maxw += w





	if (first)			// if %%indent



		maxw += cfmt.indent














	return maxw
}

/* -- 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) == 0)
				s.stem = 1
			for (; g; g = g.next) {
				g.stem = s.stem;
				g.multi = s.multi
			}
			continue
		}

		if (!s.stem			/* if not explicitly set */
		 && (s.stem = s.multi) == 0) { /* and 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;







>
|
|
|
>
>
>
|
<

<
|
<
|
<
<
<
<
<
|
<
<
<
|
|
|



<
|
|
<
|
|




>
>
|
>
>
>
>
>
>
>
>
|
>
>
|
|
>
>
>
>
>
>
|
>
>
|
|
|
|
|
|
|
|
|
>
>
>
>
|
>
>
>

>
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

















|
<
|







|
|
>







3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699

3700

3701

3702





3703



3704
3705
3706
3707
3708
3709

3710
3711

3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799

3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
		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,
	nv = voice_tb.length

	// name or subname?
	for (v = 0; v < nv; v++) {
		p_voice = voice_tb[v]
		if (!cur_sy.voices[v]
		 || !gene.st_print[p_voice.st])
			continue
		if (p_voice.new_name) {
			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;
︙︙
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
	}
}

/* -- 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, res

	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) {
				res = identify_note(s, g.dur);
				g.head = res[0];
				g.dots = res[1];
				g.nflags = res[2];
				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 += 2;
				ymn -= 2
				if (ymn < ymin)
					ymin = ymn
				else if (ymx > ymax)
					ymax = ymx;
				g.ymx = ymx;
				g.ymn = ymn
			}







|







<
<
<
<














|
|







4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465




4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
	}
}

/* -- 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
			}
︙︙
4575
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586
4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614


4615
4616
4617
4618
4619
4620
4621
4622
4623
4624
4625
4626
4627

4628

4629
4630
4631
4632
4633
4634
4635
4636
4637
4638


4639
4640
4641
4642
4643
4644
4645
4646
4647
4648
4649
			if (s.notes[s.nhd].tie)
				ymx += 3;
			s.ymx = ymx + 4
		}
	}
}

/* -- move the symbols of an empty staff to the next one -- */
function sym_staff_move(st) {
	for (var s = tsfirst; s; s = s.ts_next) {
		if (s.nl)
			break
		if (s.st == st
		 && s.type != C.CLEF) {
			s.st++;
			s.invis = true
		}
	}
}

// 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
		multicol = {
			posy: posy,
			maxy: posy,
			lmarg: cfmt.leftmargin,
			rmarg: cfmt.rightmargin


		}
		break
	case "mc_new":			// multicol new
		if (!multicol) {
			error(1, s, "%%multicol new without start")
			break
		}
		if (posy > multicol.maxy)
			multicol.maxy = posy
		cfmt.leftmargin = multicol.lmarg
		cfmt.rightmargin = multicol.rmarg
		img.chg = true
		set_page()

		posy = multicol.posy

		break
	case "mc_end":			// multicol end
		if (!multicol) {
			error(1, s, "%%multicol new without start")
			break
		}
		if (posy < multicol.maxy)
			posy = multicol.maxy
		cfmt.leftmargin = multicol.lmarg
		cfmt.rightmargin = multicol.rmarg


		multicol = undefined
		blk_flush()
		img.chg = true
		set_page()
		break
	case "ml":
		blk_flush()
		user.img_out(s.text)
		break
	case "newpage":
		if (!user.page_format)







<
<
<
<
<
<
<
<
<
<
<
<
<


















|
|
>
>



<
<
<
<


|
|
<
|
>

>


<
<
<
<


|
|
>
>


|
<







4585
4586
4587
4588
4589
4590
4591













4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602
4603
4604
4605
4606
4607
4608
4609
4610
4611
4612
4613
4614
4615
4616




4617
4618
4619
4620

4621
4622
4623
4624
4625
4626




4627
4628
4629
4630
4631
4632
4633
4634
4635

4636
4637
4638
4639
4640
4641
4642
			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
		multicol = {
			posy: posy,
			maxy: posy,
			lm: cfmt.leftmargin,
			rm: cfmt.rightmargin,
			w: cfmt.pagewidth,
			sc: cfmt.scale
		}
		break
	case "mc_new":			// multicol new




		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 (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)
︙︙
4670
4671
4672
4673
4674
4675
4676














4677
4678
4679
4680
4681
4682
4683
		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 = [],







>
>
>
>
>
>
>
>
>
>
>
>
>
>







4663
4664
4665
4666
4667
4668
4669
4670
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
		write_title(s.text, true)
		break
	case "vskip":
		vskip(s.sk);
		break
	}
}

// -- move some symbols of an empty staff to the next one --
function sym_staff_move(st) {
	for (var s = tsfirst; s; s = s.ts_next) {
		if (s.nl)
			break
		if (s.st == st
		 && s.type != C.CLEF) {
			s.st++
			if (s.type != C.TEMPO)
				s.invis = true
		}
	}
} // sym_staff_move()

/* -- 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 = [],
︙︙
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747

			// ledger lines
			// index = line number
			// values = [x symbol, x start, x stop]
			p_staff.hlu = []	// above the staff
			p_staff.hld = []	// under the staff

			if (!non_empty_gl[st]) {
				p_staff.botbar = p_staff.topbar = 0
				continue
			}
			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 '-':







<
<
<
<







4737
4738
4739
4740
4741
4742
4743




4744
4745
4746
4747
4748
4749
4750

			// 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 '-':
︙︙
4756
4757
4758
4759
4760
4761
4762


4763
4764
4765
4766
4767
4768
4769
					p_staff.topbar += 6
				} else {		// no line: big bar
					p_staff.botbar -= 12;
					p_staff.topbar += 12
					continue	// no helper line
				}
			}



			// 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(new Array((l - i + 1) * 2))
			for (j = 1; i < l; i++, j += 2) {







>
>







4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
					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(new Array((l - i + 1) * 2))
			for (j = 1; i < l; i++, j += 2) {
︙︙
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882
4883
4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
	/* set the last empty staves */
	set_brace()
	sy.st_print = new Uint8Array(non_empty);

	/* define the offsets of the measure bars */
	set_top_bot()

	/* move the symbols of the empty staves to the next staff */
//fixme: could be optimized (use a old->new staff array)
	for (st = 0; st < nstaff; st++) {
		if (!non_empty_gl[st])
			sym_staff_move(st)
	}

	/* let the last empty staff have a full height */
	if (!non_empty_gl[nstaff])
		staff_tb[nstaff].topbar = 0;

	/* initialize the music line */
	init_music_line();

	// keep the array of the staves to be printed
	gene.st_print = new Uint8Array(non_empty_gl)








|
<





|

|







4876
4877
4878
4879
4880
4881
4882
4883

4884
4885
4886
4887
4888
4889
4890
4891
4892
4893
4894
4895
4896
4897
4898
	/* set the last empty staves */
	set_brace()
	sy.st_print = new Uint8Array(non_empty);

	/* define the offsets of the measure bars */
	set_top_bot()

	// move the symbols of the empty staves to the next staff

	for (st = 0; st < nstaff; st++) {
		if (!non_empty_gl[st])
			sym_staff_move(st)
	}

	// set a null height if the last staff is empty
	if (!non_empty_gl[nstaff])
		staff_tb[nstaff].topbar = 0

	/* initialize the music line */
	init_music_line();

	// keep the array of the staves to be printed
	gene.st_print = new Uint8Array(non_empty_gl)

︙︙
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136





5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153





5154
5155
5156
5157
5158
5159
5160
5161
5162


5163
5164
5165
5166
5167
5168
5169
5170


5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
	}
	tsfirst = null			/* no more notes */
}

/* -- generate the music -- */
// (possible hook)
Abc.prototype.output_music = function() {
    var v, lwidth, indent, 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

	gen_init()			// output the blocks and define the page layout
	if (!tsfirst)
		return

	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();		/* set the width of all symbols */

	indent = set_indent(true)

	/* if single line, adjust the page width */
	if (cfmt.singleline) {
		v = get_ck_width();
		lwidth = indent + v[0] + v[1] + get_width(tsfirst, null)[0];





		img.width = lwidth * cfmt.scale + img.lm + img.rm + 2
	} else {

	/* else, split the tune into music lines */
		lwidth = get_lwidth();
		cut_tune(lwidth, indent)
	}

	// save symbol pointers for play
	ts1st = tsfirst
	v = nv
	while (--v >= 0)
		voice_tb[v].osym = voice_tb[v].sym

	spf_last = 0				// last spacing factor
	while (1) {				/* loop per music line */
		set_piece();





		self.set_sym_glue(lwidth - indent)
		if (realwidth) {
			if (indent)
				posx += indent;
			draw_sym_near();		// delayed output
			line_height = set_staff();
			draw_systems(indent);
			draw_all_sym();
			delayed_update();


			vskip(line_height)
			if (indent)
				posx -= indent;
			while (blocks.length)
				self.block_gen(blocks.shift())
		}

		blk_flush()


		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
		indent = set_indent()
	}

	// 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
	}
}







|




















|

|




|
>
>
>
>
>
|




|











>
>
>
>
>









>
>
|


<
<



>
>















<












5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181


5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201

5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
	}
	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

	gen_init()			// output the blocks and define the page layout
	if (!tsfirst)
		return

	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

	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 = 0				// 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 (indent)
				posx += indent;
			draw_sym_near();		// delayed output
			line_height = set_staff();
			draw_systems(indent);
			draw_all_sym();
			delayed_update();
//fixme: KO if at start of tune (the header is generated)
			if (output)			// if some music
				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
	}
}
Changes to core/parse.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - parse.js - ABC parse
//
// Copyright (C) 2014-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - parse.js - ABC parse
//
// 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.
︙︙
151
152
153
154
155
156
157



































































158
159
160
161
162
163
164
				return
			}
			pit[i] = abc2svg.pab40(note.pit, note.acc)
		}
	}
	return pit[1] - pit[0]
}




































































// set the linebreak character
function set_linebreak(param) {
	var i, item

	for (i = 0; i < 128; i++) {
		if (char_tb[i] == "\n")







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
				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 (a) {
	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")
︙︙
275
276
277
278
279
280
281
282

283
284
285
286
287
288
289
	sym_link(s)
	return s
}

// set the voice parameters
// (possible hook)
Abc.prototype.set_vp = function(a) {
    var	s, item, pos, val, clefpit, tr_p


	while (1) {
		item = a.shift()
		if (!item)
			break
		if (item.slice(-1) == '='
		 && !a.length) {







|
>







342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
	sym_link(s)
	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) {
︙︙
331
332
333
334
335
336
337
338

339
340
341
342
343
344

345
346
347
348
349
350
351
352
353
354
355
356
357
			// (instrument=M == instrument=M/M)
			item = a.shift()
			val = item.indexOf('/')
			if (val < 0) {
				val = get_interval('c' + item)
				if (val == undefined)
					break
				curvoice.sndtran = val

				val = 0
			} else {
				val = get_interval('c' + item.slice(val + 1))
				if (val == undefined)
					break
				curvoice.sndtran = val

				val = get_interval(item.replace('/', ''))
				if (val == undefined)
					break
			}
			curvoice.transp = cfmt.sound ? curvoice.sndtran : val
			tr_p = 1
			break
		case "map=":			// %%voicemap
			curvoice.map = a.shift()
			break
		case "name=":
		case "nm=":
			curvoice.nm = a.shift()







|
>





|
>




|
|







399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
			// (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()
︙︙
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
			// score=MN
			// (score=M == score=Mc)
			item = a.shift()
			if (cfmt.sound)
				break
			val = get_interval(item, true)
			if (val != undefined) {
				curvoice.transp = 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 = curvoice.sndsh = val
				tr_p = 1
			}
			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.sndtran = val
			if (cfmt.sound)
				curvoice.transp = val
			tr_p = 1
			break
		case "subname=":
		case "sname=":
		case "snm=":
			curvoice.snm = a.shift()
			if (curvoice.snm[0] == '"')
				curvoice.snm = curvoice.snm.slice(1, -1);







|
|









|
|













|

|
|







466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
			// 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);
︙︙
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
				syntax(1, errs.notransp)
				break
			}
			val = get_transp(a.shift())
			if (val == undefined) {
				syntax(1, errs.bad_transp)
			} else {
				curvoice.sndtran = val
				if (cfmt.sound)
					curvoice.transp = val
				tr_p = 1
			}
			break
		default:
			switch (item.slice(0, 4)) {
			case "treb":
			case "bass":
			case "alto":







|

|
|







536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
				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":
︙︙
506
507
508
509
510
511
512


513



514











515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
		s = new_clef(s)
		if (s) {
			if (clefpit)
				s.clefpit = clefpit
			get_clef(s)
		}
	}


	if (tr_p)



		set_transp()











} // 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['*'])
				a = info.V['*'].concat(a)
			if (info.V[curvoice.id])
				a = info.V[curvoice.id].concat(a)
		}
	}
	if (a.length)
		self.set_vp(a)
} // set_kv_parm()

// memorize the K:/V: parameters







>
>
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>







|
|
|
|







576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
		s = new_clef(s)
		if (s) {
			if (clefpit)
				s.clefpit = clefpit
			get_clef(s)
		}
	}

	// if transposition
	if (tr_p) {
		curvoice.tr_p = tr_p			// keep this key signature
		tr_p = (curvoice.score | 0)
			+ (curvoice.shift | 0)
			+ (cfmt.transp | 0)
		if (tr_p)
			curvoice.tr_sco = tr_p		// b40 interval
		else if (curvoice.tr_sco)
			curvoice.tr_sco = 0
		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
︙︙
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
	else
		info.V[vid] = a
}

// K: key signature
// return the key and the voice/clef parameters
function new_key(param) {
    var	i, clef, key_end, c, tmp, exp,
	sf = "FCGDAEB".indexOf(param[0]) - 1,
	mode = 0,
	s = {
		type: C.KEY,
		dur: 0
	}

	// set the accidentals when K: with modified accidentals
	function set_k_acc(s, 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 = s.k_a_acc.length
		for (i = 0; i < n; i++) {
			p_acc = s.k_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 = s.k_a_acc[i]
			if (!p_acc)
				p_acc = s.k_a_acc[i] = {}
			p_acc.acc = accs[i];
			p_acc.pit = pits[i]
		}
	} // set_k_acc()

	// code of new_key()
	set_ref(s);

	// tonic
	i = 1
    if (sf < -1) {
	switch (param[0]) {
	case 'H':				// bagpipe







|







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







627
628
629
630
631
632
633
634
635
636
637
638
639
640
641










































642
643
644
645
646
647
648
	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
︙︙
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726







727
728
729



730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
			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'");
			exp = true
		}
		c = param[0]
		if (c == '^' || c == '_' || c == '=') {
			s.k_a_acc = [];
			tmp = new scanBuf;
			tmp.buffer = param
			do {
				var 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 == '=');
			if (!exp)
				set_k_acc(s, sf)
			param = param.slice(tmp.index)
		} else if (exp && param.indexOf("none") == 0) {
			sf = 0
			param = param.replace(/\w+\s*/, '')
		}
	}








	s.k_sf = sf;

	// set the map of the notes with accidentals



	if (s.k_a_acc) {
		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
		}
	} else {
		s.k_map = s.k_bagpipe && !sf
			? abc2svg.keys[9]		// implicit F# and C#
			: abc2svg.keys[sf + 7]

	}
	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)]







|







|







<
<

|





>
>
>
>
>
>
>



>
>
>

|





<
<
<
<
<







739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761


762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788





789
790
791
792
793
794
795
			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)]
︙︙
887
888
889
890
891
892
893




894
895
896
897
898
899
900
	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]




	}
	s.wmeasure = wmeasure

	if (cfmt.writefields.indexOf('M') < 0)
		s.a_meter = []

	if (parse.state != 3) {







>
>
>
>







934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
	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) {
︙︙
1009
1010
1011
1012
1013
1014
1015
1016

1017
1018
1019
1020

1021
1022
1023
1024
1025
1026










1027
1028
1029
1030
1031
1032
1033

1034
1035

1036






1037
1038
1039
1040
1041
1042
1043
			if (!s.tempo || isNaN(s.tempo)) {
				syntax(1, "Bad tempo value")
				return
			}
		}
	}

	if (parse.state < 2) {			// if in tune header

		info.Q = txt
		glovar.tempo = s
		return
	}

	if (!curvoice.time)
		glovar.tempo = s
	else
		sym_link(s)
	if (!glovar.tempo)
		syntax(0, "No previous tempo")










}

// treat the information fields which may embedded
function do_info(info_type, text) {
	var s, d1, d2, a, vid

	// skip this line if the current voice is ignored

	if (curvoice && curvoice.ignore
	 && info_type != 'V')

		return







	switch (info_type) {

	// info fields in any state
	case 'I':
		self.do_pscom(text)
		break







|
>




>
|
<
<
<


>
>
>
>
>
>
>
>
>
>




|


>
|
|
>
|
>
>
>
>
>
>







1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074



1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
			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
	}

    var	tim = curvoice.time



	if (!glovar.tempo)
		syntax(0, "No previous tempo")
	if (!parse.ctrl)
		parse.ctrl = {}
	if (!parse.ctrl[tim])
		parse.ctrl[tim] = {}
	parse.ctrl[tim].tempo = s
	s.v = par_sy.top_voice
	s.p_v = voice_tb[s.v]
	s.fmt = cfmt
	s.st = s.p_v.st
	s.time = tim
}

// 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
︙︙
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097








1098
1099
1100
1101
1102
1103
1104
	case 'P':
		if (!parse.state)
			break
		if (parse.state == 1) {
			info.P = text
			break
		}
		if (!parse.part)
			parse.part = {}
		if (parse.part[curvoice.time])	// P: already defined
			break
		s = {
			text: text,
			time: curvoice.time
		}
		set_ref(s)
		if (cfmt.writefields.indexOf(info_type) < 0)
			s.invis = true
		parse.part[curvoice.time] = s








		break
	case 'Q':
		if (!parse.state)
			break
		new_tempo(text)
		break
	case 'V':







|
|
|
<


|




|
>
>
>
>
>
>
>
>







1147
1148
1149
1150
1151
1152
1153
1154
1155
1156

1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
	case 'P':
		if (!parse.state)
			break
		if (parse.state == 1) {
			info.P = text
			break
		}

		// memorize the part in the control
		tim = curvoice.time

		s = {
			text: text,
			time: tim
		}
		set_ref(s)
		if (cfmt.writefields.indexOf(info_type) < 0)
			s.invis = true
		if (!parse.ctrl)
			parse.ctrl = {}
		if (!parse.ctrl[tim])
			parse.ctrl[tim] = {}
		parse.ctrl[tim].part = s
		s.v = par_sy.top_voice
		s.p_v = voice_tb[s.v]
		s.fmt = cfmt
		s.st = s.p_v.st
		break
	case 'Q':
		if (!parse.state)
			break
		new_tempo(text)
		break
	case 'V':
︙︙
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
			delete s.text
		}
		curvoice.tie_s_rep = null	// no tie anymore on new variant
	}

	// handle the accidentals (ties and repeat)
	if (s.text) {
		s.rbstop = 2;
		s.rbstart = 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 {







<
|







1392
1393
1394
1395
1396
1397
1398

1399
1400
1401
1402
1403
1404
1405
1406
			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 {
︙︙
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
			}
		}
	}

	/* set some flags */
	switch (bar_type) {
	case "[":
		s.rbstop = 2
	case "[]":
	case "[|]":
		s.invis = true;
		bar_type = s.rbstart ? "[" : "[]"
		break
	case ":|:":
	case ":||:":







<







1474
1475
1476
1477
1478
1479
1480

1481
1482
1483
1484
1485
1486
1487
			}
		}
	}

	/* set some flags */
	switch (bar_type) {
	case "[":

	case "[]":
	case "[|]":
		s.invis = true;
		bar_type = s.rbstart ? "[" : "[]"
		break
	case ":|:":
	case ":||:":
︙︙
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
	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
}







|







1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
	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
}
︙︙
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
	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()








|







1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
	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()

︙︙
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802


1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842


1843
1844
1845
1846
1847
1848
1849









1850
1851
1852
1853
1854
1855
1856
		line.index++
		return ty + C.SL_BELOW
	}
	return ty + C.SL_AUTO
}

// on end of slur, create the slur
function slur_add(enote, e_is_note) {
    var	i, s, sl, snote, s_is_note

	// go back and find the last start of slur
	for (i = curvoice.sls.length; --i >= 0; ) {
		sl = curvoice.sls[i]
		snote = sl.note
		s_is_note = sl.is_note
		delete sl.is_note

		// the slur must not start and stop on a same symbol
		if (snote.s != enote.s) {


			sl.note = enote
			if (e_is_note)
				sl.is_note = e_is_note
			s = s_is_note ? snote : snote.s
			if (!s.sls)
				s.sls = [];
			s.sls.push(sl)
			curvoice.sls.splice(i, 1)

			// set a flag on the start symbol if slur from a note
			if (s_is_note)
				snote.s.sl1 = true

			// set a flag if the slur starts on a grace note
			if (sl.grace)
				sl.grace.sl1 = true

			// set a flag if the slur ends on a grace note
			if (enote.s.grace)
				enote.s.sl2 = true
			return
		}
	}

	if (enote.grace) {
		error(1, enote.s, errs.bad_slur_end)
		return
	}

	// the lack of a starting slur may be due to a repeat
	for (s = enote.s.prev; s; s = s.prev) {
		if (s.type == C.BAR
		 && s.bar_type[0] == ':'
		 && s.text) {
			if (!s.sls)
				s.sls = [];
			s.sls.push({
				note: enote,
//fixme: should go back to the bar "|1" and find the slur type...
				ty: C.SL_AUTO


			})
			if (e_is_note)
				s.sls[s.sls.length - 1].is_note = e_is_note
			return
		}
	}
	syntax(1, "End of slur without start")









}

// 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







|
|




<
<
<


|
>
>
|
|
|
|
|
|
|
<

<
<
<
<
|
|
|
<
<
<
<
|
|
|
<
<
<
<
<
<

|
|
|
|
|
|
|
<

|
>
>

|
|



|
>
>
>
>
>
>
>
>
>







1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869



1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881

1882




1883
1884
1885




1886
1887
1888






1889
1890
1891
1892
1893
1894
1895
1896

1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
		line.index++
		return ty + C.SL_BELOW
	}
	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
︙︙
1920
1921
1922
1923
1924
1925
1926

1927
1928
1929
1930
1931
1932
1933
				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
				}







>







1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
				if (!not1.tie_ty)
					continue
				if (not1.midi == mid
				 && (!se
				  || !not1.tie_e)) {	// (if unison)
					not2.tie_s = not1
					not2.s = s
					delete not2.acc	// (if transposing)
					if (se) {
						not1.tie_e = not2
						not1.s = tie_s
					}
					nt++
					break
				}
︙︙
1956
1957
1958
1959
1960
1961
1962

1963
1964
1965
1966
1967
1968
1969
		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,

	sl1 = [],
	line = parse.line,
	a_dcn_sav = a_dcn		// save parsed decoration names

	a_dcn = []
	parse.stemless = false;
	s = {







>







2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
		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 = {
︙︙
1998
1999
2000
2001
2002
2003
2004




2005
2006
2007
2008
2009
2010
2011
	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;




		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







>
>
>
>







2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
	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
︙︙
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110

2111
2112
2113
2114
2115

2116
2117
2118
2119
2120
2121
2122
2123
2124










2125
2126
2127
2128
2129

2130
2131
2132











2133
2134
2135




















2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146


2147
2148
2149
2150
2151

2152

2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177

2178

2179
2180
2181
2182
2183
2184
2185
					s.grace ? C.BLEN / 4 :
					curvoice.ulen < 0 ?
						C.BLEN :
						curvoice.ulen)
			if (!note)
				return //null

			// transpose
			if (curvoice.octave)
				note.pit += curvoice.octave * 7


			apit = note.pit + 19		// pitch from C-1
			i = note.acc

			// get the explicit or implicit accidental
			if (i) {

				curvoice.acc[apit] = i
			} else {
				i = curvoice.acc[apit]
				if (!i && acc_tie)
					i = acc_tie[apit]
				if (!i)
					i = curvoice.ckey.k_map[apit % 7] || 0
			}











			// map
			if (curvoice.map
			 && maps[curvoice.map])
				set_map(note, i)


			if (!note.midi)			// if not map play
				note.midi = pit2mid(apit, i)












			if (curvoice.ckey.k_sndtran)
				note.midi += abc2svg.b40m(curvoice.ckey.k_sndtran +
						122) - 36





















			// starting slurs
			if (sl1.length) {
				while (1) {
					i = sl1.shift()
					if (!i)
						break
					curvoice.sls.push({
						is_note: true,
						note: note,
						ty: i


					})
				}
				note.s = s;		// link the note to the chord
			}
			if (a_dcn.length)

				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 ')':
					note.s = s
					slur_add(note, true)
					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 = []
						curvoice.acc_tie[apit] =
							curvoice.acc[apit] ||

								acc_tie[apit]

					}
					c = line.char()
					continue
				case '.':
					c = line.next_char()
					switch (c) {
					case '-':







<



>


<
<
|
>
|
|
|
<
<




>
>
>
>
>
>
>
>
>
>





>
|


>
>
>
>
>
>
>
>
>
>
>
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








<
<
|
>
>


<

|
>

>









<
|












<
|
>
|
>







2173
2174
2175
2176
2177
2178
2179

2180
2181
2182
2183
2184
2185


2186
2187
2188
2189
2190


2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255


2256
2257
2258
2259
2260

2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274

2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287

2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
					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

			// map
			if (curvoice.map
			 && maps[curvoice.map])
				set_map(note, i)

			// set the MIDI pitch
			if (!note.midi)		// if not map play
				note.midi = pit2mid(apit, i)

			// transpose
			if (curvoice.tr_sco) {
				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 '-':
︙︙
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218


2219
2220
2221
2222
2223
2224
2225
2226
2227
2228

2229
2230
2231
2232
2233
2234
2235

		// handle the starting slurs
		if (sls.length) {
			while (1) {
				i = sls.shift()
				if (!i)
					break
				s.notes[0].s = s
				curvoice.sls.push({
					note: s.notes[0],
					ty: i


				})
				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

	}
	if (s.grace && s.type != C.NOTE) {
		syntax(1, errs.bad_grace)
		return //null
	}

	if (s.notes) {				// if note or rest







<

<
|
>
>










>







2321
2322
2323
2324
2325
2326
2327

2328

2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349

		// 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
︙︙
2304
2305
2306
2307
2308
2309
2310

2311
2312
2313
2314
2315
2316
2317

2318

2319
2320
2321
2322
2323
2324
2325
					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

					if (curvoice.acc[apit]
					 || (acc_tie
					  && acc_tie[apit])) {
						if (!curvoice.acc_tie)
							curvoice.acc_tie = []
						curvoice.acc_tie[apit] =
							curvoice.acc[apit] ||

								acc_tie[apit]

					}
				}
				c = line.char()
				continue
			}
			break
		}







>





<
|
>
|
>







2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430

2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
					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
		}
︙︙
2353
2354
2355
2356
2357
2358
2359

2360


2361
2362
2363
2364
2365
2366
2367
2368


2369

2370
2371
2372
2373
2374
2375
2376
		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	tim = s.time



	curvoice.time = tim + (curvoice.time - tim) * fact
	while (1) {
//fixme: tuplets in grace notes?
		s.in_tuplet = true
		if (!s.grace) {
			s.time = tim
			if (s.dur) {


				s.dur *= fact

				tim += s.dur
			}
		}
		if (!s.next) {
			if (s.tpe)
				s.tpe++
			else







>
|
>
>

|






>
>
|
>







2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
		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
︙︙
2387
2388
2389
2390
2391
2392
2393

2394
2395
2396
2397
2398
2399
2400
2401
2402
	line = parse.line,
	i = line.index,		// in case no deco end
	dcn = ""

	while (1) {
		c = line.next_char()
		if (!c) {

			syntax(1, "No end of decoration")
			line.index = i;
			return
		}
		if (c == '!')
			break
		dcn += c
	}
	if (ottava[dcn] != undefined) {







>

<







2509
2510
2511
2512
2513
2514
2515
2516
2517

2518
2519
2520
2521
2522
2523
2524
	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
	}
	if (ottava[dcn] != undefined) {
︙︙
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
	    var	c, idx, type, k, s, dcn, i, n, text, note

		while (1) {
			c = line.char()
			if (!c)
				break

			// skip definitions if the current voice is ignored
			if (curvoice.ignore) {
				while (1) {
					if (c == '['
					 && line.buffer[line.index + 1] == 'V'
					 && line.buffer[line.index + 2] == ':')
						break		// [V:nn] found
					c = line.next_char()
					if (!c)
						return
				}
			}

			// 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







<
<
<
<
<
<
<
<
<
<
<
<
<







2699
2700
2701
2702
2703
2704
2705













2706
2707
2708
2709
2710
2711
2712
	    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
︙︙
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667

2668
2669
2670
2671
2672
2673
2674
				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 == ':') {







|
<

>







2767
2768
2769
2770
2771
2772
2773
2774

2775
2776
2777
2778
2779
2780
2781
2782
2783
				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 == ':') {
︙︙
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
						}
					}
					if (qplet == 0 || qplet == undefined)
						qplet = (curvoice.wmeasure % 9) == 0 ?
									3 : 2;
					if (tpn < 0)
						tpn = tp.length	// new tuplet
					c = cfmt.tuplets
					if (curvoice.pos.tup) {
						c = Object.create(c)
						switch (curvoice.pos.tup & 0x07) {
						case C.SL_ABOVE:
							c[3] = 1
							break
						case C.SL_BELOW:
							c[3] = 2
							break
						case C.SL_HIDDEN:
							c[2] = 1
							break
						}
					}
					tp.push({
						p: pplet,
						q: qplet,
						r: rplet,
						ro: rplet,
						f: c
					})
					continue
				}
				if (c == '&') {		// voice overlay start
					if (grace) {
						syntax(1, errs.bad_grace)
						break







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





|







2792
2793
2794
2795
2796
2797
2798















2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
						}
					}
					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
︙︙
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
						break
					}
				}
				if (!s) {
					syntax(1, errs.bad_char, c)
					break
				}
				s.notes[0].s = s
				slur_add(s.notes[0])
				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







<
|







2839
2840
2841
2842
2843
2844
2845

2846
2847
2848
2849
2850
2851
2852
2853
						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
︙︙
2893
2894
2895
2896
2897
2898
2899




2900
2901
2902
2903
2904
2905
2906
				if (grace) {
					syntax(1, errs.bar_grace)
					break
				}
				new_bar()
				continue
			case '}':




				s = curvoice.last_note
				if (!grace || !s) {
					syntax(1, errs.bad_char, c)
					break
				}
				if (a_dcn.length)
					syntax(1, "Decoration ignored");







>
>
>
>







2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
				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");
︙︙
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
	if (tp.length) {
		parse.tp = tp
		parse.tps = tps
		parse.tpn = tpn
	}
	if (sls.length)
		syntax(1, "Start of slur without note")
	if (a_dcn.length) {
		syntax(1, "Decoration without symbol")
		a_dcn = []
	}
	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
}







<
<
<
<













3045
3046
3047
3048
3049
3050
3051




3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
	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
}
Changes to core/subs.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - subs.js - text output
//
// Copyright (C) 2014-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - subs.js - text output
//
// 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.
︙︙
102
103
104
105
106
107
108



109
110
111
112
113







114
115
116
117
118

119
120
121
122
123
124
125
126
127
128
129
}
// 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 = typeof document != "undefined" ?

    // .. by the browser
    (function() {
	// (re)create a text element







    var	el = document.createElement('text')
	el.style.position = 'absolute'
	el.style.top = '-1000px'
	el.style.padding = '0'
	document.body.appendChild(el)


	return function(str) {
		if (str.wh)
			return str.wh
	    var	c,
		font = gene.curfont,
		h = font.size,
		w = 0,
		n = str.length,
		i0 = 0,
		i = 0







>
>
>
|


|
|
>
>
>
>
>
>
>
|
|
|
|
|
>
|
|
<
<







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131


132
133
134
135
136
137
138
}
// 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 = abc2svg.eltxt

	// 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'
			document.body.appendChild(el)
			abc2svg.eltxt = el	// reused after new Abc()
		}



	    var	c,
		font = gene.curfont,
		h = font.size,
		w = 0,
		n = str.length,
		i0 = 0,
		i = 0
︙︙
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161


162
163
164
165
166
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
			case '&': return "&amp;"
			}
			return c		// &xxx;
		})

		while (1) {
			i = str.indexOf('$', i)
			if (i < 0)
				break
			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);
			w += el.clientWidth
//fixme: bad result if space(s) at end of string
			if (font.size > h)
				h = font.size;


			el.style.font = style_font(font).slice(5);
			i += 2;
			i0 = i
		}
		el.innerHTML = str.slice(i0);
		w += el.clientWidth;

		return [w, h]
	}
    })() :

    // .. by internal tables
    function(str) {
    var	font = gene.curfont,
	swfac = font.swfac,
	h = font.size,
	w = 0,
	i, j, c,
	n = str.length








|
<
|
|
|
|
|
|
|
|
|
>
>
|

|
|
|
|
>




<
<
<


|


|







153
154
155
156
157
158
159
160

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182



183
184
185
186
187
188
189
190
191
192
193
194
195
			case '&': return "&amp;"
			}
			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

︙︙
212
213
214
215
216
217
218
219


220
221
222
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247


248
249
250
251
252
253
254
255
				c = 'a'		// XML character reference
			}
			break
		}
		w += cwid(c) * 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	o, n_font, wh,
	o_font = gene.curfont,
	c_font = o_font


	// start a '<tspan>' 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\n\tclass="' + cl + '">'
	} // tspan()



	o = str.replace(/<|>|&[^&\s]*?;|&|\$./g, function(c){
			switch (c) {
			case '<': return "&lt;"
			case '>': return "&gt;"
			case '&': return "&amp;"
			default:
				if (c[0] != '$')
					break







|
>
>








|
|
|
>

















>
>
|







220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
				c = 'a'		// XML character reference
			}
			break
		}
		w += cwid(c) * 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 '<tspan>' 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\n\tclass="' + cl + '">'
	} // tspan()

	if (c_font != o_font)
		o = tspan(c_font, o_font)
	o += str.replace(/<|>|&[^&\s]*?;|&|\$./g, function(c){
			switch (c) {
			case '<': return "&lt;"
			case '>': return "&gt;"
			case '&': return "&amp;"
			default:
				if (c[0] != '$')
					break
︙︙
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
		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)

	output += '<text class="' + font_class(gene.curfont)
	if (action != 'j' && str.length > 5
	 && gene.curfont.wadj)
		output += '" lengthAdjust="' + gene.curfont.wadj +
			'" textLength="' + wh[0].toFixed(1);
	output += '" x="';
	out_sxsy(x, '" y="', y + wh[1] * .2)	// a bit upper for the descent
	switch (action) {
	case 'c':
		x -= wh[0] / 2;
		output += '" text-anchor="middle">'
		break
	case 'j':
		output += '" textLength="' + w.toFixed(1) + '">'







|

|
|


|







322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
		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)

	output += '<text class="' + font_class(gene.deffont)
	if (action != 'j' && str.length > 5
	 && gene.deffont.wadj)
		output += '" lengthAdjust="' + gene.deffont.wadj +
			'" textLength="' + wh[0].toFixed(1);
	output += '" x="';
	out_sxsy(x, '" y="', y)
	switch (action) {
	case 'c':
		x -= wh[0] / 2;
		output += '" text-anchor="middle">'
		break
	case 'j':
		output += '" textLength="' + w.toFixed(1) + '">'
︙︙
450
451
452
453
454
455
456

457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483

484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
				if (i == text.length)
					break
			} else {
				if (i < 0)
					str = text.slice(j)
				else
					str = text.slice(j, i)

				vskip(strwh(str)[1]  * cfmt.lineskipfac
					+ font.pad * 2)
				xy_str(x, font.pad, 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, 0, words.slice(k, j).join(' '),
						action, strlw)
					k = j;
					w = ww[0]
					wh = 0
				}
				w += cwidf(' ')
				if (ww[1] > wh)
					wh = ww[1]
			}
			if (w != 0) {			// last line
				vskip(wh * cfmt.lineskipfac)
				xy_str(0, 0, words.slice(k).join(' '))
			}
			vskip(parskip);
			blk_flush()
			if (i < 0)
				break
			while (text[i + 2] == '\n') {
				vskip(lineskip);







>
|

|




















|



>
|





<





|







463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504

505
506
507
508
509
510
511
512
513
514
515
516
517
				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] + ' ')	// &nbsp;
				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);
︙︙
539
540
541
542
543
544
545

546
547
548
549
550
551
552
553
554
555
556
					break
			}
			k = i
			while (p[i] == ' ')
				i++
		}


		if (k != 0)
			xy_str(x, 0, p.slice(0, k), 'r')
		if (i < p.length)
			xy_str(x + 5, 0, p.slice(i), 'l')
	} // put_wline()

	set_font("words")
	vskip(cfmt.wordsspace)
	svg_flush()

	// estimate the width of the lines







>

|

|







553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
					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
︙︙
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
	P: "parts",
	Q: "tempo",
	R: "info",
	T: "title",
	X: "title"
}
function write_headform(lwidth) {
	var	c, font, font_name, align, x, y, sz,
		info_val = {},
		info_font = Object.create(info_font_init),
		info_sz = {
			A: cfmt.infospace,
			C: cfmt.composerspace,
			O: cfmt.composerspace,
			R: cfmt.infospace







|







696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
	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
︙︙
Changes to core/svg.js.
︙︙
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
	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







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
	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
︙︙
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
			output += 'transform="translate(0,' +
					(posy - stv_g.dy).toFixed(1) +
				') scale(' + stv_g.scale.toFixed(2) + ')"'
	}
	if (stv_g.color) {
		if (stv_g.scale != 1)
			output += ' ';
		output += 'color="' + stv_g.color +
			'" fill="' + stv_g.color + '"'
	}
	output += ">\n";
	stv_g.started = true
}

/* set the color */
function set_color(color) {







|
<







304
305
306
307
308
309
310
311

312
313
314
315
316
317
318
			output += 'transform="translate(0,' +
					(posy - stv_g.dy).toFixed(1) +
				') scale(' + stv_g.scale.toFixed(2) + ')"'
	}
	if (stv_g.color) {
		if (stv_g.scale != 1)
			output += ' ';
		output += 'color="' + stv_g.color + '"'

	}
	output += ">\n";
	stv_g.started = true
}

/* set the color */
function set_color(color) {
︙︙
367
368
369
370
371
372
373





374
375
376
377
378
379
380
	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.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
		}







>
>
>
>
>







366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
	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 += "</g>\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
		}
︙︙
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
		n = ((w - 15) / d) | 0
	else
		n = 0;
	x += (w - d * n - 5) / 2;
	out_XYAB('<path class="stroke" stroke-width="1.2"\n\
	stroke-dasharray="5,A"\n\
	d="mX YhB"/>\n',
		x, y + 6,		// 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,







|







732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
		n = ((w - 15) / d) | 0
	else
		n = 0;
	x += (w - d * n - 5) / 2;
	out_XYAB('<path class="stroke" stroke-width="1.2"\n\
	stroke-dasharray="5,A"\n\
	d="mX YhB"/>\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,
︙︙
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
// 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 * .5 + 2,
		str, 'c')
		dx /= stv_g.scale
	if (!up)
		y += 6;
	output += '<path class="stroke" d="m';
	out_sxsy(x, ' ', y);
	dxx = dx - sw + 1







|







851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
// 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 += '<path class="stroke" d="m';
	out_sxsy(x, ' ', y);
	dxx = dx - sw + 1
︙︙
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
	out_sxsy(x, ' ', y - h);
	output += 'l' + dx.toFixed(1) + ' ' + (-dy).toFixed(1) + '"/>\n'

}
// underscore line
function out_wln(x, y, w) {
	out_XYAB('<path class="stroke" stroke-width="0.8" d="mX YhF"/>\n',
		x, y + 3, w)
}

// decorations with string
var deco_str_style = {
crdc:	{
		dx: 0,
		dy: 5,







|







876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
	out_sxsy(x, ' ', y - h);
	output += 'l' + dx.toFixed(1) + ' ' + (-dy).toFixed(1) + '"/>\n'

}
// underscore line
function out_wln(x, y, w) {
	out_XYAB('<path class="stroke" stroke-width="0.8" d="mX YhF"/>\n',
		x, y + 1, w)
}

// decorations with string
var deco_str_style = {
crdc:	{
		dx: 0,
		dy: 5,
︙︙
1069
1070
1071
1072
1073
1074
1075

1076
1077
1078






1079
1080


1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095

1096
1097
1098






1099
1100


1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
	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	de1 = de.start,
		x1 = de1.x,
		y1 = de1.y + staff_tb[de1.st].y,






		ar = Math.atan2(y1 - y2, x2 - x1),
		a = ar / Math.PI * 180,


		len = (x2 - x1) / Math.cos(ar);

	g_open(x1, y1, a);
	x1 = de1.s.dots ? 13 + de1.s.xmx : 8;
	len = (len - x1 - 6) / 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	de1 = de.start,
		x1 = de1.x,
		y1 = de1.y + staff_tb[de1.st].y,






		ar = -Math.atan2(y2 - y1, x2 - x1),
		a = ar / Math.PI * 180,


		len = (x2 - x1) / Math.cos(ar);

	g_open(x1, y1, a);
	x1 = de1.s.dots ? 13 + de1.s.xmx : 8;
	len -= x1 + 8;
	xypath(x1, 0);
	output += 'h' + len.toFixed(1) + '" stroke-width="1"/>\n';
	g_close()
}

var deco_l_tb = {
	glisq: out_glisq,
	gliss: out_gliss







>
|


>
>
>
>
>
>
|
|
>
>
|



|










>
|


>
>
>
>
>
>
|
|
>
>
|


|
<
<







1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126


1127
1128
1129
1130
1131
1132
1133
	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
︙︙
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
		bh = gene.curfont.size + 4
	}

//fixme: xy_str() cannot be used because <tspan> in s.tempo_str
//fixme: then there cannot be font changes by "$n" in the Q: texts
	output += '<text class="' + font_class(gene.curfont) +
		'" x="'
	out_sxsy(x, '" y="', y + gene.curfont.size * .2)
	output += '">' + s.tempo_str + '</text>\n'

	if (bh) {
		gene.curfont.box = true
		output += '<rect class="stroke" x="'
		out_sxsy(x - 2, '" y="', y + bh - 1)
		output += '" width="' + (s.tempo_wh[0] + 2).toFixed(1) +
			'" height="' + bh.toFixed(1) +
			'"/>\n'
	}

	// don't display anymore
	s.invis = true
} // writempo()







|






|







1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
		bh = gene.curfont.size + 4
	}

//fixme: xy_str() cannot be used because <tspan> in s.tempo_str
//fixme: then there cannot be font changes by "$n" in the Q: texts
	output += '<text class="' + font_class(gene.curfont) +
		'" x="'
	out_sxsy(x, '" y="', y + gene.curfont.size * .22)
	output += '">' + s.tempo_str + '</text>\n'

	if (bh) {
		gene.curfont.box = true
		output += '<rect class="stroke" x="'
		out_sxsy(x - 2, '" y="', y + bh - 1)
		output += '" width="' + (s.tempo_wh[0] + 4).toFixed(1) +
			'" height="' + bh.toFixed(1) +
			'"/>\n'
	}

	// don't display anymore
	s.invis = true
} // writempo()
︙︙
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302











1303
1304
1305
1306
1307
1308
1309
function svg_flush() {
	if (multicol || !output || !user.img_out || posy == 0)
		return

    var	i, font,
	head = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	color="',
	g = ''

	glout()

	if (cfmt.fgcolor)
		head += cfmt.fgcolor + '" fill="' + cfmt.fgcolor + '"'
	else
		head += 'black"';
	head += ' stroke-width=".7"'

	if (cfmt.bgcolor)
		head += ' style="background-color: ' + cfmt.bgcolor + '"';

	font = get_font("music")
	head += ' class="' + font_class(font) +
		' tune' + tunes.length + '"\n'	// tune index for play

	posy *= cfmt.scale
	if (user.imagesize) {
		head += user.imagesize +
			' viewBox="0 0 ' + img.width.toFixed(0) + ' ' +
			 posy.toFixed(0) + '">\n'
	} else {
		head += ' viewBox="0 0 ' + img.width.toFixed(0) + ' ' +
			posy.toFixed(0) +
			'" width="' + img.width.toFixed(0) +
			'px" height="' + posy.toFixed(0) + 'px">\n'
	}

	head += fulldefs

	if (style || font_style)
		head += '<style>' + font_style + style + '\n</style>\n'












	if (defs)
		head += '<defs>' + defs + '\n</defs>\n'

	// if %%pagescale != 1, do a global scale
	// (with a container: transform scale in <svg> does not work
	//	the same in all browsers)







|




<
<
<
<
<
<
<
<
<


















|
|
>
>
>
>
>
>
>
>
>
>
>







1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293









1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
function svg_flush() {
	if (multicol || !output || !user.img_out || posy == 0)
		return

    var	i, font,
	head = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	fill="currentColor" stroke-width=".7"',
	g = ''

	glout()










	font = get_font("music")
	head += ' class="' + font_class(font) +
		' tune' + tunes.length + '"\n'	// tune index for play

	posy *= cfmt.scale
	if (user.imagesize) {
		head += user.imagesize +
			' viewBox="0 0 ' + img.width.toFixed(0) + ' ' +
			 posy.toFixed(0) + '">\n'
	} else {
		head += ' viewBox="0 0 ' + img.width.toFixed(0) + ' ' +
			posy.toFixed(0) +
			'" width="' + img.width.toFixed(0) +
			'px" height="' + posy.toFixed(0) + 'px">\n'
	}

	head += fulldefs

	if (style || font_style) {
		head += '<style>' + font_style
		if (cfmt.fgcolor || cfmt.bgcolor) {
			head += '\n.f' + font.fid + (cfmt.fullsvg || '')
				+ '{'
				+ (cfmt.fgcolor ? ('color:' + cfmt.fgcolor + ';')
						: '')
				+ (cfmt.bgcolor ? ('background-color:' + cfmt.bgcolor)
						: '')
				+ '}'
		}
		head += style + '\n</style>\n'
	}

	if (defs)
		head += '<defs>' + defs + '\n</defs>\n'

	// if %%pagescale != 1, do a global scale
	// (with a container: transform scale in <svg> does not work
	//	the same in all browsers)
︙︙
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
			'<div class="nobrk newpage">')
		blkdiv = -1		// block started
	}
	user.img_out(head + output + g + "</svg>");
	output = ""

	font_style = ''
	if (cfmt.fullsvg && typeof document == "undefined") {
		defined_glyph = {}
		for (i = 0; i < font_tb.length; i++)
			font_tb[i].used = false
	} else {
		style = '';
		fulldefs = ''
	}







|







1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
			'<div class="nobrk newpage">')
		blkdiv = -1		// block started
	}
	user.img_out(head + output + g + "</svg>");
	output = ""

	font_style = ''
	if (cfmt.fullsvg) {
		defined_glyph = {}
		for (i = 0; i < font_tb.length; i++)
			font_tb[i].used = false
	} else {
		style = '';
		fulldefs = ''
	}
︙︙
Changes to core/tail.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - tail.js
//
// Copyright (C) 2014-2020 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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - tail.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.
︙︙
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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() { var t = posy; posy = 0; return t };
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 };







|







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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 };
︙︙
75
76
77
78
79
80
81

82

83
84
85
86
87
88
89
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_pagef = function() { blkdiv = 1 }

Abc.prototype.set_scale = set_scale;

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







>

>







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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_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
︙︙
Changes to core/tune.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - tune.js - tune generation
//
// 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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - tune.js - tune generation
//
// 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.
︙︙
52
53
54
55
56
57
58


59
60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
	    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 -- */


function sym_link(s) {
    var	tim = curvoice.time

	if (!s.fname)
		set_ref(s)
	parse.last_sym = s
	s.prev = curvoice.last_sym
	if (curvoice.last_sym)
		curvoice.last_sym.next = s
	else
		curvoice.sym = 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
	}
	if (parse.part && parse.part[tim]) {
		if (!parse.part[tim].done) {
			s.part = parse.part[tim]
			parse.part[tim].done = true
		}
	}
}

/* -- add a new symbol in a voice -- */
function sym_add(p_voice, type) {
	var	s = {
			type:type,
			dur:0







>
>





|





>


















<
<
<
<
<
<







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90






91
92
93
94
95
96
97
	    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
    }
	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
︙︙
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

/* -- sort all symbols by time and vertical sequence -- */
// weight of the symbols !! depends on the symbol type !!
var w_tb = new Uint8Array([
	5,	// bar
	1,	// clef
	8,	// custos
	0,	// (free)
	4,	// grace
	2,	// key
	3,	// meter
	9,	// mrest
	9,	// note
	0,	// part
	9,	// rest
	4,	// space
	0,	// staves
	7,	// stbrk
	0,	// tempo
	0,	// (free)
	0,	// block
	0	// remark
])







|
|






|







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

/* -- sort all symbols by time and vertical sequence -- */
// weight of the symbols !! depends on the symbol type !!
var w_tb = new Uint8Array([
	5,	// bar
	1,	// clef
	8,	// custos
	6,	// sm (sequence marker, after bar)
	0,	// grace (must be null)
	2,	// key
	3,	// meter
	9,	// mrest
	9,	// note
	0,	// part
	9,	// rest
	4,	// space (before bar)
	0,	// staves
	7,	// stbrk
	0,	// tempo
	0,	// (free)
	0,	// block
	0	// remark
])
︙︙
158
159
160
161
162
163
164















































165
166
167
168
169
170
171
		sy: sy,
		next: p_voice.sym,
		seqst: true
	}

	if (!p_voice.sym)
		return
















































	prev.fmt = fmt = p_voice.sym.fmt	// starting format

	// set the first symbol of each voice
	for (v = 0; v < nv; v++) {
		s = voice_tb[v].sym
		vtb[v] = s







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
		sy: sy,
		next: p_voice.sym,
		seqst: true
	}

	if (!p_voice.sym)
		return

	// check if different bars at the same time
	function b_chk() {
	    var	ir, bt, s, s2, v

		ir = 0
		bt = ""
		while (1) {
			v = vn[ir++]
			if (v == undefined)
				break
			s = vtb[v]
			if (!s || !s.bar_type || s.invis)
				continue
			if (!bt) {
				bt = s.bar_type
				continue
			}
			if (s.bar_type != bt)
				break
		}
		if (!v)
			return			// no problem

		
		if (bt == "::" || bt == ":|") {
			ir = 0
			while (1) {
				v = vn[ir++]
				if (v == undefined)
					break
				s = vtb[v]
				if (!s || s.invis
				 || s.bar_type != "::")
					continue
				s2 = clone(s)
				s.bar_type = ":|"
				s2.bar_type = "|:"
				s2.next = s.next
				s2.prev = s
				s.next = s2
			}
		} else {
			error(2, s, "Different bars $1 and $2",
				bt, s.bar_type)
		}
	} // b_chk()

	prev.fmt = fmt = p_voice.sym.fmt	// starting format

	// set the first symbol of each voice
	for (v = 0; v < nv; v++) {
		s = voice_tb[v].sym
		vtb[v] = s
︙︙
195
196
197
198
199
200
201


















202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226


227
228
229
230
231
232
233
234
235
236




237
238
239
240
241
242
243
244
245

246




247
248
249
250
251
252
253
		else
			p_voice.last_sym = s
		s.prev.next = s
		s.fmt = fmt
		glovar.tempo = null
		vtb[v] = s
	}



















	// loop on the symbols of all voices
	while (1) {
		if (new_sy) {
			sy = new_sy;
			new_sy = null;
			vn = []
			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.time < time) {
				time = s.time;
				wmin = w
			} else if (w < wmin) {
				wmin = w
			}
		}

		if (wmin > 127)
			break			// done





		/* link the vertical sequence */
		ir = 0
		while (1) {
			v = vn[ir++]
			if (v == undefined)
				break
			s = vtb[v]
			if (!s || s.time != time

			 || w_tb[s.type] != wmin)




				continue
			if (s.type == C.STAVES)
				new_sy = s.sy
			if (fl) {
				fl = 0;
				s.seqst = true
			}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






|


















>
>










>
>
>
>








|
>
|
>
>
>
>







239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
		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
			s.ts_prev = s.prev
			s.ts_next = s.next
			if (s.time != s.prev.time
			 || w_tb[s.prev.type]
			 || s.type == C.GRACE && s.prev.type == C.GRACE)
				s.seqst = 1 //true
		}
		// 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 == 5)			// 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)
				continue
			w = w_tb[s.type]
			if (s.type == C.GRACE && s.next && s.next.type == C.GRACE)
				w--
			if (s.time != time
			 || w != wmin)
				continue
			if (s.type == C.STAVES)
				new_sy = s.sy
			if (fl) {
				fl = 0;
				s.seqst = true
			}
︙︙
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351





352




353





354









355
356
357
358
359
360
361
		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.note.s
				for (s2 = s.next; s2; s2 = s2.next) {
					if (s2.bar_type && s2.bar_type[0] == ':')
						break
				}
				if (s2) {
					if (!s.sls)
						s.sls = []

					s.sls.push({
						ty: sl.ty,
						note: {s: s2}
					})
				} else {
					error(1, s, "Lack of ending slur(s)")
				}
			}
		} // not %%score
		for (s = p_voice.sym; s; s = s.next) {
			if (s.time >= staves_found)
				break
		}
		for ( ; s; s = s.next) {
			switch (s.type) {
			case C.GRACE:
				// with w_tb[C.BAR] = 4 and w_tb[C.GRACE] = 3,
				// the grace notes go after the bar;
				// if before a bar, change the grace time





				if (s.next && s.next.type == C.BAR)




					s.time--















				if (!cfmt.graceword)
					continue
				for (s2 = s.next; s2; s2 = s2.next) {
					switch (s2.type) {
					case C.SPACE:
						continue
					case C.NOTE:







|
|
<
<
<
<


>
|
<
<
<
<
<
<







|
|
|
|
|
>
>
>
>
>
|
>
>
>
>
|
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>







391
392
393
394
395
396
397
398
399




400
401
402
403






404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
		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:
︙︙
409
410
411
412
413
414
415
416

417
418
419
420

















421
422
423
424
425
426
427
	par_sy.next = sy_new;
	par_sy = sy_new
}

/* -- set the bar numbers -- */
// (possible hook)
Abc.prototype.set_bar_num = function() {
    var	s, s2, tim, rep_tim, k, n,

	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


















	// 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:







|
>




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
	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:
︙︙
448
449
450
451
452
453
454
455
456
457
458


459
460
461
462
463
464
465
466
467













468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483

484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499

500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
			}
			break
		}
	}

	// set the measure number on the top bars
	for ( ; s; s = s.ts_next) {
		if (!s.seqst)
			continue
		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
			break
		case C.BAR:
			if (s.invis)
				break
			tim = s.time













			if (s.bar_num) {
				bar_num = s.bar_num	// (%%setbarnb)
				ptim = bar_tim = tim
				continue
			}
			if (wmeasure == 1) {		// if M:none
				if (s.bar_dotted)
					continue
				if (s.text) {
					if (!cfmt.contbarnb) {
						if (s.text[0] == '1')
							rep_tim = bar_num
						else
							bar_num = rep_tim
					}
				}

				s.bar_num = ++bar_num
				continue
			}

			n = bar_num + (tim - bar_tim) / wmeasure
			k = n - (n | 0)
			if (cfmt.checkbars
			 && ((k && !s.bar_dotted && s.next)
			  || (tim > ptim + wmeasure && s.prev.type != C.MREST)))
				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 (s.text) {
				if (s.text[0] == '1') {
					if (cfmt.contbarnb)
						rep_tim = bar_tim + k * wmeasure
					else
						rep_tim = tim
					if (!k)
						s.bar_num = n
				} else {
					if (cfmt.contbarnb)
						bar_tim = rep_tim
					else
						bar_tim += tim - rep_tim
					n = bar_num + (tim - bar_tim) / wmeasure
					if (n == (n | 0))
						s.bar_num = n
				}
			} else {
				if (k)
					n -= k
				s.bar_num = n
			}
			if (!k)
				ptim = tim
			break
		}
	}







<
<


>
>






|
|

>
>
>
>
>
>
>
>
>
>
>
>
>








|

|





>
|






|
|







>
|
|




|











<
|







553
554
555
556
557
558
559


560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637

638
639
640
641
642
643
644
645
			}
			break
		}
	}

	// set the measure number on the top bars
	for ( ; s; s = s.ts_next) {


		switch (s.type) {
		case C.METER:
			if (s.time == bar_tim)
				break		// already seen
		    if (wmeasure != 1)		// if not M:none
			bar_num += (s.time - bar_tim) / wmeasure
			bar_tim = s.time
			wmeasure = s.wmeasure
			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 || s2.dur)
					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
				continue
			}
			if (wmeasure == 1) {		// if M:none
				if (s.bar_dotted)
					continue
				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
				continue
			}

			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 = bar_tim + k * wmeasure
					else
						rep_tim = tim
					if (!nu)
						s.bar_num = n
				} else {
					if (cfmt.contbarnb)
						bar_tim = rep_tim
					else
						bar_tim += tim - rep_tim
					n = bar_num + (tim - bar_tim) / wmeasure
					if (n == (n | 0))
						s.bar_num = n
				}
			} else {

				n |= 0
				s.bar_num = n
			}
			if (!k)
				ptim = tim
			break
		}
	}
︙︙
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698

699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754

755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
			}
			map[2] = a[i]
			break
		}
	}
}

// set the transposition in the previous or first key signature
function set_transp() {
    var	s, transp, sndtran

	if (curvoice.ckey.k_bagpipe || curvoice.ckey.k_drum)
		return

	if (cfmt.transp && curvoice.shift)	// if %%transpose and shift=
		syntax(0, "Mix of old and new transposition syntaxes");


	if (cfmt.transp != undefined
	 || curvoice.transp != undefined
	 || curvoice.shift != undefined)
		transp = (cfmt.transp || 0) +	 // %%transpose
			(curvoice.transp || 0) + // score= / sound= / instrument=
			(curvoice.shift || 0)	 // shift=
	if (curvoice.sndtran != undefined
	 || curvoice.sndsh != undefined)
		sndtran = (curvoice.sndtran || 0) +
			(curvoice.sndsh || 0)
	if (transp == undefined) {
		if (sndtran == undefined)
			return
	} else {
		curvoice.vtransp = transp
	}

	if (is_voice_sig()) {			// if no symbol yet
		curvoice.okey.fmt = cfmt
		curvoice.key = s = clone(curvoice.okey)
	} else {
		s = curvoice.last_sym
		while (1) {	// set the transposition in the previous K:
			if (s.type == C.KEY)
				break
			s = s.prev
			if (!s) {
				s = curvoice.key	// first key
				break
			}
		}
	}
	if (transp != undefined)

		s.k_transp = transp
	if (sndtran != undefined)
		s.k_sndtran = sndtran
	curvoice.ckey = clone(s)
	if (curvoice.key.k_none)
		s.k_sf = 0
}

/* transpose a note / chord */
function note_transp(s, sk, note) {
    var	ak, an, d, b40,
	n = note.pit,
	a = note.acc

	if (typeof a == "object") {
		d = a[1]
		a = a[0]
		if (d != 2) {
			error(1, s, "Microtone transposition not coded")
			return
		}
	}
	if (!a && sk.k_a_acc)			// if accidental list
		a = sk.k_map[(n + 19) % 7]	// invisible accidental

	b40 = abc2svg.pab40(n, a) + sk.k_transp	// base-40 transposition

	note.pit = abc2svg.b40p(b40)		// new pitch

	if (!a) {				// if no old accidental
		if (!sk.k_a_acc			// if no accidental list
		 && !sk.k_none)			// and normal key
			return			// same accidental (in the key)
	}

	an = abc2svg.b40a(b40)			// new accidental
	if (a) {
		if (sk.k_a_acc) {		// if accidental list
			ak = sk.k_map[(note.pit + 19) % 7]
			if (ak == an)
				an = 0		// accidental in the key
		}
		if (!an)
			an = 3
	} else if (sk.k_none) {			// if no key
		if (acc_same_pitch(s, note.pit)) // and accidental from previous notes
			return			// no change
	} else if (sk.k_a_acc) {		// if accidental list
		if (acc_same_pitch(s, note.pit)) // and accidental from previous notes
			return			// no change
		ak = sk.k_map[(note.pit + 19) % 7]
		if (ak)
			an = 3		// natural
	} else {
		return			// same accidental (in the key)
	}

	if (d && an != a) {
		switch (Number(a)) {
		case -3:
			switch (an) {
			case -2:
				an = -1
				break
			case 3:			// natural
				an = -3
				note.pit++
				break
			case 2:
				an = -1
				break
			}
			break
		case -1:
			switch (an) {
			case -2:
				an = -3
				break
			case 3:			// natural
				an = 1
				break
			}
			break
		case 1:
			switch (an) {
			case -1:
				an = -3
				break
			case 3:			// natural
				an = -1
				break
			case 2:
				an = 3
				break
			}
			break
		case 3:
			switch (an) {
			case -1:
				an = 1
				break
			case 1:
				an = 1
				note.pit++
				break
			}
			break
		}
	}
	note.acc = d ? [an, d] : an
}

// adjust the pitches according to the transposition(s)
function pit_adj() {
    var	i, p_v, s, sk, g,
	nv = voice_tb.length

	while (--nv >= 0) {
		p_v = voice_tb[nv]
		if (p_v.vtransp == undefined)
			continue	// no transposition in this voice
		if (p_v.key.k_transp) {
			sk = p_v.key
			key_transp(sk)
			sk.k_old_sf = sk.k_sf	// no natural
		} else {
			sk = null
		}
		s = p_v.sym
		while (s) {

			// search a transposing key signature
			if (!sk) {
				for (; s; s = s.next) {
					if (s.type == C.KEY
					 && s.k_transp)
						break
				}
			}

			// transpose
			for (; s; s = s.next) {
				switch (s.type) {
				case C.GRACE:
					for (g = s.extra; g; g = g.next) {
						for (i = 0; i <= g.nhd; i++)
							note_transp(g, sk, g.notes[i])
					}
					continue
				case C.NOTE:
					for (i = 0; i <= s.nhd; i++)
						note_transp(s, sk, s.notes[i])
					continue
				case C.KEY:
					if (sk)
						s.k_sf = sk.k_sf
					key_transp(s)
					if (!s.k_transp) // end of transposition
						break
					sk = s
				default:
					continue
				}
				break
			}
			sk = null
		}
	}
} // pit_adj()

// get a abcm2ps/abcMIDI compatible transposition value as a base-40 interval
// The value may be
// - [+|-]<number of semitones>[s|f]
// - <note1>[<note2>]  % <note2> default is 'c'
function get_transp(param) {
	if (param[0] == '0')







|

|







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




|
<
<
<
|
<
<
|
|
<
<
<
>
|
<
<





<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
|
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
>
|
<
|
<
<
|
<
<
|
<
<
|
|
<
<
<
|
<
<
|
<
<
|
<
<
<
|
<
|
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
|
<
|
<
<
<
<
|
|
|
<
<
<
<
<
|
<
<
<
<
<
<
|
|
<
<
<
|
<
<
|
<
|
<
|
<
|







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783


















784
785
786
787
788



789


790
791



792
793


794
795
796
797
798





799










800

801




























802
803
804

805


806


807


808
809



810


811


812



813

814








815
















816

















817
818
819
820

821




822
823
824





825






826
827



828


829

830

831

832
833
834
835
836
837
838
839
			}
			map[2] = a[i]
			break
		}
	}
}

// set the transposition in the first key signature or in a new key
function set_transp() {
    var	s

	if (curvoice.ckey.k_bagpipe || curvoice.ckey.k_drum)
		return

	if (cfmt.transp && curvoice.shift)	// if %%transpose and shift=
		syntax(0, "Mix of old and new transposition syntaxes");



















	if (is_voice_sig()) {			// if no symbol yet
		curvoice.okey.fmt = cfmt
		curvoice.key = s = clone(curvoice.okey)
	} else {
		s = clone(curvoice.ckey)



		s.k_old_sf = s.k_sf


		sym_link(s)
	}



	delete s.invis			// needed if K:C/none at start of tune
	key_transp(s)


	curvoice.ckey = clone(s)
	if (curvoice.key.k_none)
		s.k_sf = 0
}






// set the control values (P: and Q:)










function set_ctrl() {

    var	s, tim, e





























	for (tim in parse.ctrl) {
//		if (!parse.ctrl.hasOwnProperty(tim))

//			continue


		e = parse.ctrl[tim]


		s = tsfirst


		while (s.next && s.next.time < tim)
			s = s.next



		while (s && s.time < tim)


			s = s.ts_next


		if (!s) {



			//fixme: insert at the end

		} else {

























			// put the P: and/or Q: on a note or rest

















			while (s.time == tim && s.ts_next
			 && !s.dur)
				s = s.ts_next
			if (e.part) {

//				if (s.time != tim) {




//					// fixme: insert a part (?)
//				}
				s.part = e.part





			}






			if (e.tempo) {
				lkvsym(e.tempo, s)



				lktsym(e.tempo, s)


			}

		}

	}

} // set_ctrl()

// get a abcm2ps/abcMIDI compatible transposition value as a base-40 interval
// The value may be
// - [+|-]<number of semitones>[s|f]
// - <note1>[<note2>]  % <note2> default is 'c'
function get_transp(param) {
	if (param[0] == '0')
︙︙
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
} // 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(/(\w|-)+/)
	if (!cmd)
		return
	cmd = cmd[0];

	// ignore the command if the voice is ignored,
	// but not if %%score/%%staves!
	if (curvoice && curvoice.ignore) {







|







855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
} // 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) {
︙︙
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977


978
979
980
981
982
983
984
985
986
987
988
989
990
991


992
993
994
995
996
997
998
		if (parse.state >= 2) {
			curvoice = voice_tb[0]
			s = new_block("mc_" + param)
			break
		}
		switch (param) {
		case "start":
			multicol = {
				maxy: 0,
				lmarg: cfmt.leftmargin,
				rmarg: cfmt.rightmargin
			}
			break
		case "new":
			if (!multicol) {
				syntax(1, "%%multicol new without start")
				break
			}
			if (posy > multicol.maxy)
				multicol.maxy = posy;
			cfmt.leftmargin = multicol.lmarg;
			cfmt.rightmargin = multicol.rmarg;
			img.chg = true;
			set_page();
			posy = 0


			break
		case "end":
			if (!multicol) {
				syntax(1, "%%multicol end without start")
				break
			}
			if (posy < multicol.maxy)
				posy = multicol.maxy;
			cfmt.leftmargin = multicol.lmarg;
			cfmt.rightmargin = multicol.rmarg;
			multicol = undefined;
			blk_flush();
			img.chg = true;
			set_page()


			break
		default:
			syntax(1, "Unknown keyword '$1' in %%multicol", param)
			break
		}
		return
	case "ottava":







|
|
<
<
|






<
<
<
<
<
|
<
>
>






<
<
<
<
<
<
<
|
>
>







919
920
921
922
923
924
925
926
927


928
929
930
931
932
933
934





935

936
937
938
939
940
941
942
943







944
945
946
947
948
949
950
951
952
953
		if (parse.state >= 2) {
			curvoice = voice_tb[0]
			s = new_block("mc_" + param)
			break
		}
		switch (param) {
		case "start":
			self.block_gen({
				subtype: "mc_start"


			})
			break
		case "new":
			if (!multicol) {
				syntax(1, "%%multicol new without start")
				break
			}





			self.block_gen({

				subtype: "mc_new"
			})
			break
		case "end":
			if (!multicol) {
				syntax(1, "%%multicol end without start")
				break
			}







			self.block_gen({
				subtype: "mc_end"
			})
			break
		default:
			syntax(1, "Unknown keyword '$1' in %%multicol", param)
			break
		}
		return
	case "ottava":
︙︙
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
		case 0:
			cfmt.transp = 0
			// fall thru
		case 1:
			cfmt.transp = (cfmt.transp || 0) + val
			return
		}
		for (s = curvoice.last_sym; s; s = s.prev) {
			switch (s.type) {
			case C.NOTE:		// insert a key
				s = clone(curvoice.okey);
				s.k_old_sf = curvoice.ckey.k_sf;
				sym_link(s)
				break
			case C.KEY:
				break
			default:
				continue
			}
			break
		}
		curvoice.transp = val
		set_transp()
		return
	case "tune":
//fixme: to do
		return
	case "user":
		set_user(param)







<
<
<
<
<
<
<
<
<
<
<
<
<
<
|







1132
1133
1134
1135
1136
1137
1138














1139
1140
1141
1142
1143
1144
1145
1146
		case 0:
			cfmt.transp = 0
			// fall thru
		case 1:
			cfmt.transp = (cfmt.transp || 0) + val
			return
		}














		curvoice.tr_sco = val
		set_transp()
		return
	case "tune":
//fixme: to do
		return
	case "user":
		set_user(param)
︙︙
1317
1318
1319
1320
1321
1322
1323
1324
1325





1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341

1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353



1354
1355
1356
1357
1358
1359

1360








1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391


1392

1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447

1448
1449
1450
1451
1452
1453
1454
		}
		write_text(text, action)
		break
	}
}

/* -- generate a piece of tune -- */
function generate(in_mc) {
    var s, v, p_voice;






	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 ? '|' : ')')
	}

	voice_adj();
	sort_all()			/* define the time / vertical sequences */

	if (!tsfirst)
		return
	if (user.anno_start)
		anno_start = a_start
	if (user.anno_stop)
		anno_stop = a_stop
	self.set_bar_num()
	pit_adj()

	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()










	if (tsfirst)		// if non void, keep tune data for upper layers
		tunes.push([tsfirst, voice_tb, info, cfmt])

	// if inside multicol, reset the parser
	if (!in_mc)
		return
	voice_tb = Object.create(voice_tb)
	for (v = 0; v < voice_tb.length; v++) {
		p_voice = voice_tb[v];
		p_voice.time = 0;
		p_voice.sym = p_voice.last_sym = null;
//		p_voice.st = cur_sy.voices[v].st;
//		p_voice.second = cur_sy.voices[v].second;
//		p_voice.clef.time = 0;
		delete p_voice.have_ly;
		p_voice.sls = [];
		p_voice.hy_st = 0;
		delete p_voice.bar_start
	}
	staves_found = 0			// (for compress/dup the voices)
}

// transpose a key
//fixme: transpose of the accidental list is not done
function key_transp(sk) {
	if (sk.k_a_acc || sk.k_none)		// same displayed key
		return
    var	d,
	k_b40 = sk.k_b40,
	n_b40 = (k_b40 + 200 + sk.k_transp) % 40



	d = abc2svg.b40k[n_b40] - n_b40

	if (d) {
		if (sk.k_transp > 0)
			sk.k_transp += d
		else
			sk.k_transp -= d
		n_b40 += d
	}
	sk.k_b40 = n_b40

   var sf = abc2svg.b40sf[n_b40]
	sk.k_old_sf = sk.k_sf
	sk.k_sf = sf
	sk.k_map = abc2svg.keys[sf + 7]	// map of the notes with accidentals
}

/*
 * for transpose purpose, check if a pitch is already in the measure or
 * if it is tied from a previous note, and return the associated accidental
 */
function acc_same_pitch(s, pit) {
    var	i,
	time = s.time

	for (s = s.prev; s; s = s.prev) {
		switch (s.type) {
		case C.BAR:
			if (s.time < time)
				return //undefined // no same pitch
			while (1) {
				s = s.prev
				if (!s)
					return //undefined
				if (s.type == C.NOTE) {
					if (s.time + s.dur == time)
						break
					return //undefined
				}
				if (s.time < time)
					return //undefined
			}
			for (i = 0; i <= s.nhd; i++) {
				if (s.notes[i].pit == pit
				 && s.notes[i].tie_ty)
					return s.notes[i].acc
			}
			return //undefined
		case C.NOTE:
			for (i = 0; i <= s.nhd; i++) {
				if (s.notes[i].pit == pit)
					return s.notes[i].acc
			}
			break
		}
	}
	return //undefined

}

/* -- 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,







|

>
>
>
>
>
















>
|
<





<




>
>
>






>

>
>
>
>
>
>
>
>
|

|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<



<
|
|

<
<
<

>
>
|
>

|
|

|
|

|
<
|
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
<
<
|
<
<
<
<
<
|
|
|
<
|
<
<
|
|
|
|
<
|
<
<
>







1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289

1290
1291
1292
1293
1294

1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320















1321

1322
1323
1324

1325
1326
1327



1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340

1341



1342
1343















1344
1345


1346





1347
1348
1349

1350


1351
1352
1353
1354

1355


1356
1357
1358
1359
1360
1361
1362
1363
		}
		write_text(text, action)
		break
	}
}

/* -- generate a piece of tune -- */
function generate() {
    var s, v, p_voice;

	if (a_dcn.length) {
		syntax(1, "Decoration without symbol")
		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 ? '|' : ')')
	}

	voice_adj();
	sort_all()			/* define the time / vertical sequences */

    if (tsfirst) {

	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

	if (parse.ctrl)
		set_ctrl()		// set control values

	// 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 a key

function key_transp(s) {
	if (s.k_none)			// no key
		return




    var	n, a_acc,
	b40 = (s.k_b40 + 200 + curvoice.tr_sco) % 40,
	d = abc2svg.b40k[b40] - b40

	if (d) {
		if (curvoice.tr_sco > 0)
			curvoice.tr_sco -= d
		else
			curvoice.tr_sco += d
		b40 += d
	}
	s.k_b40 = b40

	s.k_sf = abc2svg.b40sf[b40]




	// transpose the accidental list















	if (!s.k_a_acc)
		return


	a_acc = []





	for (n = 0; n < s.k_a_acc.length; n++) {
		b40 = abc2svg.pab40(s.k_a_acc[n].pit, s.k_a_acc[n].acc)
			+ curvoice.tr_sco




		a_acc[n] = {
			pit: abc2svg.b40p(b40),
			acc: abc2svg.b40a(b40) || 3
		}

	}


	s.k_a_acc = a_acc
}

/* -- 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,
︙︙
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714




1715
1716
1717
1718
1719
1720
1721
1722

1723
1724
1725
1726
1727




















1728
1729
1730
1731
1732
1733
1734
1735

		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,
	line = parse.line

	/* treat the end of overlay */
	if (type == '|'
	 || type == ')')  {
		if (!curvoice.last_note) {
			syntax(1, errs.nonote_vo)




			return
		}
		curvoice.last_note.beam_end = true
		if (!vover) {
			syntax(1, "Erroneous end of voice overlay")
			return
		}
		if (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
		}
		curvoice.acc = []		// no accidental anymore




















		curvoice = vover.p_voice;
		vover = null
		return
	}

	/* treat the full overlay start */
	if (type == '(') {
		if (vover) {







|
<






>
>
>
>








>





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|







1609
1610
1611
1612
1613
1614
1615
1616

1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668

		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) {
︙︙
1752
1753
1754
1755
1756
1757
1758

1759

1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770

1771
1772
1773
1774
1775
1776
1777



1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827

1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839

1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861

1862
1863
1864
1865
1866
1867
1868
	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;

		v2 = p_voice2.v;

		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



		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.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

	// add a bar at start of the measure overlay
	// (needed for sort_all() in case of spaces - 'y')
	if (vover.bar && vover.time) {
		sym_link({
			type: C.BAR,
			bar_type: vover.bar,
			invis: 1,
			dur: 0,
			multi: 0
		})
	}
}

// check if a clef, key or time signature may go at start of the current voice
function is_voice_sig() {
	var s

	if (!curvoice.last_sym)
		return true
	if (curvoice.time)
		return false
	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) {

	if (is_voice_sig()) {
		curvoice.clef = s
		s.fmt = cfmt
		return
	}

	// clef change
	sym_link(s);
	s.clef_small = true

	// move the clef before a (not right repeat) bar
    var	s2 = s.prev

	if (s2 && s2.type == C.BAR
	 && s2.bar_type[0] != ':') {
		s.next = s2
		s.prev = s2.prev
		if (s.prev)
			s.prev.next = s
		s2.prev = s
		s2.next = null
		curvoice.last_sym = s2
		if (s.soln) {
			delete s.soln
			curvoice.eoln = true
		}
	}
}

// treat K: (kp = key signature + parameters)
function get_key(parm) {
	var	v, p_voice, s, transp, sndtran,
//		[s_key, a] = new_key(parm)	// KO with nodejs
		a = new_key(parm),
		s_key = a[0];


	a = a[1]

	if (parse.state == 1) {		// in tune header (first K:)
		if (s_key.k_sf == undefined && !s_key.k_a_acc) { // empty K:
			s_key.k_sf = 0;
			s_key.k_none = true







>

>











>







>
>
>








|











<
<
<
<
<
<
<
<
<
<
<
<






|
|
|
|








>
|











>
|

















|


|
>







1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736












1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
	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 = null
	    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) {
	if (!curvoice.time		// (force a clef when new voice)
	 && is_voice_sig()) {
		curvoice.clef = s
		s.fmt = cfmt
		return
	}

	// clef change
	sym_link(s);
	s.clef_small = true

	// move the clef before a (not right repeat) bar
    var	s2 = s.prev
	if (!s.invis			// if not 'K: clef=none'
	 && s2 && s2.type == C.BAR
	 && s2.bar_type[0] != ':') {
		s.next = s2
		s.prev = s2.prev
		if (s.prev)
			s.prev.next = s
		s2.prev = s
		s2.next = null
		curvoice.last_sym = s2
		if (s.soln) {
			delete s.soln
			curvoice.eoln = true
		}
	}
}

// treat K: (kp = key signature + parameters)
function get_key(parm) {
    var	v, p_voice, transp, sndtran, tr_p, nt,
//		[s_key, a] = new_key(parm)	// KO with nodejs
		a = new_key(parm),
		s_key = a[0],
		s = s_key

	a = a[1]

	if (parse.state == 1) {		// in tune header (first K:)
		if (s_key.k_sf == undefined && !s_key.k_a_acc) { // empty K:
			s_key.k_sf = 0;
			s_key.k_none = true
︙︙
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909

1910
1911
1912
1913
1914
1915
1916

1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
		if (!glovar.ulen)
			glovar.ulen = C.BLEN / 8;
		if (a.length) {
			memo_kv_parm('*', a)
			a = ''
		}
		goto_tune()
		parse.state = 3			// in tune body
	}

	set_kv_parm(a)

	if (!curvoice.ckey.k_bagpipe && !curvoice.ckey.k_drum
	 && (cfmt.transp != undefined
	  || curvoice.transp != undefined
	  || curvoice.shift != undefined))
	    transp = (cfmt.transp || 0) +
		(curvoice.transp || 0) +
		(curvoice.shift || 0)
	if (curvoice.sndtran != undefined
	 || curvoice.sndsh != undefined)
		sndtran = (curvoice.sndtran || 0) +
			(curvoice.sndsh || 0)

	if (s_key.k_sf == undefined) {
		if (!s_key.k_a_acc
		 && transp == undefined) {
			if (sndtran == undefined)
				return		// not a key signature
			s_key.invis = true	// play only
		}
		s_key.k_sf = curvoice.okey.k_sf

	}

	curvoice.okey = clone(s_key)
	if (transp != undefined) {
		curvoice.vtransp = transp;
		s_key.k_transp = transp
	}

	if (sndtran != undefined)
		s_key.k_sndtran = sndtran

	s_key.k_old_sf = curvoice.ckey.k_sf;	// memorize the key changes

	if ((!s_key.k_a_acc || !s_key.k_a_acc.length)
	 && !s_key.k_sf && !s_key.k_old_sf)
		s_key.invis = true		// don't display the key signature

	if (!s_key.k_b40)
		s_key.k_b40 = curvoice.ckey.k_b40

	curvoice.ckey = s_key

	if (is_voice_sig()) {
		s_key.fmt = cfmt
		curvoice.key = clone(s_key)
		if (s_key.k_none)
			curvoice.key.k_sf = 0
	} else {
		sym_link(s_key)			// (don't move the key)







<
<
|
|
|
|
<
|
<
<
|
<
<
<
<
<
<
|
|
<
|
|
<
<
|
>


<
<
<
<
|
>
|
|

|









<







1808
1809
1810
1811
1812
1813
1814


1815
1816
1817
1818

1819


1820






1821
1822

1823
1824


1825
1826
1827
1828




1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843

1844
1845
1846
1847
1848
1849
1850
		if (!glovar.ulen)
			glovar.ulen = C.BLEN / 8;
		if (a.length) {
			memo_kv_parm('*', a)
			a = ''
		}
		goto_tune()


	} else {
		set_kv_parm(a)
	}


	tr_p = curvoice.tr_p			// transposition change 1=sco, 2=snd


	curvoice.tr_p = 0






	if (s.k_sf == undefined) {		// if no key
		if (!s.k_a_acc			// if no accidental list

		 && !(tr_p & 1))		// and no score transposition
			return			// not a key signature


		s_key.k_sf = curvoice.okey.k_sf	// set the same key
		s_key.k_b40 = curvoice.okey.k_b40
	}






	if (curvoice.tr_sco)			// if transpose score
		key_transp(s)
	curvoice.okey = clone(s_key)

	s_key.k_old_sf = curvoice.ckey.k_sf	// memorize the key changes

	if ((!s_key.k_a_acc || !s_key.k_a_acc.length)
	 && !s_key.k_sf && !s_key.k_old_sf)
		s_key.invis = true		// don't display the key signature

	if (!s_key.k_b40)
		s_key.k_b40 = curvoice.ckey.k_b40

	curvoice.ckey = s_key

	if (is_voice_sig()) {
		s_key.fmt = cfmt
		curvoice.key = clone(s_key)
		if (s_key.k_none)
			curvoice.key.k_sf = 0
	} else {
		sym_link(s_key)			// (don't move the key)
︙︙
1951
1952
1953
1954
1955
1956
1957

1958
1959
1960
1961
1962
1963
1964
		if (!p_voice.time) {		// if no symbol yet
			p_voice.id = id
			delete p_voice.init	// reload the global attributes
			if (cfmt.transp	// != undefined
			 && parse.state >= 2) {
				p_v_sav = curvoice;
				curvoice = p_voice;

				set_transp();
				curvoice = p_v_sav
			}
			return p_voice		// default voice
		}
	}
	for (v = 0; v < n; v++) {







>







1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
		if (!p_voice.time) {		// if no symbol yet
			p_voice.id = id
			delete p_voice.init	// reload the global attributes
			if (cfmt.transp	// != undefined
			 && parse.state >= 2) {
				p_v_sav = curvoice;
				curvoice = p_voice;
				curvoice.tr_sco = cfmt.transp
				set_transp();
				curvoice = p_v_sav
			}
			return p_voice		// default voice
		}
	}
	for (v = 0; v < n; v++) {
︙︙
2100
2101
2102
2103
2104
2105
2106




2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121



2122
2123
2124
2125
2126
2127
2128
			bol: parse.iend
		}
		if (parse.file[curvoice.clone.bol - 1] != ']')
			curvoice.clone.bol++	// start of new line
	}

	set_kv_parm(a)





	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
			}



		}
	}

	if (!curvoice.filtered
	 && par_sy.voices[v]
	 && (parse.voice_opts
	  || parse.tune_v_opts)) {







>
>
>
>















>
>
>







2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
			bol: parse.iend
		}
		if (parse.file[curvoice.clone.bol - 1] != ']')
			curvoice.clone.bol++	// start of new line
	}

	set_kv_parm(a)
	if (curvoice.tr_p) {
		curvoice.tr_p = 0
		set_transp()
	}

	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)) {
︙︙
2140
2141
2142
2143
2144
2145
2146


2147
2148
2149
2150
2151
2152
2153
	write_heading();
	if (glovar.new_nbar) {
		gene.nbar = glovar.new_nbar	// measure numbering
		glovar.new_nbar = 0
	} else {
		gene.nbar = 1
	}



	// if no voice yet, create the default voice
	if (!voice_tb.length) {
		get_voice("1");
		curvoice.clef.istart = curvoice.key.istart;
		curvoice.clef.iend = curvoice.key.iend;
//		nstaff = 0;







>
>







2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
	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

	// if no voice yet, create the default voice
	if (!voice_tb.length) {
		get_voice("1");
		curvoice.clef.istart = curvoice.key.istart;
		curvoice.clef.iend = curvoice.key.iend;
//		nstaff = 0;
︙︙
Added dist_bin.




























































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
COPYING
COPYING.LESSER
MIDI-1.js
Scc1t2/
abc2svg-1.js
abc2svg.ttf
abc2svg.woff
abcdoc-1.js
abcjsc
abcmjs
abcnode
abcqjs
abctopdf
abcweb1-1.js
abcweb-1.js
ambitus-1.js
break-1.js
capo-1.js
chordnames-1.js
clair-1.js
clip-1.js
cmdline.js
combine-1.js
diag-1.js
edit-1.css
edit-1.js
edit-1.xhtml
edit-de.js
edit-en.js
edit-fr.js
edit-it.js
edit-pt_BR.js
equalbars-1.js
err-de.js
err-en.js
err-fr.js
err-it.js
err-pt_BR.js
gamelan-1.js
grid2-1.js
grid-1.js
grid3-1.js
jazzchord-1.js
jianpu-1.js
mdnn-1.js
nns-1.js
page-1.js
pedline-1.js
perc-1.js
psvg-1.js
roman-1.js
snd-1.js
soloffs-1.js
sth-1.js
strtab-1.js
temper-1.js
tropt-1.js
toabc.js
tohtml.js
tomei.js
tonotes.js
toparam.js
Changes to editor/edit.css.
︙︙
96
97
98
99
100
101
102

103
104
	#dleft, #ctxMenu, .popup { display: none }
	body {
		margin:0; padding:0; border:0;
		background-color: white;
	}
	#dright { margin:0; padding:0; border:0 }
	.newpage { page-break-before: always }

	.nobrk { page-break-inside: avoid }
}







>


96
97
98
99
100
101
102
103
104
105
	#dleft, #ctxMenu, .popup { display: none }
	body {
		margin:0; padding:0; border:0;
		background-color: white;
	}
	#dright { margin:0; padding:0; border:0 }
	.newpage { page-break-before: always }
	.newpage:first-of-type { page-break-before: avoid }
	.nobrk { page-break-inside: avoid }
}
Changes to editor/edit.js.
︙︙
290
291
292
293
294
295
296


297
298
299
300
301
302
303
304
// select a source ABC element on error
function gotoabc(l, c) {
    var	s = elt_ref.source

	selsrc(0)
	if (l >= 0)
		c = soffs(l, Number(c))


	s.focus();
	s.setSelectionRange(c, syms[c] ? syms[c].iend : c + 1)
}

// click in the target
function selsvg(evt) {
    var	v,
	cl = evt.target.getAttribute('class')







>
>
|







290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
// select a source ABC element on error
function gotoabc(l, c) {
    var	s = elt_ref.source

	selsrc(0)
	if (l >= 0)
		c = soffs(l, Number(c))
	s.blur()			// unfocus
	s.setSelectionRange(c, c)	// just set the cursor (no range)
	s.focus()			// should scroll the textarea
	s.setSelectionRange(c, syms[c] ? syms[c].iend : c + 1)
}

// click in the target
function selsvg(evt) {
    var	v,
	cl = evt.target.getAttribute('class')
︙︙
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333


334
335
336
337
338
339
340
		play.stop = -1;
		play.abcplay.stop()
		return
	}

	// highlight the clicked element or clear the selection start
	s = elt_ref.source;

	if (cl && cl.substr(0, 4) == 'abcr') {
		v = Number(cl.slice(6, -1));
		s.setSelectionRange(v, syms[v].iend)
	} else {
		s.setSelectionRange(0, 0)
	}
	s.blur();
	s.focus()


}

// set/clear a selection
function setsel(idx, v) {
    var i, elts, s,
	old_v = selx[idx];








>
|
<
<
<
|
<
<

>
>







321
322
323
324
325
326
327
328
329



330


331
332
333
334
335
336
337
338
339
340
		play.stop = -1;
		play.abcplay.stop()
		return
	}

	// highlight the clicked element or clear the selection start
	s = elt_ref.source;
	s.blur();
	v = cl && cl.substr(0, 4) == 'abcr' ? Number(cl.slice(6, -1)) : 0



	s.setSelectionRange(v, v)	// just a cursor


	s.focus()
	if (v)
		s.setSelectionRange(v, syms[v].iend)
}

// set/clear a selection
function setsel(idx, v) {
    var i, elts, s,
	old_v = selx[idx];

︙︙
353
354
355
356
357
358
359
360


361
362
363
364
365


366
367
368
369
370
371
372
373
374
375





376
377
378
379
380
381
382
			elts[i].style.fillOpacity = 0.4
	}

	selx[idx] = v
}

function do_scroll(elt) {
    var	r,


	dr = elt_ref.target.parentElement,		// <div> 'dright'
	drh = dr.getBoundingClientRect().height,	// height of the view
	ty = elt_ref.target.getBoundingClientRect().y	// y offset of <div> 'target'

		elt = elt.parentNode;


	r = elt.getBoundingClientRect()
// upper -> top, lower -> bottom
	if (r.y < 0)				// y offset of the svg container
		dr.scrollTo(0, r.y - ty)
	else if (r.y + r.height > drh)
		dr.scrollTo(0, r.y - ty - drh + r.height)
// in the middle
//	if (r.y < 0				// y offset of the svg container
//	 || r.y + r.height > drh)
//		dr.scrollTo(0, r.y - ty - drh / 2 + r.height)





}

// source text selection callback
function seltxt(evt) {
    var	s, elts,
	e = 0,
	elt = elt_ref.source,







|
>
>
|
|
<

<
>
>
|
|
|
<
|
|
|
|
|
|
>
>
>
>
>







353
354
355
356
357
358
359
360
361
362
363
364

365

366
367
368
369
370

371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
			elts[i].style.fillOpacity = 0.4
	}

	selx[idx] = v
}

function do_scroll(elt) {
    var	x = 0,
	y = 0,
	b = elt.getBoundingClientRect(),		// box of the rectangle
	d = elt_ref.target.parentElement,		// <div> 'dright'
	r = elt.parentNode.getBoundingClientRect()	// box of the svg container



	if (b.x < d.offsetLeft		// x offset of the rectangle
	 || b.x + b.width > d.offsetLeft + d.clientWidth * .7)
		x = b.x - d.offsetLeft - d.clientWidth * .3

	if (r.y < d.offsetTop		// y offset of the svg container

	 || r.y + r.height > d.offsetTop + d.clientHeight * .7)
		y = r.y - d.offsetTop - d.clientHeight * .3

	if (x || y)
//		d.scrollBy(x, y)
		d.scrollBy({
			top: y,
			left: x,
			behavior: (x < 0 || y) ? 'instant' : 'smooth'
				// 'smooth', 'instant' or 'auto' (default)
		})
}

// source text selection callback
function seltxt(evt) {
    var	s, elts,
	e = 0,
	elt = elt_ref.source,
︙︙
514
515
516
517
518
519
520


521
522
523
524
525
526
527
// start playing
//	-1: All (removed)
//	0: Tune
//	1: Selection
//	2: Loop
//	3: Continue
function play_tune(what) {


    var	i, si, ei, elt,
	C = abc2svg.C,
	tunes = abc.tunes

	if (play.playing) {
		if (!play.stop) {
			play.stop = -1;







>
>







520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
// start playing
//	-1: All (removed)
//	0: Tune
//	1: Selection
//	2: Loop
//	3: Continue
function play_tune(what) {
	if (!abc)
		return			// no generation yet
    var	i, si, ei, elt,
	C = abc2svg.C,
	tunes = abc.tunes

	if (play.playing) {
		if (!play.stop) {
			play.stop = -1;
︙︙
809
810
811
812
813
814
815
816
817
818
819
820
821

822
823
824






825

826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841

842
843
844
845
846
847
848
849
850
851
		// display the play menu
		// This function is called on non left click
		function show_menu(evt) {
		    var	x, y,
			elt = evt.target,
			cl = elt.getAttribute('class')

//			evt.stopImmediatePropagation();
			evt.preventDefault()

			// if right click on an element, select it
			if (cl && cl.substr(0, 4) == 'abcr') {
				setsel(1, Number(cl.slice(6, -1)))

				return false
			}







			// otherwise, display the play menu

			play.click = {	// keep the click references for 'play tune'
				svg: elt,
				Y: evt.pageY
			}

			ctxMenu = document.getElementById("ctxMenu");
			ctxMenu.style.display = "block";
			x = evt.pageX - elt_ref.target.parentNode.offsetLeft
					+ elt_ref.target.parentNode.scrollLeft;
			y = evt.pageY + elt_ref.target.parentNode.scrollTop;
			ctxMenu.style.left = (x - 30) + "px";
			ctxMenu.style.top = (y - 10) + "px"
		} // showmenu

		e = elt_ref.target
		e.onauxclick = show_menu	// right or middle buttons

		e.oncontextmenu = function(ev) {
			ev.preventDefault()
		}
	}
	set_pref()	// set the preferences from local storage
}

// drag and drop
function drag_enter(evt) {
	evt.stopImmediatePropagation();







<
<
<



>
|


>
>
>
>
>
>

>





<









|
>
|
<
<







817
818
819
820
821
822
823



824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843

844
845
846
847
848
849
850
851
852
853
854
855


856
857
858
859
860
861
862
		// display the play menu
		// This function is called on non left click
		function show_menu(evt) {
		    var	x, y,
			elt = evt.target,
			cl = elt.getAttribute('class')




			// if right click on an element, select it
			if (cl && cl.substr(0, 4) == 'abcr') {
				setsel(1, Number(cl.slice(6, -1)))
				evt.preventDefault()
				return
			}

			// if the play menu is already active
			// display the default context menu
			ctxMenu = document.getElementById("ctxMenu");
			if (ctxMenu.style.display == "block")
				return

			// otherwise, display the play menu
			evt.preventDefault()
			play.click = {	// keep the click references for 'play tune'
				svg: elt,
				Y: evt.pageY
			}


			ctxMenu.style.display = "block";
			x = evt.pageX - elt_ref.target.parentNode.offsetLeft
					+ elt_ref.target.parentNode.scrollLeft;
			y = evt.pageY + elt_ref.target.parentNode.scrollTop;
			ctxMenu.style.left = (x - 30) + "px";
			ctxMenu.style.top = (y - 10) + "px"
		} // showmenu

		e = elt_ref.target
//		e.onauxclick = show_menu	// right or middle buttons (KO in Safari)
		e.ondblclick = show_menu	// double click
		e.oncontextmenu = show_menu


	}
	set_pref()	// set the preferences from local storage
}

// drag and drop
function drag_enter(evt) {
	evt.stopImmediatePropagation();
︙︙
Changes to editor/edit.xhtml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link href="edit-@MAJOR@.css" rel="stylesheet" type="text/css"/>
<script src="edit-@MAJOR@.js" type="text/javascript" async="true"></script>
<script src="abc2svg-@MAJOR@.js" type="text/javascript" async="true"></script>
<script src="pref.js" type="text/javascript" async="true"></script>
<title>ABC edit with abc2svg</title>

</head>
<body ontouchstart="" bgcolor="#faf0e6">

<!-- left -->
<div id="dleft">

<!-- menu bar -->
    <ul id="nav">
	<li class="dropbutton"><label id="f">File</label>
>




<





<

|







1
2
3
4
5

6
7
8
9
10

11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

<link href="edit-@MAJOR@.css" rel="stylesheet" type="text/css"/>
<script src="edit-@MAJOR@.js" type="text/javascript" async="true"></script>
<script src="abc2svg-@MAJOR@.js" type="text/javascript" async="true"></script>
<script src="pref.js" type="text/javascript" async="true"></script>
<title>ABC edit with abc2svg</title>

</head>
<body ontouchstart="" bgcolor="#faf0e6" spellcheck="false">

<!-- left -->
<div id="dleft">

<!-- menu bar -->
    <ul id="nav">
	<li class="dropbutton"><label id="f">File</label>
︙︙
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
</ul>
</div>

<!-- about popup window -->
<div id="about" class="popup" style="width: 350px">
<div class="close" onclick="popshow('about')">x</div>
<p id="abc2svg">abc2svg</p>
<p>Copyright &#x00A9; 2016-2021 <a
href="http://moinejf.free.fr">Jean-François Moine</a></p>
</div>

<!-- javascript must be enabled -->
<noscript>
<div id="okjs" class="popup" style="width:50%;background:#ff8080;visibility:visible">
Please enable Javascript.
</div>
</noscript>

</body>
</html>







|












140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
</ul>
</div>

<!-- about popup window -->
<div id="about" class="popup" style="width: 350px">
<div class="close" onclick="popshow('about')">x</div>
<p id="abc2svg">abc2svg</p>
<p>Copyright &#x00A9; 2016-2022 <a
href="http://moinejf.free.fr">Jean-François Moine</a></p>
</div>

<!-- javascript must be enabled -->
<noscript>
<div id="okjs" class="popup" style="width:50%;background:#ff8080;visibility:visible">
Please enable Javascript.
</div>
</noscript>

</body>
</html>
Changes to err-de.js.
︙︙
88
89
90
91
92
93
94

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110


111
112
113
114
115
116
117
	"Error loading the module $1": "Error loading the module $1",
    "Incorrect 1st value in %%repeat": "erster Wert fehlerhaft in %%repeat",
    "Incorrect 2nd value in %%repeat": "zweiter Wert fehlerhaft in %%repeat",
    "Invalid 'r' in tuplet": "'r' fehlerhaft in Tuplet",
    "Invalid decoration '$1'": "fehlerhafte Dekoration '$1'",
    "Invalid note duration $1": "Fehlerhafte Notendekoration '$1'",
	"K:P is deprecated": "K:P is deprecated",

    "Lack of ']'": "']' fehlt",
	"Lack of bar after multi-measure rest":
				"Lack of bar after multi-measure rest",
	"Lack of ending slur(s)": "Lack of ending slur(s)",
	"Lack of starting [, ! or \" in U: / %%user":
				"[, ! or \" fehlt in U: / %%user",
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Probleme beim Aufteilen der Zeile - justiere maxshrink und/oder breaklimit",
    "Line too much shrunk $1 $2 $3": "Zeile zu stark geschrumpft $1 $2 $3",
    "Line underfull ($1pt of $2pt)": "Zeile nicht voll genug ($1pt of $2pt)",
	"Microtone transposition not coded":
				"Microtone transposition not coded",
    "Misplaced '$1' in %%score": "'$1' fehlerhaft platziert in %%score",
    "Misplaced dot": "fehlerhaft platzierter Punkt",
	"Mix of old and new transposition syntaxes":
				"Mix von alter und neuer Transponierungs-syntax",


    "No $1 after %%$2": "kein $1 nach %%$2",
    "No </defs> in %%beginsvg sequence": "kein </defs> in %%beginsvg squenz",
    "No </style> in %%beginsvg sequence": "kein </style> in %%beginsvg sequenz",
    "No accidental after 'exp'": "kein Vorzeichen nach 'exp'",
    "No end of decoration": "kein Ende der Dekoration",
	"No end of grace note sequence": "kein Ende der Vorschlagnoten",
    "No end of chord symbol/annotation": "kein Ende des Akkordsymbols",







>
















>
>







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
	"Error loading the module $1": "Error loading the module $1",
    "Incorrect 1st value in %%repeat": "erster Wert fehlerhaft in %%repeat",
    "Incorrect 2nd value in %%repeat": "zweiter Wert fehlerhaft in %%repeat",
    "Invalid 'r' in tuplet": "'r' fehlerhaft in Tuplet",
    "Invalid decoration '$1'": "fehlerhafte Dekoration '$1'",
    "Invalid note duration $1": "Fehlerhafte Notendekoration '$1'",
	"K:P is deprecated": "K:P is deprecated",
	"Key with double sharps/flats": "Key with double sharps/flats",
    "Lack of ']'": "']' fehlt",
	"Lack of bar after multi-measure rest":
				"Lack of bar after multi-measure rest",
	"Lack of ending slur(s)": "Lack of ending slur(s)",
	"Lack of starting [, ! or \" in U: / %%user":
				"[, ! or \" fehlt in U: / %%user",
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Probleme beim Aufteilen der Zeile - justiere maxshrink und/oder breaklimit",
    "Line too much shrunk $1 $2 $3": "Zeile zu stark geschrumpft $1 $2 $3",
    "Line underfull ($1pt of $2pt)": "Zeile nicht voll genug ($1pt of $2pt)",
	"Microtone transposition not coded":
				"Microtone transposition not coded",
    "Misplaced '$1' in %%score": "'$1' fehlerhaft platziert in %%score",
    "Misplaced dot": "fehlerhaft platzierter Punkt",
	"Mix of old and new transposition syntaxes":
				"Mix von alter und neuer Transponierungs-syntax",
	"multi-measure rest, but no measure!":
			"multi-measure rest, but no measure!",
    "No $1 after %%$2": "kein $1 nach %%$2",
    "No </defs> in %%beginsvg sequence": "kein </defs> in %%beginsvg squenz",
    "No </style> in %%beginsvg sequence": "kein </style> in %%beginsvg sequenz",
    "No accidental after 'exp'": "kein Vorzeichen nach 'exp'",
    "No end of decoration": "kein Ende der Dekoration",
	"No end of grace note sequence": "kein Ende der Vorschlagnoten",
    "No end of chord symbol/annotation": "kein Ende des Akkordsymbols",
︙︙
143
144
145
146
147
148
149

150
151
152
153
154
155
156
	"s: without music": "s: ohne Musik",
	"Too many elements in symbol line": "zu viele elemente in einer Symbolzeile",
	"Too many include levels": "Verschachtelung für include zu tief",
	"There cannot be chord symbols on measure bars":
			"There cannot be chord symbols on measure bars",
    "Too many ties": "zu viele Haltebogen",
    "Too many words in lyric line": "zu viele Worte in Liedtextzeile",

    "Unknown bagpipe-like key": "unbekante Dudelsackartige Tonart",
    "Unknown clef '$1'": "unbekannter Schlüssel '$1'",
    "Unknown decoration '$1'": "unbekannte Dekoration '$1'",
	"Unknown font $1": "unbekannte Schriftart '$1'",
    "Unknown glyph: '$1'": "Unbekanntes Schriftsybol '$1'",
	"Unknown keyword '$1' in %%multicol": "unbekanntes Schlüsselwort '$1' in %%multicol",
	"Unsecure code": "Unsicherer Code",







>







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
	"s: without music": "s: ohne Musik",
	"Too many elements in symbol line": "zu viele elemente in einer Symbolzeile",
	"Too many include levels": "Verschachtelung für include zu tief",
	"There cannot be chord symbols on measure bars":
			"There cannot be chord symbols on measure bars",
    "Too many ties": "zu viele Haltebogen",
    "Too many words in lyric line": "zu viele Worte in Liedtextzeile",
	"triple sharp/flat": "triple sharp/flat",
    "Unknown bagpipe-like key": "unbekante Dudelsackartige Tonart",
    "Unknown clef '$1'": "unbekannter Schlüssel '$1'",
    "Unknown decoration '$1'": "unbekannte Dekoration '$1'",
	"Unknown font $1": "unbekannte Schriftart '$1'",
    "Unknown glyph: '$1'": "Unbekanntes Schriftsybol '$1'",
	"Unknown keyword '$1' in %%multicol": "unbekanntes Schlüsselwort '$1' in %%multicol",
	"Unsecure code": "Unsicherer Code",
︙︙
Changes to err-fr.js.
︙︙
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116


117
118
119
120
121
122
123
	"Error loading the module $1": "Erreur de chargement du module $1",
	"Incorrect 1st value in %%repeat": "Première valeur incorrecte dans %%repeat",
	"Incorrect 2nd value in %%repeat": "Deuxième valeur incorrecte dans %%repeat",
	"Invalid 'r' in tuplet": "'r' invalide dans le tuplet",
	"Invalid decoration '$1'": "Décoration '$1' invalide",
	"Invalid note duration $1": "Durée de note $1 invalide",
	"K:P is deprecated": "K:P est déprécié",

	"Lack of ']'": "']' manquant",
	"Lack of bar after multi-measure rest":
		"Manque de barre de mesure après silence multiple",
	"Lack of ending slur(s)": "Pas de fin de liaison(s)",
	'Lack of starting [, ! or " in U: / %%user':
				'U: / %%user ne commence pas par [, ! ou "',
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Problème de découpe en lignes - ajuster maxshrink et/ou breaklimit",
	"Line too much shrunk $1 $2 $3": "Ligne trop compressée $1 $2 $3",
	"Line underfull ($1pt of $2pt)": "Trop d'espacement ($1pt of $2pt)",
	"Microtone transposition not coded":
				"Transposition de microtonalité pas codée",
	"Misplaced '$1' in %%score": "'$1' mal placé dans %%score",
	"Misplaced dot": "Point mal placé",
	"Mix of old and new transposition syntaxes":
			"Mélange des syntaxes de transposition ancienne et nouvelle",


	"No $1 after %%$2": "Pas de $1 après %%$2",
	"No </defs> in %%beginsvg sequence":
				"Pas de </defs> dans séquence %%beginsvg",
	"No </style> in %%beginsvg sequence":
				"Pas de </style> dans séquence %%beginsvg",
	"No accidental after 'exp'": "Pas d'altération après 'exp'",
	"No end of decoration": "Pas de fin de décoration",







>
















>
>







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
	"Error loading the module $1": "Erreur de chargement du module $1",
	"Incorrect 1st value in %%repeat": "Première valeur incorrecte dans %%repeat",
	"Incorrect 2nd value in %%repeat": "Deuxième valeur incorrecte dans %%repeat",
	"Invalid 'r' in tuplet": "'r' invalide dans le tuplet",
	"Invalid decoration '$1'": "Décoration '$1' invalide",
	"Invalid note duration $1": "Durée de note $1 invalide",
	"K:P is deprecated": "K:P est déprécié",
	"Key with double sharps/flats": "Armature avec doubles dièses/bémols",
	"Lack of ']'": "']' manquant",
	"Lack of bar after multi-measure rest":
		"Manque de barre de mesure après silence multiple",
	"Lack of ending slur(s)": "Pas de fin de liaison(s)",
	'Lack of starting [, ! or " in U: / %%user':
				'U: / %%user ne commence pas par [, ! ou "',
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Problème de découpe en lignes - ajuster maxshrink et/ou breaklimit",
	"Line too much shrunk $1 $2 $3": "Ligne trop compressée $1 $2 $3",
	"Line underfull ($1pt of $2pt)": "Trop d'espacement ($1pt of $2pt)",
	"Microtone transposition not coded":
				"Transposition de microtonalité pas codée",
	"Misplaced '$1' in %%score": "'$1' mal placé dans %%score",
	"Misplaced dot": "Point mal placé",
	"Mix of old and new transposition syntaxes":
			"Mélange des syntaxes de transposition ancienne et nouvelle",
	"multi-measure rest, but no measure!":
			"silence multi-mesure, mais pas de mesure!",
	"No $1 after %%$2": "Pas de $1 après %%$2",
	"No </defs> in %%beginsvg sequence":
				"Pas de </defs> dans séquence %%beginsvg",
	"No </style> in %%beginsvg sequence":
				"Pas de </style> dans séquence %%beginsvg",
	"No accidental after 'exp'": "Pas d'altération après 'exp'",
	"No end of decoration": "Pas de fin de décoration",
︙︙
155
156
157
158
159
160
161

162
163
164
165
166
167
168
	"s: without music": "s: sans musique",
	"There cannot be chord symbols on measure bars":
		"On ne peut pas mettre d'indication d'accord sur une barre de mesure",
	"Too many elements in symbol line": "Trop d'éléments dans ligne s:",
	"Too many include levels": "Trop de niveaux d'inclusion de fichier",
	"Too many ties": "Trop de liaisons de prolongation",
	"Too many words in lyric line": "Trop de mots dans la ligne de paroles",

	"Unknown bagpipe-like key": "Armature de cornemuse inconnue",
	"Unknown clef '$1'": "Clé '$1' inconnue",
	"Unknown decoration '$1'": "Décoration '$1' inconnue",
	"Unknown font $1": "Police $1 inconnue",
	"Unknown glyph: '$1'": "Glyph '$1' inconnu",
	"Unknown keyword '$1' in %%multicol": "Mot clé '$1' inconnu dans %%multicol",
	"Unsecure code": "Code dangereux",







>







158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
	"s: without music": "s: sans musique",
	"There cannot be chord symbols on measure bars":
		"On ne peut pas mettre d'indication d'accord sur une barre de mesure",
	"Too many elements in symbol line": "Trop d'éléments dans ligne s:",
	"Too many include levels": "Trop de niveaux d'inclusion de fichier",
	"Too many ties": "Trop de liaisons de prolongation",
	"Too many words in lyric line": "Trop de mots dans la ligne de paroles",
	"triple sharp/flat": "triple dièse/bémol",
	"Unknown bagpipe-like key": "Armature de cornemuse inconnue",
	"Unknown clef '$1'": "Clé '$1' inconnue",
	"Unknown decoration '$1'": "Décoration '$1' inconnue",
	"Unknown font $1": "Police $1 inconnue",
	"Unknown glyph: '$1'": "Glyph '$1' inconnu",
	"Unknown keyword '$1' in %%multicol": "Mot clé '$1' inconnu dans %%multicol",
	"Unsecure code": "Code dangereux",
︙︙
Changes to err-it.js.
︙︙
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116


117
118
119
120
121
122
123
	"Error loading the module $1": "Error loading the module $1",
	"Incorrect 1st value in %%repeat": "Primo valore in %%repeat errato",
	"Incorrect 2nd value in %%repeat": "Secondo valore in %%repeat errato",
	"Invalid 'r' in tuplet": "'r' non valido in gruppo irregolare",
	"Invalid decoration '$1'": "Decorazione '$1' non valida",
	"Invalid note duration $1": "Durée de note $1 non valida",
	"K:P is deprecated": "K:P is deprecated",

	"Lack of ']'": "']' mancante",
	"Lack of bar after multi-measure rest":
				"Lack of bar after multi-measure rest",
	"Lack of ending slur(s)": "Lack of ending slur(s)",
	'Lack of starting [, ! or " in U: / %%user':
				'U: / %%user non inizia con [, ! o "',
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Problema di linea divisa - modificare maxshrink e/o breaklimit",
	"Line too much shrunk $1 $2 $3": "Linea troppo compressa $1 $2 $3",
	"Line underfull ($1pt of $2pt)": "Linea troppo vuota ($1pt di $2pt)",
	"Microtone transposition not coded":
				"Microtone transposition not coded",
	"Misplaced '$1' in %%score": "'$1' in posizione errata in %%score",
	"Misplaced dot": "Punto in posizione errata",
	"Mix of old and new transposition syntaxes":
			"Sintassi di trasposizione vecchia e nuova allo stesso tempo",


	"No $1 after %%$2": "Manca $1 dopo %%$2",
	"No </defs> in %%beginsvg sequence":
				"Manca </defs> nella sequenza %%beginsvg",
	"No </style> in %%beginsvg sequence":
				"manca </style> nella sequenza %%beginsvg",
	"No accidental after 'exp'": "Manca l'alterazione dopo 'exp'",
	"No end of chord symbol/annotation": "Accordo di accompagnamento non terminato",







>
















>
>







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
	"Error loading the module $1": "Error loading the module $1",
	"Incorrect 1st value in %%repeat": "Primo valore in %%repeat errato",
	"Incorrect 2nd value in %%repeat": "Secondo valore in %%repeat errato",
	"Invalid 'r' in tuplet": "'r' non valido in gruppo irregolare",
	"Invalid decoration '$1'": "Decorazione '$1' non valida",
	"Invalid note duration $1": "Durée de note $1 non valida",
	"K:P is deprecated": "K:P is deprecated",
	"Key with double sharps/flats": "Key with double sharps/flats",
	"Lack of ']'": "']' mancante",
	"Lack of bar after multi-measure rest":
				"Lack of bar after multi-measure rest",
	"Lack of ending slur(s)": "Lack of ending slur(s)",
	'Lack of starting [, ! or " in U: / %%user':
				'U: / %%user non inizia con [, ! o "',
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Problema di linea divisa - modificare maxshrink e/o breaklimit",
	"Line too much shrunk $1 $2 $3": "Linea troppo compressa $1 $2 $3",
	"Line underfull ($1pt of $2pt)": "Linea troppo vuota ($1pt di $2pt)",
	"Microtone transposition not coded":
				"Microtone transposition not coded",
	"Misplaced '$1' in %%score": "'$1' in posizione errata in %%score",
	"Misplaced dot": "Punto in posizione errata",
	"Mix of old and new transposition syntaxes":
			"Sintassi di trasposizione vecchia e nuova allo stesso tempo",
	"multi-measure rest, but no measure!":
			"multi-measure rest, but no measure!",
	"No $1 after %%$2": "Manca $1 dopo %%$2",
	"No </defs> in %%beginsvg sequence":
				"Manca </defs> nella sequenza %%beginsvg",
	"No </style> in %%beginsvg sequence":
				"manca </style> nella sequenza %%beginsvg",
	"No accidental after 'exp'": "Manca l'alterazione dopo 'exp'",
	"No end of chord symbol/annotation": "Accordo di accompagnamento non terminato",
︙︙
154
155
156
157
158
159
160

161
162
163
164
165
166
167
	"s: without music": "s: senza musica",
	"There cannot be chord symbols on measure bars":
			"There cannot be chord symbols on measure bars",
	"Too many elements in symbol line": "Troppi elementi nella linea s:",
	"Too many include levels": "Troppi livelli di inclusione di file",
	"Too many ties": "Troppi simboli di legato",
	"Too many words in lyric line": "Troppe parole per la linea di musica",

	"Unknown bagpipe-like key": "Chiave per cornamusa sconosciuta",
	"Unknown clef '$1'": "Chiave '$1' sconosciuta",
	"Unknown decoration '$1'": "Decorazione '$1' sconosciuta",
	"Unknown font $1": "Font $1 sconosciuto",
	"Unknown glyph: '$1'": "Glifo '$1' sconosciuto",
	"Unknown keyword '$1' in %%multicol": "Keyword '$1' sconosciuta in %%multicol",
        "Unsecure code": "Codice pericoloso",







>







157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
	"s: without music": "s: senza musica",
	"There cannot be chord symbols on measure bars":
			"There cannot be chord symbols on measure bars",
	"Too many elements in symbol line": "Troppi elementi nella linea s:",
	"Too many include levels": "Troppi livelli di inclusione di file",
	"Too many ties": "Troppi simboli di legato",
	"Too many words in lyric line": "Troppe parole per la linea di musica",
	"triple sharp/flat": "triple sharp/flat",
	"Unknown bagpipe-like key": "Chiave per cornamusa sconosciuta",
	"Unknown clef '$1'": "Chiave '$1' sconosciuta",
	"Unknown decoration '$1'": "Decorazione '$1' sconosciuta",
	"Unknown font $1": "Font $1 sconosciuto",
	"Unknown glyph: '$1'": "Glifo '$1' sconosciuto",
	"Unknown keyword '$1' in %%multicol": "Keyword '$1' sconosciuta in %%multicol",
        "Unsecure code": "Codice pericoloso",
︙︙
Changes to err-pt_BR.js.
1
2
3
4
5
6
7
8
9
10
11
12
//JS abc2svg - Brazilian Portuguese translation of the error codes:
//			Tradução dos códigos de erro para português brasileiro
// Adicione este arquivo na linha de comando para obter as
// mensagens de erro em português do Brasil.
// (Hudson Lacerda - 11/dez/2016 - 23/set/2021)

user.textrans = {
	"+: lyric without music": "+: palavras sem música",
	"+: symbol line without music": "+: linha de símbolos sem música",
	"+: without previous info field": "+: continuação sem campo anterior",
	"'{' in grace note": "'{' em notinha ornamental",
	"'}', ')' or ']' missing in %%score":




|







1
2
3
4
5
6
7
8
9
10
11
12
//JS abc2svg - Brazilian Portuguese translation of the error codes:
//			Tradução dos códigos de erro para português brasileiro
// Adicione este arquivo na linha de comando para obter as
// mensagens de erro em português do Brasil.
// (Hudson Lacerda - 11/dez/2016 - 23/set/2021 - 02/nov/2022)

user.textrans = {
	"+: lyric without music": "+: palavras sem música",
	"+: symbol line without music": "+: linha de símbolos sem música",
	"+: without previous info field": "+: continuação sem campo anterior",
	"'{' in grace note": "'{' em notinha ornamental",
	"'}', ')' or ']' missing in %%score":
︙︙
92
93
94
95
96
97
98

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114


115
116
117
118
119
120
121
	"Error loading the module $1": "Error loading the module $1",
	"Incorrect 1st value in %%repeat": "Primeiro valor incorreto em %%repeat",
	"Incorrect 2nd value in %%repeat": "Segundo valor incorreto em %%repeat",
	"Invalid 'r' in tuplet": "'r' inválido em quiáltera",
	"Invalid decoration '$1'": "Decoração '$1' inválida",
	"Invalid note duration $1": "Duração de nota $1 inválida",
	"K:P is deprecated": "K:P é obsoleto",

	"Lack of ']'": "']' faltando",
	"Lack of bar after multi-measure rest":
				"Falta barra de compasso após pausa de múltiplos compassos",
	"Lack of ending slur(s)": "Falta fim de ligadura(s)",
	'Lack of starting [, ! or " in U: / %%user':
				'U: / %%user não começou com [, ! ou "',
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Problema de quebra de linha - ajuste maxshrink e/ou breaklimit",
	"Line too much shrunk $1 $2 $3": "Linha muito encolhida $1 $2 $3",
	"Line underfull ($1pt of $2pt)": "Linha muito espaçada ($1pt de $2pt)",
	"Microtone transposition not coded":
				"A transposição de microtons não está codificada",
	"Misplaced '$1' in %%score": "'$1' deslocado em %%score",
	"Misplaced dot": "Ponto fora de lugar",
	"Mix of old and new transposition syntaxes":
				"Mistura de sintaxes de transposição antiga e nova",


	"No $1 after %%$2": "Falta $1 após %%$2",
	"No </defs> in %%beginsvg sequence":
				"Falta </defs> em bloco %%beginsvg",
	"No </style> in %%beginsvg sequence":
				"Falta </style> em bloco %%beginsvg",
	"No accidental after 'exp'": "Falta acidente após 'exp'",
	"No end of decoration": "Decoração sem término",







>
















>
>







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
	"Error loading the module $1": "Error loading the module $1",
	"Incorrect 1st value in %%repeat": "Primeiro valor incorreto em %%repeat",
	"Incorrect 2nd value in %%repeat": "Segundo valor incorreto em %%repeat",
	"Invalid 'r' in tuplet": "'r' inválido em quiáltera",
	"Invalid decoration '$1'": "Decoração '$1' inválida",
	"Invalid note duration $1": "Duração de nota $1 inválida",
	"K:P is deprecated": "K:P é obsoleto",
	"Key with double sharps/flats": "Tonalidade com sustenidos/bemóis duplos",
	"Lack of ']'": "']' faltando",
	"Lack of bar after multi-measure rest":
				"Falta barra de compasso após pausa de múltiplos compassos",
	"Lack of ending slur(s)": "Falta fim de ligadura(s)",
	'Lack of starting [, ! or " in U: / %%user':
				'U: / %%user não começou com [, ! ou "',
	"Line split problem - adjust maxshrink and/or breaklimit":
		"Problema de quebra de linha - ajuste maxshrink e/ou breaklimit",
	"Line too much shrunk $1 $2 $3": "Linha muito encolhida $1 $2 $3",
	"Line underfull ($1pt of $2pt)": "Linha muito espaçada ($1pt de $2pt)",
	"Microtone transposition not coded":
				"A transposição de microtons não está codificada",
	"Misplaced '$1' in %%score": "'$1' deslocado em %%score",
	"Misplaced dot": "Ponto fora de lugar",
	"Mix of old and new transposition syntaxes":
				"Mistura de sintaxes de transposição antiga e nova",
	"multi-measure rest, but no measure!":
		"Pausa de múltiplos compassos, porém o compasso não foi definido!",
	"No $1 after %%$2": "Falta $1 após %%$2",
	"No </defs> in %%beginsvg sequence":
				"Falta </defs> em bloco %%beginsvg",
	"No </style> in %%beginsvg sequence":
				"Falta </style> em bloco %%beginsvg",
	"No accidental after 'exp'": "Falta acidente após 'exp'",
	"No end of decoration": "Decoração sem término",
︙︙
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
	"s: without music": "s: sem música",
	"There cannot be chord symbols on measure bars":
			"Não pode haver cifra de acorde em barra de compasso",
	"Too many elements in symbol line": "Excesso de elementos na linha de símbolos",
	"Too many include levels": "Excessivos níveis de inclusão de arquivos",
	"Too many ties": "Excesso de ligaduras de prolongamento",
	"Too many words in lyric line": "Excesso de palavras",

	"Unknown bagpipe-like key": "Armadura desconhecida para gaita de foles",
	"Unknown clef '$1'": "Clave '$1' desconhecida",
	"Unknown decoration '$1'": "Decoração '$1' desconhecida",
	"Unknown font $1": "Fonte $1 desconhecida",
	"Unknown glyph: '$1'": "Glifo '$1' desconhecido",
	"Unknown keyword '$1' in %%multicol": "Palavra-chave '$1' desconhecida em %%multicol",
	"Unsecure code": "Código inseguro",
	"Unterminated string": "Cadeia de caracteres não terminada",
	"Unterminated string in Q:": "Cadeia de caracteres não terminada em Q:",
	"Variant ending on a left repeat bar":
				"Variant ending on a left repeat bar",
	"Voice overlay already started": "Divisão de vozes já se iniciou",
	"w: without music": "w: sem música",
	"Wrong duration in voice overlay":
				"Duração incorreta na divisão de vozes",
}







>










|
|


|

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
	"s: without music": "s: sem música",
	"There cannot be chord symbols on measure bars":
			"Não pode haver cifra de acorde em barra de compasso",
	"Too many elements in symbol line": "Excesso de elementos na linha de símbolos",
	"Too many include levels": "Excessivos níveis de inclusão de arquivos",
	"Too many ties": "Excesso de ligaduras de prolongamento",
	"Too many words in lyric line": "Excesso de palavras",
	"triple sharp/flat": "sustenido/bemol triplo",
	"Unknown bagpipe-like key": "Armadura desconhecida para gaita de foles",
	"Unknown clef '$1'": "Clave '$1' desconhecida",
	"Unknown decoration '$1'": "Decoração '$1' desconhecida",
	"Unknown font $1": "Fonte $1 desconhecida",
	"Unknown glyph: '$1'": "Glifo '$1' desconhecido",
	"Unknown keyword '$1' in %%multicol": "Palavra-chave '$1' desconhecida em %%multicol",
	"Unsecure code": "Código inseguro",
	"Unterminated string": "Cadeia de caracteres não terminada",
	"Unterminated string in Q:": "Cadeia de caracteres não terminada em Q:",
	"Variant ending on a left repeat bar":
				"Variante de repetição em início de ritornello",
	"Voice overlay already started": "Divisão de vozes já foi iniciada",
	"w: without music": "w: sem música",
	"Wrong duration in voice overlay":
				"Duração incorreta na divisão de vozes"
}
Changes to font/abc2svg.sfd.
︙︙
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Layer: 0 1 "Back" 1
Layer: 1 0 "Fore" 0
XUID: [1021 296 1430826605 13506888]
OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 0
CreationTime: 1433498007
ModificationTime: 1644676399
PfmFamily: 17
TTFWeight: 400
TTFWidth: 5
LineGap: 92
VLineGap: 92
Panose: 2 0 5 3 0 0 0 0 0 0
OS2TypoAscent: 0







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Layer: 0 1 "Back" 1
Layer: 1 0 "Fore" 0
XUID: [1021 296 1430826605 13506888]
OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 0
CreationTime: 1433498007
ModificationTime: 1654926804
PfmFamily: 17
TTFWeight: 400
TTFWidth: 5
LineGap: 92
VLineGap: 92
Panose: 2 0 5 3 0 0 0 0 0 0
OS2TypoAscent: 0
︙︙
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Encoding: Custom
Compacted: 1
UnicodeInterp: none
NameList: Adobe Glyph List
DisplaySize: -96
AntiAlias: 1
FitToEm: 1
WinInfo: 80 8 2
BeginPrivate: 0
EndPrivate
TeXData: 1 0 0 346030 173015 115343 0 1048576 115343 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144
BeginChars: 143 143

StartChar: .nodef
Encoding: 0 0 0







|







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Encoding: Custom
Compacted: 1
UnicodeInterp: none
NameList: Adobe Glyph List
DisplaySize: -96
AntiAlias: 1
FitToEm: 1
WinInfo: 0 8 2
BeginPrivate: 0
EndPrivate
TeXData: 1 0 0 346030 173015 115343 0 1048576 115343 783286 444596 497025 792723 393216 433062 380633 303038 157286 324010 404750 52429 2506097 1059062 262144
BeginChars: 143 143

StartChar: .nodef
Encoding: 0 0 0
︙︙
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353

354



355

356

357
358
359
360
361
362
363
364
365
366
367
368
369
370
371


372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
 203 2 167 51 167 91 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE048
Encoding: 10 57416 10
Width: 862
Flags: MW
LayerCount: 2
Fore
SplineSet
472 596 m 1
 512 596 l 1
 512 459 l 1

 625 448 715 344 724 216 c 1



 862 216 l 1

 862 176 l 1

 724 176 l 1
 715 48 625 -56 512 -67 c 1
 512 -204 l 1
 472 -204 l 1
 472 -67 l 1
 359 -56 269 48 260 176 c 1
 122 176 l 1
 122 216 l 1
 260 216 l 1
 269 348 359 448 472 459 c 1
 472 596 l 1
472 176 m 1
 360 176 l 1
 364 35 412 -16 472 -24 c 1
 472 176 l 1


512 176 m 1
 512 -24 l 1
 572 -16 620 35 624 176 c 1
 512 176 l 1
512 216 m 1
 624 216 l 1
 620 358 572 408 512 416 c 1
 512 216 l 1
472 216 m 1
 472 417 l 1
 412 409 364 361 360 216 c 1
 472 216 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE050
Encoding: 11 57424 11
Width: 779







|
|



|
|
|
>
|
>
>
>
|
>
|
>
|
<
|
|
|
<
|
|
|
<
|
<
|
|
|
>
>
|
|
|
|
|
|
<
<
<
<
|
|







339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363

364
365
366

367
368
369

370

371
372
373
374
375
376
377
378
379
380
381




382
383
384
385
386
387
388
389
390
 203 2 167 51 167 91 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE048
Encoding: 10 57416 10
Width: 954
Flags: HMW
LayerCount: 2
Fore
SplineSet
452 866 m 1
 504 866 l 1
 504 695 l 1
 653 680 772 545 784 374 c 1
 956 374 l 1
 956 322 l 1
 784 322 l 1
 772 153 653 16 504 1 c 1
 504 -170 l 1
 452 -170 l 1
 452 1 l 1
 303 16 184 153 172 322 c 1
 -0 322 l 1

 -0 374 l 1
 172 374 l 1
 184 548 303 680 452 695 c 1

 452 866 l 1
452 322 m 1
 304 322 l 1

 309 136 373 69 452 58 c 1

 452 322 l 1
504 322 m 1
 504 58 l 1
 583 69 647 134 652 322 c 1
 504 322 l 1
504 374 m 1
 652 374 l 1
 647 563 583 627 504 638 c 1
 504 374 l 1
452 374 m 1
 452 640 l 1




 373 629 309 565 304 374 c 1
 452 374 l 1
EndSplineSet
Validated: 1
EndChar

StartChar: uniE050
Encoding: 11 57424 11
Width: 779
︙︙
765
766
767
768
769
770
771
772
773
774

775
776
777
778

779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795


796
797
798
799
800


801


802
803
804
805
806

807
808


809
810
811
812
813
814
815
816
817
818
Encoding: 19 57470 19
Width: 360
Flags: HMW
LayerCount: 2
Fore
SplineSet
212 -1 m 0
 166 -1 150 22 150 54 c 0
 150 73 158 87 174 87 c 0
 189 87 201 79 201 60 c 0

 201 45 183 39 183 25 c 0
 183 13 200 11 212 11 c 0
 243 11 279 66 279 112 c 0
 279 131 272 140 249 140 c 0

 217 140 201 115 189 115 c 0
 180 115 178 118 178 130 c 0
 178 140 198 196 216 236 c 24
 219 242 224 247 231 247 c 0
 273 248 297 248 339 248 c 0
 347 248 359 250 359 241 c 0
 359 229 349 222 337 218 c 0
 300 205 276 201 239 193 c 0
 224 190 224 186 222 174 c 0
 220 164 220 157 233 161 c 0
 241 163 250 164 259 164 c 0
 305 164 326 135 326 96 c 0
 326 39 269 -1 212 -1 c 0
9 0 m 0
 -2.48824948263 1.68260667765e-07 -3.48824945257 18.0000001683 9 18 c 0
 12 17.9999999596 14 18 18 18 c 0
 27 18 39 19 43 30 c 0


 62 75 65 84 84 134 c 0
 89 147 90 155 78 155 c 0
 69 155 66 152 61 146 c 0
 54 138 46 133 36 138 c 0
 25 144 34 153 41 162 c 0


 76 207 112 259 125 259 c 0


 140 259 150 246 165 246 c 0
 168 246 172 247 175 248 c 0
 191 253 196 249 192 238 c 0
 155 148 122 67 111 36 c 0
 109.509271996 31.8259615875 108.551888787 28.5408311683 108.551888787 25.9855943378 c 0

 108.551888787 19.9703983902 113.857348821 18 130 18 c 4
 137.838821814 18 142.400463498 13.0842298053 142.400463498 8.39053563394 c 0


 142.400463498 4.10259784076 138.593413823 -8.881784197e-16 130 0 c 0
 95.3333333333 0 76.6666666667 -3.45002388166e-07 52.3703703704 -3.45002388166e-07 c 0
 40.2222222222 -3.45002388166e-07 26.6666666667 -2.58751791125e-07 9 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE080
Encoding: 20 57472 20
Width: 430







|
|
|
>
|
|

|
>
|


|
|
|
|
|
|
|

|
|

|
|
|
>
>
|
|
|
|
|
>
>
|
>
>
|
|
|
|
<
>
|
|
>
>
|
<
|







765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813

814
815
816
817
818
819

820
821
822
823
824
825
826
827
Encoding: 19 57470 19
Width: 360
Flags: HMW
LayerCount: 2
Fore
SplineSet
212 -1 m 0
 171 -1 150 17 150 54 c 0
 150 75 159 87 174 87 c 0
 191 87 201 77 201 60 c 0
 201 53 196 47 192 42 c 0
 188 37 183 32 183 25 c 0
 183 16 193 11 212 11 c 0
 243 11 279 66 279 112 c 0
 279 132 271 140 249 140 c 0
 238 140 229 134 219 128 c 0
 209 122 200 115 189 115 c 0
 180 115 178 118 178 130 c 0
 178 140 198 196 216 236 c 24
 220 243 225 247 231 247 c 0
 266 248 284 248 339 248 c 0
 358 248 359 248 359 241 c 0
 359 231 352 223 337 218 c 0
 309 208 308 208 239 193 c 0
 226 190 224 189 222 174 c 0
 219 161 222 158 233 161 c 0
 241 163 250 164 259 164 c 0
 301 164 326 139 326 96 c 0
 326 43 274 -1 212 -1 c 0
9 0 m 0
 -3 0 -3 18 9 18 c 0
 12 18 14 18 18 18 c 0
 32 18 40 22 43 30 c 0
 44 31 44 33 45 34 c 0
 58 65 58 65 81 126 c 0
 82 129 83 131 84 134 c 0
 90 150 88 155 78 155 c 0
 70 155 67 153 61 146 c 0
 52 136 45 134 36 138 c 0
 28 143 28 146 41 162 c 0
 51 174 59 186 66 195 c 0
 111 253 116 259 120 259 c 0
 121 259 121 259 122 259 c 0
 123 259 124 259 125 259 c 0
 133 259 139 255 145 252 c 0
 151 249 157 246 165 246 c 0
 168 246 171 247 175 248 c 0
 190 253 196 249 192 238 c 0
 144 121 120 62 111 36 c 0

 109 31 109 28 109 26 c 0
 109 20 114 18 130 18 c 0
 137 18 142 13 142 8 c 0
 142 3 138 0 130 0 c 0
 115 0 103 0 91 -0 c 0
 79 -0 67 0 52 -0 c 0

 38 0 25 0 9 0 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE080
Encoding: 20 57472 20
Width: 430
︙︙
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526

2527
2528
2529
2530
2531
2532
2533
Width: 400
Flags: HMW
LayerCount: 2
Fore
SplineSet
152 35 m 2
 357 483 l 2
 363.701063819 494.913002345 369.847754103 501.282269343 377.916665809 501.282269343 c 0
 380.684870018 501.282269343 383.679314405 500.532624121 387 499 c 0
 394.855750978 495.072124511 397.988581796 490.19966499 397.988581796 483.852591655 c 0
 397.988581796 478.888330538 396.072124511 473.021998882 393 466 c 2
 189 17 l 2
 183.913450587 6.82690117401 177.770860973 -0.178077048204 169.421173872 -0.178077048204 c 0
 166.280201006 -0.178077048204 162.826901174 0.813199329137 159 3 c 0
 151.569978236 7.33417936247 147.973652542 13.5852067603 147.973652542 20.8036006853 c 0
 147.973652542 25.2434131355 149.334179362 30.0491902446 152 35 c 2
9 35 m 2
 214 483 l 2
 220.701063819 494.913002345 226.847754103 501.282269343 234.916665809 501.282269343 c 0
 237.684870018 501.282269343 240.679314405 500.532624121 244 499 c 0
 251.855750978 495.072124511 254.988581796 490.19966499 254.988581796 483.852591655 c 0
 254.988581796 478.888330538 253.072124511 473.021998882 250 466 c 2
 46 17 l 2
 40.913450587 6.82690117401 34.7708609728 -0.178077048204 26.4211738717 -0.178077048204 c 0
 23.2802010063 -0.178077048204 19.826901174 0.813199329137 16 3 c 0
 8.56997823576 7.33417936247 4.97365254215 13.5852067603 4.97365254215 20.8036006853 c 0
 4.97365254215 25.2434131355 6.33417936247 30.0491902446 9 35 c 2
EndSplineSet

EndChar

StartChar: uniE4E1
Encoding: 83 58593 83
Width: 130
Flags: HMW
LayerCount: 2







|
|
|
|

|
|
|
|


|
|
|
|

|
|
|
|

>







2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
Width: 400
Flags: HMW
LayerCount: 2
Fore
SplineSet
152 35 m 2
 357 483 l 2
 364 495 370 501 378 501 c 0
 381 501 384 501 387 499 c 0
 395 495 398 490 398 484 c 0
 398 479 396 473 393 466 c 2
 189 17 l 2
 184 7 178 -0 169 -0 c 0
 166 -0 163 1 159 3 c 0
 152 7 148 14 148 21 c 0
 148 25 149 30 152 35 c 2
9 35 m 2
 214 483 l 2
 221 495 227 501 235 501 c 0
 238 501 241 501 244 499 c 0
 252 495 255 490 255 484 c 0
 255 479 253 473 250 466 c 2
 46 17 l 2
 41 7 35 -0 26 -0 c 0
 23 -0 20 1 16 3 c 0
 9 7 5 14 5 21 c 0
 5 25 6 30 9 35 c 2
EndSplineSet
Validated: 1
EndChar

StartChar: uniE4E1
Encoding: 83 58593 83
Width: 130
Flags: HMW
LayerCount: 2
︙︙
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
Encoding: 101 58665 101
Width: 703
Flags: HMW
LayerCount: 2
Fore
SplineSet
522 40 m 0
 524.179123831 39.0525548563 526.555731167 38.5988184777 529.0907001 38.5988184777 c 0
 553.311569742 38.5988184777 591.989634802 80.0214592618 611 128 c 0
 618.801273837 147.763227053 623.897664408 174.829638922 623.897664408 197.52305288 c 0
 623.897664408 218.46369363 619.558131706 235.680764302 609 240 c 0
 607.059714009 240.759242344 604.934395026 241.11995209 602.659464452 241.11995209 c 0
 577.96754404 241.11995209 535.650235832 198.625589581 521 162 c 0
 510.665333312 135.693575703 506.068143327 108.945807045 506.068143327 87.1471403297 c 0
 506.068143327 62.5417301443 511.925393958 44.2419393862 522 40 c 0
197 40 m 0
 199.179123831 39.0525548563 201.555731167 38.5988184777 204.0907001 38.5988184777 c 0
 228.311569742 38.5988184777 266.989634802 80.0214592618 286 128 c 0
 293.801273837 147.763227053 298.897664408 174.829638922 298.897664408 197.52305288 c 0
 298.897664408 218.46369363 294.558131706 235.680764302 284 240 c 0
 282.059714009 240.759242344 279.934395026 241.11995209 277.659464452 241.11995209 c 0
 252.96754404 241.11995209 210.650235832 198.625589581 196 162 c 0
 185.665333312 135.693575703 181.068143327 108.945807045 181.068143327 87.1471403297 c 0
 181.068143327 62.5417301443 186.925393958 44.2419393862 197 40 c 0
200 0 m 0
 183 0 173 6 153 32 c 1
 108 -90 l 1
 160 -90 l 2
 175 -90 175 -120 160 -120 c 2
 -65 -120 l 2
 -79 -120 -80 -90 -65 -90 c 2
 10 -90 l 1
 114 195 l 2
 115.167116963 197.917792406 120.420719938 212.754502351 120.420719938 224.605732511 c 0
 120.420719938 233.06305846 117.745271893 240 109 240 c 0
 83 240 72 207 28 135 c 0
 21 124 -6 139 1 151 c 0
 56 249 69 280 125 280 c 0
 170 280 193 271 203 235 c 1
 244 278 263 280 305 280 c 0
 331 280 358 264 371 230 c 1
 395 267 413 280 450 280 c 0







|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|









|
|







3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
Encoding: 101 58665 101
Width: 703
Flags: HMW
LayerCount: 2
Fore
SplineSet
522 40 m 0
 524 39 526 39 529 39 c 0
 553 39 592 80 611 128 c 0
 619 148 624 175 624 198 c 0
 624 219 620 236 609 240 c 0
 607 241 605 241 603 241 c 0
 578 241 536 199 521 162 c 0
 511 136 506 109 506 87 c 0
 506 62 512 44 522 40 c 0
197 40 m 0
 199 39 201 39 204 39 c 0
 228 39 267 80 286 128 c 0
 294 148 299 175 299 198 c 0
 299 219 295 236 284 240 c 0
 282 241 280 241 278 241 c 0
 253 241 211 199 196 162 c 0
 186 136 181 109 181 87 c 0
 181 62 187 44 197 40 c 0
200 0 m 0
 183 0 173 6 153 32 c 1
 108 -90 l 1
 160 -90 l 2
 175 -90 175 -120 160 -120 c 2
 -65 -120 l 2
 -79 -120 -80 -90 -65 -90 c 2
 10 -90 l 1
 114 195 l 2
 115 198 120 213 120 225 c 0
 120 233 118 240 109 240 c 0
 83 240 72 207 28 135 c 0
 21 124 -6 139 1 151 c 0
 56 249 69 280 125 280 c 0
 170 280 193 271 203 235 c 1
 244 278 263 280 305 280 c 0
 331 280 358 264 371 230 c 1
 395 267 413 280 450 280 c 0
︙︙
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
 1083 -90 l 1
 1135 -90 l 2
 1150 -90 1150 -120 1135 -120 c 2
 910 -120 l 2
 896 -120 895 -90 910 -90 c 2
 985 -90 l 1
 1089 195 l 2
 1090.16711696 197.917792406 1095.42071994 212.754502351 1095.42071994 224.605732511 c 0
 1095.42071994 233.06305846 1092.74527189 240 1084 240 c 0
 1064 240 1053 221 1030 181 c 0
 1030 181 1030 180 1030 180 c 0
 1030 87 949 0 850 0 c 0
 833 0 823 6 803 32 c 1
 758 -90 l 1
 810 -90 l 2
 825 -90 825 -120 810 -120 c 2
 585 -120 l 2
 571 -120 570 -90 585 -90 c 2
 660 -90 l 1
 764 195 l 2
 765.167116963 197.917792406 770.420719938 212.754502351 770.420719938 224.605732511 c 0
 770.420719938 233.06305846 767.745271893 240 759 240 c 0
 739 240 728 221 705 181 c 0
 705 181 705 180 705 180 c 0
 705 87 624 0 525 0 c 0
 508 0 498 6 478 32 c 1
 433 -90 l 1
 485 -90 l 2
 500 -90 500 -120 485 -120 c 2
 260 -120 l 2
 246 -120 245 -90 260 -90 c 2
 335 -90 l 1
 439 195 l 2
 440.167116963 197.917792406 445.420719938 212.754502351 445.420719938 224.605732511 c 0
 445.420719938 233.06305846 442.745271893 240 434 240 c 0
 414 240 403 221 380 181 c 0
 380 181 380 180 380 180 c 0
 380 87 299 0 200 0 c 0
1172 40 m 0
 1174.17912383 39.0525548563 1176.55573117 38.5988184777 1179.0907001 38.5988184777 c 0
 1203.31156974 38.5988184777 1241.9896348 80.0214592618 1261 128 c 0
 1268.80127384 147.763227053 1273.89766441 174.829638922 1273.89766441 197.52305288 c 0
 1273.89766441 218.46369363 1269.55813171 235.680764302 1259 240 c 0
 1257.05971401 240.759242344 1254.93439503 241.11995209 1252.65946445 241.11995209 c 0
 1227.96754404 241.11995209 1185.65023583 198.625589581 1171 162 c 0
 1160.66533331 135.693575703 1156.06814333 108.945807045 1156.06814333 87.1471403297 c 0
 1156.06814333 62.5417301443 1161.92539396 44.2419393862 1172 40 c 0
847 40 m 0
 849.179123831 39.0525548563 851.555731167 38.5988184777 854.0907001 38.5988184777 c 0
 878.311569742 38.5988184777 916.989634802 80.0214592618 936 128 c 0
 943.801273837 147.763227053 948.897664408 174.829638922 948.897664408 197.52305288 c 0
 948.897664408 218.46369363 944.558131706 235.680764302 934 240 c 0
 932.059714009 240.759242344 929.934395026 241.11995209 927.659464452 241.11995209 c 0
 902.96754404 241.11995209 860.650235832 198.625589581 846 162 c 0
 835.665333312 135.693575703 831.068143327 108.945807045 831.068143327 87.1471403297 c 0
 831.068143327 62.5417301443 836.925393958 44.2419393862 847 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52A
Encoding: 102 58666 102
Width: 563
Flags: W
HStem: -120 30<-74.2249 10 108 169.668 250.775 335 433 494.668 575.775 660 758 819.668> 0 38.5988<191.014 237.319 516.014 562.319 841.014 887.319> 240 40<80.1149 147.5 239.117 318 404.386 472.5 564.117 643 729.386 797.5 889.117 973>
VStem: 120.421 60.6474<87.1471 224.606> 298.898 81.1023<108.679 217.246> 445.421 60.6474<87.1471 224.606> 623.898 81.1023<108.679 217.246> 770.421 60.6474<87.1471 224.606> 948.898 81.1023<108.462 239.669>
LayerCount: 2
Fore
SplineSet
850 0 m 0
 833 0 823 6 803 32 c 1
 758 -90 l 1
 810 -90 l 2
 825 -90 825 -120 810 -120 c 2
 585 -120 l 2
 571 -120 570 -90 585 -90 c 2
 660 -90 l 1
 764 195 l 2
 765.167116963 197.917792406 770.420719938 212.754502351 770.420719938 224.605732511 c 0
 770.420719938 233.06305846 767.745271893 240 759 240 c 0
 739 240 728 221 705 181 c 0
 705 181 705 180 705 180 c 0
 705 87 624 0 525 0 c 0
 508 0 498 6 478 32 c 1
 433 -90 l 1
 485 -90 l 2
 500 -90 500 -120 485 -120 c 2
 260 -120 l 2
 246 -120 245 -90 260 -90 c 2
 335 -90 l 1
 439 195 l 2
 440.167116963 197.917792406 445.420719938 212.754502351 445.420719938 224.605732511 c 0
 445.420719938 233.06305846 442.745271893 240 434 240 c 0
 414 240 403 221 380 181 c 0
 380 181 380 180 380 180 c 0
 380 87 299 0 200 0 c 0
 183 0 173 6 153 32 c 1
 108 -90 l 1
 160 -90 l 2
 175 -90 175 -120 160 -120 c 2
 -65 -120 l 2
 -79 -120 -80 -90 -65 -90 c 2
 10 -90 l 1
 114 195 l 2
 115.167116963 197.917792406 120.420719938 212.754502351 120.420719938 224.605732511 c 0
 120.420719938 233.06305846 117.745271893 240 109 240 c 0
 83 240 72 207 28 135 c 0
 21 124 -6 139 1 151 c 0
 56 249 69 280 125 280 c 0
 170 280 193 271 203 235 c 1
 244 278 263 280 305 280 c 0
 331 280 358 264 371 230 c 1
 395 267 413 280 450 280 c 0
 495 280 518 271 528 235 c 1
 569 278 588 280 630 280 c 0
 656 280 683 264 696 230 c 1
 720 267 738 280 775 280 c 0
 820 280 843 271 853 235 c 1
 894 278 913 280 955 280 c 0
 991 280 1030 248 1030 180 c 0
 1030 87 949 0 850 0 c 0
847 40 m 0
 849.179123831 39.0525548563 851.555731167 38.5988184777 854.0907001 38.5988184777 c 0
 878.311569742 38.5988184777 916.989634802 80.0214592618 936 128 c 0
 943.801273837 147.763227053 948.897664408 174.829638922 948.897664408 197.52305288 c 0
 948.897664408 218.46369363 944.558131706 235.680764302 934 240 c 0
 932.059714009 240.759242344 929.934395026 241.11995209 927.659464452 241.11995209 c 0
 902.96754404 241.11995209 860.650235832 198.625589581 846 162 c 0
 835.665333312 135.693575703 831.068143327 108.945807045 831.068143327 87.1471403297 c 0
 831.068143327 62.5417301443 836.925393958 44.2419393862 847 40 c 0
522 40 m 0
 524.179123831 39.0525548563 526.555731167 38.5988184777 529.0907001 38.5988184777 c 0
 553.311569742 38.5988184777 591.989634802 80.0214592618 611 128 c 0
 618.801273837 147.763227053 623.897664408 174.829638922 623.897664408 197.52305288 c 0
 623.897664408 218.46369363 619.558131706 235.680764302 609 240 c 0
 607.059714009 240.759242344 604.934395026 241.11995209 602.659464452 241.11995209 c 0
 577.96754404 241.11995209 535.650235832 198.625589581 521 162 c 0
 510.665333312 135.693575703 506.068143327 108.945807045 506.068143327 87.1471403297 c 0
 506.068143327 62.5417301443 511.925393958 44.2419393862 522 40 c 0
197 40 m 0
 199.179123831 39.0525548563 201.555731167 38.5988184777 204.0907001 38.5988184777 c 0
 228.311569742 38.5988184777 266.989634802 80.0214592618 286 128 c 0
 293.801273837 147.763227053 298.897664408 174.829638922 298.897664408 197.52305288 c 0
 298.897664408 218.46369363 294.558131706 235.680764302 284 240 c 0
 282.059714009 240.759242344 279.934395026 241.11995209 277.659464452 241.11995209 c 0
 252.96754404 241.11995209 210.650235832 198.625589581 196 162 c 0
 185.665333312 135.693575703 181.068143327 108.945807045 181.068143327 87.1471403297 c 0
 181.068143327 62.5417301443 186.925393958 44.2419393862 197 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52B
Encoding: 103 58667 103
Width: 703
Flags: HMW
LayerCount: 2
Fore
SplineSet
200 0 m 0
 183 0 173 6 153 32 c 1
 108 -90 l 1
 160 -90 l 2
 175 -90 175 -120 160 -120 c 2
 -65 -120 l 2
 -79 -120 -80 -90 -65 -90 c 2
 10 -90 l 1
 114 195 l 2
 115.167116963 197.917792406 120.420719938 212.754502351 120.420719938 224.605732511 c 0
 120.420719938 233.06305846 117.745271893 240 109 240 c 0
 83 240 72 207 28 135 c 0
 21 124 -6 139 1 151 c 0
 56 249 69 280 125 280 c 0
 170 280 193 271 203 235 c 1
 244 278 263 280 305 280 c 0
 331 280 358 264 371 230 c 1
 395 267 413 280 450 280 c 0
 495 280 518 271 528 235 c 1
 569 278 588 280 630 280 c 0
 666 280 705 248 705 180 c 0
 705 87 624 0 525 0 c 0
 508 0 498 6 478 32 c 1
 433 -90 l 1
 485 -90 l 2
 500 -90 500 -120 485 -120 c 2
 260 -120 l 2
 246 -120 245 -90 260 -90 c 2
 335 -90 l 1
 439 195 l 2
 440.167116963 197.917792406 445.420719938 212.754502351 445.420719938 224.605732511 c 0
 445.420719938 233.06305846 442.745271893 240 434 240 c 0
 414 240 403 221 380 181 c 0
 380 181 380 180 380 180 c 0
 380 87 299 0 200 0 c 0
197 40 m 0
 199.179123831 39.0525548563 201.555731167 38.5988184777 204.0907001 38.5988184777 c 0
 228.311569742 38.5988184777 266.989634802 80.0214592618 286 128 c 0
 293.801273837 147.763227053 298.897664408 174.829638922 298.897664408 197.52305288 c 0
 298.897664408 218.46369363 294.558131706 235.680764302 284 240 c 0
 282.059714009 240.759242344 279.934395026 241.11995209 277.659464452 241.11995209 c 0
 252.96754404 241.11995209 210.650235832 198.625589581 196 162 c 0
 185.665333312 135.693575703 181.068143327 108.945807045 181.068143327 87.1471403297 c 0
 181.068143327 62.5417301443 186.925393958 44.2419393862 197 40 c 0
522 40 m 0
 524.179123831 39.0525548563 526.555731167 38.5988184777 529.0907001 38.5988184777 c 0
 553.311569742 38.5988184777 591.989634802 80.0214592618 611 128 c 0
 618.801273837 147.763227053 623.897664408 174.829638922 623.897664408 197.52305288 c 0
 623.897664408 218.46369363 619.558131706 235.680764302 609 240 c 0
 607.059714009 240.759242344 604.934395026 241.11995209 602.659464452 241.11995209 c 0
 577.96754404 241.11995209 535.650235832 198.625589581 521 162 c 0
 510.665333312 135.693575703 506.068143327 108.945807045 506.068143327 87.1471403297 c 0
 506.068143327 62.5417301443 511.925393958 44.2419393862 522 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52C
Encoding: 104 58668 104
Width: 811







|
|

|









|
|

|









|
|

|


|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|








|
|












|
|

|









|
|

|









|
|
















|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|




















|
|



















|
|

|


|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|







3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
 1083 -90 l 1
 1135 -90 l 2
 1150 -90 1150 -120 1135 -120 c 2
 910 -120 l 2
 896 -120 895 -90 910 -90 c 2
 985 -90 l 1
 1089 195 l 2
 1090 198 1095 213 1095 225 c 0
 1095 233 1093 240 1084 240 c 0
 1064 240 1053 221 1030 181 c 0
 1030 180 l 0
 1030 87 949 0 850 0 c 0
 833 0 823 6 803 32 c 1
 758 -90 l 1
 810 -90 l 2
 825 -90 825 -120 810 -120 c 2
 585 -120 l 2
 571 -120 570 -90 585 -90 c 2
 660 -90 l 1
 764 195 l 2
 765 198 770 213 770 225 c 0
 770 233 768 240 759 240 c 0
 739 240 728 221 705 181 c 0
 705 180 l 0
 705 87 624 0 525 0 c 0
 508 0 498 6 478 32 c 1
 433 -90 l 1
 485 -90 l 2
 500 -90 500 -120 485 -120 c 2
 260 -120 l 2
 246 -120 245 -90 260 -90 c 2
 335 -90 l 1
 439 195 l 2
 440 198 445 213 445 225 c 0
 445 233 443 240 434 240 c 0
 414 240 403 221 380 181 c 0
 380 180 l 0
 380 87 299 0 200 0 c 0
1172 40 m 0
 1174 39 1176 39 1179 39 c 0
 1203 39 1242 80 1261 128 c 0
 1269 148 1274 175 1274 198 c 0
 1274 219 1270 236 1259 240 c 0
 1257 241 1255 241 1253 241 c 0
 1228 241 1186 199 1171 162 c 0
 1161 136 1156 109 1156 87 c 0
 1156 62 1162 44 1172 40 c 0
847 40 m 0
 849 39 851 39 854 39 c 0
 878 39 917 80 936 128 c 0
 944 148 949 175 949 198 c 0
 949 219 945 236 934 240 c 0
 932 241 930 241 928 241 c 0
 903 241 861 199 846 162 c 0
 836 136 831 109 831 87 c 0
 831 62 837 44 847 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52A
Encoding: 102 58666 102
Width: 563
Flags: W
HStem: -120 30<-74.2249 10 108 169.668 250.775 335 433 494.668 575.775 660 758 819.668> 0 39<191.01 237.957 516.01 562.957 841.01 887.957> 240 40<80.1149 147.5 239.21 318 404.386 472.5 564.21 643 729.386 797.5 889.21 973>
VStem: 120 61<87 225> 299 81<108.679 217.246> 445 61<87 225> 624 81<108.679 217.246> 770 61<87 225> 949 81<108.469 239.676>
LayerCount: 2
Fore
SplineSet
850 0 m 0
 833 0 823 6 803 32 c 1
 758 -90 l 1
 810 -90 l 2
 825 -90 825 -120 810 -120 c 2
 585 -120 l 2
 571 -120 570 -90 585 -90 c 2
 660 -90 l 1
 764 195 l 2
 765 198 770 213 770 225 c 0
 770 233 768 240 759 240 c 0
 739 240 728 221 705 181 c 0
 705 180 l 0
 705 87 624 0 525 0 c 0
 508 0 498 6 478 32 c 1
 433 -90 l 1
 485 -90 l 2
 500 -90 500 -120 485 -120 c 2
 260 -120 l 2
 246 -120 245 -90 260 -90 c 2
 335 -90 l 1
 439 195 l 2
 440 198 445 213 445 225 c 0
 445 233 443 240 434 240 c 0
 414 240 403 221 380 181 c 0
 380 180 l 0
 380 87 299 0 200 0 c 0
 183 0 173 6 153 32 c 1
 108 -90 l 1
 160 -90 l 2
 175 -90 175 -120 160 -120 c 2
 -65 -120 l 2
 -79 -120 -80 -90 -65 -90 c 2
 10 -90 l 1
 114 195 l 2
 115 198 120 213 120 225 c 0
 120 233 118 240 109 240 c 0
 83 240 72 207 28 135 c 0
 21 124 -6 139 1 151 c 0
 56 249 69 280 125 280 c 0
 170 280 193 271 203 235 c 1
 244 278 263 280 305 280 c 0
 331 280 358 264 371 230 c 1
 395 267 413 280 450 280 c 0
 495 280 518 271 528 235 c 1
 569 278 588 280 630 280 c 0
 656 280 683 264 696 230 c 1
 720 267 738 280 775 280 c 0
 820 280 843 271 853 235 c 1
 894 278 913 280 955 280 c 0
 991 280 1030 248 1030 180 c 0
 1030 87 949 0 850 0 c 0
847 40 m 0
 849 39 851 39 854 39 c 0
 878 39 917 80 936 128 c 0
 944 148 949 175 949 198 c 0
 949 219 945 236 934 240 c 0
 932 241 930 241 928 241 c 0
 903 241 861 199 846 162 c 0
 836 136 831 109 831 87 c 0
 831 62 837 44 847 40 c 0
522 40 m 0
 524 39 526 39 529 39 c 0
 553 39 592 80 611 128 c 0
 619 148 624 175 624 198 c 0
 624 219 620 236 609 240 c 0
 607 241 605 241 603 241 c 0
 578 241 536 199 521 162 c 0
 511 136 506 109 506 87 c 0
 506 62 512 44 522 40 c 0
197 40 m 0
 199 39 201 39 204 39 c 0
 228 39 267 80 286 128 c 0
 294 148 299 175 299 198 c 0
 299 219 295 236 284 240 c 0
 282 241 280 241 278 241 c 0
 253 241 211 199 196 162 c 0
 186 136 181 109 181 87 c 0
 181 62 187 44 197 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52B
Encoding: 103 58667 103
Width: 703
Flags: HMW
LayerCount: 2
Fore
SplineSet
200 0 m 0
 183 0 173 6 153 32 c 1
 108 -90 l 1
 160 -90 l 2
 175 -90 175 -120 160 -120 c 2
 -65 -120 l 2
 -79 -120 -80 -90 -65 -90 c 2
 10 -90 l 1
 114 195 l 2
 115 198 120 213 120 225 c 0
 120 233 118 240 109 240 c 0
 83 240 72 207 28 135 c 0
 21 124 -6 139 1 151 c 0
 56 249 69 280 125 280 c 0
 170 280 193 271 203 235 c 1
 244 278 263 280 305 280 c 0
 331 280 358 264 371 230 c 1
 395 267 413 280 450 280 c 0
 495 280 518 271 528 235 c 1
 569 278 588 280 630 280 c 0
 666 280 705 248 705 180 c 0
 705 87 624 0 525 0 c 0
 508 0 498 6 478 32 c 1
 433 -90 l 1
 485 -90 l 2
 500 -90 500 -120 485 -120 c 2
 260 -120 l 2
 246 -120 245 -90 260 -90 c 2
 335 -90 l 1
 439 195 l 2
 440 198 445 213 445 225 c 0
 445 233 443 240 434 240 c 0
 414 240 403 221 380 181 c 0
 380 180 l 0
 380 87 299 0 200 0 c 0
197 40 m 0
 199 39 201 39 204 39 c 0
 228 39 267 80 286 128 c 0
 294 148 299 175 299 198 c 0
 299 219 295 236 284 240 c 0
 282 241 280 241 278 241 c 0
 253 241 211 199 196 162 c 0
 186 136 181 109 181 87 c 0
 181 62 187 44 197 40 c 0
522 40 m 0
 524 39 526 39 529 39 c 0
 553 39 592 80 611 128 c 0
 619 148 624 175 624 198 c 0
 624 219 620 236 609 240 c 0
 607 241 605 241 603 241 c 0
 578 241 536 199 521 162 c 0
 511 136 506 109 506 87 c 0
 506 62 512 44 522 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52C
Encoding: 104 58668 104
Width: 811
︙︙
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
 539 -90 l 1
 591 -90 l 2
 606 -90 606 -120 591 -120 c 2
 366 -120 l 2
 352 -120 351 -90 366 -90 c 2
 441 -90 l 1
 545 195 l 2
 546.167116963 197.917792406 551.420719938 212.754502351 551.420719938 224.605732511 c 0
 551.420719938 233.06305846 548.745271893 240 540 240 c 0
 514 240 491 175 454 100 c 0
 423 37 388 -10 323 -10 c 0
 295.96461562 -10 276.685848474 4.31990873934 276.685848474 30.6552962356 c 0
 276.685848474 38.4136929968 278.359032859 47.2149008096 282 57 c 2
 334 193 l 2
 334.687448544 195.062345631 335.007330587 196.961328373 335.007330587 198.698285174 c 0
 335.007330587 208.224115412 325.386390673 212.876768553 314.009865299 212.876768553 c 0
 300.302784706 212.876768553 284.047157224 206.122608782 279 193 c 2
 214 22 l 2
 211 14 207 0 191 -1 c 2
 153 0 l 2
 134 -2 132 12 136 22 c 2
 203 193 l 2
 203.698722351 195.018531237 204.023750859 196.886379473 204.023750859 198.6021409 c 0
 204.023750859 208.040661137 194.187819777 212.876768553 182.617370253 212.876768553 c 0
 169.199127766 212.876768553 153.448127054 206.372675496 148 193 c 2
 80 21 l 2
 76 11 74 1 57 0 c 2
 4 0 l 2
 -6 0 0 18 4 26 c 2
 70 193 l 2
 77 211 69 221 60 221 c 0
 42 221 35 210 -11 138 c 0
 -18 127 -39 139 -33 151 c 0
 3 216 36 280 86 280 c 0
 128 280 145 266 152 240 c 1
 170 260 194 280 224 280 c 0
 254 280 273 263 278 240 c 1
 292 259 322 280 349 280 c 0
 382.297060451 280 409.658220776 253.618221661 409.658220776 218.530504648 c 0
 409.658220776 210.41331474 408.193901764 201.830198995 405 193 c 2
 358 68 l 2
 355.211869734 60.3326417674 353.881302664 53.2726015335 353.881302664 47.6241881024 c 0
 353.881302664 39.5840041338 356.577277218 34.4040367054 361.602949871 34.4040367054 c 0
 363.177144158 34.4040367054 364.979913018 34.9122608557 367 36 c 0
 389 48 418 94 429 119 c 0
628 40 m 0
 630.179123831 39.0525548563 632.555731167 38.5988184777 635.0907001 38.5988184777 c 0
 659.311569742 38.5988184777 697.989634802 80.0214592618 717 128 c 0
 724.801273837 147.763227053 729.897664408 174.829638922 729.897664408 197.52305288 c 0
 729.897664408 218.46369363 725.558131706 235.680764302 715 240 c 0
 713.059714009 240.759242344 710.934395026 241.11995209 708.659464452 241.11995209 c 0
 683.96754404 241.11995209 641.650235832 198.625589581 627 162 c 0
 616.665333312 135.693575703 612.068143327 108.945807045 612.068143327 87.1471403297 c 0
 612.068143327 62.5417301443 617.925393958 44.2419393862 628 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52D
Encoding: 105 58669 105
Width: 351







|
|


|
|

|
|
|





|
|
|













|
|

|
|
|


|
|
|
|
|
|
|
|







3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
 539 -90 l 1
 591 -90 l 2
 606 -90 606 -120 591 -120 c 2
 366 -120 l 2
 352 -120 351 -90 366 -90 c 2
 441 -90 l 1
 545 195 l 2
 546 198 551 213 551 225 c 0
 551 233 549 240 540 240 c 0
 514 240 491 175 454 100 c 0
 423 37 388 -10 323 -10 c 0
 296 -10 277 5 277 31 c 0
 277 39 278 47 282 57 c 2
 334 193 l 2
 335 195 335 197 335 199 c 0
 335 209 325 213 314 213 c 0
 300 213 284 206 279 193 c 2
 214 22 l 2
 211 14 207 0 191 -1 c 2
 153 0 l 2
 134 -2 132 12 136 22 c 2
 203 193 l 2
 204 195 204 197 204 199 c 0
 204 208 195 213 183 213 c 0
 170 213 153 206 148 193 c 2
 80 21 l 2
 76 11 74 1 57 0 c 2
 4 0 l 2
 -6 0 0 18 4 26 c 2
 70 193 l 2
 77 211 69 221 60 221 c 0
 42 221 35 210 -11 138 c 0
 -18 127 -39 139 -33 151 c 0
 3 216 36 280 86 280 c 0
 128 280 145 266 152 240 c 1
 170 260 194 280 224 280 c 0
 254 280 273 263 278 240 c 1
 292 259 322 280 349 280 c 0
 382 280 410 254 410 219 c 0
 410 211 408 202 405 193 c 2
 358 68 l 2
 355 60 354 54 354 48 c 0
 354 40 357 34 362 34 c 0
 364 34 365 35 367 36 c 0
 389 48 418 94 429 119 c 0
628 40 m 0
 630 39 632 39 635 39 c 0
 659 39 698 80 717 128 c 0
 725 148 730 175 730 198 c 0
 730 219 726 236 715 240 c 0
 713 241 711 241 709 241 c 0
 684 241 642 199 627 162 c 0
 617 136 612 109 612 87 c 0
 612 62 618 44 628 40 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52D
Encoding: 105 58669 105
Width: 351
︙︙
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
 42 221 35 210 -11 138 c 0
 -18 127 -39 139 -33 151 c 0
 3 216 36 280 86 280 c 0
 128 280 145 266 152 240 c 1
 170 260 194 280 224 280 c 0
 254 280 273 263 278 240 c 1
 292 259 322 280 349 280 c 0
 382.297060451 280 409.658220776 253.618221661 409.658220776 218.530504648 c 0
 409.658220776 210.41331474 408.193901764 201.830198995 405 193 c 0
 358 68 l 0
 355.211869734 60.3326417674 353.881302664 53.2726015335 353.881302664 47.6241881024 c 0
 353.881302664 39.5840041338 356.577277218 34.4040367054 361.602949871 34.4040367054 c 0
 363.177144158 34.4040367054 364.979913018 34.9122608557 367 36 c 0
 389 48 404 72 419 93 c 0
 427 104 449 93 440 78 c 0
 411 27 388 -10 323 -10 c 0
 295.96461562 -10 276.685848474 4.31990873934 276.685848474 30.6552962356 c 0
 276.685848474 38.4136929968 278.359032859 47.2149008096 282 57 c 0
 334 193 l 0
 334.687448544 195.062345631 335.007330587 196.961328373 335.007330587 198.698285174 c 0
 335.007330587 208.224115412 325.386390673 212.876768553 314.009865299 212.876768553 c 0
 300.302784706 212.876768553 284.047157224 206.122608782 279 193 c 0
 214 22 l 0
 211 14 207 0 191 -1 c 1
 153 0 l 16
 134 -2 132 12 136 22 c 0
 203 193 l 0
 203.698722351 195.018531237 204.023750859 196.886379473 204.023750859 198.6021409 c 0
 204.023750859 208.040661137 194.187819777 212.876768553 182.617370253 212.876768553 c 0
 169.199127766 212.876768553 153.448127054 206.372675496 148 193 c 0
 80 21 l 0
393 -160 m 0
 353 -160 313 -135 313 -90 c 0
 313 -55 330 -30 355 -30 c 0
 380 -30 396 -45 396 -65 c 0
 396 -75 389 -83 378 -90 c 0
 368 -97 368 -100 368 -110 c 0
 368 -120 378 -124 393 -124 c 0
 456 -124 459 -35 526 230 c 1
 507 230 508 230 473 230 c 0
 460.239470207 230 453.992185238 240.421191718 453.992185238 250.625180963 c 0
 453.992185238 260.412503619 459.73968741 270 471 270 c 0
 502 270 504 270 536 270 c 1
 564 374 647 440 713 440 c 0
 753 440 793 415 793 370 c 0
 793 335 776 310 751 310 c 0
 726 310 710 325 710 345 c 0
 710 355 717 363 728 370 c 0
 739 377 738 380 738 390 c 0
 738 400 728 404 713 404 c 0
 671 404 661 332 648 270 c 1
 675 270 672 270 702 270 c 0
 715 270 721.5 260 721.5 250 c 0
 721.5 240 715 230 702 230 c 0
 676 230 672 230 639 230 c 1
 596 -26 523 -160 393 -160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52F







|
|

|
|
|



|
|

|
|
|





|
|
|










|
|










|
|







3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
 42 221 35 210 -11 138 c 0
 -18 127 -39 139 -33 151 c 0
 3 216 36 280 86 280 c 0
 128 280 145 266 152 240 c 1
 170 260 194 280 224 280 c 0
 254 280 273 263 278 240 c 1
 292 259 322 280 349 280 c 0
 382 280 410 254 410 219 c 0
 410 211 408 202 405 193 c 0
 358 68 l 0
 355 60 354 54 354 48 c 0
 354 40 357 34 362 34 c 0
 364 34 365 35 367 36 c 0
 389 48 404 72 419 93 c 0
 427 104 449 93 440 78 c 0
 411 27 388 -10 323 -10 c 0
 296 -10 277 5 277 31 c 0
 277 39 278 47 282 57 c 0
 334 193 l 0
 335 195 335 197 335 199 c 0
 335 209 325 213 314 213 c 0
 300 213 284 206 279 193 c 0
 214 22 l 0
 211 14 207 0 191 -1 c 1
 153 0 l 16
 134 -2 132 12 136 22 c 0
 203 193 l 0
 204 195 204 197 204 199 c 0
 204 208 195 213 183 213 c 0
 170 213 153 206 148 193 c 0
 80 21 l 0
393 -160 m 0
 353 -160 313 -135 313 -90 c 0
 313 -55 330 -30 355 -30 c 0
 380 -30 396 -45 396 -65 c 0
 396 -75 389 -83 378 -90 c 0
 368 -97 368 -100 368 -110 c 0
 368 -120 378 -124 393 -124 c 0
 456 -124 459 -35 526 230 c 1
 507 230 508 230 473 230 c 0
 460 230 454 241 454 251 c 0
 454 261 460 270 471 270 c 0
 502 270 504 270 536 270 c 1
 564 374 647 440 713 440 c 0
 753 440 793 415 793 370 c 0
 793 335 776 310 751 310 c 0
 726 310 710 325 710 345 c 0
 710 355 717 363 728 370 c 0
 739 377 738 380 738 390 c 0
 738 400 728 404 713 404 c 0
 671 404 661 332 648 270 c 1
 675 270 672 270 702 270 c 0
 715 270 722 260 722 250 c 0
 722 240 715 230 702 230 c 0
 676 230 672 230 639 230 c 1
 596 -26 523 -160 393 -160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE52F
︙︙
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
 -130 -55 -113 -30 -88 -30 c 0
 -63 -30 -47 -45 -47 -65 c 0
 -47 -75 -54 -83 -65 -90 c 0
 -75 -97 -75 -100 -75 -110 c 0
 -75 -120 -65 -124 -50 -124 c 0
 13 -124 16 -35 83 230 c 1
 64 230 65 230 30 230 c 0
 17.239470207 230 10.992185238 240.421191718 10.992185238 250.625180963 c 0
 10.992185238 260.412503619 16.7396874095 270 28 270 c 0
 59 270 61 270 93 270 c 1
 121 374 204 440 270 440 c 0
 310 440 350 415 350 370 c 0
 350 335 333 310 308 310 c 0
 283 310 267 325 267 345 c 0
 267 355 274 363 285 370 c 0
 296 377 295 380 295 390 c 0
 295 400 285 404 270 404 c 0
 228 404 218 333 205 271 c 1
 360 270 l 1
 388 374 471 440 537 440 c 0
 577 440 617 415 617 370 c 0
 617 335 600 310 575 310 c 0
 550 310 534 325 534 345 c 0
 534 355 541 363 552 370 c 0
 563 377 562 380 562 390 c 0
 562 400 552 404 537 404 c 0
 495 404 485 332 472 270 c 1
 499 270 496 270 526 270 c 0
 539 270 545.5 260 545.5 250 c 0
 545.5 240 539 230 526 230 c 0
 500 230 496 230 463 230 c 1
 420 -26 347 -160 217 -160 c 0
 177 -160 137 -135 137 -90 c 0
 137 -55 154 -30 179 -30 c 0
 204 -30 220 -45 220 -65 c 0
 220 -75 213 -83 202 -90 c 0
 192 -97 192 -100 192 -110 c 0







|
|



















|
|







3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
 -130 -55 -113 -30 -88 -30 c 0
 -63 -30 -47 -45 -47 -65 c 0
 -47 -75 -54 -83 -65 -90 c 0
 -75 -97 -75 -100 -75 -110 c 0
 -75 -120 -65 -124 -50 -124 c 0
 13 -124 16 -35 83 230 c 1
 64 230 65 230 30 230 c 0
 17 230 11 241 11 251 c 0
 11 261 17 270 28 270 c 0
 59 270 61 270 93 270 c 1
 121 374 204 440 270 440 c 0
 310 440 350 415 350 370 c 0
 350 335 333 310 308 310 c 0
 283 310 267 325 267 345 c 0
 267 355 274 363 285 370 c 0
 296 377 295 380 295 390 c 0
 295 400 285 404 270 404 c 0
 228 404 218 333 205 271 c 1
 360 270 l 1
 388 374 471 440 537 440 c 0
 577 440 617 415 617 370 c 0
 617 335 600 310 575 310 c 0
 550 310 534 325 534 345 c 0
 534 355 541 363 552 370 c 0
 563 377 562 380 562 390 c 0
 562 400 552 404 537 404 c 0
 495 404 485 332 472 270 c 1
 499 270 496 270 526 270 c 0
 539 270 546 260 546 250 c 0
 546 240 539 230 526 230 c 0
 500 230 496 230 463 230 c 1
 420 -26 347 -160 217 -160 c 0
 177 -160 137 -135 137 -90 c 0
 137 -55 154 -30 179 -30 c 0
 204 -30 220 -45 220 -65 c 0
 220 -75 213 -83 202 -90 c 0
 192 -97 192 -100 192 -110 c 0
︙︙
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
 884 335 867 310 842 310 c 0
 817 310 801 325 801 345 c 0
 801 355 808 363 819 370 c 0
 830 377 829 380 829 390 c 0
 829 400 819 404 804 404 c 0
 762 404 752 332 739 270 c 1
 766 270 763 270 793 270 c 0
 806 270 812.5 260 812.5 250 c 0
 812.5 240 806 230 793 230 c 0
 767 230 763 230 730 230 c 1
 687 -26 614 -160 484 -160 c 0
 444 -160 404 -135 404 -90 c 0
 404 -55 421 -30 446 -30 c 0
 471 -30 487 -45 487 -65 c 0
 487 -75 480 -83 469 -90 c 0
 459 -97 459 -100 459 -110 c 0







|
|







3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
 884 335 867 310 842 310 c 0
 817 310 801 325 801 345 c 0
 801 355 808 363 819 370 c 0
 830 377 829 380 829 390 c 0
 829 400 819 404 804 404 c 0
 762 404 752 332 739 270 c 1
 766 270 763 270 793 270 c 0
 806 270 812 260 812 250 c 0
 812 240 806 230 793 230 c 0
 767 230 763 230 730 230 c 1
 687 -26 614 -160 484 -160 c 0
 444 -160 404 -135 404 -90 c 0
 404 -55 421 -30 446 -30 c 0
 471 -30 487 -45 487 -65 c 0
 487 -75 480 -83 469 -90 c 0
 459 -97 459 -100 459 -110 c 0
︙︙
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
 -130 -55 -113 -30 -88 -30 c 0
 -63 -30 -47 -45 -47 -65 c 0
 -47 -75 -54 -83 -65 -90 c 0
 -75 -97 -75 -100 -75 -110 c 0
 -75 -120 -65 -124 -50 -124 c 0
 13 -124 16 -35 83 230 c 1
 64 230 65 230 30 230 c 0
 17.239470207 230 10.992185238 240.421191718 10.992185238 250.625180963 c 0
 10.992185238 260.412503619 16.7396874095 270 28 270 c 0
 59 270 61 270 93 270 c 1
 121 374 204 440 270 440 c 0
 310 440 350 415 350 370 c 0
 350 335 333 310 308 310 c 0
 283 310 267 325 267 345 c 0
 267 355 274 363 285 370 c 0
 296 377 295 380 295 390 c 0







|
|







3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
 -130 -55 -113 -30 -88 -30 c 0
 -63 -30 -47 -45 -47 -65 c 0
 -47 -75 -54 -83 -65 -90 c 0
 -75 -97 -75 -100 -75 -110 c 0
 -75 -120 -65 -124 -50 -124 c 0
 13 -124 16 -35 83 230 c 1
 64 230 65 230 30 230 c 0
 17 230 11 241 11 251 c 0
 11 261 17 270 28 270 c 0
 59 270 61 270 93 270 c 1
 121 374 204 440 270 440 c 0
 310 440 350 415 350 370 c 0
 350 335 333 310 308 310 c 0
 283 310 267 325 267 345 c 0
 267 355 274 363 285 370 c 0
 296 377 295 380 295 390 c 0
︙︙
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
 1152 335 1135 310 1110 310 c 0
 1085 310 1069 325 1069 345 c 0
 1069 355 1076 363 1087 370 c 0
 1098 377 1097 380 1097 390 c 0
 1097 400 1087 404 1072 404 c 0
 1030 404 1020 332 1007 270 c 1
 1034 270 1031 270 1061 270 c 0
 1074 270 1080.5 260 1080.5 250 c 0
 1080.5 240 1074 230 1061 230 c 0
 1035 230 1031 230 998 230 c 1
 955 -26 882 -160 752 -160 c 0
 712 -160 672 -135 672 -90 c 0
 672 -55 689 -30 714 -30 c 0
 739 -30 755 -45 755 -65 c 0
 755 -75 748 -83 737 -90 c 0
 727 -97 727 -100 727 -110 c 0







|
|







3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
 1152 335 1135 310 1110 310 c 0
 1085 310 1069 325 1069 345 c 0
 1069 355 1076 363 1087 370 c 0
 1098 377 1097 380 1097 390 c 0
 1097 400 1087 404 1072 404 c 0
 1030 404 1020 332 1007 270 c 1
 1034 270 1031 270 1061 270 c 0
 1074 270 1080 260 1080 250 c 0
 1080 240 1074 230 1061 230 c 0
 1035 230 1031 230 998 230 c 1
 955 -26 882 -160 752 -160 c 0
 712 -160 672 -135 672 -90 c 0
 672 -55 689 -30 714 -30 c 0
 739 -30 755 -45 755 -65 c 0
 755 -75 748 -83 737 -90 c 0
 727 -97 727 -100 727 -110 c 0
︙︙
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
 -130 -55 -113 -30 -88 -30 c 0
 -63 -30 -47 -45 -47 -65 c 0
 -47 -75 -54 -83 -65 -90 c 0
 -75 -97 -75 -100 -75 -110 c 0
 -75 -120 -65 -124 -50 -124 c 0
 13 -124 16 -35 83 230 c 1
 64 230 65 230 30 230 c 0
 17.239470207 230 10.992185238 240.421191718 10.992185238 250.625180963 c 0
 10.992185238 260.412503619 16.7396874095 270 28 270 c 0
 59 270 61 270 93 270 c 1
 121 374 204 440 270 440 c 0
 310 440 350 415 350 370 c 0
 350 335 333 310 308 310 c 0
 283 310 267 325 267 345 c 0
 267 355 274 363 285 370 c 0
 296 377 295 380 295 390 c 0







|
|







3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
 -130 -55 -113 -30 -88 -30 c 0
 -63 -30 -47 -45 -47 -65 c 0
 -47 -75 -54 -83 -65 -90 c 0
 -75 -97 -75 -100 -75 -110 c 0
 -75 -120 -65 -124 -50 -124 c 0
 13 -124 16 -35 83 230 c 1
 64 230 65 230 30 230 c 0
 17 230 11 241 11 251 c 0
 11 261 17 270 28 270 c 0
 59 270 61 270 93 270 c 1
 121 374 204 440 270 440 c 0
 310 440 350 415 350 370 c 0
 350 335 333 310 308 310 c 0
 283 310 267 325 267 345 c 0
 267 355 274 363 285 370 c 0
 296 377 295 380 295 390 c 0
︙︙
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
 60 34 69 30 85 30 c 0
 113 30 140 45 140 70 c 0
 140 100 120 107 94 124 c 0
 61 145 30 167 30 200 c 0
 30 247 70 280 130 280 c 0
 175 280 218 253 218 220 c 0
 218 199 202 180 180 180 c 0
 163.575188443 180 145.315176651 194.681601887 145.315176651 213.518742217 c 0
 145.315176651 218.765893239 146.732008255 224.335480975 150 230 c 1
 142 240 133 250 120 250 c 0
 101 250 80 239 80 220 c 0
 80 193 127 175 150 160 c 0
 184 137 200 120 200 90 c 0
 200 26 156 0 80 0 c 0xd8
494 3 m 0
 482 -6 470 16 481 26 c 0
 655 217 l 0
 647.065121876 221.231935 637.451249329 222.385713558 625.122178884 222.385713558 c 0
 614.144520003 222.385713558 601.014276252 221.471008125 585 221 c 0
 574 221 554 173 541 162 c 0
 527 150 511 153 524 187 c 0
 543 238 542 251 548 267 c 1
 580.854598393 264.712654542 605.061346797 263.852204333 623.335584921 263.852204333 c 0
 678.854023456 263.852204333 679.616120157 271.794054171 701.762784585 271.794054171 c 0
 707.383128795 271.794054171 714.380713088 271.282571588 724 270 c 0
 739 268 737 254 728 245 c 0
 574 82 l 0
 571.290722326 77.5296918374 570.067832218 74.1420610385 570.067832218 71.5487518924 c 0
 570.067832218 54.9981455171 619.876699814 70.8006196048 651 44 c 0
 660 36 672 52 668 64 c 0
 661 84 629 65 629 100 c 0
 629 115 645 130 661 130 c 0
 681 130 699 117 699 75 c 0
 699 35.6904170379 666.82658183 2.81551770962 623.631918047 2.81551770962 c 0
 621.774432159 2.81551770962 619.896565457 2.87631094845 618 3 c 0
 574.626134889 6.18925478761 553.222377354 17.5155864555 532.50873405 17.5155864555 c 0
 520.748265557 17.5155864555 509.210259784 13.8644712743 494 3 c 0
182 -160 m 0
 142 -160 102 -135 102 -90 c 0
 102 -55 119 -30 144 -30 c 0
 169 -30 185 -45 185 -65 c 0
 185 -75 178 -83 167 -90 c 0
 157 -97 165 -100 165 -110 c 0
 165 -120 167 -124 182 -124 c 0
 245 -124 248 -35 315 230 c 1
 296 230 297 230 262 230 c 0
 249.239470207 230 242.992185238 240.421191718 242.992185238 250.625180963 c 0
 242.992185238 260.412503619 248.73968741 270 260 270 c 0
 291 270 293 270 325 270 c 1
 353 374 436 440 502 440 c 0
 542 440 582 415 582 370 c 0
 582 335 565 310 540 310 c 0
 515 310 499 325 499 345 c 0
 499 355 506 363 517 370 c 0
 528 377 527 380 527 390 c 0
 527 400 517 404 502 404 c 0
 460 404 450 332 437 270 c 1
 464 270 461 270 491 270 c 0
 504 270 510.5 260 510.5 250 c 0
 510.5 240 504 230 491 230 c 0
 465 230 461 230 428 230 c 1
 385 -26 312 -160 182 -160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE566







|
|








|
|



|
|
|


|
|




|
|
|
|









|
|










|
|







3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
 60 34 69 30 85 30 c 0
 113 30 140 45 140 70 c 0
 140 100 120 107 94 124 c 0
 61 145 30 167 30 200 c 0
 30 247 70 280 130 280 c 0
 175 280 218 253 218 220 c 0
 218 199 202 180 180 180 c 0
 164 180 145 195 145 214 c 0
 145 219 147 224 150 230 c 1
 142 240 133 250 120 250 c 0
 101 250 80 239 80 220 c 0
 80 193 127 175 150 160 c 0
 184 137 200 120 200 90 c 0
 200 26 156 0 80 0 c 0xd8
494 3 m 0
 482 -6 470 16 481 26 c 0
 655 217 l 0
 647 221 637 222 625 222 c 0
 614 222 601 221 585 221 c 0
 574 221 554 173 541 162 c 0
 527 150 511 153 524 187 c 0
 543 238 542 251 548 267 c 1
 581 265 605 264 623 264 c 0
 679 264 680 272 702 272 c 0
 708 272 714 271 724 270 c 0
 739 268 737 254 728 245 c 0
 574 82 l 0
 571 78 570 75 570 72 c 0
 570 55 620 71 651 44 c 0
 660 36 672 52 668 64 c 0
 661 84 629 65 629 100 c 0
 629 115 645 130 661 130 c 0
 681 130 699 117 699 75 c 0
 699 36 667 3 624 3 c 0
 622 3 620 3 618 3 c 0
 575 6 554 18 533 18 c 0
 521 18 509 14 494 3 c 0
182 -160 m 0
 142 -160 102 -135 102 -90 c 0
 102 -55 119 -30 144 -30 c 0
 169 -30 185 -45 185 -65 c 0
 185 -75 178 -83 167 -90 c 0
 157 -97 165 -100 165 -110 c 0
 165 -120 167 -124 182 -124 c 0
 245 -124 248 -35 315 230 c 1
 296 230 297 230 262 230 c 0
 249 230 243 241 243 251 c 0
 243 261 249 270 260 270 c 0
 291 270 293 270 325 270 c 1
 353 374 436 440 502 440 c 0
 542 440 582 415 582 370 c 0
 582 335 565 310 540 310 c 0
 515 310 499 325 499 345 c 0
 499 355 506 363 517 370 c 0
 528 377 527 380 527 390 c 0
 527 400 517 404 502 404 c 0
 460 404 450 332 437 270 c 1
 464 270 461 270 491 270 c 0
 504 270 510 260 510 250 c 0
 510 240 504 230 491 230 c 0
 465 230 461 230 428 230 c 1
 385 -26 312 -160 182 -160 c 0
EndSplineSet
Validated: 1
EndChar

StartChar: uniE566
︙︙
4159
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
EndChar

StartChar: uniE650
Encoding: 124 58960 124
Width: 1037
Flags: W
HStem: -2 60<361.035 435.033 953.253 1010.82> 0 49<543.265 652.027> 286 36<514.943 580.797> 359 66<58.4784 125.129> 402 58<367.211 444.917> 607 32<177.106 261>
VStem: 0 57<406 505.924> 148 26<463.933 475.584> 200 67<410.241 442> 274.641 84.3586<205.588 329> 425 56<126.64 255.856> 452 64<466.983 558.522> 585 40<194.878 284.873> 675 60<72.5613 225.441> 887 59<79.8865 267.532> 952 60<-0.820206 56.8202>
LayerCount: 2
Fore
SplineSet
152 90 m 0x67ef
 221.394397964 119.985233688 274.64143816 164.649883528 274.64143816 208.902553279 c 0
 274.64143816 216.303422646 273.152138825 223.692769088 270 231 c 0
 252 273 185 332 185 389 c 0
 185 407 200 441 200 442 c 2
 261 607 l 1
 114 601 57 505 57 460 c 0x6fef
 57 436 69 425 89 425 c 0
 110 425 128 443 148 485 c 1
 174 472 l 1







|




|
|







4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
EndChar

StartChar: uniE650
Encoding: 124 58960 124
Width: 1037
Flags: W
HStem: -2 60<361.035 435.033 953.253 1010.82> 0 49<543.265 652.027> 286 36<514.943 580.797> 359 66<58.4784 125.129> 402 58<367.211 444.917> 607 32<177.106 261>
VStem: 0 57<406 505.924> 148 26<463.933 475.584> 200 67<410.241 442> 275 84<205.648 329> 425 56<126.64 255.856> 452 64<466.983 558.522> 585 40<194.878 284.873> 675 60<72.5613 225.441> 887 59<79.8865 267.532> 952 60<-0.820206 56.8202>
LayerCount: 2
Fore
SplineSet
152 90 m 0x67ef
 221 120 275 165 275 209 c 0
 275 216 273 224 270 231 c 0
 252 273 185 332 185 389 c 0
 185 407 200 441 200 442 c 2
 261 607 l 1
 114 601 57 505 57 460 c 0x6fef
 57 436 69 425 89 425 c 0
 110 425 128 443 148 485 c 1
 174 472 l 1
︙︙
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
 437 460 452 482 452 510 c 0x2fdf
 452 530 431 601 303 608 c 1
 276 542 l 2
 275 538 267 498 267 479 c 0
 267 390 359 361 359 297 c 0
 359 211 296 140 296 140 c 1
 334 114 366 63 394 63 c 0
 399 63 402.9375 62.625 406.671875 62.625 c 0
 417.875 62.625 427.25 66 458 93 c 1
 438 125 425 152 425 186 c 27
 425 226 431 247 453 276 c 0
 479 311 522 322 563 322 c 0
 599 322 625 289 625 247 c 3
 625 233 625 214 615 195 c 0
 591 148 551 110 526 90 c 1
 550 61 566 49 604 49 c 0x6fef







|
|







4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
 437 460 452 482 452 510 c 0x2fdf
 452 530 431 601 303 608 c 1
 276 542 l 2
 275 538 267 498 267 479 c 0
 267 390 359 361 359 297 c 0
 359 211 296 140 296 140 c 1
 334 114 366 63 394 63 c 0
 399 63 403 63 407 63 c 0
 418 63 427 66 458 93 c 1
 438 125 425 152 425 186 c 27
 425 226 431 247 453 276 c 0
 479 311 522 322 563 322 c 0
 599 322 625 289 625 247 c 3
 625 233 625 214 615 195 c 0
 591 148 551 110 526 90 c 1
 550 61 566 49 604 49 c 0x6fef
︙︙
Changes to modules/MIDI.js.
1
2
3
4
5
6
7
8
9
10
// MIDI.js - module to handle the %%MIDI parameters
//
// Copyright (C) 2019-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// MIDI.js - module to handle the %%MIDI parameters
//
// Copyright (C) 2019-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.
︙︙
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//				//	M:9/8	fzcfzcfzc
//		break
	case "gchordon":	// %%MIDI gchordon
	case "gchordoff":	// %%MIDI gchordoff
		if (!cfmt.chord)
			cfmt.chord = {}
		if (abc.parse.state >= 2
		 && abc.get_curvoice()) {
			s = abc.new_block("midigch")
			s.play = true
			s.on = a[1][7] == 'n'
		} else {
			cfmt.chord.gchon = a[1][7] == 'n'
		}
		break
	case "channel":
		v = parseInt(a[2])
		if (isNaN(v) || v <= 0 || v > 16) {
			abc.syntax(1, abc.errs.bad_val, "%%MIDI channel")
			break
		}
		if (--v != 9) {			// channel range 1..16 => 0..15
			if (abc.parse.state == 3) {
				s = abc.new_block("midichn");
				s.play = true
				s.chn = v
			} else {
				abc.set_v_param("channel", v)
			}
			break
		}

		// channel 10 is bank 128
		abc2svg.MIDI.do_midi.call(abc, "MIDI control 0 1")	// MSB bank
		abc2svg.MIDI.do_midi.call(abc, "MIDI control 32 0")	// LSB bank
		break
	case "drummap":
//fixme: should have a 'MIDIdrum' per voice?
		v = Number(a[3])
		if (isNaN(v)) {
			abc.syntax(1, abc.errs.bad_val, "%%MIDI drummap")
			break







|

|











|

|
|
|



<
<
<
<
<
<







130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158






159
160
161
162
163
164
165
//				//	M:9/8	fzcfzcfzc
//		break
	case "gchordon":	// %%MIDI gchordon
	case "gchordoff":	// %%MIDI gchordoff
		if (!cfmt.chord)
			cfmt.chord = {}
		if (abc.parse.state >= 2
		 && curvoice) {
			s = abc.new_block("midigch")
			s.play = s.invis = 1 //true
			s.on = a[1][7] == 'n'
		} else {
			cfmt.chord.gchon = a[1][7] == 'n'
		}
		break
	case "channel":
		v = parseInt(a[2])
		if (isNaN(v) || v <= 0 || v > 16) {
			abc.syntax(1, abc.errs.bad_val, "%%MIDI channel")
			break
		}
		v--				// channel range 1..16 => 0..15
			if (abc.parse.state == 3) {
				s = abc.new_block("midiprog")
				s.play = s.invis = 1 //true
				curvoice.chn = s.chn = v
			} else {
				abc.set_v_param("channel", v)
			}






		break
	case "drummap":
//fixme: should have a 'MIDIdrum' per voice?
		v = Number(a[3])
		if (isNaN(v)) {
			abc.syntax(1, abc.errs.bad_val, "%%MIDI drummap")
			break
︙︙
192
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
		v = parseInt(v)
		if (isNaN(v) || v < 0 || v > 127) {
			abc.syntax(1, abc.errs.bad_val, "%%MIDI program")
			break
		}
		if (abc.parse.state == 3) {
			s = abc.new_block("midiprog");
			s.play = true
			s.instr = v

		} else {
			abc.set_v_param("instr", v)
		}
		break
	case "control":
		n = parseInt(a[2])
		if (isNaN(n) || n < 0 || n > 127) {
			abc.syntax(1, "Bad controller number in %%MIDI")
			break
		}
		v = parseInt(a[3])
		if (isNaN(v) || v < 0 || v > 127) {
			abc.syntax(1, "Bad controller value in %%MIDI")
			break
		}
		if (abc.parse.state == 3) {
			s = abc.new_block("midictl");
			s.play = true
			s.ctrl = n;
			s.val = v
		} else {
			abc.set_v_param("midictl", a[2] + ' ' + a[3])
		}
		break
	case "temperamentequal":







|

>

















|







186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
		v = parseInt(v)
		if (isNaN(v) || v < 0 || v > 127) {
			abc.syntax(1, abc.errs.bad_val, "%%MIDI program")
			break
		}
		if (abc.parse.state == 3) {
			s = abc.new_block("midiprog");
			s.play = s.invis = 1 //true
			s.instr = v
			s.chn = curvoice.chn
		} else {
			abc.set_v_param("instr", v)
		}
		break
	case "control":
		n = parseInt(a[2])
		if (isNaN(n) || n < 0 || n > 127) {
			abc.syntax(1, "Bad controller number in %%MIDI")
			break
		}
		v = parseInt(a[3])
		if (isNaN(v) || v < 0 || v > 127) {
			abc.syntax(1, "Bad controller value in %%MIDI")
			break
		}
		if (abc.parse.state == 3) {
			s = abc.new_block("midictl");
			s.play = s.invis = 1 //true
			s.ctrl = n;
			s.val = v
		} else {
			abc.set_v_param("midictl", a[2] + ' ' + a[3])
		}
		break
	case "temperamentequal":
︙︙
292
293
294
295
296
297
298
299

300
301
302
303
304


305
306
307


308






309
310
311
312
313
314
315

		break
	}
    }, // do_midi()

    // set the MIDI parameters in the current voice
    set_vp: function(of, a) {
    var	i, item,

	curvoice = this.get_curvoice()

	for (i = 0; i < a.length; i++) {
		switch (a[i]) {
		case "channel=":		// %%MIDI channel


			curvoice.chn = a[++i]
			break
		case "instr=":			// %%MIDI program


			curvoice.instr = a[++i]






			break
		case "midictl=":		// %%MIDI control
			if (!curvoice.midictl)
				curvoice.midictl = []
			item = a[++i].split(' ');
			curvoice.midictl[item[0]] = Number(item[1])
			break







|
>
|




>
>
|


>
>
|
>
>
>
>
>
>







287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321

		break
	}
    }, // do_midi()

    // set the MIDI parameters in the current voice
    set_vp: function(of, a) {
    var	i, item, s,
	abc = this,
	curvoice = abc.get_curvoice()

	for (i = 0; i < a.length; i++) {
		switch (a[i]) {
		case "channel=":		// %%MIDI channel
			s = abc.new_block("midiprog")
			s.play = s.invis = 1 //true
			s.chn = curvoice.chn = a[++i]
			break
		case "instr=":			// %%MIDI program
			s = abc.new_block("midiprog")
			s.play = s.invis = 1 //true
			s.instr = a[++i]
			if (curvoice.chn == undefined) {
				curvoice.chn = curvoice.v < 9 ?
						curvoice.v :
						curvoice.v + 1
			}
			s.chn = curvoice.chn
			break
		case "midictl=":		// %%MIDI control
			if (!curvoice.midictl)
				curvoice.midictl = []
			item = a[++i].split(' ');
			curvoice.midictl[item[0]] = Number(item[1])
			break
︙︙
Changes to modules/capo.js.
︙︙
32
33
34
35
36
37
38
39

40

41
42
43
44
45
46
47

	if (p_v.capo && a_gch) {
		t = p_v.capo
		i = 0

		while (1) {
			gch = a_gch[i++]
			if (!gch)

				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.ifb40[t % 12])
		if (!p_v.capo_first) {		// if new voice







|
>

>







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

	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.ifb40[t % 12])
		if (!p_v.capo_first) {		// if new voice
︙︙
Changes to modules/combine.js.
1
2
3
4
5
6
7
8
9
10
// combine.js - module to add a combine chord line
//
// Copyright (C) 2018-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// combine.js - module to add a combine chord line
//
// Copyright (C) 2018-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.
︙︙
23
24
25
26
27
28
29

30
















31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//	%%voicecombine n	'n' is the combine level

abc2svg.combine = {

    // function called at start of the generation when multi-voices
    comb_v: function() {
    var	C = abc2svg.C,

	sy

















    // check if voice combine may occur
    function may_combine(s) {
    var	nhd2,
	s2 = s.ts_next

	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
	 || sy.voices[s2.v].range != sy.voices[s.v].range + 1)	// next voice only
		return false
	if (s.combine <= 0
	 && s2.type != s.type)
		return false
//	if (s2.a_dd) { //fixme: should check the double decorations
//		return false
//	}







>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|





|
<







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
//	%%voicecombine n	'n' is the combine level

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
//	}
︙︙
64
65
66
67
68
69
70
71
72

73
74

75

76
77
78
79

80
81
82
83
84
85
86
	 && 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;


	for (m = 0; m <= s2.nhd; m++)	// change the container of the notes
		s2.notes[m].s = s

	Array.prototype.push.apply(s.notes, s2.notes);

	s.nhd = nhd = s.notes.length - 1;
	s.notes.sort(abc2svg.pitcmp)	// sort the notes by pitch

	if (s.combine >= 3) {		// remove unison heads

		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
	}







|

>
|
|
>
|
>




>







80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
	 && 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
	}
︙︙
94
95
96
97
98
99
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130



131
132
133
134
135





136






137






138
139
140
141
142
143
144
145
146
147
148

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
		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

function do_combine(s) {
	var s2, nhd, nhd2, type

		s2 = s.ts_next

		// there may be more voices
		if (!s.in_tuplet
		 && s2.combine != undefined && s2.combine >= 0
		 && may_combine.call(this, s2))
			do_combine.call(this, s2)

		nhd = s.nhd;
		nhd2 = s2.nhd
		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
		} else {
			combine_notes.call(this, s, s2)
			if (s2.ti1)
				s.ti1 = true
			if (s2.ti2)
				s.ti2 = true
		}




		if (s2.sls) {
			if (s.sls)
				Array.prototype.push.apply(s.sls, s2.sls)
			else
				s.sls = s2.sls





		}






		if (s2.sl1)






			s.sl1 = true
		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)
		}

		this.unlksym(s2)			// remove the next symbol

} // do_combine()

	// code of comb_v()
	var s, s2, g, i, r

	for (s = this.get_tsfirst(); s; s = s.ts_next) {
		switch (s.type) {
		case C.REST:
			if (s.combine == undefined || s.combine < 0)
				continue
			if (may_combine.call(this, s))
				do_combine.call(this, s)
			continue
		case C.STAVES:
			sy = s.sy
			// 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.call(this, 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) {
			do_combine.call(this, 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)
		}







>

|

|




|
|

<
<










|






>
>
>





>
>
>
>
>
|
>
>
>
>
>
>
|
>
>
>
>
>
>
|









|
>





|




|
|
<
<
<
|













|














|







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
		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
		} 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)
		}
︙︙
Changes to modules/diag.js.
︙︙
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
				+ '" y="' + ((l[1] || 1) * 6 - 35)
				+ '" class="frn">' + l[0] + '</text>\n'
		decos[nm] = "3 " + nm + " 40 " + (l[0] ? "30" : "10") + " 0"
		// fingers
		dc += '<text x="' + (n == 6 ? '-12,-8,-4,0,4,8'
					: '-8,-4,0,4')
				+ '" y="-36" class="fng">'
				+ d[2].replace(/[y0]/g, ' ')
				+ '</text>\n'
		// dots
		for (i = 0; i < n; i++) {
			l = d[0][i]
			if (l && l != 'x' && l != '0')
				dc += '<use x="' + (i * 4 + x)
					+ '" y="' + (l * 6 - 37)







|







167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
				+ '" y="' + ((l[1] || 1) * 6 - 35)
				+ '" class="frn">' + l[0] + '</text>\n'
		decos[nm] = "3 " + nm + " 40 " + (l[0] ? "30" : "10") + " 0"
		// fingers
		dc += '<text x="' + (n == 6 ? '-12,-8,-4,0,4,8'
					: '-8,-4,0,4')
				+ '" y="-36" class="fng">'
				+ d[2].replace(/[y0]/g, ' ')
				+ '</text>\n'
		// dots
		for (i = 0; i < n; i++) {
			l = d[0][i]
			if (l && l != 'x' && l != '0')
				dc += '<use x="' + (i * 4 + x)
					+ '" y="' + (l * 6 - 37)
︙︙
Changes to modules/equalbars.js.
1
2
3
4
5
6
7
8
9
10
// equalbars.js - module to set equal spaced measure bars
//
// Copyright (C) 2018-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// equalbars.js - module to set equal spaced measure bars
//
// Copyright (C) 2018-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.
︙︙
27
28
29
30
31
32
33
34
35
36
37
38





39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90















91
92
93
94
95
96
97
98
99
100
101





102
103
104
105
106
107
108
    // 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, param) {
	if (cmd == "equalbars")
		this.cfmt().equalbars = this.get_bool(param)
	else
		of(cmd, param)





    },

    // adjust the symbol offsets of a music line

    set_sym_glue: function(of, width) {
    var	C = abc2svg.C,
	s, s2, 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.ts_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.ts_next; s = s.ts_next) {
		if (s.type == C.BAR && s.seqst) {
			bars.push([s, s.time - t]);
			t = s.time
		}
	}

	// push the last bar if it is not the invisible bar after a key change
	if (!s.invis || s.prev.type != C.KEY)
		bars.push([s, s.time - t])
	else
		bars[bars.length - 1][0] = s	// replace the last bar
	width = s.x
	t = s.time
	if (s.dur)
		t += s.dur;

	n = bars.length
	if (n <= 1)
		return				// one or no bar
















	// set the measure parameters
	x = s2.type == C.GRACE ? s2.extra.x : s2.x;
	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++) {





		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)







|
|
<
<
|
>
>
>
>
>



>











|

















|







|



|







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











>
>
>
>
>







27
28
29
30
31
32
33
34
35


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
    // 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, 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) {
			bars.push([s, s.time - t]);
			t = s.time
		}
	}

	// push the last bar if it is not the invisible bar after a key change
	if (!s.invis || (s.prev && s.prev.type != C.KEY))
		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;
	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
			s2.x = d + s2.x - x	// from the bar
			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)
︙︙
Changes to modules/gamelan.js.
1
2
3
4
5
6
7
8
9
10
// gamelan.js - module to output Gamelan (indonesian) music sheets
//
// Copyright (C) 2020-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// gamelan.js - module to output Gamelan (indonesian) music sheets
//
// 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.
︙︙
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
	} // do_tie()

	function set_sym(p_v) {
	    var s, s2, note, pit, nn, p, a, m, i,
		sf = p_v.key.k_sf,
		delta = abc2svg.gamelan.cgd2cde[sf + 7] - 2

		p_v.key.k_a_acc = []	// no accidental

		// no (visible) clef
		p_v.clef.invis = true

		// scan the voice
		for (s = p_v.sym; s; s = s.next) {
			s.st = p_v.st







|







165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
	} // do_tie()

	function set_sym(p_v) {
	    var s, s2, note, pit, nn, p, a, m, i,
		sf = p_v.key.k_sf,
		delta = abc2svg.gamelan.cgd2cde[sf + 7] - 2

		delete p_v.key.k_a_acc		// no accidental

		// no (visible) clef
		p_v.clef.invis = true

		// scan the voice
		for (s = p_v.sym; s; s = s.next) {
			s.st = p_v.st
︙︙
354
355
356
357
358
359
360

361

362


363




364



365
366

367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
			continue
		switch (s.type) {
		case C.NOTE:
		case C.REST:
			x = s.x
			y = staff_tb[s.st].y
			draw_hd(s, x, y)



			if (s.nflags > 0) {


				if (s.beam_st) {




					nl = s.nflags



					s2 = s
					while (1) {

						if (s2.nflags && s2.nflags > nl)
							nl = s2.nflags
						if (s2.beam_end)
							break
						if (!s2.next
						 || !s2.next.nflags
						 || s2.next.nflags <= 0)
							break
						s2 = s2.next
					}
					draw_dur(s, y + 7, s2, 1, nl)
				}
			}
			break
		}
	}
  }, // draw_symbols()

// set some parameters
    set_fmt: function(of, cmd, param) {







>
|
>
|
>
>
|
>
>
>
>
|
>
>
>
|
|
>
|
|
|
|
<
<
<
<
<
|
|
<
<







354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382





383
384


385
386
387
388
389
390
391
			continue
		switch (s.type) {
		case C.NOTE:
		case C.REST:
			x = s.x
			y = staff_tb[s.st].y
			draw_hd(s, x, y)
			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





			}
			draw_dur(s2, y + 7, s, 1, nl)


			break
		}
	}
  }, // draw_symbols()

// set some parameters
    set_fmt: function(of, cmd, param) {
︙︙
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
    var	w, m, note,
	C = abc2svg.C

	switch (s.type) {
	case C.CLEF:
	case C.KEY:
	case C.METER:
		s.wl = s.wr = 0
		break
	}
    }, // set_width()

    set_hooks: function(abc) {
	abc.do_pscom = abc2svg.gamelan.do_pscom.bind(abc, abc.do_pscom)
	abc.draw_symbols = abc2svg.gamelan.draw_symbols.bind(abc, abc.draw_symbols)







|







439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    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
	}
    }, // set_width()

    set_hooks: function(abc) {
	abc.do_pscom = abc2svg.gamelan.do_pscom.bind(abc, abc.do_pscom)
	abc.draw_symbols = abc2svg.gamelan.draw_symbols.bind(abc, abc.draw_symbols)
︙︙
Changes to modules/grid.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - grid.js - module to insert a chord grid before or after a tune
//
// Copyright (C) 2018-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - grid.js - module to insert a chord grid before or after a tune
//
// Copyright (C) 2018-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.
︙︙
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    var	abc = this,
	img, cls,
	cfmt = abc.cfmt(),
	grid = cfmt.grid

// generate the grid
function build_grid(s, font) {
    var	i, k, l, nr, bar, w, hr, x0, x, y, yl, ps,
	lc = '',
	chords = s.chords,
	bars = s.bars,
	parts = s.parts || [],
	wmx = s.wmx,
	cells = [],
	nc = grid.n







|







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    var	abc = this,
	img, cls,
	cfmt = abc.cfmt(),
	grid = cfmt.grid

// generate the grid
function build_grid(s, font) {
    var	i, k, l, nr, bar, w, hr, x0, x, y, yl, ps, d,
	lc = '',
	chords = s.chords,
	bars = s.bars,
	parts = s.parts || [],
	wmx = s.wmx,
	cells = [],
	nc = grid.n
︙︙
123
124
125
126
127
128
129




















































130
131
132
133
134
135
136
			  }
			}
		} else {
			abc.set_font('grid')
			abc.xy_str(x, y, cell[0])
		}
	} // build_cell()





















































	// ------- build_grid() -------

	// set some chords in each cell
	set_chords()

	// build the content of the cells







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
			  }
			}
		} 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
︙︙
176
177
178
179
180
181
182
183
184

185
186
187
188
189
190
191
192
193
194
195

196

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
		nc /= 2;
		w /= 2
	}

	// generate the cells
	yl = -1
	y = -1 + font.size * .6
	nr = 0
	x0 = (x0 / cfmt.scale - w) / 2

	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++

		}

		k++
		build_cell(cells[i], x, y, yl, hr)
		x += wmx
	}

	// draw the lines
	abc.out_svg('<path class="stroke" stroke-width="1" d="\n')
	y = -1
	for (i = 0; i <= nr; i++) {
		abc.out_svg('M')
		abc.out_sxsy(x0, ' ', y)
		abc.out_svg('h' + w.toFixed(1)+ '\n')
		y -= hr
	}
	x = x0
	for (i = 0; i <= nc; i++) {
		abc.out_svg('M')
		abc.out_sxsy(x, ' ', -1)
		abc.out_svg('v' + (hr * nr).toFixed(1) + '\n')
		x += wmx
	}
	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]







|

>











>

>







<
<
<
<
<
|
<
<
<
<
<
<
|
<







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258





259






260

261
262
263
264
265
266
267
		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('<path class="stroke" stroke-width="1" d="\n')





	draw_hl()






	draw_vl()

	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]
︙︙
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
			abc.out_svg('<text class="' + cls + '" x="')
			abc.out_sxsy(x + 5, '" y="', y)
			abc.out_svg('" style="font-weight:bold;font-size:' +
				(font.size * 1.5).toFixed(1) + 'px">:</text>\n')
		}
		x += wmx
	}
	abc.vskip(hr * nr + 6)
} // build_grid()

	// ----- block_gen() -----
    var	p_voice, n, font, f2

	img = abc.get_img()

	// set the text style
	if (!cfmt.gridfont)
		abc.param_set_font("gridfont", "serif 16")
	font = abc.get_font('grid')
	if (font.class)
		font.class += ' mid'
	else
		font.class = 'mid'
	cls = abc.font_class(font)








|








<
<







292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307


308
309
310
311
312
313
314
			abc.out_svg('<text class="' + cls + '" x="')
			abc.out_sxsy(x + 5, '" y="', y)
			abc.out_svg('" style="font-weight:bold;font-size:' +
				(font.size * 1.5).toFixed(1) + 'px">:</text>\n')
		}
		x += wmx
	}
	abc.vskip(hr * (nr + 1) + 6)
} // build_grid()

	// ----- block_gen() -----
    var	p_voice, n, font, f2

	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)

︙︙
289
290
291
292
293
294
295
296

























297
298
299
300
301
302
303
    }, // block_gen()

    set_stems: function(of) {
    var	C = abc2svg.C,
	abc = this,
	tsfirst = abc.get_tsfirst(),
	voice_tb = abc.get_voice_tb(),
	grid = abc.cfmt().grid


























	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







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
    }, // 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
︙︙
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350

351
352



353
354
355
356
357
358
359
			if (s.part)
				parts[chords.length] = s.part.text
			switch (s.type) {
			case C.NOTE:
			case C.REST:
				if (!s.a_gch || chord[beat_i])
					break
				bt = abc2svg.cs_filter(s.a_gch,
							abc.cfmt().altchord)
				if (!bt)
					break
				chord[beat_i] = bt
				for (i = 0; i < s.a_gch.length; i++) {
					if (s.a_gch[i].type == 'g')
						break
				}
				abc.set_font(s.a_gch[i].font)
				w = abc.strwh(bt)[0]

				if (w > wmx)
					wmx = w



				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)
						break







|
<


<
<
<
<
<
<
|
>
|
|
>
>
>







399
400
401
402
403
404
405
406

407
408






409
410
411
412
413
414
415
416
417
418
419
420
421
422
			if (s.part)
				parts[chords.length] = s.part.text
			switch (s.type) {
			case C.NOTE:
			case C.REST:
				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)
						break
︙︙
437
438
439
440
441
442
443



444
445
446
447
448
449
450
			dur: 0,
			time: 0,
			p_v: p_v,
			v: p_v.v,
			st: p_v.st
		}




		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)







>
>
>







500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
			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)
︙︙
Changes to modules/grid2.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32





33
34
35
36


37
38

39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// grid2.js - module to replace a voice in the music by a chord grid
//
// Copyright (C) 2018-2021 Jean-Francois Moine
//
// This file is part of abc2svg.
//
// abc2svg is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// abc2svg is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.
//
// This module is loaded when "%%grid2" appears in a ABC source.
//
// Parameters
//	%%grid2 y
// This command must appear in a voice.

abc2svg.grid2 = {

// function called before tune generation
    do_grid: function() {
    var s, v, p_v, ix, cs, c_a_cs, bt,
	voice_tb = this.get_voice_tb()






	for (v = 0; v < voice_tb.length; v++) {
		p_v = voice_tb[v]
		if (!p_v.grid2)
			continue


		p_v.clef.invis = true;		// no clef
		p_v.key.k_sf = p_v.key.k_a_acc = 0; // no key signature

		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.tie_s	//  and no tie
				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 = 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 = s.a_gch
						break
					}
				}
		}
	}
    }, // do_grid()



|




















|





|


>
>
>
>
>




>
>

|
>












>
|






|





|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// 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 <http://www.gnu.org/licenses/>.
//
// This module is loaded when "%%grid2" appears in a ABC source.
//
// Parameters
//	%%grid2 y
// This command may appear globally or in a voice.

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()

︙︙
89
90
91
92
93
94
95
96


97
98
99
100


101
102
103
104
105
106
107
    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()


		if (curvoice) {
			this.set_v_param("stafflines", "...");	// no staff lines
			curvoice.grid2 = param
		}


		return
	}
	of(cmd, param)
    },

    set_hooks: function(abc) {
	abc.draw_gchord = abc2svg.grid2.draw_gchord.bind(abc, abc.draw_gchord);







|
>
>
|
<
|
<
>
>







98
99
100
101
102
103
104
105
106
107
108

109

110
111
112
113
114
115
116
117
118
    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);
︙︙
Changes to modules/grid3.js.
1
2
3
4
5
6
7
8
9
10
// grid3.js - module to insert a manual chords
//
// Copyright (C) 2020-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// grid3.js - module to insert a manual chords
//
// Copyright (C) 2020-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.
︙︙
35
36
37
38
39
40
41


42
43
44
45

46
47
48
49
50
51
52

// generate the grid
    block_gen: function(of, s) {
	if (s.subtype != "grid3") {
		of(s)
		return
	}



    var	abc = this,
	cfmt = abc.cfmt(),
	img = abc.get_img(),

	txt = s.text,
	font, font_cl, cls, w,
	ln, i,
	lines = [],
	cl = [],
	bars = [],
	cells = [],







>
>




>







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

// generate the grid
    block_gen: function(of, s) {
	if (s.subtype != "grid3") {
		of(s)
		return
	}

	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 = [],
︙︙
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

149
150

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
		// 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 = 3
		y = 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 = '<path class="stroke" d="\n'
		y = 3
		for (i = 0; i <= nr; i++) {
			line += 'M' + x0.toFixed(1) + ' ' + y.toFixed(1) +
				'h' + w.toFixed(1)+ '\n'
			y += hr
		}
		x = x0

		for (i = 0; i <= nc; i++) {
			line += 'M' + x.toFixed(1) + ' 3v' + (hr * nr).toFixed(1) + '\n'

			x += wc
		}
		line += '"/>\n'

		// insert the cells
		line += lc

		// show the repeat signs
		y = 3 - font.size * .7
		while (1) {
			bl = bars.shift()
			if (!bl)
				break
			x = x0
			y += hr
			while (1) {







|
|



















|






>

|
>








|







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
		// 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 = '<path class="stroke" d="\n'
		y = posy + 3
		for (i = 0; i <= nr; i++) {
			line += 'M' + x0.toFixed(1) + ' ' + y.toFixed(1) +
				'h' + w.toFixed(1)+ '\n'
			y += hr
		}
		x = x0
		y = posy + 3
		for (i = 0; i <= nc; i++) {
			line += 'M' + x.toFixed(1) + ' ' + y.toFixed(1) +
				'v' + (hr * nr).toFixed(1) + '\n'
			x += wc
		}
		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) {
︙︙
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
				wc = w
		}
		nr++
	}
	wc += abc.strwh('  ')[0]

	// build the grid and insert it in the music
	abc.blk_flush()
	build_grid()
	abc.blk_flush()
    }, // block_gen()

// handle %%begingrid
    do_begin_end: function(of, type, opt, txt) {
    var	vt = this.get_voice_tb()







<







255
256
257
258
259
260
261

262
263
264
265
266
267
268
				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()
︙︙
Changes to modules/jazzchord.js.
︙︙
39
40
41
42
43
44
45
46
47
48
49
50
51
52



















53
54
55
56
57
58
59
//	"M": "Δ",
//	"min": "–",
//	"m": "–",
	"^": "∆"
    },

    gch_build: function(of, s) {
    var	gch, ix, t,
	fmt = s.fmt

	if (!fmt.jazzchord) {
		of(s)
		return
	}




















	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







|






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//	"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])
			r += (a[2] ? "$0" : '') + "$8" + a[3]
		if (a[4])
			r += ((a[2] || a[3]) ? "$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
︙︙
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
						return fmt.jzrep[x]
			})
		}

		if (fmt.jazzchord == 1) {
			if (t[0] == '(')
				t = t.slice(1, -1)
			a = t.match(/([A-G])([#♯b♭]?)([^/]*)\/?(.*)/)
			// a[1]=note, a[2]=acc, a[3]=quality, a[4]=bass
			if (!a)
				continue
			t = a[1] + "$7" + (a[2] || '')
			if (a[3])
				t += "$8" + a[3]
			if (a[4])
				t += "$0/$9" + a[4]
			t += "$0"
		}
		if (gch.text[0] == '(')
			gch.text = '(' + t + ')'
		else
			gch.text = t
	}
	of(s)				// build the chord symbols
    }, // gch_build()

    set_fmt: function(of, cmd, parm) {
    var	i, k, s,
	cfmt = this.cfmt()







|
<
<
<
|
|
|
|
<
|


|

|







88
89
90
91
92
93
94
95



96
97
98
99

100
101
102
103
104
105
106
107
108
109
110
111
112
						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()
︙︙
Changes to modules/jianpu.js.
1
2
3
4
5
6
7
8
9
10
// jianpu.js - module to output jiănpŭ (简谱) music sheets
//
// Copyright (C) 2020-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.


|







1
2
3
4
5
6
7
8
9
10
// jianpu.js - module to output jiănpŭ (简谱) music sheets
//
// 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.
︙︙
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

57
58
59
60
61




62




















63






64

65




66
67
68

69



70






71
72
73
74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91





92
93
94

95
96
97
98
99
100
101
102

103
104
105
106
107
108
109

110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
132
133
  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 (!this.cfmt().jianpu)
		return of(bm, s1)
//	return 0
  }, // calc_beam()

// change %%staves and %%score
  do_pscom: function(of, p) {
    if (this.cfmt().jianpu)
	switch (p.match(/\w+/)[0]) {
	case 'staves':
	case 'score':
		p = p.replace(/\(|\)/g, '')
		break
	}
	of(p)
  },

// adjust some symbols before the generation
  output_music: function(of) {

    var	C = abc2svg.C,
	abc = this,
	cur_sy = abc.get_cur_sy(),
	voice_tb = abc.get_voice_tb()





	if (!abc.cfmt().jianpu) {




















		of()






		return

	}





	// output the key and time signatures
	function set_head() {

	    var	tsfirst = abc.get_tsfirst(),



		p_v = voice_tb[0],






		mt = p_v.meter.a_meter[0],
		sk = p_v.key,
		s2 = voice_tb[0].sym,
		s = {
			type: C.BLOCK,
			subtype: "text",

			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)






		s2.prev = s
		s.next = s2
		voice_tb[0].sym = s

		tsfirst.ts_prev = s
		s.ts_next = tsfirst
		abc.set_tsfirst(s)
	} // set head()

	// expand a long note/rest
	function slice(s) {
	    var	n, s2, s3


		if (s.dur >= C.BLEN)
			n = 3 
		else if (s.dur == C.BLEN / 2)
			n = 1
		else
			n = 2

		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: [{

					pit: s.notes[0].pit,
					jn: 8
				}],
				xmx: 0,
				noplay: true,
				time: s.time + C.BLEN / 4,
				prev: s,
				next: s.next
			}







|




<
<
<
<
<
<
<
<
<
<
<
<


>
|




>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
|
>
|
>
>
>
>



>
|
>
>
>
|
>
>
>
>
>
>
|
|
|



>















>
>
>
>
>
|
|
|
>
|
|
<




|
>







>















>

|







31
32
33
34
35
36
37
38
39
40
41
42












43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  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
					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
		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
			}
︙︙
157
158
159
160
161
162
163




























































164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205

206
207
208

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272


273




274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295

296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
				break
			    }
			}
			s = s2
		}
	} // slice()





























































	function set_sym(p_v) {
	    var s, s2, note, pit, nn, p, a, m, i,
		sf = p_v.key.k_sf,
		delta = abc2svg.jianpu.cgd2cde[sf + 7] - 2

		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:
				delta = abc2svg.jianpu.cgd2cde[s.k_sf + 7] - 2
				s.a_gch = [{
					type: '@',
					font: abc.get_font("annotation"),
					wh: [10, 10],
					x: -5,
					y: 30,
					text: (s.k_mode + 1) + "=" +
						(abc2svg.jianpu.k_tb[s.k_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)

					slice(s)
				continue
			case C.NOTE:			// change the notes

				break
			}

			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)
				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_sym()

	// -- output_music --

	set_head()

	for (v = 0; v < voice_tb.length; v++)


		set_sym(voice_tb[v])





	of()
  }, // output_music()

  draw_symbols: function(of, p_voice) {
    var	i, m, nl, note, s, s2, x, y,
	C = abc2svg.C,
	abc = this,
	dot = "\ue1e7",
	staff_tb = abc.get_staff_tb(),
	out_svg = abc.out_svg,
	out_sxsy = abc.out_sxsy,
	xypath = abc.xypath

	if (!abc.cfmt().jianpu) {
		of(p_voice)
		return
	}

	// draw the duration lines under the notes
	function draw_dur(s1, y, s2, n, nl) {
	    var s, s3


		xypath(s1.x - 3, y + 5)
		out_svg('h' + (s2.x - s1.x + 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, y, s, n, nl)
				}
				if (s == s2)
					break
				s = s.next
			}
		}
	} // draw_dur()







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|
|
<

|



















|





|

|







|
>



>

<
|
<
<
|
<
<
<
<
|
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








|
>
>
|
>
>
>
>





|








|





|
|
>

|
|














|







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313

314


315




316


317









































318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
				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 (v > 0 && voice_tb[v - 1].st == p_v.st)
				ov_def(v)
		}
	}

	of()
  }, // output_music()

  draw_symbols: function(of, p_voice) {
    var	s, s2, nl, y,
	C = abc2svg.C,
	abc = this,
	dot = "\ue1e7",
	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()
︙︙
356
357
358
359
360
361
362




363


364





















365
366
367
368
369
370
371
372
373
374


375
376

377

378
379

380
381
382

383
384
385



386
387

388
389
390
391
392
393
394
395
396
397


398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432


433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450

451
452
453
454





















455
456
457
458

459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487

488
489
490
491
492
493
494
					out_mus(x - 1, ym, dot)
				}
			}
			y += 20
		}
	} // draw_hd()





	// -- 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:
			x = s.x


			y = staff_tb[s.st].y
			draw_hd(s, x, y)



			if (s.nflags >= 0 && s.dots)
				out_mus(x + 8, y + 13, dot)

			if (s.nflags > 0) {
				if (s.time == p_voice.sym.time)
					s.beam_st = 1	// beam continuation

//fixme: ko with rests because no beam_st /_end
				if (s.beam_st || s.type == C.REST) {
					nl = s.nflags



					s2 = s
					while (1) {

						if (s2.nflags && s2.nflags > nl)
							nl = s2.nflags
						if (s2.beam_end)
							break
						if (!s2.next
						 || !s2.next.nflags
						 || s2.next.nflags <= 0)
							break
						s2 = s2.next
					}


					draw_dur(s, y, s2, 1, nl)
				}
			}
			break
		}
	}
  }, // draw_symbols()

// set some parameters
    set_fmt: function(of, cmd, param) {
	if (cmd == "jianpu") {
	    var	cfmt = this.cfmt()

		if (!this.get_bool(param))
			return
		cfmt.jianpu = true
		cfmt.staffsep = 20
		cfmt.sysstaffsep = 14
		this.set_v_param("stafflines", "...")
		cfmt.tuplets = [0, 1, 0, 1]	// [auto, slur, number, above]
		return
	}
	of(cmd, param)
    }, // set_fmt()

// adjust some values
    set_pitch: function(of, last_s) {
	of(last_s)
	if (!last_s
	 || !this.cfmt().jianpu)
		return			// first time

    var	C = abc2svg.C
	
	for (var s = this.get_tsfirst(); s; s = s.ts_next) {


		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
			}

			break
		}
	}
    }, // set_pitch()






















// set the width of some symbols
    set_width: function(of, s) {
	of(s)

	if (!this.cfmt().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 = 0
		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.do_pscom = abc2svg.jianpu.do_pscom.bind(abc, abc.do_pscom)
	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_width = abc2svg.jianpu.set_width.bind(abc, abc.set_width)

	// big staccato dot
	abc.get_glyphs().gstc = '<circle id="gstc" cx="0" cy="-3" r="2"/>'
	abc.get_decos().gstc = "0 gstc 5 1 1"

	abc.add_style("\n.fj{font:15px sans-serif}")







>
>
>
>
|
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









|
>
>
|
|
>
|
>
|
<
>
|
|
|
>
|
|
|
>
>
>
|
|
>
|
|
|
|
<
<
<
<
<
|
>
>
|
<
<








<
<
<
<
<
<
<
|
<








|
<





>
>


















>




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




>
|









|













<




>







417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470

471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488





489
490
491
492


493
494
495
496
497
498
499
500







501

502
503
504
505
506
507
508
509
510

511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590

591
592
593
594
595
596
597
598
599
600
601
602
					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('<g transform="translate(')
			out_sxsy(x, ',', y + 15)	// (font height)
			out_svg(') scale(.5)">\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('</g>\n')
			abc.stv_g().g--
		}
	} // 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 = '<circle id="gstc" cx="0" cy="-3" r="2"/>'
	abc.get_decos().gstc = "0 gstc 5 1 1"

	abc.add_style("\n.fj{font:15px sans-serif}")
︙︙
Changes to modules/mdnn.js.
1
2
3
4
5
6
7
8
9
10
11
// 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-2021 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.



|







1
2
3
4
5
6
7
8
9
10
11
// 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.
︙︙
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
	prev_oct = -10

	if (!cfmt.mdnn) {
		of()
		return
	}

	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
//			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







|











|







116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
	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
︙︙
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
		return

    var	C = abc2svg.C,
	s = this.get_tsfirst()

	if (s && s.next && s.next.type == C.KEY)
//fixme:nsk
		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)







|







265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
		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)
︙︙
Changes to modules/nns.js.
︙︙
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
	} // get_l()

	function build_cell(c) {
	    var	i, j, k,
		u = '',
		t = ''

		row += '   '
		if (c.length == 1) {
			row += c[0]	// one chord in the measure
			return
		}
		i = 0
		while (i < 4) {
			t += c[i]
			k = get_l(c[i])
			j = k
			while (--j >= 0)
				u += ' '	// &nbsp;
			if (i < 3 && !c[i + 1]) {
				while (i < 3 && !c[i + 1])
					i++
				i++
				if (i < 4 && c[i])
					t += ' '
			} else {







|










|







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
	} // get_l()

	function build_cell(c) {
	    var	i, j, k,
		u = '',
		t = ''

		row += '   '
		if (c.length == 1) {
			row += c[0]	// one chord in the measure
			return
		}
		i = 0
		while (i < 4) {
			t += c[i]
			k = get_l(c[i])
			j = k
			while (--j >= 0)
				u += ' '
			if (i < 3 && !c[i + 1]) {
				while (i < 3 && !c[i + 1])
					i++
				i++
				if (i < 4 && c[i])
					t += ' '
			} else {
︙︙
Changes to modules/page.js.
1
2
3
4
5
6
7
8
9
10
// page.js - module to generate pages
//
// Copyright (C) 2018-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// page.js - module to generate pages
//
// Copyright (C) 2018-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.
︙︙
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// This module is loaded when "%%pageheight" appears in a ABC source.
//
// Parameters
//	%%pageheight <unit>

abc2svg.page = {

    // output a new block
    img_out: function(page, p) {
    var	cur_img_out = user.img_out;

	page.user_out(p)
	
	// if user.img_out has been changed
	if (user.img_out != cur_img_out) {
		page.user_out = user.img_out	// save the new function
		user.img_out = cur_img_out	// and restore ours
	}
    },

    // function called at end of generation
    abc_end: function(of) {
    var page = this.page
	if (page && page.in_page) {
		abc2svg.page.close_page(page)

		if (user.img_out == page.img_out_sav)
			user.img_out = page.user_out
	}
	of()
    }, // abc_end()

    // 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,
	font = page.abc.get_font(ty),
	str = page[ty],
	cfmt = page.abc.cfmt(),
	fh = font.size * 1.1,
	pos = [ '">',
		'" text-anchor="middle">',
		'" text-anchor="end">' ]

	// replace <>& by XML character references







<
<
<
<
<
<
<
<
<
<
<
<
<



|

>
|
|






|

<







20
21
22
23
24
25
26













27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
// This module is loaded when "%%pageheight" appears in a ABC source.
//
// Parameters
//	%%pageheight <unit>

abc2svg.page = {














    // function called at end of generation
    abc_end: function(of) {
    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()

    // 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),

	cfmt = page.abc.cfmt(),
	fh = font.size * 1.1,
	pos = [ '">',
		'" text-anchor="middle">',
		'" text-anchor="end">' ]

	// replace <>& by XML character references
︙︙
185
186
187
188
189
190
191





192
193
194
195
196
197
198
	function font_class(font) {
		if (font.class)
			return 'f' + font.fid + cfmt.fullsvg + ' ' + font.class
		return 'f' + font.fid + cfmt.fullsvg
	}

	// gen_hf






	if (str[0] == '-') {		// not on 1st page
		if (page.pn == 1)
			return 0
		str = str.slice(1)
	}








>
>
>
>
>







172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
	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)
	}

︙︙
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
	if (page.first)
		page.first = false
	else
		sty += ";page-break-before:always"
	if (page.gutter)
		sty += ";margin-left:" +
			((page.pn & 1) ? page.gutter : -page.gutter).toFixed(1) + "px"
	abc2svg.page.img_out(page, sty + '">')
	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")
		sty = abc.get_font_style().slice(l)		// new style(s)
		if (cfmt.fullsvg || sty != page.hsty) {
			page.hsty = sty
			sty = '<style>' + sty + '\n</style>\n'
		} else {
			sty = ''
		}
		abc2svg.page.img_out(page,
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	width="' + cfmt.pagewidth.toFixed(0) +
			'px" height="' + (ht + h).toFixed(0) +
			'px">\n' + sty +
			'<g transform="translate(0,' +
				page.topmargin.toFixed(1) + ')">' +
				page.hf + '</g>\n</svg>')
		page.hmax -= h;
		page.hf = ''
	} else {
		abc2svg.page.img_out(page,
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	width="' + cfmt.pagewidth.toFixed(0) +
			'px" height="' + ht.toFixed(0) +
			'px">\n</svg>')
	}
	if (page.footer) {







|

















|











|







239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
	if (page.first)
		page.first = false
	else
		sty += ";page-break-before:always"
	if (page.gutter)
		sty += ";margin-left:" +
			((page.pn & 1) ? page.gutter : -page.gutter).toFixed(1) + "px"
	abc2svg.page.user_out(sty + '">')
	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")
		sty = abc.get_font_style().slice(l)		// new style(s)
		if (cfmt.fullsvg || sty != page.hsty) {
			page.hsty = sty
			sty = '<style>' + sty + '\n</style>\n'
		} else {
			sty = ''
		}
		abc2svg.page.user_out(
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	width="' + cfmt.pagewidth.toFixed(0) +
			'px" height="' + (ht + h).toFixed(0) +
			'px">\n' + sty +
			'<g transform="translate(0,' +
				page.topmargin.toFixed(1) + ')">' +
				page.hf + '</g>\n</svg>')
		page.hmax -= h;
		page.hf = ''
	} else {
		abc2svg.page.user_out(
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	width="' + cfmt.pagewidth.toFixed(0) +
			'px" height="' + ht.toFixed(0) +
			'px">\n</svg>')
	}
	if (page.footer) {
︙︙
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
    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.img_out(page,
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	width="' + cfmt.pagewidth.toFixed(0) +
			'px" height="' + h.toFixed(0) +
			'px">\n' + page.ffsty +
			'<g transform="translate(0,' +
				(h - page.fh).toFixed(1) + ')">' +
			page.hf + '</g>\n</svg>')
	}
	abc2svg.page.img_out(page, '</div>')
	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.img_out(page, page.blk.shift())
		page.blk = null			// direct output
	} // blkcpy()

	// img_in()
	switch (p.slice(0, 4)) {
	case "<div":				// block of new tune
		if (p.indexOf('newpage') > 0







|









|











|







299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
    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.user_out(
			'<svg xmlns="http://www.w3.org/2000/svg" version="1.1"\n\
	xmlns:xlink="http://www.w3.org/1999/xlink"\n\
	width="' + cfmt.pagewidth.toFixed(0) +
			'px" height="' + h.toFixed(0) +
			'px">\n' + page.ffsty +
			'<g transform="translate(0,' +
				(h - page.fh).toFixed(1) + ')">' +
			page.hf + '</g>\n</svg>')
	}
	abc2svg.page.user_out('</div>')
	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 "<div":				// block of new tune
		if (p.indexOf('newpage') > 0
︙︙
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
				break		// error
		}

		// if no overflow yet, keep the block
		if (page.blk)
			page.blk.push(p)
		else
			abc2svg.page.img_out(page, p)
		page.h += h
		break
	case "</di":				// end of block
		if (page.blk)
			blkcpy(page)
		break
//	default:







|







368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
				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 "</di":				// end of block
		if (page.blk)
			blkcpy(page)
		break
//	default:
︙︙
401
402
403
404
405
406
407


408


409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432








433
434
435
436
437
438
439

	if (cmd == "pageheight") {
		v = this.get_unit(parm)
		if (isNaN(v)) {
			this.syntax(1, this.errs.bad_val, '%%' + cmd)
			return
		}


		cfmt.pageheight = v



		// if first definition, install the hook
		if (!page && user.img_out && abc2svg.abc_end) {
			this.page = page = {
				abc: this,
				topmargin: 38,	// 1cm
				botmargin: 38,	// 1cm
//				gutter: 0,
				h: 0,		// current page height
				pn: 0,		// page number
				pna: 0,		// absolute page number
				first: true,	// no skip to next page
				user_out: user.img_out
			}

			// don't let the backend handle the header/footer
			if (cfmt.header) {
				page.header = cfmt.header;
				cfmt.header = null
			}
			if (cfmt.footer) {
				page.footer = cfmt.footer;
				cfmt.footer = null
			}









			// get the previously defined page parameters
			if (cfmt.botmargin != undefined) {
				v = this.get_unit(cfmt.botmargin)
				if (!isNaN(v))
					page.botmargin = v
			}







>
>

>
>


|








|
<











>
>
>
>
>
>
>
>







393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416

417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442

	if (cmd == "pageheight") {
		v = this.get_unit(parm)
		if (isNaN(v)) {
			this.syntax(1, this.errs.bad_val, '%%' + cmd)
			return
		}
		if (!user.img_out || !abc2svg.abc_end)
			v = 0
		cfmt.pageheight = v
		if (!v)
			return

		// if first definition, install the hook
		if (!page || !abc2svg.page.user_out) {
			this.page = page = {
				abc: this,
				topmargin: 38,	// 1cm
				botmargin: 38,	// 1cm
//				gutter: 0,
				h: 0,		// current page height
				pn: 0,		// page number
				pna: 0,		// absolute page number
				first: true	// no skip to next page

			}

			// don't let the backend handle the header/footer
			if (cfmt.header) {
				page.header = cfmt.header;
				cfmt.header = null
			}
			if (cfmt.footer) {
				page.footer = cfmt.footer;
				cfmt.footer = null
			}
			if (cfmt.header2) {
				page.header2 = cfmt.header2
				cfmt.header2 = null
			}
			if (cfmt.footer2) {
				page.footer2 = cfmt.footer2
				cfmt.footer2 = null
			}

			// get the previously defined page parameters
			if (cfmt.botmargin != undefined) {
				v = this.get_unit(cfmt.botmargin)
				if (!isNaN(v))
					page.botmargin = v
			}
︙︙
449
450
451
452
453
454
455


456
457
458
459
460
461
462
463
464
465
466


467
468
469
470
471
472
473
			}
			if (cfmt.oneperpage)
				page.oneperpage = this.get_bool(cfmt.oneperpage)
			if (!cfmt.dateformat)
				cfmt.dateformat = "%b %e, %Y %H:%M"

			// set the hooks


			user.img_out = abc2svg.page.img_in.bind(this);
			page.img_out_sav = user.img_out;
			abc2svg.abc_end = abc2svg.page.abc_end.bind(this,
								abc2svg.abc_end)
		}
		return
	}
	if (page) {
		switch (cmd) {
		case "header":
		case "footer":


			page[cmd] = parm
			return
		case "newpage":
			if (!parm)
				break
			v = Number(parm)
			if (isNaN(v)) {







>
>

<









>
>







452
453
454
455
456
457
458
459
460
461

462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
			}
			if (cfmt.oneperpage)
				page.oneperpage = this.get_bool(cfmt.oneperpage)
			if (!cfmt.dateformat)
				cfmt.dateformat = "%b %e, %Y %H:%M"

			// set the hooks
			if (!abc2svg.page.user_out)
				abc2svg.page.user_out = user.img_out
			user.img_out = abc2svg.page.img_in.bind(this);

			abc2svg.abc_end = abc2svg.page.abc_end.bind(this,
								abc2svg.abc_end)
		}
		return
	}
	if (page) {
		switch (cmd) {
		case "header":
		case "footer":
		case "header2":
		case "footer2":
			page[cmd] = parm
			return
		case "newpage":
			if (!parm)
				break
			v = Number(parm)
			if (isNaN(v)) {
︙︙
Changes to modules/pedline.js.
1
2
3
4
5
6
7
8
9
10
// pedline.js - module to draw pedal lines instead of 'Ped .. *'
//
// Copyright (C) 2020-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// pedline.js - module to draw pedal lines instead of 'Ped .. *'
//
// Copyright (C) 2020-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.
︙︙
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//	\ de.prev
// \ de.prev.start
// |_____/\____|
				de.defl.nost = true
				de.prev.defl.noen = true
				de.x = de.prev.s.x - 5
				de.val = de.s.x - de.x - 5
				if (!de.prev.prev)
					de.prev.val = de.x - de.prev.x
			} else {
				de.x -= 3
				de.val += 10
			}
		}
	}
	of()







<
|







41
42
43
44
45
46
47

48
49
50
51
52
53
54
55
//	\ de.prev
// \ de.prev.start
// |_____/\____|
				de.defl.nost = true
				de.prev.defl.noen = true
				de.x = de.prev.s.x - 5
				de.val = de.s.x - de.x - 5

				de.prev.val = de.x - de.prev.x
			} else {
				de.x -= 3
				de.val += 10
			}
		}
	}
	of()
︙︙
Changes to modules/perc.js.
1
2
3
4
5
6
7
8
9
10
// perc.js - module to handle %%percmap
//
// Copyright (C) 2018-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// 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.
︙︙
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
		" 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,
	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];
			if (!curvoice.midictl)
				curvoice.midictl = []
			curvoice.midictl[0] = 1		// bank 128
			break
		}
	}
    }, // set_perc()

    do_pscom: function(of, text) {
	if (text.slice(0, 8) == "percmap ")







|








|
|
|







205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
		" 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 ")
︙︙
Changes to modules/psvg.js.
︙︙
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
	} else if (g != 2) {
		if (!g)
			defg1()
		svgbuf += '<text x="' + (x + gcur.xoffs).toFixed(1) + '" y="' +
				(gcur.yoffs - y).toFixed(1) + '">';
		g = 2
	}
	svgbuf += s.replace(/<|>|&|  /g, function(c){
			switch (c) {
			case '<': return "&lt;"
			case '>': return "&gt;"
			case '&': return "&amp;"
			case '  ': return '  '		// space + nbspace
			}
		})
	if (span)
		svgbuf += "</tspan>";
	gcur.cx = x + strw(s)
    }








|




<







469
470
471
472
473
474
475
476
477
478
479
480

481
482
483
484
485
486
487
	} else if (g != 2) {
		if (!g)
			defg1()
		svgbuf += '<text x="' + (x + gcur.xoffs).toFixed(1) + '" y="' +
				(gcur.yoffs - y).toFixed(1) + '">';
		g = 2
	}
	svgbuf += s.replace(/<|>|&/g, function(c){
			switch (c) {
			case '<': return "&lt;"
			case '>': return "&gt;"
			case '&': return "&amp;"

			}
		})
	if (span)
		svgbuf += "</tspan>";
	gcur.cx = x + strw(s)
    }

︙︙
Changes to modules/sth.js.
︙︙
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 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)
		s.ys = s.sth
	if (s2.sth)
		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







|

|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 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
︙︙
Changes to modules/strtab.js.
︙︙
22
23
24
25
26
27
28





29
30
31
32
33
34
35
36
37
38
39


































































































40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82


83
84

85
86
87
88
89
90
91
92
93
94
95
96

97














98
99
100
101
102
103
104
105
106
107





108
109
110
111
112
113
114




115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Syntax:
//	%%strtab <string list> [diafret]
// <string list> 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')





//
// 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,
	C = abc2svg.C,
	abc = this



































































































	if (!p_v.tab) {
		of(p_v)
		return
	}

	// define the 'bgx' filter if not done yet
	m = abc.cfmt().bgcolor || "white"
	if (abc.bgt != m) {
		if (!abc.bgn)
			abc.bgn = 1
		else
			abc.bgn++
		abc.bgt = m
		abc.defs_add('\
<filter x="-0.1" y="0.2" width="1.2" height=".8" id="bg' + abc.bgn + '">\n\
<feFlood flood-color="' + m + '"/>\n\
<feComposite in="SourceGraphic" operator="over"/>\n\
</filter>')
		abc.add_style('\n.bg' + abc.bgn + '{filter:url(#bg' + abc.bgn + ')}')
	}

	// adjust the symbol before generation
	for (s = p_v.sym; s; s = s.next) {
		switch (s.type) {
		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
	abc.out_svg('<g class="bn">\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('<text class="bg' + abc.bgn +
					'" x="')
				abc.out_sxsy(x, '" y="', stb + y - 2.5)
				abc.out_svg('">' + not.nb + '</text>\n')

			}














		}
	}
	abc.out_svg('</g>\n')
    }, // draw_symbols()

    // set a format parameter
    set_fmt: function(of, cmd, parm) {
	if (cmd == "strtab") {
		if (!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




	}
	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,
	C = abc2svg.C,
	abc = this,
	s = abc.get_tsfirst(),		// first symbol
	strs = [],			// notes per staff - index = staff
	lstr = []			// lowest string per staff - index staff

	// set a string (pitch) and a fret number







>
>
>
>
>








|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>















|














<
<
<
<






<

|
>
>

|
>
|
|
<
|
<
<
|
<
<
<
|
<
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>










>
>
>
>
>







>
>
>
>




<
<
<
<
<
<
<
<
<
<
<
<
<
<


|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172




173
174
175
176
177
178

179
180
181
182
183
184
185
186
187

188


189



190

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236














237
238
239
240
241
242
243
244
245
246
// Syntax:
//	%%strtab <string list> [diafret]
// <string list> 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 [ <string number> : <fret number> ]*
// 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, 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('<text class="bg' + abc.bgn +
				'" x="')
			abc.out_sxsy(x, '" y="', stb + y - 2.5)
			abc.out_svg('">' + not.nb + '</text>\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('<path class="sW" d="M')
		abc.out_sxsy(s.x, ' ', y)
		abc.out_svg('v' + h.toFixed(1) +'"/>\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('<text class="f'
				+ abc.get_font('music').fid
				+ '" transform="translate(')
			abc.out_sxsy(s.x, ',', y)
			abc.out_svg(') scale('
				+ (s.grace ? '.5,.5' : '.7,.7') + ')">'
				+ String.fromCharCode(0xe23f + 2 * s.nflags)
				+ '</text>\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('<path d="M')
		abc.out_sxsy(s1.x, ' ', y)
		abc.out_svg('h' + l
				+ 'v-3h-' + l
				+ 'v3"/>\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('<path d="M')
			abc.out_sxsy(s1.x, ' ', y)
			abc.out_svg('h' + l
					+ 'v-3h-' + l
					+ 'v3"/>\n')
			if (s == s2)
				break
			s = s.next
			if (s == s2)
				break
		}
	} // draw_stems()

	if (!p_v.tab) {
		of(p_v)
		return
	}

	// define the 'bgx' filter if not done yet
	m = abc.cfmt().bgcolor || "white"
	if (abc.bgt != m) {
		if (!abc.bgn)
			abc.bgn = 1
		else
			abc.bgn++
		abc.bgt = m
		abc.defs_add('\
<filter x="-0.1" y="0.1" width="1.2" height=".8" id="bg' + abc.bgn + '">\n\
<feFlood flood-color="' + m + '"/>\n\
<feComposite in="SourceGraphic" operator="over"/>\n\
</filter>')
		abc.add_style('\n.bg' + abc.bgn + '{filter:url(#bg' + abc.bgn + ')}')
	}

	// adjust the symbol before generation
	for (s = p_v.sym; s; s = s.next) {
		switch (s.type) {
		case C.KEY:
		case C.METER:
		case C.REST:
			s.invis = true
			break




		}
	}
	of(p_v)

	abc.glout()			// output the stems


	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('<g class="bn">\n')
	for (s = p_v.sym; s; s = s.next) {
		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('</g>\n')
    }, // draw_symbols()

    // 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()















    // change the notes when the global generation settings are done
    set_stems: function(of) {
    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

	// set a string (pitch) and a fret number
︙︙
150
151
152
153
154
155
156

157
158
159
160
161
162
163
164
165
166
167










































































168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186


187
188
189











190
191
192
193
194
195
196
197
198
199
200
201

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
			nt.pit = i * 2 + 18
		} else {
			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 (!lstr[st])
				lstr[st] = [ 10 ]
			if (lstr[st][0] > i) {
				lstr[st][0] = i
				lstr[st][1] = s
			}
			s.stemless = true
		}
	} // set_pit()











































































	// get the string number from the decoration
	// format is either !<n>s! or !<n>!
	function strnum(n) {
		n = n.match(/^([1-9])s?$/)
		return n ? p_v.tab.length - n[1] : -1
	} // strnum()

	// change the notes of the strings when a capo
	p_v = abc.get_voice_tb()
	for (n = 0; n < p_v.length; n++) {
		if (!p_v[n].tab)
			continue
		m = p_v[n].capo
		if (m) {
			for (i = 0; i < p_v[n].tab.length; i++)
				p_v[n].tab[i] += m
		}
	}



	// loop on the notes of the voices with a tablature
	for ( ; s; s = s.ts_next) {











		p_v = s.p_v
		if (!p_v.tab)
			continue
		strss = strs[s.st]
		if (!strss)
			strss = strs[s.st] = []
		switch (s.type) {
		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()
    }, // 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,
		t = []

		if (p_v.diafret) {
		    for (i = 0; i < p.length; i++) {
			c = p[i]
			c = "CDEFGABcdefgab".indexOf(c)
			if (c < 0)







>
|
|









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



















>
>



>
>
>
>
>
>
>
>
>
>
>












>

|
<
<
|
<
|
<
<
|
<
<
|
|
|
<
|
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
|
<
<
<
<
<
<
|
|
<
<
<









|







254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396


397

398


399


400
401
402

403



404
















405















406

407






408
409



410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
			nt.pit = i * 2 + 18
		} else {
			nt.nb = -1
			nt.pit = 18
		}
		nt.acc = 0
		nt.invis = true
		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 !<n>s! or !<n>!
	function strnum(n) {
		n = n.match(/^([1-9])s?$/)
		return n ? p_v.tab.length - n[1] : -1
	} // strnum()

	// change the notes of the strings when a capo
	p_v = abc.get_voice_tb()
	for (n = 0; n < p_v.length; n++) {
		if (!p_v[n].tab)
			continue
		m = p_v[n].capo
		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)
			strss = strs[s.st] = []
		switch (s.type) {
		case C.KEY:
		case C.REST:
		case C.TIME:
			s.invis = true
		default:
			delete s.a_dd
			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, a,
		t = []

		if (p_v.diafret) {
		    for (i = 0; i < p.length; i++) {
			c = p[i]
			c = "CDEFGABcdefgab".indexOf(c)
			if (c < 0)
︙︙
297
298
299
300
301
302
303










304
305
306
307
308
309
310
311
312
313
314
				}
			}
			t.push(c)
		    }
		} else {
		    for (i = 0; i < p.length; i++) {
			c = p[i]










			c = "CCDDEFFGGAABccddeffggaab".indexOf(c)
			if (c < 0)
				return // null
			c += 60
			while (1) {
				if (p[i + 1] == "'") {
					c += 12
					i++
				} else if (p[i + 1] == ",") {
					c -= 12
					i++







>
>
>
>
>
>
>
>
>
>



|







438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
				}
			}
			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 + a
			while (1) {
				if (p[i + 1] == "'") {
					c += 12
					i++
				} else if (p[i + 1] == ",") {
					c -= 12
					i++
︙︙
340
341
342
343
344
345
346










347


348
349
350
351
352
353
354

















355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374

375
376
377
378
379
380
381
382
383



384
385
386
387
388







389

390
391
392
393
394
395
396
397
		    }
		} else {
		    while (1) {
			str = a.shift()
			if (!str)
				break
			p = "CCDDEFFGGAAB".indexOf(str[0])










			o = Number(str[1])


			if (p < 0 || isNaN(o))
				return // null
			t.push((o + 1) * 12 + p)	// C4 = 60
		    }
		}
		return t
	} // str2tab()


















	for (i = 0; i < a.length; i++) {
		switch (a[i]) {
		case "clef=":
			e = a[i + 1]
			if (e != "tab")
				break
			a.splice(i, 1)
			// fall thru
		case "tab":
			a.splice(i, 1)
			i--
			ok = true
			break
		case "strings=":
			strs = a[++i]
			ok = true
			break
		case "nostems":
			p_v.pos.stm = abc2svg.C.SL_HIDDEN

			break
		case "capo=":
			p_v.capo = Number(a[++i])
			break
		case "diafret=":
			i++
		case "diafret":
			p_v.diafret = true
			break



		}
	}

	// define the elements of the tablature
	if (ok) {







		if (strs) {

			if (strs[1] >= '1' && strs[1] <= '9')
				tab = str2tab(strs.split(','))	// W.V.'s syntax
			else
				tab = abc2tab(strs)		// ABC syntax
			if (!tab) {
				this.syntax(1, "Bad strings in tablature")
				ok = false
			}







>
>
>
>
>
>
>
>
>
>
|
>
>
|
|





>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




















>









>
>
>





>
>
>
>
>
>
>

>
|







491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
		    }
		} else {
		    while (1) {
			str = a.shift()
			if (!str)
				break
			p = "CCDDEFFGGAAB".indexOf(str[0])
			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 <string number> ':' <fret number>
	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]
			if (e != "tab")
				break
			a.splice(i, 1)
			// fall thru
		case "tab":
			a.splice(i, 1)
			i--
			ok = true
			break
		case "strings=":
			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) {
			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")
				ok = false
			}
︙︙
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
		a.push("clef=")				// set the clef
		g = this.get_glyphs()
		if (tab.length == 3) {
			a.push('"tab3"')
			if (!g.tab3)
// SMuFL: -none-
				g.tab3 = '<text id="tab3"\
 x="-4,-4,-4" y="-4,3,10"\
 style="font:bold 8px sans-serif">TAB</text>'
		} else if (tab.length == 4) {
			a.push('"tab4"')
			if (!g.tab4)
// SMuFL: \ue06e
				g.tab4 = '<text id="tab4"\
 x="-4,-4,-4" y="-8,1,10"\
 style="font:bold 12px sans-serif">TAB</text>'
		} else if (tab.length == 5) {
			a.push('"tab5"')
			if (!g.tab5)
// SMuFL: -none-
				g.tab5 = '<text id="tab5"\
 x="-4,-4,-4" y="-11,-2,7"\







|






|







607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
		a.push("clef=")				// set the clef
		g = this.get_glyphs()
		if (tab.length == 3) {
			a.push('"tab3"')
			if (!g.tab3)
// SMuFL: -none-
				g.tab3 = '<text id="tab3"\
 x="-2,-2,-2" y="-4,3,10"\
 style="font:bold 8px sans-serif">TAB</text>'
		} else if (tab.length == 4) {
			a.push('"tab4"')
			if (!g.tab4)
// SMuFL: \ue06e
				g.tab4 = '<text id="tab4"\
 x="-3,-3,-3" y="-8,1,10"\
 style="font:bold 12px sans-serif">TAB</text>'
		} else if (tab.length == 5) {
			a.push('"tab5"')
			if (!g.tab5)
// SMuFL: -none-
				g.tab5 = '<text id="tab5"\
 x="-4,-4,-4" y="-11,-2,7"\
︙︙
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
	of(a)
    }, // set_vp()

    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"
	decos["2s"] = "0 nul 0 0 0"
	decos["3s"] = "0 nul 0 0 0"
	decos["4s"] = "0 nul 0 0 0"
	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}")
    } // set_hooks()
} // strtab

abc2svg.modules.hooks.push(abc2svg.strtab.set_hooks)
abc2svg.modules.strtab.loaded = true







<













|





644
645
646
647
648
649
650

651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
	of(a)
    }, // set_vp()

    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_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"
	decos["2s"] = "0 nul 0 0 0"
	decos["3s"] = "0 nul 0 0 0"
	decos["4s"] = "0 nul 0 0 0"
	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 7px sans-serif}")
    } // set_hooks()
} // strtab

abc2svg.modules.hooks.push(abc2svg.strtab.set_hooks)
abc2svg.modules.strtab.loaded = true
Added modules/tropt.js.




















































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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 <http://www.gnu.org/licenses/>.
//
// 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
Changes to package.json.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17




18
19
20
21
22
23
24
{
  "name": "abc2svg",
  "version": "1.21.6",
  "description": "ABC music notation translator",
  "files": [
    "COPYING",
    "COPYING.LESSER",
    "MIDI-1.js",
    "Scc1t2/",
    "abc2odt",
    "abc2svg",
    "abc2svg-1.js",
    "abc2svg.ttf",
    "abcdoc-1.js",
    "abcemb-1.js",
    "abcemb1-1.js",
    "abcemb2-1.js",




    "abctopdf",
    "abcweb-1.js",
    "abcweb1-1.js",
    "abcweb2-1.js",
    "ambitus-1.js",
    "break-1.js",
    "capo-1.js",


|














>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
  "name": "abc2svg",
  "version": "1.22.1",
  "description": "ABC music notation translator",
  "files": [
    "COPYING",
    "COPYING.LESSER",
    "MIDI-1.js",
    "Scc1t2/",
    "abc2odt",
    "abc2svg",
    "abc2svg-1.js",
    "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",
    "break-1.js",
    "capo-1.js",
︙︙
39
40
41
42
43
44
45

46
47
48
49
50
51
52
    "edit-pt_BR.js",
    "equalbars-1.js",
    "err-de.js",
    "err-en.js",
    "err-fr.js",
    "err-it.js",
    "err-pt_BR.js",

    "grid-1.js",
    "grid2-1.js",
    "grid3-1.js",
    "json-1.js",
    "jazzchord-1.js",
    "jianpu-1.js",
    "mdnn-1.js",







>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    "edit-pt_BR.js",
    "equalbars-1.js",
    "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",
    "jianpu-1.js",
    "mdnn-1.js",
︙︙
74
75
76
77
78
79
80




81
82
83
84
85
86
87
  ],
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "bin": {
    "abc2odt": "abc2odt",
    "abc2svg": "abc2svg",




    "abctopdf": "abctopdf"
  },
  "repository": {
    "type": "fossil",
    "url": "https://chiselapp.com/user/moinejf/repository/abc2svg"
  },
  "keywords": [







>
>
>
>







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  ],
  "scripts": {
    "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"
  },
  "keywords": [
︙︙
Changes to toabc.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - toabc.js - convert ABC to ABC
//
// Copyright (C) 2016-2021 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
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - toabc.js - convert ABC to ABC
//
// 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
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
225
226
227
228
229
230
231
232


233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249
250
251
252
253


254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271

272



273
274
275
276
277
278
279
280
281
282
283
284
285


286
287
288
289
290
291
292
			ln += (s.clef_oct_transp ? '^' : '+') + '8'
		else if (s.clef_octave == -7)
			ln += (s.clef_oct_transp ? '_' : '-') + '8'
		return "clef=" + ln
	} // clef_dump()

	function deco_dump(a_dd) {
	    var	i, n, dd


//fixme: check if user deco
		for (i = 0; i < a_dd.length; i++) {
			dd = a_dd[i]
			n = dd.name
			if (dd.ty) {
				if (dd.ty != '@')
					line += '!' +dd.ty + n + '!'
				else
					line += '!@' + dd.x + ',' + dd.y + n + '!'
			} else {
				if (deco_l[n])
					line += deco_l[n]
				else
					line += '!' + n + '!'
			}
		}

	} // deco_dump

	function dur_dump(dur, grace) {
	    var	d = 0,
		l = grace ? C.BLEN / 4 : ulen


		if (dur == l)
			return
		while (1) {
			if (dur % l == 0) {
				dur /= l
				if (dur != 1)
					line += dur.toString()
				break
			}
			dur *= 2
if (d > 6) {
abc2svg.print("% *** bad duration")
break
}
			d++
		}
		if (d > 0)
			line += "//////".slice(0, d)

	} // dur_dump()




	function font_def(fn, p) {
	    var	c, f,
		i = p.indexOf('$')

		abc.get_font(fn)		// used font
// fixme: one '$' only
		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!


			}
		}
		font_dump()		// dump the new fonts
	} // font_def()

	function font_dump() {
	    var	k, f, def







|
>
>
















>




|
>
>

|




|










|
>

>
>
>












|
>
>







225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
			ln += (s.clef_oct_transp ? '^' : '+') + '8'
		else if (s.clef_octave == -7)
			ln += (s.clef_oct_transp ? '_' : '-') + '8'
		return "clef=" + ln
	} // clef_dump()

	function deco_dump(a_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) {
				if (dd.ty != '@')
					line += '!' +dd.ty + n + '!'
				else
					line += '!@' + dd.x + ',' + dd.y + n + '!'
			} else {
				if (deco_l[n])
					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,
		ln = ""

		if (dur == l)
			return ""
		while (1) {
			if (dur % l == 0) {
				dur /= l
				if (dur != 1)
					ln += dur.toString()
				break
			}
			dur *= 2
if (d > 6) {
abc2svg.print("% *** bad duration")
break
}
			d++
		}
		if (d > 0)
			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('$')

		abc.get_font(fn)		// used font
// fixme: one '$' only
		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 = 100 + +c
				f.used = 1
			}
		}
		font_dump()		// dump the new fonts
	} // font_def()

	function font_dump() {
	    var	k, f, def
︙︙
300
301
302
303
304
305
306
307
308
309
310
311





















312
313
314
315
316
317
318
319
320
321
322
			if (!f.used)
				continue	// not used
			if (old_font[f.fid])
				continue	// already out
			old_font[f.fid] = true
			def = f.name || ""
			if (f.weight)
				def += f.weight
			if (f.style)
				def += f.style
			if (!def)
				def = "*"





















			if (k[0] == "u")
				k = "setfont-" + k[1]
			abc2svg.print('%%' + k + ' ' +
				def + ' ' + (f.size || "*"))
		}
	} // font_dump()

	function gch_dump(a_gch) {
	    var i, j, gch

		for (i = 0; i < a_gch.length; i++) {







|

|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|
<







311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
			if (!f.used)
				continue	// not used
			if (old_font[f.fid])
				continue	// already out
			old_font[f.fid] = true
			def = f.name || ""
			if (f.weight)
				def += ft_w[(f.weight / 100) | 0]
			if (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)

		}
	} // font_dump()

	function gch_dump(a_gch) {
	    var i, j, gch

		for (i = 0; i < a_gch.length; i++) {
︙︙
359
360
361
362
363
364
365
366

367
368
369
370
371
372
373
374
375
376
377








378
379
380
381
382
383
384
			l = hl[i]
			if (info[l])
				abc2svg.print(l + ':' + info[l].replace(/\n/g, '\n' + l + ':'))
		}
	} // header_dump()

	function key_dump(s, clef) {
	    var	ln

		if (s.k_bagpipe) {
			bagpipe = true			// for grace notes
			ln = "K:H" + s.k_bagpipe
		} else if (s.k_drum) {
			ln = "K:P"
		} else if (s.k_none) {
			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]








		}

		if (clef && clef.clef_type != 'a')
			ln += ' ' + clef_dump(clef)
//fixme: to continue
		return ln
	} // key_dump()







|
>











>
>
>
>
>
>
>
>







390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
			l = hl[i]
			if (info[l])
				abc2svg.print(l + ':' + info[l].replace(/\n/g, '\n' + l + ':'))
		}
	} // header_dump()

	function key_dump(s, clef) {
	    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"
		} else if (s.k_none) {
			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
		return ln
	} // key_dump()
︙︙
448
449
450
451
452
453
454
455


456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516

517
518
519


520
521
522
523
524
525
526
527
528
529
530

531
532
533
534
535
536
537
			if (s.a_meter[i].bot)
				ln += '/' + s.a_meter[i].bot
		}
		return ln
	} // meter_dump()

	function note_dump(s, note, tie_ch) {
	    var	p, j, sl, s2, a, d


		if (note.sls) {
			for (j = 0; j < note.sls.length; j++) {
				sl = note.sls[j];
				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)
		if (note.color)
			line += "!" + note.color + "!"
		a = note.acc
		if (typeof a == "object") {
			d = a[1]
			a = a[0]
			if (a > 0) {
				line += '^'
			} else {
				line += '_'
				a = -a
			}
			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
			}
		}
		p = note.pit
		if (p >= 23) {
			line += "abcdefg"[p % 7]
			if (p >= 30) {
				line += "'"
				if (p >= 37)
					line += "'"
			}
		} else {
			p += 7;			// for very low notes
			line += "ABCDEFG"[p % 7]
			if (p < 23) {
				line += ","
				if (p < 16) {
					line += ","
					if (p < 9)
						line += ","
				}
			}
		}
		if (!tie_ch && note.tie_ty)
			slti_dump(note.tie_ty, '-')
		while (note.sl2) {
			line += ')';
			note.sl2--
		}

	} // note_dump()

	function slti_dump(fl, ty) {


		if (fl & C.SL_DOTTED)
			line += ".";
		line += ty
		switch (fl & 0x07) {
		case C.SL_ABOVE:
			line += "'"
			break
		case C.SL_BELOW:
			line += ','
			break
		}

	} // slti_dump()

//fixme: missing: '+' (master voice) and '*' (floating voice)
	function staves_dump(s) {
	    var	v, p_v, staff, ln,
		in_parenth,
		vn = [],







|
>
>



|








|

|





|

|


|


|
|
|
|
|




|

|

|



|

|

|

|




|

|


>



>
>

|
|


|


|


>







488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
			if (s.a_meter[i].bot)
				ln += '/' + s.a_meter[i].bot
		}
		return ln
	} // meter_dump()

	function note_dump(s, note, tie_ch) {
	    var	p, j, sl, s2, a, d,
		ln = ""

		if (note.sls) {
			for (j = 0; j < note.sls.length; j++) {
				sl = note.sls[j];
				ln += slti_dump(sl.ty, '(')
				s2 = sl.note.s
				if (s2.sl2)
					s2.sl2++
				else
					s2.sl2 = 1
			}
		}
		if (note.a_dd)
			ln += deco_dump(note.a_dd)
		if (note.color)
			ln += "!" + note.color + "!"
		a = note.acc
		if (typeof a == "object") {
			d = a[1]
			a = a[0]
			if (a > 0) {
				ln += '^'
			} else {
				ln += '_'
				a = -a
			}
			ln += dur_dump(C.BLEN / 4 * a / d, true)
		} else {
			switch (a) {
			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) {
			ln += "abcdefg"[p % 7]
			if (p >= 30) {
				ln += "'"
				if (p >= 37)
					ln += "'"
			}
		} else {
			p += 7;			// for very low notes
			ln += "ABCDEFG"[p % 7]
			if (p < 23) {
				ln += ","
				if (p < 16) {
					ln += ","
					if (p < 9)
						ln += ","
				}
			}
		}
		if (!tie_ch && note.tie_ty)
			ln += slti_dump(note.tie_ty, '-')
		while (note.sl2) {
			ln += ')'
			note.sl2--
		}
		return ln
	} // note_dump()

	function slti_dump(fl, ty) {
	    var	ln = ""

		if (fl & C.SL_DOTTED)
			ln += '.'
		ln += ty
		switch (fl & 0x07) {
		case C.SL_ABOVE:
			ln += "'"
			break
		case C.SL_BELOW:
			ln += ','
			break
		}
		return ln
	} // slti_dump()

//fixme: missing: '+' (master voice) and '*' (floating voice)
	function staves_dump(s) {
	    var	v, p_v, staff, ln,
		in_parenth,
		vn = [],
︙︙
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
					' ' + s.repeat_k + ']'
		}
		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, '(');
				if (sl.is_note) {
					if (sl.note.sl2)
						sl.note.sl2++
					else
						sl.note.sl2 = 1
				} else {
					if (sl.note.s.sl2)
						sl.note.s.sl2++
					else
						sl.note.s.sl2 = 1
				}
			}
		}
		if (s.a_gch)
			gch_dump(s.a_gch)
		if (s.a_dd)
			deco_dump(s.a_dd)
		if (s.color)
			line += "!" + s.color + "!"
		switch (s.type) {
		case C.BAR:
			if (s.beam_on)
				line += "!beamon!"
			if (s.bar_dotted)







|
















|







736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
					' ' + s.repeat_k + ']'
		}
		if (s.tp)
			tuplet_dump(s)
		if (s.sls) {
			for (i = 0; i < s.sls.length; i++) {
				sl = s.sls[i];
				line += slti_dump(sl.ty, '(')
				if (sl.is_note) {
					if (sl.note.sl2)
						sl.note.sl2++
					else
						sl.note.sl2 = 1
				} else {
					if (sl.note.s.sl2)
						sl.note.s.sl2++
					else
						sl.note.s.sl2 = 1
				}
			}
		}
		if (s.a_gch)
			gch_dump(s.a_gch)
		if (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)
				line += "!beamon!"
			if (s.bar_dotted)
︙︙
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
						s.p_v.pos.stm = C.SL_BELOW
						line += "[I:pos stem down]"
					}
				}
			}
			tie_ch = s.ti1
			if (s.notes.length == 1) {
				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 += ']'
			}
			if (s.grace) {
				if (bagpipe) {
					tmp = s.dur * 8
				} else {
					tmp = s.dur * 2
					if (s.prev || s.next)
						tmp *= 2
				}
				dur_dump(tmp, true)
			} else {
				tmp = s.notes[0].dur	// head duration
				if (s.trem2)
					tmp /= 2;
				dur_dump(tmp)
			}
			if (tie_ch)
				slti_dump(s.notes[0].tie_ty, '-');
			tmp = s.sl2
			while (tmp) {
				line += ')';
				tmp--
			}
			break
		case C.TEMPO:







|









|










|




|


|







814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
						s.p_v.pos.stm = C.SL_BELOW
						line += "[I:pos stem down]"
					}
				}
			}
			tie_ch = s.ti1
			if (s.notes.length == 1) {
				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++)
					line += note_dump(s, s.notes[i], tie_ch)
				line += ']'
			}
			if (s.grace) {
				if (bagpipe) {
					tmp = s.dur * 8
				} else {
					tmp = s.dur * 2
					if (s.prev || s.next)
						tmp *= 2
				}
				line += dur_dump(tmp, true)
			} else {
				tmp = s.notes[0].dur	// head duration
				if (s.trem2)
					tmp /= 2;
				line += dur_dump(tmp)
			}
			if (tie_ch)
				line += slti_dump(s.notes[0].tie_ty, '-')
			tmp = s.sl2
			while (tmp) {
				line += ')';
				tmp--
			}
			break
		case C.TEMPO:
︙︙
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
					abc2svg.print("%%writefields Q 1")
					cfmt.writefields += 'Q'
				}
			}
			tempo_dump(s)
			break
		case C.REST:
			line += s.invis ? 'x' : 'z';
			dur_dump(s.dur_orig)
			break
		case C.SPACE:
			line += 'y'
			if (s.width != 10)
				line += s.width
			break
		case C.STAVES:







<
|







866
867
868
869
870
871
872

873
874
875
876
877
878
879
880
					abc2svg.print("%%writefields Q 1")
					cfmt.writefields += 'Q'
				}
			}
			tempo_dump(s)
			break
		case C.REST:

			line += (s.invis ? 'x' : 'z') + dur_dump(s.dur_orig)
			break
		case C.SPACE:
			line += 'y'
			if (s.width != 10)
				line += s.width
			break
		case C.STAVES:
︙︙
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910

	for (v = 0; v < nv; v++) {
		vo[v] = ""
		vti[v] = 0
	}

	if (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
		}
	}







|







941
942
943
944
945
946
947
948
949
950
951
952
953
954
955

	for (v = 0; v < nv; v++) {
		vo[v] = ""
		vti[v] = 0
	}

	if (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
		}
	}
︙︙
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
		}
		line = "";

		// (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]);
			}
			vti[v] = s.time
		}
		sym_dump(s)
		if (s.dur)
			vti[v] = s.time + s.dur
		if (s.next) {







<
|







979
980
981
982
983
984
985

986
987
988
989
990
991
992
993
		}
		line = "";

		// (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])
			}
			vti[v] = s.time
		}
		sym_dump(s)
		if (s.dur)
			vti[v] = s.time + s.dur
		if (s.next) {
︙︙
Changes to tohtml.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// abc2svg - tohtml.js - HTML+SVG generation
//
// Copyright (C) 2014-2021 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
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

    var	init_done, pw, ml, mr, pkf, lkf

// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"


|
















|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// abc2svg - tohtml.js - HTML+SVG generation
//
// 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
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

    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) {
		case '<': return "&lt;"
		case '>': return "&gt;"
︙︙
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
			pw = npw
		psty += 'width:' + (npw - nml - nmr).toFixed(1) + 'px;'
	}
	return psty
}

// entry point from cmdline
abc2svg.abc_init = function() {
    var cfmt = abc.cfmt()

	// output a header or footer
	function gen_hf(type, str) {
	    var	i, page,
		lcr = ["l", "c", "r"],
//fixme: handle font changes?







|







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
			pw = npw
		psty += 'width:' + (npw - nml - nmr).toFixed(1) + 'px;'
	}
	return psty
}

// entry point from cmdline
abc2svg.abc_init = function(args) {
    var cfmt = abc.cfmt()

	// output a header or footer
	function gen_hf(type, str) {
	    var	i, page,
		lcr = ["l", "c", "r"],
//fixme: handle font changes?
︙︙
178
179
180
181
182
183
184





185
186
187
188
189
190
191
		abc2svg.print('</table>')
	}

	user.page_format = true

	// output the html header
	user.img_out = function(str) {





		var	header = cfmt.header,
			footer = cfmt.footer,
			topmargin = cfmt.topmargin || "1cm",
			botmargin = cfmt.botmargin || "1cm",
			media_s = '@media print {\n\
	body {margin:0; padding:0; border:0}\n\
	.newpage {page-break-before: always}\n\







>
>
>
>
>







178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
		abc2svg.print('</table>')
	}

	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\
	body {margin:0; padding:0; border:0}\n\
	.newpage {page-break-before: always}\n\
︙︙
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
// important for chrome and --headless (abctopdf)
		if (abc.page)
			abc2svg.print('@page{size:' +
				(cfmt.pagewidth / 96).toFixed(2) + 'in ' +
				(cfmt.pageheight / 96).toFixed(2) + 'in;margin:0}')

		abc2svg.print('</style>\n\
<title> ' + abc.parse.fname.replace(/.*\//, '')
			+ '</title>\n\
<body>')
		if (header || footer) {
			if (header)
				gen_hf("header", header)
			if (footer)
				gen_hf("footer", footer)







|







252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// important for chrome and --headless (abctopdf)
		if (abc.page)
			abc2svg.print('@page{size:' +
				(cfmt.pagewidth / 96).toFixed(2) + 'in ' +
				(cfmt.pageheight / 96).toFixed(2) + 'in;margin:0}')

		abc2svg.print('</style>\n\
<title>' + fn.replace(/.*\//,'')
			+ '</title>\n\
<body>')
		if (header || footer) {
			if (header)
				gen_hf("header", header)
			if (footer)
				gen_hf("footer", footer)
︙︙
269
270
271
272
273
274
275
276
277



278






279
280
281
282
283
284
285
			init_done = 2		// with header/footer
		} else {
			init_done = 1
		}

		// output the first generated string
		abc2svg.print(str)

		// change the output function



		user.img_out = abc2svg.print






	}
}

abc2svg.abc_end = function() {
    var	font_style = abc.get_font_style()

	if (!init_done)				// if empty document







|
|
>
>
>
|
>
>
>
>
>
>







274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
			init_done = 2		// with header/footer
		} else {
			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]

		if (a[0] == '-') {
			i++
			continue
		}
		fn = a
		break
	}
}

abc2svg.abc_end = function() {
    var	font_style = abc.get_font_style()

	if (!init_done)				// if empty document
︙︙
Changes to tonotes.js.
1
2
3
4
5
6
7
8
9
10
// abc2svg - tonotes.js - convert ABC to a list of MIDI notes
//
// Copyright (C) 2021 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
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.


|







1
2
3
4
5
6
7
8
9
10
// abc2svg - tonotes.js - convert ABC to a list of MIDI notes
//
// 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
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
︙︙
33
34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58













59
60
61
62
63
64
65


66
67
68
69
70
71
72
// The time and the durations are in 1/100s.
// Lines starting with '#' are comments.

// don't load tohtml.js
abc2svg.abc_init = function(args) {
}

// but load the sound generator
abc2svg.loadjs('util/sndgen.js')


abc2svg.abc_end = function() {
	function pit(v) {
		if (v - (v | 0))
			return v.toFixed(2)
		return v.toString()
	} // pit()

    var	e, t, vn,
	audio = ToAudio(),		// (in sndgen.js)
	po = {				// play object
		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) { },













		note_run: function(po, s, k, t, d) {
			abc2svg.print(' '+ (t * 100).toFixed(0) +
				'\t' + s.instr +
				'\t' + pit(k) +
				'\t' + (d * 100).toFixed(0) +
				'\t' + vn[s.p_v.id])
		} // note_run()


	},
	tunes = abc.tunes.slice(0)	// get a copy of the generated tunes

	// define the voice numbers
	function dvn(v_tb) {
	    var	i, n, p_v,
		v = 0







|

>
















|
>
>
>
>
>
>
>
>
>
>
>
>
>

|
|



|
>
>







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// The time and the durations are in 1/100s.
// Lines starting with '#' are comments.

// don't load tohtml.js
abc2svg.abc_init = function(args) {
}

// 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)
		return v.toString()
	} // pit()

    var	e, t, vn,
	audio = ToAudio(),		// (in sndgen.js)
	po = {				// play object
		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) {
			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' + po.c_i[po.v_c[s.v]] +
				'\t' + pit(k) +
				'\t' + (d * 100).toFixed(0) +
				'\t' + vn[s.p_v.id])
		}, // 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) {
	    var	i, n, p_v,
		v = 0
︙︙
Changes to toxhtml.js.
︙︙
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
			abc2svg.print('@page{size:' +
				(cfmt.pagewidth / 96).toFixed(2) + 'in ' +
				(cfmt.pageheight / 96).toFixed(2) + 'in;margin:0}')
		else
			abc2svg.print('@page{margin:' + topmargin + ' 0 ' +
							botmargin + ' 0}')
		abc2svg.print('</style>\n\
<title> ' + abc.parse.fname.replace(/.*\//, '') + '</title>\n\
</head>\n\
<body>')
		if (header)
			gen_hf("header", header)
		if (footer)
			gen_hf("footer", footer);








|







237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
			abc2svg.print('@page{size:' +
				(cfmt.pagewidth / 96).toFixed(2) + 'in ' +
				(cfmt.pageheight / 96).toFixed(2) + 'in;margin:0}')
		else
			abc2svg.print('@page{margin:' + topmargin + ' 0 ' +
							botmargin + ' 0}')
		abc2svg.print('</style>\n\
<title>' + abc.parse.fname.replace(/.*\//, '') + '</title>\n\
</head>\n\
<body>')
		if (header)
			gen_hf("header", header)
		if (footer)
			gen_hf("footer", footer);

︙︙
Changes to util/abcdoc.js.
︙︙
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
		}
		return c
	})
}

// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
		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)
		}
		document.head.appendChild(s)
	}

    // search the ABC tunes and add their rendering as SVG images
    function render() {
    var	i = 0, j, k, res, re,
	re_stop = /\n<|\n%.begin/g,	// stop on "<" and skip "%%begin"
	abc = new abc2svg.Abc(user);

	// get the ABC insertion mode
	if (page.indexOf('<script type="text/vnd.abc"') >= 0)
		re = /<script type="text\/vnd.abc"/g
	else
		re = /\n%abc|\nX:/g

	abc.tosvg('abcdoc', '%%bgcolor white\n\
%%rightmargin 0.8cm\n\
%%leftmargin 0.8cm\n\
%%topspace 0')
	for (;;) {

		// get the start of a ABC sequence
		res = re.exec(page)
		if (!res)
			break
		j = re.lastIndex - res[0].length;







|



|
<
|
|
>
>
>
|
















<
<
<
<







69
70
71
72
73
74
75
76
77
78
79
80

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102




103
104
105
106
107
108
109
		}
		return c
	})
}

// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
		var s = document.createElement('script')
		if (/:\/\//.test(fn))
			s.src = fn		// absolute URL
		else
			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
    function render() {
    var	i = 0, j, k, res, re,
	re_stop = /\n<|\n%.begin/g,	// stop on "<" and skip "%%begin"
	abc = new abc2svg.Abc(user);

	// get the ABC insertion mode
	if (page.indexOf('<script type="text/vnd.abc"') >= 0)
		re = /<script type="text\/vnd.abc"/g
	else
		re = /\n%abc|\nX:/g





	for (;;) {

		// get the start of a ABC sequence
		res = re.exec(page)
		if (!res)
			break
		j = re.lastIndex - res[0].length;
︙︙
Changes to util/abcweb.js.
︙︙
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84
85


























































































































































































































































































































































86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113




114
115
116
117
118
119
120
		alert("window error: " + msg.type + ' ' + msg.target.src)
	else
		alert("window error: " + msg)
	return false
}

    var	user,
	abcplay

if (typeof abc2svg == "undefined")
    var abc2svg = {}

// function called when abc2svg is fully loaded
function dom_loaded() {
var	abc, src,
	a_inc = {},
	new_page = '',
	errtxt = '',
	html,			// set if some pure HTML in the file
	app = "abcweb",
	glopar = '',		// global parameters from URL
	playing,
	playconf = {
		onend: function() {
			playing = false
		}
	},
	page,				// document source
	tune_lst = [],	// array of [tsfirst, voice_tb, info, cfmt] per tune

	jsdir = document.currentScript ?
		    document.currentScript.src.match(/.*\//) :
		    (function() {
		     var s_a = document.getElementsByTagName('script')
			for (var k = 0; k < s_a.length; k++) {
				if (s_a[k].src.indexOf(app) >= 0)
					return s_a[k].src.match(/.*\//) || ''
			}
			return ""	// ??
	})()



























































































































































































































































































































































// -- abc2svg init argument
    user = {
	read_file: function(fn) {
		return a_inc[fn]
	}, // read_file()
	errmsg: function(msg, l, c) {	// get the errors
		errtxt += clean_txt(msg) + '\n'
	},
	img_out: function(str) {	// image output
		new_page += str
	}
    }

// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"
		case '&': return "&amp;"
		}
		return c
	})
}

	// function called on click on the music
	abc2svg.playseq = function(evt) {




	    var	i, j,
		svg = evt.target,
		e = svg			// keep the clicked element

		// search if click in a SVG image
		while (svg.tagName != 'svg') {
			svg = svg.parentNode







|




|
|
<
<
<
<
<
<
<
<
<
|
<
|
<
<
<
>
|




|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







|

|
|

















>
>
>
>







47
48
49
50
51
52
53
54
55
56
57
58
59
60









61

62



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
		alert("window error: " + msg.type + ' ' + msg.target.src)
	else
		alert("window error: " + msg)
	return false
}

    var	user,
	abcplay				// (usable for volume or tempo changes)

if (typeof abc2svg == "undefined")
    var abc2svg = {}

	abc2svg.mu = ""			// container of the current played tune










	abc2svg.abc_end = function() {}	// accept page formatting





	// get the path to the abc2svg scripts
	abc2svg.jsdir = document.currentScript ?
		    document.currentScript.src.match(/.*\//) :
		    (function() {
		     var s_a = document.getElementsByTagName('script')
			for (var k = 0; k < s_a.length; k++) {
				if (s_a[k].src.indexOf("abcweb") >= 0)
					return s_a[k].src.match(/.*\//) || ''
			}
			return ""	// ??
	})()

	// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
	    var	s = document.createElement('script')
		if (/:\/\//.test(fn))
			s.src = fn		// absolute URL
		else
			s.src = abc2svg.jsdir + fn
		s.onload = relay
		s.onerror = function() {
			if (onerror)
				onerror(fn)
			else
				alert('error loading ' + fn)
		}
		document.head.appendChild(s)
	} // loadjs()

// function called when abcweb-1.js is fully loaded
function dom_loaded() {
    var	abc, src, outb, err,
	a_inc = {},
	tune_lst = [],	// array of [tsfirst, voice_tb, info, cfmt] per tune
			// created on playback start
	html,			// set if some pure HTML in the file
	busy,
	playing,
	playconf = {
		onend: function() {
			playing = 0 //false
		}
	}

	// check which <div> became visible
	// if yes, generate it
	function visible() {
	    var	mu, r,
		wh = window.innerHeight || document.documentElement.clientHeight

		while (1) {
			mu = abc2svg.alldiv[0]
			if (!mu)
				break
			r = mu.d.getBoundingClientRect()
			if (r.top > wh)
				break
			musgen(mu)
			abc2svg.alldiv.shift()
		}
		if (abc2svg.alldiv.length) {
			if (!abc2svg.onscroll) {
				abc2svg.onscroll = visible
				window.addEventListener("scroll", visible)
			}
		} else {
			window.removeEventListener("scroll", visible)
		}
	} // visible()
	
	// get the custom properties of a HTML element
	// from 'class="<class>"' and from 'style="--<key>:<value>"'
	function get_p(e) {
	    var	i, j, k, r,
		o = '',
		sh = document.styleSheets,
		s = e.style

		// from class=".."
		c = e.getAttribute("class")
		if (c) {
			c = '.' + c				// selector
			for (i = 0; i < sh.length; i++) {
				r = sh[i].rules
				for (j = 0; j < r.length; j++) {
					if (r[j].selectorText == c)
						break
				}
				if (j < r.length)
					break
			}
			if (i < sh.length) {
				r = r[j]			// rule
				for (i = 0; i < r.style.length; i++) {
					k = r.style[i]
					if (k[0] == '-' && k[1] == '-')
						o += '%%' + k.slice(2) + ' '
							+ r.style.getPropertyValue(k)
							+ '\n'
				}
			}
		}

		// from style=".."
		for (i = 0; i < s.length; i++) {
			k = s[i]
			if (k[0] == '-' && k[1] == '-')
				o += '%%' + k.slice(2) + ' '
					+ s.getPropertyValue(k)
					+ '\n'
		}
		return o
	} // get_p()

	// move the musique sequences to <script> text/vnd.abc
	//	<mei> .. </mei>
	//	<anytag .. class="abc" .. > ..ABC.. </anytag>
	//	%abc-n ..ABC.. '<' with skip %%beginxxx .. %%endxxx
	//	X:n ..ABC.. '<' with skip %%beginxxx .. %%endxxx
	function move_music(src) {
	    var	re, res, i, j, k, t,
		re_stop = /\n<|\n%.begin[^\s]+/g,
		ss = 0,
		out = ""

		if (/<[^>]* class="[^"]*abc[^"]*/.test(src))	// "
			re = '<[^>]* class="[^"]*abc[^"]*'
		else
			re = '%abc-\\d|X:\\s*\\d'
		re = new RegExp('(^|\n)(' + re + ')', 'g')

		while (1) {
			res = re.exec(src)
			if (!res)
				break
			i = re.lastIndex - res[0].length // end of HTML
			if (i > ss) {
				out += src.slice(ss, i)	// copy HTML
				html = 1
			}
			t = res[2]
			if (t[0] == '<') {
				i = src.indexOf('>', i) + 1 // start of ABC
				j = res[2].indexOf(' ')	// get the tag
				t = res[2].slice(1, j)
				j = src.indexOf('</' + t + '>', i)
				ss = j + t.length + 4
			} else {		// %abc or X:

				// get the end of the ABC sequence
				// including the %%beginxxx/%%endxxx sequences
				//	(that may contain XML)
				re_stop.lastIndex = i
				while (1) {
					res = re_stop.exec(src)
					if (!res || res[0] == "\n<")
						break
					k = src.indexOf(res[0].replace("begin", "end"),
							re_stop.lastIndex)
					if (k < 0)
						break
					re_stop.lastIndex = k
				}
				if (!res || k < 0)
					j = src.length
				else
					j = re_stop.lastIndex - 1
				ss = j
			}
			out += '<script type="text/vnd.abc">\n'
				+ src.slice(i, j)
				+ '</script>\n'
			re.lastIndex = ss		// new HTML start
		}
		out += src.slice(ss)		// copy the last HTML sequence
		if (abc2svg.page && html)
			out += '\
<pre class="nop" style="background:#ff8080">\
Printing may be bad because the file contains pure HTML and %%pageheight\
</pre>\n'
		document.body.innerHTML = out
	} // move_music()

	// save the music sequences and replace them by empty <div>'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 <script>'s of the HTML page
		// and replace the music <script>'s by empty <div>'s
		while (1) {
			sa = document.getElementsByTagName('script')
			for (i = 0; i < sa.length; i++) {
				s = sa[i]
				if (s.type == 'text/vnd.abc')
					break
			}
			if (i >= sa.length)
				break

			c = get_p(s)			// get custom properties
			div = document.createElement('div')
			if (s.text.indexOf('\nX:') < 0) {
				abc2svg.music[0].t += c + s.innerHTML // global
				if (!abc2svg.music[0].d)
					abc2svg.music[0].d = div
			} else {
				abc2svg.music.push({
					n: "mus" + abc2svg.music.length,
					t: c + s.innerHTML,
					d: div
				})
			}
			s.parentNode.replaceChild(div, s)
		}

		// special treatment for MEI
		save_spe('mei')
	} // save_music()

	// generate a music sequence
	// @mu = object { t: music source, d: <div> }
	function musgen(mu) {
	    var	t = mu.t

		if (busy) {
			mu.w = 1 //true
			return
		}
		busy = 1 //true

		// render a music sequence
		function render() {
		    var	i, j, e

			// start the generation
			outb = err = ""
			abc.tosvg(mu.n, t)	// music source

			abc2svg.abc_end()	// close the page if %%pageheight

			if (err)
				outb += '<pre class="nop" style="background:#ff8080">'
					+ err + "</pre>\n"

			if (abc.cfmt().with_source && outb)
				outb = '<pre class="source">'
					+ clean_txt(t)
					+ '</pre>\n\
<div class="source">\n'
					+ outb
					+ '</div>\n'
			
			mu.d.innerHTML = outb	// update the browser

			mu.d.addEventListener('click', abc2svg.playseq)
			e = mu.d.getElementsByTagName('svg')
			for (i = 0; i < e.length; i++) {
				j = e[i].getAttribute('class')
				if (!j)
					continue	// (page formatting)
				j = j.match(/tune(\d+)/)
				if (!j)
					continue
				j = j[1]		// tune number
				tune_lst[j] = null	// get new play references
			}

			// if some generation waiting, start it
			mu.w = busy = 0 //false
			for (i = 1; i < abc2svg.music.length; i++) {
				if (abc2svg.music[i].w) {
					musgen(abc2svg.music[i])
					break
				}
			}
		} // render()

		// load the possible %%abc-include files
		function include() {
		    var	i, j, fn, r,
			k = 0

			while (1) {
				i = t.indexOf('%%abc-include ', k)
				if (i < 0) {
					render()
					return
				}
				i += 14
				j = t.indexOf('\n', i)
				fn = t.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()

		// load the required modules and the include files before rendering
		if (abc2svg.modules.load(t, include))
			include()
	} // musgen()
//test
abc2svg.musgen = musgen

// -- abc2svg init argument
    user = {
	read_file: function(fn) {
		return a_inc[fn]
	}, // read_file()
	errmsg: function(msg, l, c) {	// get the errors
		err += clean_txt(msg) + '\n'
	},
	img_out: function(p) {		// image output
		outb += p
	}
    }

// replace <>& by XML character references
function clean_txt(txt) {
	return txt.replace(/<|>|&.*?;|&/g, function(c) {
		switch (c) {
		case '<': return "&lt;"
		case '>': return "&gt;"
		case '&': return "&amp;"
		}
		return c
	})
}

	// function called on click on the music
	abc2svg.playseq = function(evt) {
		if (playing) {		// stop playing
			abcplay.stop()
			return
		}
	    var	i, j,
		svg = evt.target,
		e = svg			// keep the clicked element

		// search if click in a SVG image
		while (svg.tagName != 'svg') {
			svg = svg.parentNode
︙︙
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172









173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

379
380
381
382
383
384
385
386

387
388
389

390
391
392
393
394



395
396


397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412



























413
414

		// check if click on a music symbol
		// (this works when 'follow' is active)
		s = tune_lst[i][0]		// first symbol of the tune
		i = e.getAttribute('class')
		if (i)
			i = i.match(/abcr _(\d+)_/)
		if (playing) {
			abcplay.stop();
			if (!i)
				return
		}
		if (i) {
			i = i[1]		// symbol offset in the source
			while (s && s.istart != i)
				s = s.ts_next
			if (!s) {		// fixme: error ?!
				alert("play bug: no such symbol in the tune")
				return
			}
		}










		playing = true
		abcplay.play(s, null)
	} // playseq()

	// convert HTML to ABC
	function toabc(s) {
		return s.replace(/&gt;/g, '>')
			.replace(/&lt;/g, '<')
			.replace(/&amp;/g, '&')
			.replace(/[ \t]+(%%|.:)/g, '$1')
	} // toabc()

// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
		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)
		}
		document.head.appendChild(s)
	}

function render() {

	// search the ABC tunes,
	// replace them by SVG images with play on click

//search in page
// 1- <script type="text/vnd.abc"> ..ABC.. <script>
// 2- <anytag .. class="abc" .. > ..ABC.. </anytag>
// 3- %abc-n ..ABC.. '<' with skip %%beginxxx .. %%endxxx
// 4- X:n ..ABC.. '<' with skip %%beginxxx .. %%endxxx

    var	re, src, res,
	ss = 0,
	re_stop = /\n<|\n%.begin[^\s]+/g

	// extract a music sequence
	// @re is the regular expression starting a music sequence
	function extract() {
	    var	i, j, k, t

		res = re.exec(page)
		if (!res) {

			// alert about bad print
			if (abc2svg.page && html) {
				new_page += '\
<pre class="nop" style="background:#ff8080">\
Printing may be bad because the file contains pure HTML and %%pageheight\
</pre>\n'
				errtxt = ""
			}

			document.body.innerHTML = new_page // browser page update
				+ page.slice(ss)	// HTML end
			window.onclick = abc2svg.playseq	// prepare for play on click
			return				// done
		}
		i = re.lastIndex - res[0].length	// start of music sequence
		if (i > ss) {
			new_page += page.slice(ss, i)		// HTML copy
			html = 1
		}
		t = res[2]
		if (t[0] == '<') {
			t = t.slice(1, 4)
			switch (t) {
			case "scr":			// <script
				i = page.indexOf('>', i)
				i = page.indexOf('\n', i) + 1
				j = page.indexOf('</script>', 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('</' + t + '>', 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 += '<pre class="nop" style="background:#ff8080">'
					+ errtxt + "</pre>\n"
			errtxt = ""
		}

		// try the next sequence
		extract()
	} // render1()

	// get the ABC insertion mode
	if (/<script[^t>]*type="text\/vnd.abc"/.test(page))
		re = '<script[^t>]*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 <script>

	if (!abc2svg.Abc) {
		abc2svg.loadjs(document.body.innerHTML.indexOf("<mei ") >= 0 ?
					"mei2svg-1.js" :
					"abc2svg-1.js",
						dom_loaded)
		return
	}


	window.removeEventListener("load", dom_loaded)

	// use the query string of the URL for global parameters

	{
	    var	i, j,
		k = location.search.substr(1).split("&")

	    for (i = 0; i < k.length; i++) {



		j = k[i].split('=')
		if (j[0])


			glopar += "%%" + j[0] + " "
				+ decodeURIComponent(j[1]) + '\n'
	    }
	}

	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the included files if any
	page = document.body.innerHTML		// initial page

	// load the required modules and the include files before rendering
	if (abc2svg.modules.load(page, include))
		include()
} // dom_loaded()




























// wait for the scripts to be loaded
window.addEventListener("load", dom_loaded)







<
<
<
<
<










>
>
>
>
>
>
>
>
>
|



<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



>

|






>
|

<
>
|
<
|
|
|
>
>
>
|
|
>
>
|
|
|


|
|
|
<
<
|
<
<
|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|
489
490
491
492
493
494
495





496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518







































































































































































































519
520
521
522
523
524
525
526
527
528
529
530
531
532
533

534
535

536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553


554


555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586

		// check if click on a music symbol
		// (this works when 'follow' is active)
		s = tune_lst[i][0]		// first symbol of the tune
		i = e.getAttribute('class')
		if (i)
			i = i.match(/abcr _(\d+)_/)





		if (i) {
			i = i[1]		// symbol offset in the source
			while (s && s.istart != i)
				s = s.ts_next
			if (!s) {		// fixme: error ?!
				alert("play bug: no such symbol in the tune")
				return
			}
		}

		// keep the container of the tune being played
		while (s && !s.fname)		// (no .fname in the 1st %%staves)
			s = s.ts_next
		for (i = 1; i < abc2svg.music.length; i++) {
			if (abc2svg.music[i].n == s.fname)
				break
		}
		abc2svg.mu = abc2svg.music[i]	// current container

		playing = 1 //true
		abcplay.play(s, null)
	} // playseq()








































































































































































































	// --- dom_loaded() main code ---

	// load the abc2svg core if not done by <script>
	src = document.body.innerHTML
	if (!abc2svg.Abc) {
		abc2svg.loadjs(src.indexOf("<mei ") >= 0 ?
					"mei2svg-1.js" :
					"abc2svg-1.js",
						dom_loaded)
		return
	}

	if (src.indexOf('type="text/vnd.abc"') < 0)
		move_music(src)


	save_music()	// save the music sequences and replace them by <div>


	// initialize the generation
	abc = new abc2svg.Abc(user)
	if (typeof follow == "function")	// if snd-1.js loaded
		follow(abc, user, playconf)	// initialize the play follow
	if (abc2svg.music[0].t) {
		abc.tosvg(abc2svg.music[0].n,	// global definitions
			  abc2svg.music[0].t)
		if (abc.cfmt().with_source
		 && abc.cfmt().with_source.indexOf('nohead') < 0) {
			abc2svg.music[0].d.innerHTML = '<pre class="source">'
					+ clean_txt(abc2svg.music[0].t)
					+ '</pre>\n'
		}
	}

	// create a list of all <div>'s
	abc2svg.alldiv = []
	for (var i = 1; i < abc2svg.music.length; i++)


		abc2svg.alldiv.push(abc2svg.music[i])


	visible()
} // dom_loaded()

// ---- interface for changing the music source and redisplaying it ----
// get the music source of a <div> element
abc2svg.get_music = function(d) {
    var	i, mu

	for (var i = 1; i < abc2svg.music.length; i++) {
		mu = abc2svg.music[i]
		if (mu.d == d)
			return mu.t
	}
//	return ""
} // get_music()

// set the music source of a <div> element and redisplay it
abc2svg.set_music = function(d, t) {
    var	i, mu

	for (var i = 1; i < abc2svg.music.length; i++) {
		mu = abc2svg.music[i]
		if (mu.d == d) {
			mu.t = t
			abc2svg.musgen(mu)
			break
		}
	}
} // set_music()

// wait for the scripts to be loaded
window.addEventListener("load", dom_loaded, {once:true})
Changes to util/abcweb1.js.
1
2
3
4
5
6
7
8
9
10
// abcweb1-1.js file to include in html pages with abc2svg-1.js
//
// Copyright (C) 2019-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// abcweb1-1.js file to include in html pages with abc2svg-1.js
//
// Copyright (C) 2019-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.
︙︙
218
219
220
221
222
223
224
225
226
227



228
229
230
231
232
233
234
235
	// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
	    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)
		}
		document.head.appendChild(s)
	} // loadjs()

	// build a list of the tunes
	abc2svg.get_sel = function() {
	    var	j, k,







<
|
|
>
>
>
|







218
219
220
221
222
223
224

225
226
227
228
229
230
231
232
233
234
235
236
237
	// function to load javascript files
	abc2svg.loadjs = function(fn, relay, onerror) {
	    var	s = document.createElement('script')
		if (/:\/\//.test(fn))
			s.src = fn		// absolute URL
		else
			s.src = jsdir + fn

		s.onload = relay
		s.onerror = function() {
			if (onerror)
				onerror(fn)
			else
				alert('error loading ' + fn)
		}
		document.head.appendChild(s)
	} // loadjs()

	// build a list of the tunes
	abc2svg.get_sel = function() {
	    var	j, k,
︙︙
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363





364
365
366
367
368
369
370
			errtxt = ""
		}

		// add the menu
		new_page += '\
<div id="dd" class="dd nop">\
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="db">\
<path d="M4 6h15v2.5H4zm0 5h15v2.5H4zm0 5h15v2.5H4z" fill=black/>\
</svg>\
<div id="dc" class="dc">\
<label id="edit" onclick="abc2svg.src_edit()">Source edit</label>\
<label id="list" onclick="abc2svg.get_sel()">Tune list</label>\
<label id="play" onclick="abc2svg.st_scroll()">Scroll</label>\
</div>\
</div>\
<label id="ss" class="dd nop" onclick="abc2svg.st_scroll()">Scroll<br/>stop</label>'

		// change the page
		try {
			document.body.innerHTML = new_page
		} catch (e) {
			alert("abc2svg bad generated SVG: " + e.message +
				"\nStack:\n" + e.stack)
			return
		}






		// update the menu
		setTimeout(function() {

			// remove scroll in menu if play or not a big tune
			if (typeof AbcPlay != "undefined"
			 || document.documentElement.scrollHeight <= window.innerHeight)
				document.getElementById("play").style.display = "none"







|


















>
>
>
>
>







340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
			errtxt = ""
		}

		// add the menu
		new_page += '\
<div id="dd" class="dd nop">\
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" class="db">\
<path d="M4 6h15v2.5H4zm0 5h15v2.5H4zm0 5h15v2.5H4z" fill="black"/>\
</svg>\
<div id="dc" class="dc">\
<label id="edit" onclick="abc2svg.src_edit()">Source edit</label>\
<label id="list" onclick="abc2svg.get_sel()">Tune list</label>\
<label id="play" onclick="abc2svg.st_scroll()">Scroll</label>\
</div>\
</div>\
<label id="ss" class="dd nop" onclick="abc2svg.st_scroll()">Scroll<br/>stop</label>'

		// change the page
		try {
			document.body.innerHTML = new_page
		} catch (e) {
			alert("abc2svg bad generated SVG: " + e.message +
				"\nStack:\n" + e.stack)
			return
		}

		// add the event handlers
	   var	elts = document.getElementsByTagName('svg')
		for (var i = 0; i < elts.length; i++)
			elts[i].addEventListener('click', click)

		// update the menu
		setTimeout(function() {

			// remove scroll in menu if play or not a big tune
			if (typeof AbcPlay != "undefined"
			 || document.documentElement.scrollHeight <= window.innerHeight)
				document.getElementById("play").style.display = "none"
︙︙
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448



449

450


451
452
453
454
455
456
457
		abc2svg.loadjs("abc2svg-1.js", dom_loaded)
		return
	}

	// get the page content
	page = fix_abc(document.body.innerHTML)

	// mouse/tap events
	window.onclick = function(evt) {
		if (playing) {			// stop playing
			abcplay.stop()
			return
		}

	    var	e, s, j,
		c = evt.target

		// remove the menu if active
		e = document.getElementById("dc")
		if (e && e.classList.contains("show")) {
			e.classList.remove("show") // remove the menu
			return
		}

		// search if click in a SVG image
		e = c				// keep the clicked element
		while (c && c.tagName != 'svg')
			c = c.parentNode
		if (!c)
			return





		c = c.getAttribute('class')



		// if click in the menu button, show the menu
		if (c == "db") {
			e = document.getElementById("dc")
			e.classList.toggle("show")
			return
		}







|
|

















|
<
|
|
>
>
>
|
>

>
>







426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452

453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
		abc2svg.loadjs("abc2svg-1.js", dom_loaded)
		return
	}

	// get the page content
	page = fix_abc(document.body.innerHTML)

	// click on a SVG element
	function click(evt) {
		if (playing) {			// stop playing
			abcplay.stop()
			return
		}

	    var	e, s, j,
		c = evt.target

		// remove the menu if active
		e = document.getElementById("dc")
		if (e && e.classList.contains("show")) {
			e.classList.remove("show") // remove the menu
			return
		}

		// search if click in a SVG image
		e = c				// keep the clicked element
		while (1) {

			if (c == document)
				return
			if (c.tagName.toLowerCase() == 'svg')
				break
			c = c.parentNode
		}

		c = c.getAttribute('class')
		if (!c)
			return

		// if click in the menu button, show the menu
		if (c == "db") {
			e = document.getElementById("dc")
			e.classList.toggle("show")
			return
		}
︙︙
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
				alert("play bug: no such symbol in the tune")
				return
			}
		}

		playing = true
		abcplay.play(s, null)
	} // onclick()

	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the required modules, then render the music
	if (abc2svg.modules.load(page, include))
		include()
} // dom_loaded()

// wait for the scripts to be loaded
window.addEventListener("load", dom_loaded)







|











509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
				alert("play bug: no such symbol in the tune")
				return
			}
		}

		playing = true
		abcplay.play(s, null)
	} // click()

	// accept page formatting
	abc2svg.abc_end = function() {}

	// load the required modules, then render the music
	if (abc2svg.modules.load(page, include))
		include()
} // dom_loaded()

// wait for the scripts to be loaded
window.addEventListener("load", dom_loaded)
Changes to util/chord.js.
1
2
3
4
5
6
7
8
9
10
// chord.js - generation of accompaniment
//
// Copyright (C) 2020-2021 Jean-Francois Moine and Seymour Shlien
//
// 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.


|







1
2
3
4
5
6
7
8
9
10
// chord.js - generation of accompaniment
//
// Copyright (C) 2020-2022 Jean-Francois Moine and Seymour Shlien
//
// 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.
︙︙
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

109



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129





130
131
132
133
134
135
136
			case '-': v -= 12; break
			}
			r.push(v)
		}
		return r
	} // chcr()

	// (quite the same as abc2svg.cs_filter() but with otext)
	function filter(a_cs, sel) {
	    var	i, cs,
		tcs = ""

		for (i = 0; i < a_cs.length; i++) {
			cs = a_cs[i]
			if (cs.type == 'g')

				tcs += cs.otext



		}
		return tcs.replace(sel ? abc2svg.cs_sel1 : abc2svg.cs_sel0, '')
	} // filter()

	// generate a chord
	function gench(sb) {
	    var	r, ch, b, m, n, not,
		a = filter(sb.a_gch, cfmt.altchord).
			match(/([A-G])([#♯b♭]?)([^/]*)\/?(.*)/),
			// a[1] = note, a[2] = acc, a[3] = type, a[4] = bass
		s = {
			v: vch.v,
			p_v: vch,
			type: C.NOTE,
			time: sb.time,
			notes: []
		}

		if (!a)
			return





		r = abc2svg.letmid[a[1]]		// root
		if (r == undefined) {
			if (a[1] != "N")
				return
			s.type = C.REST			// ("N") = no chord
			ch = [0]
			r = 0







|
|
|
<



|
>
|
>
>
>

<





|
<
<










>
>
>
>
>







94
95
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112
113

114
115
116
117
118
119


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
			case '-': v -= 12; break
			}
			r.push(v)
		}
		return r
	} // chcr()

	// get the playback part of the first chord symbol
	function 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.otext
			if (t.slice(-1) == ')')		// if alternate chord
				t = t.replace(/\(.*/, '') // remove it
			return t.replace(/\(|\)|\[|\]/g,'') // remove ()[]
		}

	} // filter()

	// generate a chord
	function gench(sb) {
	    var	r, ch, b, m, n, not,
		a = filter(sb.a_gch),


		s = {
			v: vch.v,
			p_v: vch,
			type: C.NOTE,
			time: sb.time,
			notes: []
		}

		if (!a)
			return
		a = a.match(/([A-GN])([#♯b♭]?)([^/]*)\/?(.*)/)
			// a[1] = note, a[2] = acc, a[3] = type, a[4] = bass
		if (!a)
			return

		r = abc2svg.letmid[a[1]]		// root
		if (r == undefined) {
			if (a[1] != "N")
				return
			s.type = C.REST			// ("N") = no chord
			ch = [0]
			r = 0
︙︙
167
168
169
170
171
172
173


174
175
176
177
178
179
180
		if (b == undefined)
			b = 0
		ch = chcr(b, ch)

		// generate the notes of the chord
		n = ch.length
		r += trans


		for (m = 0; m < n; m++) {
			not = {
				midi: r + ch[m]
			}
			s.notes.push(not)
		}
		s.nhd = n - 1







>
>







172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
		if (b == undefined)
			b = 0
		ch = chcr(b, ch)

		// generate the notes of the chord
		n = ch.length
		r += trans
		if (sb.p_v.tr_snd)
			r += sb.p_v.tr_snd
		for (m = 0; m < n; m++) {
			not = {
				midi: r + ch[m]
			}
			s.notes.push(not)
		}
		s.nhd = n - 1
︙︙
206
207
208
209
210
211
212









213
214
215
216
217
218
219



220


221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
//fixme: no inversion
			chnm[k] = [ vch ]
		}
	} else {
		chnm = abc2svg.ch_names
	}










	// create the chord voice
	vch = {
		v: voice_tb.length,
		id: "_chord",
		time: 0,
		sym: {			// dummy symbol to simplify chord insersion
			type: C.REST,



			time: 0


		},
		instr: cfmt.chord.prog || 0,
		vol: cfmt.chord.vol || .7
	}
	vch.last_sym = vch.sym
	voice_tb.push(vch)

	// define the MIDI channel
	k = 0
	for (i = 0; i < vch.v; i++) {
		if (k < voice_tb[i].chn)
			k = voice_tb[i].chn
	}
	if (k == 8)
		k++			// skip the channel 10
	vch.chn = k + 1

	// loop on the symbols and add the accompaniment chords
	gchon = cfmt.chord.gchon
	s = first
	while (1) {
		if (!s.ts_next) {
			if (gchon)







>
>
>
>
>
>
>
>
>





|
|
>
>
>
|
>
>

<
|



|
<
<
<
<
<
<
<
<
<







213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

243
244
245
246
247









248
249
250
251
252
253
254
//fixme: no inversion
			chnm[k] = [ vch ]
		}
	} else {
		chnm = abc2svg.ch_names
	}

	// define the MIDI channel
	k = 0
	for (i = 0; i < voice_tb.length; i++) {
		if (k < voice_tb[i].chn)
			k = voice_tb[i].chn
	}
	if (k == 8)
		k++			// skip the channel 10

	// create the chord voice
	vch = {
		v: voice_tb.length,
		id: "_chord",
		time: 0,
		sym: {
			type: C.BLOCK,
			subtype: "midiprog",
			chn: k + 1,
			instr: cfmt.chord.prog || 0,
			time: 0,
			ts_prev: first,
			ts_next: first.ts_next
		},

		vol: cfmt.chord.vol || .6	// (external default 76.2)
	}
	vch.last_sym = vch.sym
	voice_tb.push(vch)
	first.ts_next = vch.sym










	// loop on the symbols and add the accompaniment chords
	gchon = cfmt.chord.gchon
	s = first
	while (1) {
		if (!s.ts_next) {
			if (gchon)
︙︙
Changes to util/follow.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44






45


46

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69





70
71
72
73
74
75
76
77
78
79
80
// follow-1.js - file included in snd-1.js
//
// This script permits to follow the notes while playing.
// Scrolling the music may be disabled setting 'no_scroll' in the window object.
//
// Copyright (C) 2015-2020 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
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

// init
function follow(abc, user, playconf) {
var	ref = [],
	keep_types = {
		note: true,
		rest: true
	}

user.anno_stop = function(type, start, stop, x, y, w, h) {
	if (!keep_types[type])
		return
	ref[start] = stop;		// keep the source reference

	// create a rectangle
	abc.out_svg('<rect class="abcr _' + start + '_" x="');
	abc.out_sxsy(x, '" y="', y);
	abc.out_svg('" width="' + w.toFixed(2) +
		'" height="' + abc.sh(h).toFixed(2) + '"/>\n')
}

	playconf.onnote = function(i, on) {
	    var	b, x, y,






		elts = document.getElementsByClassName('_' + i + '_')


		if (elts && elts[0]) {

			elts[0].style.fillOpacity = on ? 0.4 : 0

			// scroll for the element to be in the screen
			if (on && !window.no_scroll) {	
				b = elts[0].getBoundingClientRect()

				// normal
				if (b.top < 0)
					y = window.scrollY + b.top -
							window.innerHeight / 2
				else if (b.bottom > window.innerHeight)
					y = window.scrollY + b.bottom +
							window.innerHeight / 2

				// single line
				if (b.left < 0)
					x = window.scrollX + b.left -
							window.innerWidth / 2
				else if (b.right > window.innerWidth)
					x = window.scrollX + b.right +
							window.innerWidth / 2
				if (x != undefined || y != undefined)
					window.scrollTo(x || 0, y || 0)





			}
		}
	}
} // follow()

// create the style of the rectangles
(function () {
    var	sty = document.createElement("style")
	sty.innerHTML = ".abcr {fill: #d00000; fill-opacity: 0; z-index: 15}"
	document.head.appendChild(sty)
})()





|


















<
|







<









|
>
>
>
>
>
>
|
>
>
|
>
|



|


|
<
<
|
<
|


|
<
<
|
<
|
|
|
>
>
>
>
>
|
|









1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61


62

63
64
65
66


67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// follow-1.js - file included in snd-1.js
//
// This script permits to follow the notes while playing.
// Scrolling the music may be disabled setting 'no_scroll' in the window object.
//
// Copyright (C) 2015-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
// 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with abc2svg.  If not, see <http://www.gnu.org/licenses/>.

// init
function follow(abc, user, playconf) {

    var	keep_types = {
		note: true,
		rest: true
	}

user.anno_stop = function(type, start, stop, x, y, w, h) {
	if (!keep_types[type])
		return


	// create a rectangle
	abc.out_svg('<rect class="abcr _' + start + '_" x="');
	abc.out_sxsy(x, '" y="', y);
	abc.out_svg('" width="' + w.toFixed(2) +
		'" height="' + abc.sh(h).toFixed(2) + '"/>\n')
}

	playconf.onnote = function(i, on) {
	    var	b, i, e, elts,
		x = 0,
		y = 0

		if (abc2svg.mu)			// if many tunes with same offsets
			elts = abc2svg.mu.d.getElementsByClassName('_' + i + '_')
		else
			elts = document.getElementsByClassName('_' + i + '_')
		if (!elts || !elts.length)
			return			// no symbol?
		e = elts[0]

		e.style.fillOpacity = on ? 0.4 : 0

			// scroll for the element to be in the screen
			if (on && !window.no_scroll) {	
				b = e.getBoundingClientRect()

				// normal
				if (b.top < 0


				 || b.bottom > window.innerHeight * .8)

					y = b.top - window.innerHeight * .3

				// single line
				if (b.left < 0


				 || b.right > window.innerWidth * .8)

					x = b.left - window.innerWidth * .3
				if (x || y)
					window.scrollBy({
						top: y,
						left: x,
						behavior: (x < 0 || y)
								? 'instant'
								: 'smooth'
					})
			}
	}
} // follow()

// create the style of the rectangles
(function () {
    var	sty = document.createElement("style")
	sty.innerHTML = ".abcr {fill: #d00000; fill-opacity: 0; z-index: 15}"
	document.head.appendChild(sty)
})()
Changes to util/play.js.
︙︙
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

		// set the current output changing the play functions
		current = out[o] == 'sf2' ? audio5 : midi5;
		abcplay.play = current.play;
		abcplay.stop = current.stop
		if (current.set_output)
			current.set_output(out[o]);
		if (abc2svg.pwait) {
			if (typeof abc2svg.pwait == "boolean") {
				abc2svg.pwait = function() {
					abcplay.play(init.istart,
							init.i_iend, init.a_e)
				}
			}
			return
		}
		abcplay.play(init.istart, init.i_iend, init.a_e);
	} // play2()

	// set default configuration values
	conf.gain = 0.7;
	conf.speed = 1;








<
<
<
<
<
<
<
<
<







115
116
117
118
119
120
121









122
123
124
125
126
127
128

		// set the current output changing the play functions
		current = out[o] == 'sf2' ? audio5 : midi5;
		abcplay.play = current.play;
		abcplay.stop = current.stop
		if (current.set_output)
			current.set_output(out[o]);









		abcplay.play(init.istart, init.i_iend, init.a_e);
	} // play2()

	// set default configuration values
	conf.gain = 0.7;
	conf.speed = 1;

︙︙
Changes to util/sndaud.js.
1
2
3
4
5
6
7
8
9
10
// sndaud.js - audio output using HTML5 audio
//
// Copyright (C) 2019-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// sndaud.js - audio output using HTML5 audio
//
// Copyright (C) 2019-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.
︙︙
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
		a = b.getChannelData(0)		// destination = array of float32

		for (i = 0; i < s.length; i++)
			a[i] = s[i] / 196608	// volume divided by 6
	}

	// create all notes of an instrument
	function sf2_create(instr) {

		// get the instrument parameters
		// adapted from getInstruments() in sf2-parser.js
		function get_instr(i) {
		    var	instrument = parser.instrument,
			zone = parser.instrumentZone,
			j = instrument[i].instrumentBagIndex,
			jl = instrument[i + 1]
				? instrument[i + 1].instrumentBagIndex
				: zone.length,
			info = []

			while (j < jl) {
				instrumentGenerator =
					parser.createInstrumentGenerator_(zone, j)
//				instrumentModulator =
//					parser.createInstrumentModulator_(zone, j)

				info.push({
					generator: instrumentGenerator.generator,
//					modulator: instrumentModulator.modulator
				})
				j++
			}
//console.log('instr: '+instrument[i].instrumentName)
		return {
//			name: instrument[i].instrumentName,
			info: info
		}
	} // get_instr()

	// sf2_create
	    var i, j, k, sid, gen, parm, gparm, sample, infos,
		sampleRate, scale,
		b = instr >> 7,			// bank
		p = instr % 128,		// preset
		pr = presets

		rates[instr] = []

		// search the bank:preset
		for (i = 0; i < pr.length; i++) {
			gen = pr[i].header
			if (gen.preset == p







|




|
|








|

|







|











|







123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
		a = b.getChannelData(0)		// destination = array of float32

		for (i = 0; i < s.length; i++)
			a[i] = s[i] / 196608	// volume divided by 6
	}

	// create all notes of an instrument
	function sf2_create(instr, sf2par, sf2pre) {	// parser, presets

		// get the instrument parameters
		// adapted from getInstruments() in sf2-parser.js
		function get_instr(i) {
		    var	instrument = sf2par.instrument,
			zone = sf2par.instrumentZone,
			j = instrument[i].instrumentBagIndex,
			jl = instrument[i + 1]
				? instrument[i + 1].instrumentBagIndex
				: zone.length,
			info = []

			while (j < jl) {
				instrumentGenerator =
					sf2par.createInstrumentGenerator_(zone, j)
//				instrumentModulator =
//					sf2par.createInstrumentModulator_(zone, j)

				info.push({
					generator: instrumentGenerator.generator,
//					modulator: instrumentModulator.modulator
				})
				j++
			}
//console.log('created instr: '+instrument[i].instrumentName)
		return {
//			name: instrument[i].instrumentName,
			info: info
		}
	} // get_instr()

	// sf2_create
	    var i, j, k, sid, gen, parm, gparm, sample, infos,
		sampleRate, scale,
		b = instr >> 7,			// bank
		p = instr % 128,		// preset
		pr = sf2pre

		rates[instr] = []

		// search the bank:preset
		for (i = 0; i < pr.length; i++) {
			gen = pr[i].header
			if (gen.preset == p
︙︙
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

280
281
282
283
284
285
286
287
288
289
290

































































291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
			if (gen.sampleModes && gen.sampleModes.amount & 1)
				parm.sm = 1

			if (!gen.sampleID)	// (empty generator!)
				continue

			sid = gen.sampleID.amount
			sampleRate = parser.sampleHeader[sid].sampleRate
			sample = parser.sample[sid]
			parm.buffer = ac.createBuffer(1,
						sample.length,
						sampleRate)

			parm.hold += parm.attack
			parm.decay += parm.hold

			// sustain > 40dB is not audible
			if (parm.sustain >= .4)
				parm.sustain = 0.01	// must not be null
			else
				parm.sustain = 1 - parm.sustain / .4

			sample_cp(parm.buffer, sample)

			if (parm.sm) {
				parm.loopStart = parser.sampleHeader[sid].startLoop /
					sampleRate
				parm.loopEnd = parser.sampleHeader[sid].endLoop /
					sampleRate
			}

			// define the notes
			scale = (gen.scaleTuning ?
					gen.scaleTuning.amount : 100) / 100,
			tune = (gen.coarseTune ? gen.coarseTune.amount : 0) +
				(gen.fineTune ? gen.fineTune.amount : 0) / 100 +
				parser.sampleHeader[sid].pitchCorrection / 100 -
				(gen.overridingRootKey ?
					gen.overridingRootKey.amount :
					parser.sampleHeader[sid].originalPitch)

			for (j = gen.keyRange.lo; j <= gen.keyRange.hi; j++) {
				rates[instr][j] = Math.pow(Math.pow(2, 1 / 12),
							(j + tune) * scale)
				params[instr][j] = parm
			}
		    }
		}
	} // sf2_create()

	// load an instrument (.js file)
	function load_instr(instr) {
		w_instr++
		abc2svg.loadjs(conf.sfu + '/' + instr + '.js',
			function() {
				parser = new sf2.Parser(b64dcod(abcsf2[instr]))
				parser.parse()
				presets = parser.getPresets()
				sf2_create(instr)

				if (--w_instr == 0)
					play_start()
			},
			function() {
				errmsg('could not find the instrument ' +
					((instr / 128) | 0).toString() + '-' +
					(instr % 128).toString())
				if (--w_instr == 0)
					play_start()
			})
	} // load_instr()


































































	// load the needed instruments
	function load_res(s) {
	    if (abc2svg.sf2
	     || conf.sfu.slice(-4) == ".sf2"
	     || conf.sfu.slice(-3) == ".js") {

		// if the soundfont is loaded as .js
		if (abc2svg.sf2) {
			if (!parser) {
				parser = new sf2.Parser(b64dcod(abc2svg.sf2))
				parser.parse()
				presets = parser.getPresets()
			}

		// load the soundfont if no done yet
		} else if (!parser) {
		    if (conf.sfu.slice(-3) == ".js") {
			abc2svg.loadjs(conf.sfu,
				function() {
					load_res(s)	// load the instruments
				},
				function() {







|
|
















|

|








|


|















|
|
|
|
>











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>















|







221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
			if (gen.sampleModes && gen.sampleModes.amount & 1)
				parm.sm = 1

			if (!gen.sampleID)	// (empty generator!)
				continue

			sid = gen.sampleID.amount
			sampleRate = sf2par.sampleHeader[sid].sampleRate
			sample = sf2par.sample[sid]
			parm.buffer = ac.createBuffer(1,
						sample.length,
						sampleRate)

			parm.hold += parm.attack
			parm.decay += parm.hold

			// sustain > 40dB is not audible
			if (parm.sustain >= .4)
				parm.sustain = 0.01	// must not be null
			else
				parm.sustain = 1 - parm.sustain / .4

			sample_cp(parm.buffer, sample)

			if (parm.sm) {
				parm.loopStart = sf2par.sampleHeader[sid].startLoop /
					sampleRate
				parm.loopEnd = sf2par.sampleHeader[sid].endLoop /
					sampleRate
			}

			// define the notes
			scale = (gen.scaleTuning ?
					gen.scaleTuning.amount : 100) / 100,
			tune = (gen.coarseTune ? gen.coarseTune.amount : 0) +
				(gen.fineTune ? gen.fineTune.amount : 0) / 100 +
				sf2par.sampleHeader[sid].pitchCorrection / 100 -
				(gen.overridingRootKey ?
					gen.overridingRootKey.amount :
					sf2par.sampleHeader[sid].originalPitch)

			for (j = gen.keyRange.lo; j <= gen.keyRange.hi; j++) {
				rates[instr][j] = Math.pow(Math.pow(2, 1 / 12),
							(j + tune) * scale)
				params[instr][j] = parm
			}
		    }
		}
	} // sf2_create()

	// load an instrument (.js file)
	function load_instr(instr) {
		w_instr++
		abc2svg.loadjs(conf.sfu + '/' + instr + '.js',
			function() {
			    var	sf2par = new sf2.Parser(b64dcod(abcsf2[instr]))
				sf2par.parse()
			    var	sf2pre = sf2par.getPresets()
				sf2_create(instr, sf2par, sf2pre)

				if (--w_instr == 0)
					play_start()
			},
			function() {
				errmsg('could not find the instrument ' +
					((instr / 128) | 0).toString() + '-' +
					(instr % 128).toString())
				if (--w_instr == 0)
					play_start()
			})
	} // load_instr()

	// define the instruments of the tune
	function def_instr(s, f, sf2par, sf2pre) {
	    var	i,
		bk = [],		// bank number per voice
		nv = -1,		// highest voice number
		vb = 0			// bitmap of voices with instruments

		// scan from the beginning of the tune
		s = s.p_v.sym
		while (s.ts_prev)
			s = s.ts_prev

		for ( ; s; s = s.ts_next) {
			if (s.v < nv) {			// if new voice
				nv = s.v
				bk[nv] = 0		// bank 0
				if (p_v.midictl) {
					if (p_v.midictl[0])	// MSB
						bk[s.v] = (bk[s.v] & ~0x1fc000)
								+ (p_v.midictl[0] << 14)
					if (p_v.midictl[32])	// LSB
						bk[s.v] = (bk[s.v] & ~0x3f80)
								+ (p_v.midictl[32] << 7)
				}
			}
			switch (s.subtype) {
			case "midiprog":
				break
			case "midictl":
				if (s.ctrl != 0 && s.ctrl != 32)
					continue	// not bank LSB or MSB
				if (bk[s.v] == undefined)
					bk[s.v] = 0
				if (s.ctrl == 0)			// MSB
					bk[s.v] = (bk[s.v] & ~0x1fc000)
							+ (s.val << 14)
				else					// LSB
					bk[s.v] = (bk[s.v] & ~0x3f80)
							+ (s.val << 7)
//				continue
			default:
				continue
			}
			vb |= 1 << s.v
			i = s.instr
			if (i == undefined) {		// channel only
				if (s.chn != 9)
					continue
				i = bk[s.v] ? 0 : 128 * 128	// bank 256 program 0
			}
			if (bk[s.v]) 
				i += bk[s.v]		// bank number
			if (!params[i]) {
				params[i] = []		// instrument being loaded
				f(i, sf2par, sf2pre)	// sf2_create or load_instr
			}
		}
		nv = (2 << nv) - 1
		if (!(nv & vb)			// if some voice(s) without instrument
		 && !params[0]) {
			params[0] = []		// load the piano
			f(0, sf2par, sf2pre)
		}
	} // def_instr()

	// load the needed instruments
	function load_res(s) {
	    if (abc2svg.sf2
	     || conf.sfu.slice(-4) == ".sf2"
	     || conf.sfu.slice(-3) == ".js") {

		// if the soundfont is loaded as .js
		if (abc2svg.sf2) {
			if (!parser) {
				parser = new sf2.Parser(b64dcod(abc2svg.sf2))
				parser.parse()
				presets = parser.getPresets()
			}

		// load the soundfont if not done yet
		} else if (!parser) {
		    if (conf.sfu.slice(-3) == ".js") {
			abc2svg.loadjs(conf.sfu,
				function() {
					load_res(s)	// load the instruments
				},
				function() {
︙︙
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377







378








379
















380
381
382
383
384
385
386
387

388

389
390
391
392
393
394
395
396
397
398
					errmsg('could not load the sound file '
						+ conf.sfu)
			}
			r.send()
			return
		}

		// create the instruments
		while (s) {
		    var	i = s.instr
			if (i != undefined && !params[i]) {
				params[i] = []	// instrument being loaded
				sf2_create(i)
			}
			s = s.ts_next
		}
		play_start()
	   } else {

	// (case instruments as base64 encoded js file,
	//  one file per instrument)
		w_instr++			// play lock
		while (s) {
		    var	i = s.instr
			if (i != undefined && !params[i]) {
				params[i] = []	// instrument being loaded
				load_instr(i)
			}
			s = s.ts_next
		}
		if (--w_instr == 0)		// all resources were there already
			play_start()
	    }
	} // load_res()

	// return the play real time in seconds
	function get_time(po) {
		return po.ac.currentTime
	} // get_time()

	// MIDI control
	function midi_ctrl(po, s, t) {
		if (s.ctrl == 7)		// if volume







			s.p_v.vol = s.val / 127








	} // midi_ctrl()

















	// create a note
	// @po = play object
	// @s = symbol
	// @key = MIDI key + detune
	// @t = audio start time
	// @d = duration adjusted for speed
	function note_run(po, s, key, t, d) {

	    var	g, st,

		instr = s.instr,
		k = key | 0,
		parm = po.params[instr][k],
		o = po.ac.createBufferSource(),
		v = s.p_v.vol == undefined ? 1 : s.p_v.vol	// volume (gain)

		if (!v			// mute voice
		 || !parm)		// if the instrument could not be loaded
			return		// or if it has not this key
		o.buffer = parm.buffer







|
<
<
<
<
|
<
<
<
<
|



<
<
<
<
<
|
<
<
<
<
<










|
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








>

>
|

|







401
402
403
404
405
406
407
408




409




410
411
412
413





414





415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
					errmsg('could not load the sound file '
						+ conf.sfu)
			}
			r.send()
			return
		}

		// create the instruments and start playing




		def_instr(s, sf2_create, parser, presets)




	    } else {

	// (case instruments as base64 encoded js file,
	//  one file per instrument)





		def_instr(s, load_instr)





	    }
	} // load_res()

	// return the play real time in seconds
	function get_time(po) {
		return po.ac.currentTime
	} // get_time()

	// MIDI control
	function midi_ctrl(po, s, t) {
		switch (s.ctrl) {
		case 0:				// bank MSB
			if (po.v_b[s.v] == undefined)
				po.v_b[s.v] = 0
			po.v_b[s.v] = (po.v_b[s.v] & ~0x1fc000)
					+ (s.val << 14)
			break
		case 7:				// volume
			s.p_v.vol = s.val / 127
			break
		case 32:			// bank LSB
			if (po.v_b[s.v] == undefined)
				po.v_b[s.v] = 0
			po.v_b[s.v] = (po.v_b[s.v] & ~0x3f80)
					+ (s.val << 7)
			break
		}
	} // midi_ctrl()

	// MIDI prog or channel
	function midi_prog(po, s) {
	    var	i = s.instr

		po.v_c[s.v] = s.chn
		if (i == undefined) {
			if (s.chn != 9)			// if not channel 9
				return
			i = po.v_b[s.v] ? 0 : 128 * 128	// bank 256 program 0
		}
		if (po.v_b[s.v])
			i += po.v_b[s.v]
		po.c_i[s.chn] = i
//console.log('prog i:'+i+' ch:'+s.chn+' v:'+s.v)
	} // midi_prog()

	// create a note
	// @po = play object
	// @s = symbol
	// @key = MIDI key + detune
	// @t = audio start time
	// @d = duration adjusted for speed
	function note_run(po, s, key, t, d) {
//console.log('run c:'+po.v_c[s.v]+' i:'+po.c_i[po.v_c[s.v]])
	    var	g, st,
		c = po.v_c[s.v],
		instr = po.c_i[c],
		k = key | 0,
		parm = params[instr][k],
		o = po.ac.createBufferSource(),
		v = s.p_v.vol == undefined ? 1 : s.p_v.vol	// volume (gain)

		if (!v			// mute voice
		 || !parm)		// if the instrument could not be loaded
			return		// or if it has not this key
		o.buffer = parm.buffer
︙︙
431
432
433
434
435
436
437

438
439
440
441
442
443
444
		// start the note
		o.start(t)
		o.stop(t + d)
	} // note_run()

	// wait for all resources, then start playing
	function play_start() {

		if (po.stop) {			// stop playing
			po.onend(repv)
			return
		}

		// all resources are there
		gain.connect(ac.destination)







>







512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
		// start the note
		o.start(t)
		o.stop(t + d)
	} // note_run()

	// wait for all resources, then start playing
	function play_start() {
//console.log('- play start')
		if (po.stop) {			// stop playing
			po.onend(repv)
			return
		}

		// all resources are there
		gain.connect(ac.destination)
︙︙
511
512
513
514
515
516
517

518
519



520
521
522
523
524
525
526

527


528
529
530
531
532
533
534
			s_end: i_end,	// last music symbol / null
			s_cur: i_start,	// current music symbol
//			repn: false,	// don't repeat
			repv: i_lvl || 0, // repeat variant number
			tgen: 2,	// // generate by 2 seconds
			get_time: get_time,
			midi_ctrl: midi_ctrl,

			note_run: note_run,
			timouts: [],




			// audio specific
			ac: ac,
			gain: gain,
			params: params,
			rates: rates
		}

		load_res(i_start)


	}, // play()

	// stop playing
	stop: function() {
		po.stop = true
		po.timouts.forEach(function(id) {
					clearTimeout(id)







>


>
>
>




<


>

>
>







593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609

610
611
612
613
614
615
616
617
618
619
620
621
622
			s_end: i_end,	// last music symbol / null
			s_cur: i_start,	// current music symbol
//			repn: false,	// don't repeat
			repv: i_lvl || 0, // repeat variant number
			tgen: 2,	// // generate by 2 seconds
			get_time: get_time,
			midi_ctrl: midi_ctrl,
			midi_prog: midi_prog,
			note_run: note_run,
			timouts: [],
			v_c: [],	// voice to channel
			c_i: [],	// channel to instrument
			v_b: [],	// voice to bank

			// audio specific
			ac: ac,
			gain: gain,

			rates: rates
		}
		w_instr++			// play lock
		load_res(i_start)
		if (--w_instr == 0)		// all resources are there
			play_start()
	}, // play()

	// stop playing
	stop: function() {
		po.stop = true
		po.timouts.forEach(function(id) {
					clearTimeout(id)
︙︙
Changes to util/sndgen.js.
1
2
3
4
5
6
7
8
9
10
// sndgen.js - sound generation
//
// Copyright (C) 2019-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// sndgen.js - sound generation
//
// Copyright (C) 2019-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.
︙︙
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//	s.ptim = play time
// - in BAR
//	rep_p = on a right repeat bar, pointer to the left repeat symbol
//	rep_s = on the first repeat variant, array of pointers to the next symbols,
//						indexed by the repeat number
// - in NOTE and REST
//	s.pdur = play duration
//	s.instr = bank + instrument
//	s.chn = MIDI channel
// - in the notes[] of NOTE
//	s.notes[i].midi

if (!abc2svg)
    var	abc2svg = {}

function ToAudio() {
 return {

   // generate the play data of a tune
   add: function(first,		// starting symbol
		voice_tb,	// voice table
		cfmt) {		// tune parameters
    var	toaud = this,
	C = abc2svg.C,
	p_time = 0,		// last playing time
	abc_time = 0,		// last ABC time
	play_fac = C.BLEN / 4 * 120 / 60, // play time factor - default: Q:1/4=120
	i, n, dt, d, v, c,
	s = first,
	rst = s,		// left repeat (repeat restart)
	rst_fac,		// play factor on repeat restart
	rsk,			// repeat variant array (repeat skip)
	b_tim,			// time of last measure bar
	b_typ,			// type of last measure bar
	instr = [],		// [voice] bank + instrument
	chn = []		// [voice] MIDI channel

	// build the information about the parts (P:)
	function build_parts(first) {
	    var	i, j, c, n, v,
		s = first,
		p = s.parts,
		st = [],







<
<


















|



|

|
<
<







22
23
24
25
26
27
28


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53


54
55
56
57
58
59
60
//	s.ptim = play time
// - in BAR
//	rep_p = on a right repeat bar, pointer to the left repeat symbol
//	rep_s = on the first repeat variant, array of pointers to the next symbols,
//						indexed by the repeat number
// - in NOTE and REST
//	s.pdur = play duration


// - in the notes[] of NOTE
//	s.notes[i].midi

if (!abc2svg)
    var	abc2svg = {}

function ToAudio() {
 return {

   // generate the play data of a tune
   add: function(first,		// starting symbol
		voice_tb,	// voice table
		cfmt) {		// tune parameters
    var	toaud = this,
	C = abc2svg.C,
	p_time = 0,		// last playing time
	abc_time = 0,		// last ABC time
	play_fac = C.BLEN / 4 * 120 / 60, // play time factor - default: Q:1/4=120
	i, n, dt, d, v,
	s = first,
	rst = s,		// left repeat (repeat restart)
	rst_fac,		// play factor on repeat restart
	rsk = [],		// repeat variant array (repeat skip)
	b_tim,			// time of last measure bar
	b_typ			// type of last measure bar



	// build the information about the parts (P:)
	function build_parts(first) {
	    var	i, j, c, n, v,
		s = first,
		p = s.parts,
		st = [],
︙︙
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215


216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
					if (first.parts[i] == v)
						first.p_s[i] = s
				}
			}
		}
	} // build_parts()

	// set the starting MIDI instruments and channels
	function midi_start() {
	    var	v, p_v, c, i, ii

		for (v = 0; v < voice_tb.length; v++) {
			p_v = voice_tb[v]
			ii = p_v.instr || 0		// instrument
			c = p_v.chn			// channel
			if (c == undefined)
				c = p_v.v < 9 ? p_v.v : p_v.v + 1
			else if (c == 9)		// percussion
				ii = (ii & ~0x7f) | 16384

			if (p_v.midictl) {		// if MIDI controls
				for (i in p_v.midictl) {
					switch (Number(i)) {
					case 0:		// MSB bank
						ii = (ii & 0x3fff) |
							(p_v.midictl[i] << 14)
						break
					case 32:	// LSB bank
						ii = (ii & 0x1fc07f) |
							(p_v.midictl[i] << 7)
						break
					}
				}
			}

			if ((ii & ~0x7f) == 16384) // if bank 128 (percussion)
				c = 9			// channel '10'
			chn[v] = c
			instr[c] = ii
		}
	} // midi_start()

	// handle a block symbol
	function do_block(s) {
	    var	v = s.v,
		c = chn[v]

		switch (s.subtype) {
		case "midichn":
			chn[v] = s.chn
			break
		case "midictl":
			switch (s.ctrl) {
			case 0:			// MSB bank
				instr[c] = (instr[c] & 0x3fff) |
					(s.val << 14)
				break
			case 32:		// LSB bank
				instr[c] = (instr[c] & 0x1fc07f) |
					(s.val << 7)
				break
//			case 121:		// reset all controllers
//				instr = []
//				break
			}
			if ((instr[c] & ~0x7f) == 16384) { // if percussion
				instr[9] = instr[c]	// force the channel 10
				chn[v] = c = 9
			}
			s.chn = c
			break
		case "midiprog":
			instr[c] = (instr[c] & ~0x7f) | s.instr
			s.chn = c
			break
		}
	} // do_block()

	// generate the grace notes
	function gen_grace(s) {
	    var	g, i, n, t, d, s2,
		next = s.next

		// before beat
		if (s.sappo) {
			d = C.BLEN / 16
		} else if ((!next || next.type != C.NOTE)
			&& s.prev && s.prev.type == C.NOTE) {
			d = s.prev.dur / 2

		// on beat
		} else {
			d = next.dur / 12
			if (!(d & (d - 1)))
				d = next.dur / 2	// no dot
			else
				d = next.dur / 3


			next.time += d
			next.dur -= d
		}
//fixme: assume the grace notes in the sequence have the same duration
		n = 0
		for (g = s.extra; g; g = g.next)
			n++
		d /= n * play_fac
		t = p_time
		for (g = s.extra; g; g = g.next) {
			g.ptim = t
			g.pdur = d
			g.chn = chn[s.v]
			g.instr = instr[g.chn]
			t += d
		}
	} // gen_grace()

	// change the tempo
	function set_tempo(s) {
	    var	i,
		d = 0,
		n = s.tempo_notes.length

		for (i = 0; i < n; i++)
			d += s.tempo_notes[i]
		return d * s.tempo / 60
	} // set_tempo()

	function set_variant(rsk, n, s) {
	    var	d
		n = n.match(/[1-8]-[2-9]|[1-9,.]|[^\s]+$/g)

		while (1) {
			d = n.shift()
			if (!d)
				break
			if (d[1] == '-')
				for (i = d[0]; i <= d[2]; i++)
					rsk[i] = s
			else if (d >= '1' && d <= '9')
				rsk[Number(d)] = s
			else if (d != ',')
				rsk.push(s)	// last
		}
	} // set_variant()

	// add() main

	// if some MIDI stuff, load the associated module
	if (cfmt.chord) {
		if (!abc2svg.chord) {
			abc2svg.pwait = true	// don't start playing now

			abc2svg.loadjs("chord-1.js",
					function(){	// ok
						toaud.add(first, voice_tb, cfmt)
					},
					function(){	// KO
						cfmt.chord = null
						toaud.add(first, voice_tb, cfmt)
					})
			return
		}
		abc2svg.chord(first, voice_tb, cfmt)
	}

	if (s.parts)
		build_parts(s)

	// get the starting MIDI parameters
	midi_start()

	// set the time parameters
	rst_fac = play_fac
	while (s) {
		if (s.noplay) {			// in display macro sequence
			s = s.ts_next
			continue
		}







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



















>
>












<
<















|
|
|
>

















|
<
<
<
<
<
<
<
<
<
<
<
<
<

<




<
<
<







115
116
117
118
119
120
121







































































122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154


155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191













192

193
194
195
196



197
198
199
200
201
202
203
					if (first.parts[i] == v)
						first.p_s[i] = s
				}
			}
		}
	} // build_parts()








































































	// generate the grace notes
	function gen_grace(s) {
	    var	g, i, n, t, d, s2,
		next = s.next

		// before beat
		if (s.sappo) {
			d = C.BLEN / 16
		} else if ((!next || next.type != C.NOTE)
			&& s.prev && s.prev.type == C.NOTE) {
			d = s.prev.dur / 2

		// on beat
		} else {
			d = next.dur / 12
			if (!(d & (d - 1)))
				d = next.dur / 2	// no dot
			else
				d = next.dur / 3
			if (s.p_v.key.k_bagpipe)
				d /= 2
			next.time += d
			next.dur -= d
		}
//fixme: assume the grace notes in the sequence have the same duration
		n = 0
		for (g = s.extra; g; g = g.next)
			n++
		d /= n * play_fac
		t = p_time
		for (g = s.extra; g; g = g.next) {
			g.ptim = t
			g.pdur = d


			t += d
		}
	} // gen_grace()

	// change the tempo
	function set_tempo(s) {
	    var	i,
		d = 0,
		n = s.tempo_notes.length

		for (i = 0; i < n; i++)
			d += s.tempo_notes[i]
		return d * s.tempo / 60
	} // set_tempo()

	function set_variant(s) {
	    var	d,
		n = s.text.match(/[1-8]-[2-9]|[1-9,.]|[^\s]+$/g)

		while (1) {
			d = n.shift()
			if (!d)
				break
			if (d[1] == '-')
				for (i = d[0]; i <= d[2]; i++)
					rsk[i] = s
			else if (d >= '1' && d <= '9')
				rsk[Number(d)] = s
			else if (d != ',')
				rsk.push(s)	// last
		}
	} // set_variant()

	// add() main

	// if some MIDI stuff, load the associated module
	if (cfmt.chord)













		abc2svg.chord(first, voice_tb, cfmt)


	if (s.parts)
		build_parts(s)




	// set the time parameters
	rst_fac = play_fac
	while (s) {
		if (s.noplay) {			// in display macro sequence
			s = s.ts_next
			continue
		}
︙︙
306
307
308
309
310
311
312
313

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
		}
		switch (s.type) {
		case C.BAR:
			if (s.time != b_tim) {
				b_tim = s.time
				b_typ = 0
			}
			if (s.text && rsk		// if new variant

			 && s.text[0] != '1') {
				if (b_typ & 1)
					break
				b_typ |= 1
				set_variant(rsk, s.text, s)
				play_fac = rst_fac
				rst = rsk[0]		// reinit the restart
			}

			// right repeat
			if (s.bar_type[0] == ':') {
				if (b_typ & 2)
					break
				b_typ |= 2
				s.rep_p = rst		// :| to |:
				if (rsk && rst == rsk[0])
					s.rep_v = rsk	// to know the number of variants
			}

			// 1st time repeat
			if (s.text) {
			    if (s.text[0] == '1') {
				if (b_typ & 1)
					break
				b_typ |= 1
				s.rep_s = rsk = [rst]	// repeat skip
							// and memorize the restart
				if (rst.bar_type
				 && rst.bar_type.slice(-1) != ':')
					rst.bar_type += ':' // restart confirmed
				set_variant(rsk, s.text, s)
				rst_fac = play_fac
			    }

			// left repeat
			} else if (s.rbstop) {
				if (s.bar_type.slice(-1) == ':') {
					if (b_typ & 4)
						break
					b_typ |= 4
				} else {
					if (b_typ & 8)
						break
					b_typ |= 8
				}
				rst = s			// new possible restart
				rst_fac = play_fac
			}
			break
		case C.BLOCK:
			do_block(s)
			break
		case C.GRACE:
			if (s.time == 0		// if before beat at start time
			 && abc_time == 0) {
				dt = 0
				if (s.sappo)
					dt = C.BLEN / 16
				else if (!s.next || s.next.type != C.NOTE)







|
>




|










|














|




|













<
<
<







215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272



273
274
275
276
277
278
279
		}
		switch (s.type) {
		case C.BAR:
			if (s.time != b_tim) {
				b_tim = s.time
				b_typ = 0
			}
			if (s.text			// if new variant
			 && rsk.length > 1
			 && s.text[0] != '1') {
				if (b_typ & 1)
					break
				b_typ |= 1
				set_variant(s)
				play_fac = rst_fac
				rst = rsk[0]		// reinit the restart
			}

			// right repeat
			if (s.bar_type[0] == ':') {
				if (b_typ & 2)
					break
				b_typ |= 2
				s.rep_p = rst		// :| to |:
				if (rst == rsk[0])
					s.rep_v = rsk	// to know the number of variants
			}

			// 1st time repeat
			if (s.text) {
			    if (s.text[0] == '1') {
				if (b_typ & 1)
					break
				b_typ |= 1
				s.rep_s = rsk = [rst]	// repeat skip
							// and memorize the restart
				if (rst.bar_type
				 && rst.bar_type.slice(-1) != ':')
					rst.bar_type += ':' // restart confirmed
				set_variant(s)
				rst_fac = play_fac
			    }

			// left repeat
			} else if (s.rbstop == 2) {
				if (s.bar_type.slice(-1) == ':') {
					if (b_typ & 4)
						break
					b_typ |= 4
				} else {
					if (b_typ & 8)
						break
					b_typ |= 8
				}
				rst = s			// new possible restart
				rst_fac = play_fac
			}
			break



		case C.GRACE:
			if (s.time == 0		// if before beat at start time
			 && abc_time == 0) {
				dt = 0
				if (s.sappo)
					dt = C.BLEN / 16
				else if (!s.next || s.next.type != C.NOTE)
︙︙
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
					dt = d / 2
				s.next.time -= dt
				d -= dt
			}
			d /= play_fac
			s.pdur = d
			v = s.v
			c = chn[v]			// channel
			s.chn = c
			s.instr = instr[c]
			break
		case C.TEMPO:
			if (s.tempo)
				play_fac = set_tempo(s)
			break
		}
		s = s.ts_next
	} // loop

	// if playing was waiting for a resource, start it now
	if (abc2svg.pwait) {
		i = abc2svg.pwait
		delete abc2svg.pwait
		if (typeof i == "function")
			i()
	}
   } // add()
 } // return
} // ToAudio()

// play some next symbols
//
// This function is called to start playing.







<
<
<








<
<
<
<
<
<
<
<







293
294
295
296
297
298
299



300
301
302
303
304
305
306
307








308
309
310
311
312
313
314
					dt = d / 2
				s.next.time -= dt
				d -= dt
			}
			d /= play_fac
			s.pdur = d
			v = s.v



			break
		case C.TEMPO:
			if (s.tempo)
				play_fac = set_tempo(s)
			break
		}
		s = s.ts_next
	} // loop








   } // add()
 } // return
} // ToAudio()

// play some next symbols
//
// This function is called to start playing.
︙︙
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
//  - s_cur: current symbol (next to play)
//		must be set to the first symbol to be played at startup time
//  - s_end: stop playing on this symbol
//		this symbol is not played. It may be null.
//  - conf
//    - speed: current speed factor
//		must be set to 1 at startup time
//    - new_conf: new speed factor
//		set by the user
// - internal variables
//  - stim: start time
//  - repn: don't repeat
//  - repv: variant number
//  - timouts: array of the current timeouts
//		this array may be used by the upper function in case of hard stop
//  - p_v: voice table used for MIDI control
// - methods
//  - onend: (optional)
//  - onnote: (optional)
//  - note_run: start playing a note
//  - get_time: return the time of the underlaying sound system
abc2svg.play_next = function(po) {

	// handle a tie
	function do_tie(note, d) {
	    var	i,
		s = note.s,
		midi = note.midi,
		C = abc2svg.C,
		v = s.v,
		end_time = s.time + s.dur,
		repv = po.repv

		// search the end of the tie
		while (1) {







|
















|

|
<







324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350

351
352
353
354
355
356
357
//  - s_cur: current symbol (next to play)
//		must be set to the first symbol to be played at startup time
//  - s_end: stop playing on this symbol
//		this symbol is not played. It may be null.
//  - conf
//    - speed: current speed factor
//		must be set to 1 at startup time
//    - new_speed: new speed factor
//		set by the user
// - internal variables
//  - stim: start time
//  - repn: don't repeat
//  - repv: variant number
//  - timouts: array of the current timeouts
//		this array may be used by the upper function in case of hard stop
//  - p_v: voice table used for MIDI control
// - methods
//  - onend: (optional)
//  - onnote: (optional)
//  - note_run: start playing a note
//  - get_time: return the time of the underlaying sound system
abc2svg.play_next = function(po) {

	// handle a tie
	function do_tie(not_s, d) {
	    var	i,
		s = not_s.s,

		C = abc2svg.C,
		v = s.v,
		end_time = s.time + s.dur,
		repv = po.repv

		// search the end of the tie
		while (1) {
︙︙
474
475
476
477
478
479
480

481
482

483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518


519








520
521
522
523
524
525
526
527























528
529
530
531
532
533
534
					if (!s.rep_s[repv])
						break
					s = s.rep_s[repv++]
					end_time = s.time
				}
				while (s.ts_next && !s.ts_next.dur)
					s = s.ts_next

			}
			if (s.type != C.NOTE)

				continue

			i = s.notes.length
			while (--i >= 0) {
				note = s.notes[i]
				if (note.midi == midi) {
					note.ti2 = true	// the sound is generated
					d += s.pdur / po.conf.speed
					return note.tie_ty ? do_tie(note, d) : d
				}
			}
		}

		return d
	} // do_tie()

	// set the MIDI controls up to now
	function set_ctrl(po, s2, t) {
	    var	i,
		p_v = s2.p_v,
		tim = s2.time,
		s = {
			subtype: "midictl",
			p_v: p_v,
			v: p_v.v,
			chn: p_v.chn
		}

		for (i in p_v.midictl) { // MIDI controls at voice start time
			s.ctrl = Number(i)
			s.val = p_v.midictl[i]
			po.midi_ctrl(po, s, t)
		}
		for (s = p_v.sym; s && s.time <= tim; s = s.next) {
			if (s.subtype == "midictl")
				po.midi_ctrl(po, s, t)


		}








		po.p_v[s2.v] = true	// synchronization done
	} // set_ctrl()

    // start and continue to play
    function play_cont(po) {
    var	d, i, st, m, note, g, s2, t, maxt, now,
	C = abc2svg.C,
	s = po.s_cur
























	if (po.stop) {
		if (po.onend)
			po.onend(po.repv)
		return
	}








>

|
>





|
<

|











<












|


>
>

>
>
>
>
>
>
>
>








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385

386
387
388
389
390
391
392
393
394
395
396
397
398

399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
					if (!s.rep_s[repv])
						break
					s = s.rep_s[repv++]
					end_time = s.time
				}
				while (s.ts_next && !s.ts_next.dur)
					s = s.ts_next
				continue
			}
			if (s.time < end_time
			 || !s.ti2)			// if not end of tie
				continue

			i = s.notes.length
			while (--i >= 0) {
				note = s.notes[i]
				if (note.tie_s == not_s) {

					d += s.pdur / po.conf.speed
					return note.tie_e ? do_tie(note, d) : d
				}
			}
		}

		return d
	} // do_tie()

	// set the MIDI controls up to now
	function set_ctrl(po, s2, t) {
	    var	i,
		p_v = s2.p_v,

		s = {
			subtype: "midictl",
			p_v: p_v,
			v: p_v.v,
			chn: p_v.chn
		}

		for (i in p_v.midictl) { // MIDI controls at voice start time
			s.ctrl = Number(i)
			s.val = p_v.midictl[i]
			po.midi_ctrl(po, s, t)
		}
		for (s = p_v.sym; s != s2; s = s.next) {
			if (s.subtype == "midictl")
				po.midi_ctrl(po, s, t)
			else if (s.subtype == 'midiprog')
				po.midi_prog(po, s)
		}

		// if no %%MIDI, set 'grand acoustic piano' as the instrument
		i = po.v_c[s2.v]
		if (i == undefined)
			po.v_c[s2.v] = i = s2.v < 9 ? s2.v : s2.v + 1
		if (po.c_i[i] == undefined)
			po.c_i[i] = 0	// piano

		po.p_v[s2.v] = true	// synchronization done
	} // set_ctrl()

    // start and continue to play
    function play_cont(po) {
    var	d, i, st, m, note, g, s2, t, maxt, now,
	C = abc2svg.C,
	s = po.s_cur

	// search the end of a sequence of variants
	function var_end(s) {
	    var	i, s2, s3,
		a = s.rep_v || s.rep_s
		ti = 0

		for (i = 1; i < a.length; i++) {
			s2 = a[i]
			if (s2.time > ti) {
				ti = s2.time
				s3 = s2
			}
		}
		for (s = s3; s != po.s_end; s = s.ts_next) {
			if (s.time == ti)
				continue
			if (s.rbstop == 2)
				break
		}
		po.repv = 1		// repeat end
		return s
	} // var_end()

	if (po.stop) {
		if (po.onend)
			po.onend(po.repv)
		return
	}

︙︙
555
556
557
558
559
560
561

562
563
564
565
566
567
568
569

570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586


587

588
589
590
591
592

593











594
595
596
597
598
599
600
	maxt = t + po.tgen		// max time = now + 'tgen' seconds
	po.timouts = []
	while (1) {
		if (!po.p_v[s.v])		// if new voice
			set_ctrl(po, s, t)	// set the MIDI controls
		switch (s.type) {
		case C.BAR:

			if (s.rep_p) {		// right repeat
				po.repv++
				if (!po.repn	// if repeat a first time
				 && (!s.rep_v	// and no variant (anymore)
				  || po.repv < s.rep_v.length)) {
					po.stim += (s.ptim - s.rep_p.ptim) /
							po.conf.speed
					s = s.rep_p	// left repeat

					while (s.ts_next
					 && !s.ts_next.dur)
						s = s.ts_next
					t = po.stim + s.ptim / po.conf.speed
					po.repn = true
					break
				}
				po.repn = false
			}
			if (s.rep_s) {			// first variant
				s2 = s.rep_s[po.repv]	// next variant
				if (s2) {
					po.stim += (s.ptim - s2.ptim) /
							po.conf.speed
					s = s2
					t = po.stim + s.ptim / po.conf.speed
					po.repn = false


				} else {		// end of tune

					s = po.s_end
					break
				}
			}
			if (s.bar_type.slice(-1) == ':') // left repeat

				po.repv = 1












		    if (!s.part1) {
			while (s.ts_next && !s.ts_next.seqst) {
				s = s.ts_next
				if (s.part1)
					break
			}







>




|
<
<
|
>
|
|
|
<
|
<

<




<
<
<
<

>
>
|
>
|
|


|
>

>
>
>
>
>
>
>
>
>
>
>







483
484
485
486
487
488
489
490
491
492
493
494
495


496
497
498
499
500

501

502

503
504
505
506




507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
	maxt = t + po.tgen		// max time = now + 'tgen' seconds
	po.timouts = []
	while (1) {
		if (!po.p_v[s.v])		// if new voice
			set_ctrl(po, s, t)	// set the MIDI controls
		switch (s.type) {
		case C.BAR:
			s2 = null
			if (s.rep_p) {		// right repeat
				po.repv++
				if (!po.repn	// if repeat a first time
				 && (!s.rep_v	// and no variant (anymore)
				  || po.repv <= s.rep_v.length)) {


					s2 = s.rep_p	// left repeat
					po.repn = true
				} else {
					if (s.rep_v)
						s2 = var_end(s)

					po.repn = false

				}

			}
			if (s.rep_s) {			// first variant
				s2 = s.rep_s[po.repv]	// next variant
				if (s2) {




					po.repn = false
					if (s2 == s)
						s2 = null
				} else {		// end of variants
					s2 = var_end(s)
					if (s2 == po.s_end)
						break
				}
			}
			if (s.bar_type.slice(-1) == ':' // left repeat
			 && s.bar_type[0] != ':')	// but not ::
				po.repv = 1

			if (s2) {			// if skip
				po.stim += (s.ptim - s2.ptim) / po.conf.speed
				s = s2
				while (s && !s.dur)
					s = s.ts_next
				if (!s)
					break		// no ending variant
				t = po.stim + s.ptim / po.conf.speed
				break
			}

		    if (!s.part1) {
			while (s.ts_next && !s.ts_next.seqst) {
				s = s.ts_next
				if (s.part1)
					break
			}
︙︙
620
621
622
623
624
625
626


627
628
629
630
631
632

633
634
635
636
637
638
639
640
641
642
643
644
645
646
647

648
649
650
651
652
653
654
655
656
657
658
659
	    if (s && s != po.s_end) {
		switch (s.type) {
		case C.BAR:
			break
		case C.BLOCK:
			if (s.subtype == "midictl")
				po.midi_ctrl(po, s, t)


			break
		case C.GRACE:
			for (g = s.extra; g; g = g.next) {
				d = g.pdur / po.conf.speed
				for (m = 0; m <= g.nhd; m++) {
					note = g.notes[m]

					po.note_run(po, g,
						note.midi,
						t + g.ptim - s.ptim,
//fixme: there may be a tie...
						d)
				}
			}
			break
		case C.NOTE:
		case C.REST:
			d = s.pdur / po.conf.speed
		    if (s.type == C.NOTE) {
			for (m = 0; m <= s.nhd; m++) {
				note = s.notes[m]
				if (note.ti2)

					continue
				po.note_run(po, s,
					note.midi,
					t,
					note.tie_ty ?
						do_tie(note, d) : d)
			}
		    }

			// follow the notes/rests while playing
			if (po.onnote && s.istart) {
				i = s.istart







>
>






>
|













|
>
|



|







556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
	    if (s && s != po.s_end) {
		switch (s.type) {
		case C.BAR:
			break
		case C.BLOCK:
			if (s.subtype == "midictl")
				po.midi_ctrl(po, s, t)
			else if (s.subtype == 'midiprog')
				po.midi_prog(po, s)
			break
		case C.GRACE:
			for (g = s.extra; g; g = g.next) {
				d = g.pdur / po.conf.speed
				for (m = 0; m <= g.nhd; m++) {
					note = g.notes[m]
					if (!note.noplay)
					    po.note_run(po, g,
						note.midi,
						t + g.ptim - s.ptim,
//fixme: there may be a tie...
						d)
				}
			}
			break
		case C.NOTE:
		case C.REST:
			d = s.pdur / po.conf.speed
		    if (s.type == C.NOTE) {
			for (m = 0; m <= s.nhd; m++) {
				note = s.notes[m]
				if (note.tie_s		// end of tie
				 || note.noplay)	// (%%voicecombine)
					continue	// already generated
				po.note_run(po, s,
					note.midi,
					t,
					note.tie_e ?
						do_tie(note, d) : d)
			}
		    }

			// follow the notes/rests while playing
			if (po.onnote && s.istart) {
				i = s.istart
︙︙
Changes to util/sndmem.js.
1
2
3
4
5
6
7
8
9
10
// sndmem.js - generate play events for use with toaudio5 or tomidi5
//
// Copyright (C) 2020-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// sndmem.js - generate play events for use with toaudio5 or tomidi5
//
// Copyright (C) 2020-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.
︙︙
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
	// @k = MIDI key + detune
	// @t = audio start time (ms)
	// @d = duration adjusted for speed (ms)
	function note_run(po, s, k, t, d) {
		po.a_e.push(new Float32Array([
				s.istart,
				t,
				s.instr,
				k,
				d,
				1,
				s.v]))
	} // note_run()

	// return the play real time in seconds







|







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
	// @k = MIDI key + detune
	// @t = audio start time (ms)
	// @d = duration adjusted for speed (ms)
	function note_run(po, s, k, t, d) {
		po.a_e.push(new Float32Array([
				s.istart,
				t,
				po.c_i[po.v_c[s.v]],
				k,
				d,
				1,
				s.v]))
	} // note_run()

	// return the play real time in seconds
︙︙
63
64
65
66
67
68
69







70
71
72
73
74
75
76
77
78

79


80
81
82
83
84
85
86
				0,	// (time - unused)
				-1,	// MIDI control
				s.ctrl,
				s.val,
				1,
				s.v]))
	} // midi_ctrl()








	// define the play object
	po = {
		conf: {		// configuration
			speed: 1
		},
		tgen: 3600, 	// generate by (for) 1 hour
		get_time: get_time,
		midi_ctrl: midi_ctrl,

		note_run: note_run,



		// sndmem specific
		a_e: []
	}

	// loop on the tunes
	while (1) {







>
>
>
>
>
>
>









>

>
>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
				0,	// (time - unused)
				-1,	// MIDI control
				s.ctrl,
				s.val,
				1,
				s.v]))
	} // midi_ctrl()

	// MIDI channel and/or program
	function midi_prog(po, s) {
		po.v_c[s.v] = s.chn
		if (s.instr)
			po.c_i[s.chn] = s.instr
	} // midi_prog()

	// define the play object
	po = {
		conf: {		// configuration
			speed: 1
		},
		tgen: 3600, 	// generate by (for) 1 hour
		get_time: get_time,
		midi_ctrl: midi_ctrl,
		midi_prog: midi_prog,
		note_run: note_run,
		v_c: [],	// voice to channel
		c_i: [],	// channel to instrument

		// sndmem specific
		a_e: []
	}

	// loop on the tunes
	while (1) {
︙︙
Changes to util/sndmid.js.
1
2
3
4
5
6
7
8
9
10
// sndmid.js - audio output using HTML5 MIDI
//
// Copyright (C) 2019-2021 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.


|







1
2
3
4
5
6
7
8
9
10
// sndmid.js - audio output using HTML5 MIDI
//
// Copyright (C) 2019-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.
︙︙
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
	// @s = symbol
	// @k = MIDI key + detune
	// @t = audio start time (s)
	// @d = duration adjusted for speed (s)
	function note_run(po, s, k, t, d) {
	    var	j,
		a = (k * 100) % 100,	// detune in cents
		i = s.instr,
		c = s.chn

		k |= 0			// remove the detune value

		t *= 1000		// convert to ms
		d *= 1000		

		if (i != po.c_i[c]) {		// if program change

			// at channel start, reset and initialize the controllers
			if (po.c_i[c] == undefined) {
//fixme: does not work with fluidsynth
				po.op.send(new Uint8Array([0xb0 + c, 121, 0]))
				if (s.p_v.midictl) {
				    for (j in s.p_v.midictl)
					po.op.send(new Uint8Array([0xb0 + c,
								j,
								s.p_v.midictl[j]]))
				}
			}

			po.c_i[c] = i
			po.op.send(new Uint8Array([0xc0 + c, i & 0x7f])) // program
		}
		if (a && Midi5.ma.sysexEnabled) {	// if microtone
// fixme: should cache the current microtone values
			po.op.send(new Uint8Array([
				0xf0, 0x7f,	// realtime SysEx
				0x7f,		// all devices
				0x08,		// MIDI tuning standard
				0x02,		// note change







|
|






<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

















77
78
79
80
81
82
83
	// @s = symbol
	// @k = MIDI key + detune
	// @t = audio start time (s)
	// @d = duration adjusted for speed (s)
	function note_run(po, s, k, t, d) {
	    var	j,
		a = (k * 100) % 100,	// detune in cents
		c = po.v_c[s.v],
		i = po.c_i[c]

		k |= 0			// remove the detune value

		t *= 1000		// convert to ms
		d *= 1000		


















		if (a && Midi5.ma.sysexEnabled) {	// if microtone
// fixme: should cache the current microtone values
			po.op.send(new Uint8Array([
				0xf0, 0x7f,	// realtime SysEx
				0x7f,		// all devices
				0x08,		// MIDI tuning standard
				0x02,		// note change
︙︙
113
114
115
116
117
118
119


























120
121
122
123
124
125
126

	// send a MIDI control
	function midi_ctrl(po, s, t) {
		po.op.send(new Uint8Array([0xb0 + s.chn,
					s.ctrl, s.val]),
			t * 1000)
	} // midi_ctrl()



























	// MIDI output is possible,
	// return the possible ports in return to get_outputs()
	function send_outputs(access) {
	    var	o, os,
		out = []








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

	// send a MIDI control
	function midi_ctrl(po, s, t) {
		po.op.send(new Uint8Array([0xb0 + s.chn,
					s.ctrl, s.val]),
			t * 1000)
	} // midi_ctrl()

	// change the channel and/or send a MIDI program	
	function midi_prog(po, s) {
	    var	i,
		c = s.chn

		po.v_c[s.v] = c
		if (s.instr != undefined)
			return
		i = po.c_i[c]

		// at channel start, reset and initialize the controllers
		if (i == undefined) {
//fixme: does not work with fluidsynth
			po.op.send(new Uint8Array([0xb0 + c, 121, 0]))
			if (s.p_v.midictl) {
			    for (i in s.p_v.midictl)
				po.op.send(new Uint8Array([0xb0 + c,
							i,
							s.p_v.midictl[i]]))
			}
		}

		po.c_i[c] = i = s.instr
		po.op.send(new Uint8Array([0xc0 + c, i & 0x7f])) // program
	} // midi_prog()

	// MIDI output is possible,
	// return the possible ports in return to get_outputs()
	function send_outputs(access) {
	    var	o, os,
		out = []

︙︙
192
193
194
195
196
197
198

199
200
201
202
203

204
205
206
207
208
209
210
				s_end: i_end,	// last music symbol / null
				s_cur: i_start,	// current music symbol
//				repn: false,	// don't repeat
				repv: i_lvl || 0, // repeat variant number
				tgen: 2, 	// generate by 2 seconds
				get_time: get_time,
				midi_ctrl: midi_ctrl,

				note_run: note_run,
				timouts: [],

				// MIDI specific
				op: op,		// output port

				c_i: []		// channel to instrument
			}
if (0) {
// temperament
			op.send(new Uint8Array([
				0xf0, 0x7f,	// realtime SysEx
				0x7f,		// all devices







>





>







201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
				s_end: i_end,	// last music symbol / null
				s_cur: i_start,	// current music symbol
//				repn: false,	// don't repeat
				repv: i_lvl || 0, // repeat variant number
				tgen: 2, 	// generate by 2 seconds
				get_time: get_time,
				midi_ctrl: midi_ctrl,
				midi_prog: midi_prog,
				note_run: note_run,
				timouts: [],

				// MIDI specific
				op: op,		// output port
				v_c: [],	// voice to channel
				c_i: []		// channel to instrument
			}
if (0) {
// temperament
			op.send(new Uint8Array([
				0xf0, 0x7f,	// realtime SysEx
				0x7f,		// all devices
︙︙