abcjs 6.0.0-beta.9 → 6.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/.github/workflows/tests.yml +29 -0
  2. package/CODE_OF_CONDUCT.md +76 -0
  3. package/CONTRIBUTING.md +1 -0
  4. package/LICENSE.md +1 -1
  5. package/README.md +88 -7
  6. package/RELEASE.md +961 -1
  7. package/abcjs-audio.css +14 -5
  8. package/dist/.gitignore +1 -2
  9. package/dist/abcjs-basic-min.js +3 -0
  10. package/dist/abcjs-basic-min.js.LICENSE +23 -0
  11. package/dist/abcjs-basic.js +28232 -0
  12. package/dist/abcjs-basic.js.map +1 -0
  13. package/dist/abcjs-plugin-min.js +3 -0
  14. package/dist/abcjs-plugin-min.js.LICENSE +23 -0
  15. package/index.js +27 -2
  16. package/{static-wrappers/license.js → license.js} +1 -1
  17. package/package.json +26 -29
  18. package/{src/plugin/abc_plugin.js → plugin.js} +31 -19
  19. package/src/api/abc_animation.js +1 -17
  20. package/src/api/abc_tablatures.js +144 -0
  21. package/src/api/abc_timing_callbacks.js +216 -117
  22. package/src/api/abc_tunebook.js +18 -67
  23. package/src/api/abc_tunebook_svg.js +40 -48
  24. package/src/data/abc_tune.js +232 -972
  25. package/src/data/deline-tune.js +199 -0
  26. package/src/edit/abc_editarea.js +112 -0
  27. package/src/edit/abc_editor.js +95 -221
  28. package/src/midi/abc_midi_create.js +48 -50
  29. package/src/parse/abc_common.js +0 -14
  30. package/src/parse/abc_parse.js +167 -1321
  31. package/src/parse/abc_parse_book.js +62 -0
  32. package/src/parse/abc_parse_directive.js +164 -41
  33. package/src/parse/abc_parse_header.js +116 -145
  34. package/src/parse/abc_parse_key_voice.js +26 -20
  35. package/src/parse/abc_parse_music.js +1337 -0
  36. package/src/parse/abc_tokenizer.js +21 -15
  37. package/src/parse/abc_transpose.js +3 -15
  38. package/src/parse/tune-builder.js +896 -0
  39. package/src/parse/wrap_lines.js +205 -453
  40. package/src/synth/abc_midi_flattener.js +1292 -0
  41. package/src/{midi → synth}/abc_midi_renderer.js +44 -17
  42. package/src/synth/abc_midi_sequencer.js +648 -0
  43. package/src/synth/active-audio-context.js +3 -14
  44. package/src/synth/cents-to-factor.js +10 -0
  45. package/src/synth/create-note-map.js +21 -32
  46. package/src/synth/create-synth-control.js +20 -103
  47. package/src/synth/create-synth.js +185 -77
  48. package/src/synth/download-buffer.js +7 -21
  49. package/src/synth/get-midi-file.js +13 -20
  50. package/src/synth/images/{loading.svg → loading.svg.js} +4 -0
  51. package/src/synth/images/loop.svg.js +65 -0
  52. package/src/synth/images/pause.svg.js +10 -0
  53. package/src/synth/images/play.svg.js +9 -0
  54. package/src/synth/images/{reset.svg → reset.svg.js} +5 -1
  55. package/src/synth/instrument-index-to-name.js +1 -16
  56. package/src/synth/load-note.js +37 -76
  57. package/src/synth/pitch-to-note-name.js +0 -15
  58. package/src/synth/pitches-to-perc.js +64 -0
  59. package/src/synth/place-note.js +78 -68
  60. package/src/synth/play-event.js +17 -18
  61. package/src/synth/register-audio-context.js +11 -23
  62. package/src/synth/sounds-cache.js +0 -15
  63. package/src/synth/supports-audio.js +9 -23
  64. package/src/synth/synth-controller.js +80 -49
  65. package/src/synth/synth-sequence.js +20 -34
  66. package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
  67. package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
  68. package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
  69. package/src/tablatures/instruments/string-patterns.js +277 -0
  70. package/src/tablatures/instruments/string-tablature.js +56 -0
  71. package/src/tablatures/instruments/tab-note.js +282 -0
  72. package/src/tablatures/instruments/tab-notes.js +41 -0
  73. package/src/tablatures/instruments/violin/tab-violin.js +47 -0
  74. package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
  75. package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
  76. package/src/tablatures/tab-absolute-elements.js +310 -0
  77. package/src/tablatures/tab-common.js +29 -0
  78. package/src/tablatures/tab-renderer.js +243 -0
  79. package/src/tablatures/transposer.js +110 -0
  80. package/src/test/abc_midi_lint.js +5 -22
  81. package/src/test/abc_midi_sequencer_lint.js +11 -14
  82. package/src/test/abc_parser_lint.js +136 -32
  83. package/src/test/abc_vertical_lint.js +94 -32
  84. package/src/test/rendering-lint.js +38 -5
  85. package/src/write/abc_absolute_element.js +112 -120
  86. package/src/write/abc_abstract_engraver.js +102 -253
  87. package/src/write/abc_beam_element.js +30 -290
  88. package/src/write/abc_brace_element.js +12 -121
  89. package/src/write/abc_create_clef.js +21 -32
  90. package/src/write/abc_create_key_signature.js +8 -26
  91. package/src/write/abc_create_note_head.js +107 -0
  92. package/src/write/abc_create_time_signature.js +2 -21
  93. package/src/write/abc_crescendo_element.js +3 -50
  94. package/src/write/abc_decoration.js +7 -30
  95. package/src/write/abc_dynamic_decoration.js +3 -37
  96. package/src/write/abc_ending_element.js +1 -57
  97. package/src/write/abc_engraver_controller.js +111 -234
  98. package/src/write/abc_glyphs.js +8 -18
  99. package/src/write/abc_relative_element.js +57 -97
  100. package/src/write/abc_renderer.js +10 -832
  101. package/src/write/abc_spacing.js +0 -15
  102. package/src/write/abc_staff_group_element.js +14 -349
  103. package/src/write/abc_tempo_element.js +9 -117
  104. package/src/write/abc_tie_element.js +5 -68
  105. package/src/write/abc_triplet_element.js +6 -124
  106. package/src/write/abc_voice_element.js +7 -222
  107. package/src/write/add-chord.js +103 -0
  108. package/src/write/add-text-if.js +33 -0
  109. package/src/write/bottom-text.js +79 -0
  110. package/src/write/calcHeight.js +17 -0
  111. package/src/write/classes.js +100 -0
  112. package/src/write/draw/absolute.js +68 -0
  113. package/src/write/draw/beam.js +56 -0
  114. package/src/write/draw/brace.js +106 -0
  115. package/src/write/draw/crescendo.js +38 -0
  116. package/src/write/draw/debug-box.js +8 -0
  117. package/src/write/draw/draw.js +56 -0
  118. package/src/write/draw/dynamics.js +20 -0
  119. package/src/write/draw/ending.js +46 -0
  120. package/src/write/draw/group-elements.js +66 -0
  121. package/src/write/draw/horizontal-line.js +25 -0
  122. package/src/write/draw/non-music.js +50 -0
  123. package/src/write/draw/print-line.js +24 -0
  124. package/src/write/draw/print-path.js +7 -0
  125. package/src/write/draw/print-stem.js +30 -0
  126. package/src/write/draw/print-symbol.js +59 -0
  127. package/src/write/draw/print-vertical-line.js +18 -0
  128. package/src/write/draw/relative.js +77 -0
  129. package/src/write/draw/round-number.js +5 -0
  130. package/src/write/draw/selectables.js +59 -0
  131. package/src/write/draw/separator.js +16 -0
  132. package/src/write/draw/set-paper-size.js +45 -0
  133. package/src/write/{sprintf.js → draw/sprintf.js} +0 -0
  134. package/src/write/draw/staff-group.js +226 -0
  135. package/src/write/draw/staff-line.js +9 -0
  136. package/src/write/draw/staff.js +33 -0
  137. package/src/write/draw/tab-line.js +40 -0
  138. package/src/write/draw/tempo.js +45 -0
  139. package/src/write/draw/text.js +71 -0
  140. package/src/write/draw/tie.js +97 -0
  141. package/src/write/draw/triplet.js +46 -0
  142. package/src/write/draw/voice.js +102 -0
  143. package/src/write/format-jazz-chord.js +15 -0
  144. package/src/write/free-text.js +41 -0
  145. package/src/write/get-font-and-attr.js +37 -0
  146. package/src/write/get-text-size.js +56 -0
  147. package/src/write/highlight.js +11 -0
  148. package/src/write/layout/VoiceElements.js +121 -0
  149. package/src/write/layout/beam.js +213 -0
  150. package/src/write/layout/get-left-edge-of-staff.js +56 -0
  151. package/src/write/layout/getBarYAt.js +6 -0
  152. package/src/write/layout/layout.js +94 -0
  153. package/src/write/layout/setUpperAndLowerElements.js +232 -0
  154. package/src/write/layout/staffGroup.js +146 -0
  155. package/src/write/layout/triplet.js +75 -0
  156. package/src/write/layout/voice.js +137 -0
  157. package/src/write/selection.js +188 -70
  158. package/src/write/separator.js +10 -0
  159. package/src/write/set-class.js +21 -0
  160. package/src/write/subtitle.js +12 -0
  161. package/src/write/svg.js +95 -43
  162. package/src/write/top-text.js +54 -0
  163. package/src/write/unhighlight.js +11 -0
  164. package/test.js +27 -64
  165. package/types/index.d.ts +1095 -0
  166. package/version.js +1 -1
  167. package/.babelrc +0 -5
  168. package/.eslintrc +0 -3
  169. package/.gitmodules +0 -3
  170. package/Dockerfile +0 -8
  171. package/abcjs-midi.css +0 -166
  172. package/build-utils/loadPresets.js +0 -14
  173. package/build-utils/presets/webpack.analyze.js +0 -6
  174. package/build-utils/presets/webpack.optimize.js +0 -30
  175. package/build-utils/webpack.development.js +0 -14
  176. package/build-utils/webpack.production.js +0 -35
  177. package/deploy-docs.sh +0 -25
  178. package/docker-compose.yml +0 -13
  179. package/docs/README.md +0 -33
  180. package/fix-versions.sh +0 -23
  181. package/midi.js +0 -62
  182. package/src/api/abc_tunebook_midi.js +0 -116
  183. package/src/midi/abc_midi_controls.js +0 -701
  184. package/src/midi/abc_midi_flattener.js +0 -1119
  185. package/src/midi/abc_midi_js_preparer.js +0 -243
  186. package/src/midi/abc_midi_sequencer.js +0 -401
  187. package/src/midi/abc_midi_ui_generator.js +0 -86
  188. package/src/plugin/abc_plugin_midi.js +0 -220
  189. package/src/synth/images/loop.svg +0 -61
  190. package/src/synth/images/pause.svg +0 -6
  191. package/src/synth/images/play.svg +0 -5
  192. package/src/transform/abc2abc_write.js +0 -395
  193. package/static-wrappers/basic.js +0 -2
  194. package/static-wrappers/midi.js +0 -2
  195. package/static-wrappers/plugin-midi.js +0 -6
  196. package/static-wrappers/plugin.js +0 -6
  197. package/webpack.config.js +0 -29
@@ -0,0 +1,62 @@
1
+ // abc_parse_book.js: parses a string representing ABC Music Notation into a usable internal structure.
2
+
3
+ var parseCommon = require('./abc_common');
4
+
5
+ var bookParser = function(book) {
6
+ "use strict";
7
+
8
+ var directives = "";
9
+ book = parseCommon.strip(book);
10
+ var tuneStrings = book.split("\nX:");
11
+ // Put back the X: that we lost when splitting the tunes.
12
+ for (var i = 1; i < tuneStrings.length; i++)
13
+ tuneStrings[i] = "X:" + tuneStrings[i];
14
+ // Keep track of the character position each tune starts with.
15
+ var pos = 0;
16
+ var tunes = [];
17
+ parseCommon.each(tuneStrings, function(tune) {
18
+ tunes.push({ abc: tune, startPos: pos});
19
+ pos += tune.length + 1; // We also lost a newline when splitting, so count that.
20
+ });
21
+ if (tunes.length > 1 && !parseCommon.startsWith(tunes[0].abc, 'X:')) { // If there is only one tune, the X: might be missing, otherwise assume the top of the file is "intertune"
22
+ // There could be file-wide directives in this, if so, we need to insert it into each tune. We can probably get away with
23
+ // just looking for file-wide directives here (before the first tune) and inserting them at the bottom of each tune, since
24
+ // the tune is parsed all at once. The directives will be seen before the engraver begins processing.
25
+ var dir = tunes.shift();
26
+ var arrDir = dir.abc.split('\n');
27
+ parseCommon.each(arrDir, function(line) {
28
+ if (parseCommon.startsWith(line, '%%'))
29
+ directives += line + '\n';
30
+ });
31
+ }
32
+ var header = directives;
33
+
34
+ // Now, the tune ends at a blank line, so truncate it if needed. There may be "intertune" stuff.
35
+ parseCommon.each(tunes, function(tune) {
36
+ var end = tune.abc.indexOf('\n\n');
37
+ if (end > 0)
38
+ tune.abc = tune.abc.substring(0, end);
39
+ tune.pure = tune.abc;
40
+ tune.abc = directives + tune.abc;
41
+
42
+ // for the user's convenience, parse and store the title separately. The title is between the first T: and the next \n
43
+ tune.title = "";
44
+ var title = tune.pure.split("T:");
45
+ if (title.length > 1) {
46
+ title = title[1].split("\n");
47
+ tune.title = parseCommon.strip(title[0]);
48
+ }
49
+
50
+ // for the user's convenience, parse and store the id separately. The id is between the first X: and the next \n
51
+ var id = tune.pure.substring(2, tune.pure.indexOf("\n"));
52
+ tune.id = parseCommon.strip(id);
53
+ });
54
+
55
+ return {
56
+ header: header,
57
+ tunes: tunes
58
+ };
59
+ };
60
+
61
+ module.exports = bookParser;
62
+
@@ -1,18 +1,3 @@
1
- // Copyright (C) 2010-2020 Paul Rosen (paul at paulrosen dot net)
2
- //
3
- // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
- // documentation files (the "Software"), to deal in the Software without restriction, including without limitation
5
- // the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
6
- // to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
- //
8
- // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
- //
10
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
11
- // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
12
- // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
13
- // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
14
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15
-
16
1
  var parseCommon = require('./abc_common');
17
2
 
18
3
  var parseDirective = {};
@@ -23,11 +8,13 @@ var parseDirective = {};
23
8
  var warn;
24
9
  var multilineVars;
25
10
  var tune;
26
- parseDirective.initialize = function(tokenizer_, warn_, multilineVars_, tune_) {
11
+ var tuneBuilder;
12
+ parseDirective.initialize = function(tokenizer_, warn_, multilineVars_, tune_, tuneBuilder_) {
27
13
  tokenizer = tokenizer_;
28
14
  warn = warn_;
29
15
  multilineVars = multilineVars_;
30
16
  tune = tune_;
17
+ tuneBuilder = tuneBuilder_;
31
18
  initializeFonts();
32
19
  };
33
20
 
@@ -52,6 +39,9 @@ var parseDirective = {};
52
39
  tune.formatting.footerfont = { face: "\"Times New Roman\"", size: 12, weight: "normal", style: "normal", decoration: "none" };
53
40
  tune.formatting.headerfont = { face: "\"Times New Roman\"", size: 12, weight: "normal", style: "normal", decoration: "none" };
54
41
  tune.formatting.voicefont = { face: "\"Times New Roman\"", size: 13, weight: "bold", style: "normal", decoration: "none" };
42
+ tune.formatting.tablabelfont = { face: "\"Trebuchet MS\"", size: 16, weight: "normal", style: "normal", decoration: "none" };
43
+ tune.formatting.tabnumberfont = { face: "\"Arial\"", size: 11, weight: "normal", style: "normal", decoration: "none" };
44
+ tune.formatting.tabgracefont = { face: "\"Arial\"", size: 8, weight: "normal", style: "normal", decoration: "none" };
55
45
 
56
46
  // these are the default fonts for these element types. In the printer, these fonts might change as the tune progresses.
57
47
  tune.formatting.annotationfont = multilineVars.annotationfont;
@@ -67,7 +57,7 @@ var parseDirective = {};
67
57
  tune.formatting.wordsfont = multilineVars.wordsfont;
68
58
  }
69
59
 
70
- var fontTypeCanHaveBox = { gchordfont: true, measurefont: true, partsfont: true };
60
+ var fontTypeCanHaveBox = { gchordfont: true, measurefont: true, partsfont: true, annotationfont: true, composerfont: true, historyfont: true, infofont: true, subtitlefont: true, textfont: true, titlefont: true, voicefont: true };
71
61
 
72
62
  var fontTranslation = function(fontFace) {
73
63
  // This translates Postscript fonts for a web alternative.
@@ -349,6 +339,74 @@ var parseDirective = {};
349
339
  tune.formatting.scale = num;
350
340
 
351
341
  };
342
+ // starts at 35
343
+ var drumNames = [
344
+ "acoustic-bass-drum",
345
+ "bass-drum-1",
346
+ "side-stick",
347
+ "acoustic-snare",
348
+ "hand-clap",
349
+ "electric-snare",
350
+ "low-floor-tom",
351
+ "closed-hi-hat",
352
+ "high-floor-tom",
353
+ "pedal-hi-hat",
354
+ "low-tom",
355
+ "open-hi-hat",
356
+ "low-mid-tom",
357
+ "hi-mid-tom",
358
+ "crash-cymbal-1",
359
+ "high-tom",
360
+ "ride-cymbal-1",
361
+ "chinese-cymbal",
362
+ "ride-bell",
363
+ "tambourine",
364
+ "splash-cymbal",
365
+ "cowbell",
366
+ "crash-cymbal-2",
367
+ "vibraslap",
368
+ "ride-cymbal-2",
369
+ "hi-bongo",
370
+ "low-bongo",
371
+ "mute-hi-conga",
372
+ "open-hi-conga",
373
+ "low-conga",
374
+ "high-timbale",
375
+ "low-timbale",
376
+ "high-agogo",
377
+ "low-agogo",
378
+ "cabasa",
379
+ "maracas",
380
+ "short-whistle",
381
+ "long-whistle",
382
+ "short-guiro",
383
+ "long-guiro",
384
+ "claves",
385
+ "hi-wood-block",
386
+ "low-wood-block",
387
+ "mute-cuica",
388
+ "open-cuica",
389
+ "mute-triangle",
390
+ "open-triangle",
391
+ ];
392
+
393
+ var interpretPercMap = function(restOfString) {
394
+ var tokens = restOfString.split(/\s+/); // Allow multiple spaces.
395
+ if (tokens.length !== 2 && tokens.length !== 3)
396
+ return { error: 'Expected parameters "abc-note", "drum-sound", and optionally "note-head"'};
397
+ var key = tokens[0];
398
+ // The percussion sound can either be a MIDI number or a drum name. If it is not a number then check for a name.
399
+ var pitch = parseInt(tokens[1], 10);
400
+ if ((isNaN(pitch) || pitch < 35 || pitch > 81) && tokens[1]) {
401
+ pitch = drumNames.indexOf(tokens[1].toLowerCase()) + 35;
402
+ }
403
+ if ((isNaN(pitch) || pitch < 35 || pitch > 81))
404
+ return { error: 'Expected drum name, received "' + tokens[1] + '"' };
405
+ var value = { sound: pitch };
406
+ if (tokens.length === 3)
407
+ value.noteHead = tokens[2];
408
+ return { key: key, value: value };
409
+ };
352
410
 
353
411
  var getRequiredMeasurement = function(cmd, tokens) {
354
412
  var points = tokenizer.getMeasurement(tokens);
@@ -459,10 +517,6 @@ var parseDirective = {};
459
517
  var midiCmdParam5Integer = [
460
518
  "drone"
461
519
  ];
462
- var midiCmdParam1IntegerOptionalOctave = [
463
- "bassprog",
464
- "chordprog"
465
- ];
466
520
  var midiCmdParam1String1Integer = [
467
521
  "portamento"
468
522
  ];
@@ -618,8 +672,8 @@ var parseDirective = {};
618
672
  }
619
673
  }
620
674
 
621
- if (tune.hasBeginMusic())
622
- tune.appendElement('midi', -1, -1, { cmd: midi_cmd, params: midi_params });
675
+ if (tuneBuilder.hasBeginMusic())
676
+ tuneBuilder.appendElement('midi', -1, -1, { cmd: midi_cmd, params: midi_params });
623
677
  else {
624
678
  if (tune.formatting['midi'] === undefined)
625
679
  tune.formatting['midi'] = {};
@@ -659,6 +713,7 @@ var parseDirective = {};
659
713
  restOfString = tokenizer.stripComment(restOfString);
660
714
  var cmd = tokens.shift().token.toLowerCase();
661
715
  var scratch = "";
716
+ var line;
662
717
  switch (cmd)
663
718
  {
664
719
  // The following directives were added to abc_parser_lint, but haven't been implemented here.
@@ -671,7 +726,6 @@ var parseDirective = {};
671
726
  // exprabove: { type: "boolean", optional: true },
672
727
  // exprbelow: { type: "boolean", optional: true },
673
728
  // gchordbox: { type: "boolean", optional: true },
674
- // graceslurs: { type: "boolean", optional: true },
675
729
  // gracespacebefore: { type: "number", optional: true },
676
730
  // gracespaceinside: { type: "number", optional: true },
677
731
  // gracespaceafter: { type: "number", optional: true },
@@ -691,10 +745,26 @@ var parseDirective = {};
691
745
  // titleformat: { type: "string", optional: true },
692
746
  case "bagpipes":tune.formatting.bagpipes = true;break;
693
747
  case "flatbeams":tune.formatting.flatbeams = true;break;
748
+ case "jazzchords":tune.formatting.jazzchords = true;break;
694
749
  case "landscape":multilineVars.landscape = true;break;
695
750
  case "papersize":multilineVars.papersize = restOfString;break;
696
- case "slurgraces":tune.formatting.slurgraces = true;break;
697
- case "stretchlast":tune.formatting.stretchlast = true;break;
751
+ case "graceslurs":
752
+ if (tokens.length !== 1)
753
+ return "Directive graceslurs requires one parameter: 0 or 1";
754
+ if (tokens[0].token === '0' || tokens[0].token === 'false')
755
+ tune.formatting.graceSlurs = false;
756
+ else if (tokens[0].token === '1' || tokens[0].token === 'true')
757
+ tune.formatting.graceSlurs = true;
758
+ else
759
+ return "Directive graceslurs requires one parameter: 0 or 1 (received " + tokens[0].token + ')';
760
+ break;
761
+ case "stretchlast":
762
+ var sl = parseStretchLast(tokens);
763
+ if (sl.value !== undefined)
764
+ tune.formatting.stretchlast = sl.value;
765
+ if (sl.error)
766
+ return sl.error;
767
+ break;
698
768
  case "titlecaps":multilineVars.titlecaps = true;break;
699
769
  case "titleleft":tune.formatting.titleleft = true;break;
700
770
  case "measurebox":tune.formatting.measurebox = true;break;
@@ -734,21 +804,21 @@ var parseDirective = {};
734
804
  var voiceScale = tokens.shift();
735
805
  if (multilineVars.currentVoice) {
736
806
  multilineVars.currentVoice.scale = voiceScale.floatt;
737
- tune.changeVoiceScale(multilineVars.currentVoice.scale);
807
+ tuneBuilder.changeVoiceScale(multilineVars.currentVoice.scale);
738
808
  }
739
809
  return null;
740
810
  case "vskip":
741
811
  var vskip = Math.round(getRequiredMeasurement(cmd, tokens));
742
812
  if (vskip.error)
743
813
  return vskip.error;
744
- tune.addSpacing(vskip);
814
+ tuneBuilder.addSpacing(vskip);
745
815
  return null;
746
816
  case "scale":
747
817
  setScale(cmd, tokens);
748
818
  break;
749
819
  case "sep":
750
820
  if (tokens.length === 0)
751
- tune.addSeparator(14,14,85); // If no parameters are given, then there is a default size.
821
+ tuneBuilder.addSeparator(14,14,85, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+5}); // If no parameters are given, then there is a default size.
752
822
  else {
753
823
  var points = tokenizer.getMeasurement(tokens);
754
824
  if (points.used === 0)
@@ -764,7 +834,7 @@ var parseDirective = {};
764
834
  if (points.used === 0 || tokens.length !== 0)
765
835
  return "Directive \"" + cmd + "\" requires 3 numbers: space above, space below, length of line";
766
836
  var lenLine = points.value;
767
- tune.addSeparator(spaceAbove, spaceBelow, lenLine);
837
+ tuneBuilder.addSeparator(spaceAbove, spaceBelow, lenLine, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+restOfString.length});
768
838
  }
769
839
  break;
770
840
  case "barsperstaff":
@@ -804,16 +874,28 @@ var parseDirective = {};
804
874
  if (tokens.length !== 1 || tokens[0].type !== 'number') {
805
875
  return 'Directive setbarnb requires a number as a parameter.';
806
876
  }
807
- multilineVars.currBarNumber = tune.setBarNumberImmediate(tokens[0].intt);
877
+ multilineVars.currBarNumber = tuneBuilder.setBarNumberImmediate(tokens[0].intt);
808
878
  break;
809
879
  case "begintext":
810
- multilineVars.inTextBlock = true;
880
+ var textBlock = '';
881
+ line = tokenizer.nextLine();
882
+ while(line && line.indexOf('%%endtext') !== 0) {
883
+ if (parseCommon.startsWith(line, "%%"))
884
+ textBlock += line.substring(2) + "\n";
885
+ else
886
+ textBlock += line + "\n";
887
+ line = tokenizer.nextLine();
888
+ }
889
+ tuneBuilder.addText(textBlock, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+textBlock.length+7});
811
890
  break;
812
891
  case "continueall":
813
892
  multilineVars.continueall = true;
814
893
  break;
815
894
  case "beginps":
816
- multilineVars.inPsBlock = true;
895
+ line = tokenizer.nextLine();
896
+ while(line && line.indexOf('%%endps') !== 0) {
897
+ tokenizer.nextLine();
898
+ }
817
899
  warn("Postscript ignored", str, 0);
818
900
  break;
819
901
  case "deco":
@@ -823,11 +905,11 @@ var parseDirective = {};
823
905
  break;
824
906
  case "text":
825
907
  var textstr = tokenizer.translateString(restOfString);
826
- tune.addText(parseDirective.parseFontChangeLine(textstr));
908
+ tuneBuilder.addText(parseDirective.parseFontChangeLine(textstr), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+restOfString.length+7});
827
909
  break;
828
910
  case "center":
829
911
  var centerstr = tokenizer.translateString(restOfString);
830
- tune.addCentered(parseDirective.parseFontChangeLine(centerstr));
912
+ tuneBuilder.addCentered(parseDirective.parseFontChangeLine(centerstr));
831
913
  break;
832
914
  case "font":
833
915
  // don't need to do anything for this; it is a useless directive
@@ -891,8 +973,8 @@ var parseDirective = {};
891
973
  multilineVars.staves.push({index: multilineVars.staves.length, numVoices: 0});
892
974
  }
893
975
  var staff = parseCommon.last(multilineVars.staves);
894
- if (bracket !== undefined) staff.bracket = bracket;
895
- if (brace !== undefined) staff.brace = brace;
976
+ if (bracket !== undefined && staff.bracket === undefined) staff.bracket = bracket;
977
+ if (brace !== undefined && staff.brace === undefined) staff.brace = brace;
896
978
  if (continueBar) staff.connectBarLines = 'end';
897
979
  if (multilineVars.voices[id] === undefined) {
898
980
  multilineVars.voices[id] = {staffNum: staff.index, index: staff.numVoices};
@@ -977,7 +1059,7 @@ var parseDirective = {};
977
1059
 
978
1060
  case "newpage":
979
1061
  var pgNum = tokenizer.getInt(restOfString);
980
- tune.addNewPage(pgNum.digits === 0 ? -1 : pgNum.value);
1062
+ tuneBuilder.addNewPage(pgNum.digits === 0 ? -1 : pgNum.value);
981
1063
  break;
982
1064
 
983
1065
  case "abc":
@@ -989,7 +1071,7 @@ var parseDirective = {};
989
1071
  case "-version":
990
1072
  case "-charset":
991
1073
  var subCmd = arr.shift();
992
- tune.addMetaText(cmd+subCmd, arr.join(' '));
1074
+ tuneBuilder.addMetaText(cmd+subCmd, arr.join(' '), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+restOfString.length+5});
993
1075
  break;
994
1076
  default:
995
1077
  return "Unknown directive: " + cmd+arr[0];
@@ -1012,7 +1094,7 @@ var parseDirective = {};
1012
1094
  if (footerArr.length > 3)
1013
1095
  warn("Too many tabs in " + cmd + ": " + footerArr.length + " found.", restOfString, 0);
1014
1096
 
1015
- tune.addMetaTextObj(cmd, footer);
1097
+ tuneBuilder.addMetaTextObj(cmd, footer, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+str.length});
1016
1098
  break;
1017
1099
 
1018
1100
  case "midi":
@@ -1024,9 +1106,18 @@ var parseDirective = {};
1024
1106
  else
1025
1107
  parseMidiCommand(midi, tune, restOfString);
1026
1108
  break;
1109
+ case "percmap":
1110
+ var percmap = interpretPercMap(restOfString);
1111
+ if (percmap.error)
1112
+ warn(percmap.error, str, 8);
1113
+ else {
1114
+ if (!tune.formatting.percmap)
1115
+ tune.formatting.percmap = {};
1116
+ tune.formatting.percmap[percmap.key] = percmap.value;
1117
+ }
1118
+ break;
1027
1119
 
1028
1120
  case "map":
1029
- case "percmap":
1030
1121
  case "playtempo":
1031
1122
  case "auquality":
1032
1123
  case "continuous":
@@ -1064,6 +1155,9 @@ var parseDirective = {};
1064
1155
  case "vocalfont":
1065
1156
  case "wordsfont":
1066
1157
  case "annotationfont":
1158
+ case "tablabelfont":
1159
+ case "tabnumberfont":
1160
+ case "tabgracefont":
1067
1161
  getChangingFont(cmd, tokens, value);
1068
1162
  break;
1069
1163
  case "scale":
@@ -1077,12 +1171,41 @@ var parseDirective = {};
1077
1171
  case "freegchord":
1078
1172
  scratch = addMultilineVarBool('freegchord', cmd, tokens);
1079
1173
  if (scratch !== null) warn(scratch);
1174
+ break;
1175
+ case "fontboxpadding":
1176
+ if (tokens.length !== 1 || tokens[0].type !== 'number')
1177
+ warn("Directive \"" + cmd + "\" requires a number as a parameter.");
1178
+ tune.formatting.fontboxpadding = tokens[0].floatt;
1179
+ break;
1180
+ case "stretchlast":
1181
+ var sl = parseStretchLast(tokens);
1182
+ if (sl.value !== undefined)
1183
+ tune.formatting.stretchlast = sl.value;
1184
+ if (sl.error)
1185
+ return sl.error;
1186
+ break;
1080
1187
  default:
1081
1188
  warn("Formatting directive unrecognized: ", cmd, 0);
1082
1189
  }
1083
1190
  }
1084
1191
  }
1085
1192
  };
1193
+
1194
+ function parseStretchLast(tokens) {
1195
+ if (tokens.length === 0)
1196
+ return { value: 1 }; // if there is no value then the presence of this is the same as "true"
1197
+ else if (tokens.length === 1) {
1198
+ if (tokens[0].type === "number") {
1199
+ if (tokens[0].floatt >= 0 || tokens[0].floatt <= 1)
1200
+ return {value: tokens[0].floatt};
1201
+ } else if (tokens[0].token === 'false') {
1202
+ return { value: 0 };
1203
+ } else if (tokens[0].token === 'true') {
1204
+ return {value: 1};
1205
+ }
1206
+ }
1207
+ return { error: "Directive stretchlast requires zero or one parameter: false, true, or number between 0 and 1 (received " + tokens[0].token + ')' };
1208
+ }
1086
1209
  })();
1087
1210
 
1088
1211
  module.exports = parseDirective;