abcjs 6.1.9 → 6.2.0

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 (129) hide show
  1. package/RELEASE.md +42 -0
  2. package/dist/abcjs-basic-min.js +2 -2
  3. package/dist/abcjs-basic.js +4318 -4411
  4. package/dist/abcjs-basic.js.map +1 -1
  5. package/dist/abcjs-plugin-min.js +2 -2
  6. package/index.js +1 -1
  7. package/package.json +1 -1
  8. package/plugin.js +1 -1
  9. package/src/api/abc_timing_callbacks.js +7 -3
  10. package/src/api/abc_tunebook_svg.js +1 -2
  11. package/src/data/abc_tune.js +2 -2
  12. package/src/parse/abc_common.js +0 -47
  13. package/src/parse/abc_parse.js +16 -16
  14. package/src/parse/abc_parse_book.js +3 -3
  15. package/src/parse/abc_parse_directive.js +26 -7
  16. package/src/parse/abc_parse_header.js +11 -9
  17. package/src/parse/abc_parse_key_voice.js +17 -17
  18. package/src/parse/abc_parse_music.js +88 -105
  19. package/src/parse/abc_tokenizer.js +60 -60
  20. package/src/parse/tune-builder.js +19 -14
  21. package/src/synth/abc_midi_flattener.js +25 -9
  22. package/src/synth/create-synth.js +41 -0
  23. package/src/synth/note-to-midi.js +50 -0
  24. package/src/tablatures/instruments/guitar/tab-guitar.js +0 -2
  25. package/src/tablatures/instruments/string-patterns.js +46 -28
  26. package/src/tablatures/instruments/tab-note.js +26 -103
  27. package/src/tablatures/instruments/violin/tab-violin.js +0 -2
  28. package/src/tablatures/tab-absolute-elements.js +9 -31
  29. package/src/tablatures/tab-renderer.js +2 -2
  30. package/src/test/abc_parser_lint.js +7 -4
  31. package/src/write/README.md +31 -0
  32. package/src/write/creation/abstract-engraver.js +1036 -0
  33. package/src/write/creation/add-chord.js +102 -0
  34. package/src/write/{add-text-if.js → creation/add-text-if.js} +6 -6
  35. package/src/write/{calcHeight.js → creation/calc-height.js} +2 -2
  36. package/src/write/creation/create-clef.js +72 -0
  37. package/src/write/creation/create-key-signature.js +31 -0
  38. package/src/write/creation/create-note-head.js +107 -0
  39. package/src/write/creation/create-time-signature.js +55 -0
  40. package/src/write/creation/decoration.js +357 -0
  41. package/src/write/{abc_absolute_element.js → creation/elements/absolute-element.js} +14 -15
  42. package/src/write/creation/elements/beam-element.js +113 -0
  43. package/src/write/{bottom-text.js → creation/elements/bottom-text.js} +14 -15
  44. package/src/write/{abc_brace_element.js → creation/elements/brace-element.js} +5 -5
  45. package/src/write/creation/elements/free-text.js +41 -0
  46. package/src/write/{abc_relative_element.js → creation/elements/relative-element.js} +7 -7
  47. package/src/write/{separator.js → creation/elements/separator.js} +2 -2
  48. package/src/write/{abc_staff_group_element.js → creation/elements/staff-group-element.js} +4 -4
  49. package/src/write/{subtitle.js → creation/elements/subtitle.js} +3 -3
  50. package/src/write/creation/elements/tempo-element.js +63 -0
  51. package/src/write/{abc_tie_element.js → creation/elements/tie-element.js} +15 -11
  52. package/src/write/{top-text.js → creation/elements/top-text.js} +12 -12
  53. package/src/write/creation/elements/triplet-element.js +28 -0
  54. package/src/write/{abc_voice_element.js → creation/elements/voice-element.js} +3 -3
  55. package/src/write/creation/glyphs.js +226 -0
  56. package/src/write/creation/translate-chord.js +37 -0
  57. package/src/write/draw/absolute.js +5 -5
  58. package/src/write/draw/beam.js +8 -8
  59. package/src/write/draw/brace.js +33 -33
  60. package/src/write/draw/crescendo.js +4 -4
  61. package/src/write/draw/debug-box.js +1 -1
  62. package/src/write/draw/draw.js +7 -7
  63. package/src/write/draw/dynamics.js +2 -2
  64. package/src/write/draw/ending.js +6 -6
  65. package/src/write/draw/glissando.js +17 -17
  66. package/src/write/draw/group-elements.js +51 -51
  67. package/src/write/draw/horizontal-line.js +9 -9
  68. package/src/write/draw/non-music.js +1 -1
  69. package/src/write/draw/print-line.js +15 -16
  70. package/src/write/draw/print-stem.js +5 -5
  71. package/src/write/draw/print-symbol.js +12 -12
  72. package/src/write/draw/print-vertical-line.js +8 -8
  73. package/src/write/draw/relative.js +17 -16
  74. package/src/write/draw/selectables.js +5 -5
  75. package/src/write/draw/separator.js +4 -4
  76. package/src/write/draw/set-paper-size.js +2 -2
  77. package/src/write/draw/sprintf.js +31 -31
  78. package/src/write/draw/staff-group.js +36 -30
  79. package/src/write/draw/staff-line.js +2 -2
  80. package/src/write/draw/staff.js +4 -4
  81. package/src/write/draw/tab-line.js +26 -26
  82. package/src/write/draw/tempo.js +30 -30
  83. package/src/write/draw/text.js +5 -5
  84. package/src/write/draw/tie.js +18 -18
  85. package/src/write/draw/triplet.js +6 -6
  86. package/src/write/draw/voice.js +16 -17
  87. package/src/write/{abc_engraver_controller.js → engraver-controller.js} +58 -51
  88. package/src/write/{classes.js → helpers/classes.js} +6 -6
  89. package/src/write/{get-font-and-attr.js → helpers/get-font-and-attr.js} +9 -7
  90. package/src/write/{get-text-size.js → helpers/get-text-size.js} +5 -5
  91. package/src/write/{set-class.js → helpers/set-class.js} +1 -1
  92. package/src/write/{abc_spacing.js → helpers/spacing.js} +1 -1
  93. package/src/write/{highlight.js → interactive/highlight.js} +1 -1
  94. package/src/write/{selection.js → interactive/selection.js} +27 -27
  95. package/src/write/{unhighlight.js → interactive/unhighlight.js} +1 -1
  96. package/src/write/layout/beam.js +13 -13
  97. package/src/write/layout/get-left-edge-of-staff.js +4 -4
  98. package/src/write/layout/layout.js +74 -74
  99. package/src/write/layout/{setUpperAndLowerElements.js → set-upper-and-lower-elements.js} +8 -8
  100. package/src/write/layout/{staffGroup.js → staff-group.js} +32 -32
  101. package/src/write/layout/triplet.js +4 -4
  102. package/src/write/layout/{VoiceElements.js → voice-elements.js} +23 -23
  103. package/src/write/layout/voice.js +6 -6
  104. package/src/write/{abc_renderer.js → renderer.js} +35 -32
  105. package/src/write/svg.js +35 -35
  106. package/test.js +1 -1
  107. package/types/index.d.ts +36 -7
  108. package/version.js +1 -1
  109. package/src/tablatures/instruments/guitar/guitar-fonts.js +0 -19
  110. package/src/tablatures/instruments/violin/violin-fonts.js +0 -19
  111. package/src/tablatures/transposer.js +0 -110
  112. package/src/write/abc_abstract_engraver.js +0 -1026
  113. package/src/write/abc_beam_element.js +0 -113
  114. package/src/write/abc_create_clef.js +0 -72
  115. package/src/write/abc_create_key_signature.js +0 -33
  116. package/src/write/abc_create_note_head.js +0 -107
  117. package/src/write/abc_create_time_signature.js +0 -55
  118. package/src/write/abc_decoration.js +0 -357
  119. package/src/write/abc_glyphs.js +0 -224
  120. package/src/write/abc_tempo_element.js +0 -63
  121. package/src/write/abc_triplet_element.js +0 -28
  122. package/src/write/add-chord.js +0 -103
  123. package/src/write/format-jazz-chord.js +0 -15
  124. package/src/write/free-text.js +0 -41
  125. /package/src/write/{abc_crescendo_element.js → creation/elements/crescendo-element.js} +0 -0
  126. /package/src/write/{abc_dynamic_decoration.js → creation/elements/dynamic-decoration.js} +0 -0
  127. /package/src/write/{abc_ending_element.js → creation/elements/ending-element.js} +0 -0
  128. /package/src/write/{abc_glissando_element.js → creation/elements/glissando-element.js} +0 -0
  129. /package/src/write/layout/{getBarYAt.js → get-bar-y-at.js} +0 -0
package/index.js CHANGED
@@ -42,7 +42,7 @@ Object.keys(tuneBook).forEach(function (key) {
42
42
  abcjs.renderAbc = require('./src/api/abc_tunebook_svg');
43
43
  abcjs.TimingCallbacks = require('./src/api/abc_timing_callbacks');
44
44
 
45
- var glyphs = require('./src/write/abc_glyphs');
45
+ var glyphs = require('./src/write/creation/glyphs');
46
46
  abcjs.setGlyph = glyphs.setSymbol;
47
47
  abcjs.strTranspose = strTranspose;
48
48
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abcjs",
3
- "version": "6.1.9",
3
+ "version": "6.2.0",
4
4
  "description": "Renderer for abc music notation",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
package/plugin.js CHANGED
@@ -28,7 +28,7 @@ THE SOFTWARE.
28
28
 
29
29
  var TuneBook = require('./src/api/abc_tunebook').TuneBook;
30
30
  var Parse = require('./src/parse/abc_parse');
31
- var EngraverController = require('./src/write/abc_engraver_controller');
31
+ var EngraverController = require('./src/write/engraver-controller');
32
32
 
33
33
  var Plugin = function() {
34
34
  "use strict";
@@ -128,7 +128,7 @@ var TimingCallbacks = function(target, params) {
128
128
  var ev;
129
129
  if (next < self.noteTimings.length) {
130
130
  endMs = self.noteTimings[next].milliseconds;
131
- next = self.currentEvent - 1;
131
+ next = Math.max(0, self.currentEvent - 1);
132
132
  while (next >= 0 && self.noteTimings[next].left === null)
133
133
  next--;
134
134
 
@@ -153,15 +153,19 @@ var TimingCallbacks = function(target, params) {
153
153
  var offMs = Math.max(0, timestamp-self.startTime-ev.milliseconds); // Offset in time from the last beat
154
154
  var gapMs = endMs - ev.milliseconds; // Length of this event in time
155
155
  var gapPx = ev.endX - ev.left; // The length in pixels
156
- var offPx = offMs * gapPx / gapMs;
156
+ var offPx = gapMs ? offMs * gapPx / gapMs : 0;
157
157
  position.left = ev.left + offPx;
158
+ // See if this is before the first event - that is the case where there are "prep beats"
159
+ if (self.currentEvent === 0 && ev.milliseconds > timestamp-self.startTime)
160
+ position.left = undefined
161
+
158
162
  debugInfo = {
159
163
  timestamp: timestamp,
160
164
  startTime: self.startTime,
161
165
  ev: ev,
162
166
  endMs: endMs,
163
167
  offMs: offMs,
164
- offPs: offPx,
168
+ offPx: offPx,
165
169
  gapMs: gapMs,
166
170
  gapPx: gapPx
167
171
  };
@@ -1,10 +1,9 @@
1
1
  var tunebook = require('./abc_tunebook');
2
2
  var Tune = require('../data/abc_tune');
3
3
 
4
- var EngraverController = require('../write/abc_engraver_controller');
4
+ var EngraverController = require('../write/engraver-controller');
5
5
  var Parse = require('../parse/abc_parse');
6
6
  var wrap = require('../parse/wrap_lines');
7
- var parseCommon = require("../parse/abc_common");
8
7
  // var tablatures = require('./abc_tablatures');
9
8
 
10
9
 
@@ -1,7 +1,7 @@
1
1
  // abc_tune.js: a computer usable internal structure representing one tune.
2
2
 
3
3
  var parseCommon = require('../parse/abc_common');
4
- var spacing = require('../write/abc_spacing');
4
+ var spacing = require('../write/helpers/spacing');
5
5
  var sequence = require('../synth/abc_midi_sequencer');
6
6
  var flatten = require('../synth/abc_midi_flattener');
7
7
  var delineTune = require("./deline-tune");
@@ -611,7 +611,7 @@ var Tune = function() {
611
611
  this.setUpAudio = function(options) {
612
612
  if (!options) options = {};
613
613
  var seq = sequence(this, options);
614
- return flatten(seq, options, this.formatting.percmap);
614
+ return flatten(seq, options, this.formatting.percmap, this.formatting.midi);
615
615
  };
616
616
  this.deline = function(options) {
617
617
  return delineTune(this.lines, options);
@@ -34,10 +34,6 @@ parseCommon.cloneHashOfArrayOfHash = function(source) {
34
34
  return destination;
35
35
  };
36
36
 
37
- parseCommon.gsub = function(source, pattern, replacement) {
38
- return source.split(pattern).join(replacement);
39
- };
40
-
41
37
  parseCommon.strip = function(str) {
42
38
  return str.replace(/^\s+/, '').replace(/\s+$/, '');
43
39
  };
@@ -51,54 +47,11 @@ parseCommon.endsWith = function(str, pattern) {
51
47
  return d >= 0 && str.lastIndexOf(pattern) === d;
52
48
  };
53
49
 
54
- parseCommon.each = function(arr, iterator, context) {
55
- for (var i = 0, length = arr.length; i < length; i++)
56
- iterator.apply(context, [arr[i],i]);
57
- };
58
-
59
50
  parseCommon.last = function(arr) {
60
51
  if (arr.length === 0)
61
52
  return null;
62
53
  return arr[arr.length-1];
63
54
  };
64
55
 
65
- parseCommon.compact = function(arr) {
66
- var output = [];
67
- for (var i = 0; i < arr.length; i++) {
68
- if (arr[i])
69
- output.push(arr[i]);
70
- }
71
- return output;
72
- };
73
-
74
- parseCommon.detect = function(arr, iterator) {
75
- for (var i = 0; i < arr.length; i++) {
76
- if (iterator(arr[i]))
77
- return true;
78
- }
79
- return false;
80
- };
81
56
 
82
- // The following is a polyfill for Object.remove for IE9, IE10, and IE11.
83
- // from:https://github.com/jserz/js_piece/blob/master/DOM/ChildNode/remove()/remove().md
84
- try {
85
- (function (arr) {
86
- arr.forEach(function (item) {
87
- if (item.hasOwnProperty('remove')) {
88
- return;
89
- }
90
- Object.defineProperty(item, 'remove', {
91
- configurable: true,
92
- enumerable: true,
93
- writable: true,
94
- value: function remove() {
95
- if (this.parentNode !== null)
96
- this.parentNode.removeChild(this);
97
- }
98
- });
99
- });
100
- })([Element.prototype, CharacterData.prototype, DocumentType.prototype]);
101
- } catch(e) {
102
- // if we aren't in a browser, this code will crash, but it is not needed then either.
103
- }
104
57
  module.exports = parseCommon;
@@ -181,16 +181,16 @@ var Parse = function() {
181
181
  };
182
182
 
183
183
  var encode = function(str) {
184
- var ret = parseCommon.gsub(str, '\x12', ' ');
185
- ret = parseCommon.gsub(ret, '&', '&amp;');
186
- ret = parseCommon.gsub(ret, '<', '&lt;');
187
- return parseCommon.gsub(ret, '>', '&gt;');
184
+ var ret = str.replace(/\x12/g, ' ');
185
+ ret = ret.replace(/&/g, '&amp;');
186
+ ret = ret.replace(/</g, '&lt;');
187
+ return ret.replace(/>/g, '&gt;');
188
188
  };
189
189
 
190
190
  var warn = function(str, line, col_num) {
191
191
  if (!line) line = " ";
192
- var bad_char = line.charAt(col_num);
193
- if (bad_char === ' ')
192
+ var bad_char = line[col_num];
193
+ if (bad_char === ' ' || !bad_char)
194
194
  bad_char = "SPACE";
195
195
  var clean_line = encode(line.substring(col_num - 64, col_num)) + '<span style="text-decoration:underline;font-size:1.3em;font-weight:bold;">' + bad_char + '</span>' + encode(line.substring(col_num + 1).substring(0,64));
196
196
  addWarning("Music Line:" + tokenizer.lineIndex + ":" + (col_num+1) + ': ' + str + ": " + clean_line);
@@ -217,7 +217,7 @@ var Parse = function() {
217
217
 
218
218
  if (!line) { warn("Can't add words before the first line of music", line, 0); return; }
219
219
  words = parseCommon.strip(words);
220
- if (words.charAt(words.length-1) !== '-')
220
+ if (words[words.length-1] !== '-')
221
221
  words = words + ' '; // Just makes it easier to parse below, since every word has a divider after it.
222
222
  var word_list = [];
223
223
  // first make a list of words from the string we are passed. A word is divided on either a space or dash.
@@ -229,8 +229,8 @@ var Parse = function() {
229
229
  last_divider = i+1;
230
230
  if (word.length > 0) {
231
231
  if (replace)
232
- word = parseCommon.gsub(word,'~', ' ');
233
- var div = words.charAt(i);
232
+ word = word.replace(/~/g, ' ');
233
+ var div = words[i];
234
234
  if (div !== '_' && div !== '-')
235
235
  div = ' ';
236
236
  word_list.push({syllable: tokenizer.translateString(word), divider: div});
@@ -280,7 +280,7 @@ var Parse = function() {
280
280
  }
281
281
 
282
282
  var inSlur = false;
283
- parseCommon.each(line, function(el) {
283
+ line.forEach(function(el) {
284
284
  if (word_list.length !== 0) {
285
285
  if (word_list[0].skip) {
286
286
  switch (word_list[0].to) {
@@ -320,7 +320,7 @@ var Parse = function() {
320
320
  // TODO-PER: Currently copied from w: line. This needs to be read as symbols instead.
321
321
  if (!line) { warn("Can't add symbols before the first line of music", line, 0); return; }
322
322
  words = parseCommon.strip(words);
323
- if (words.charAt(words.length-1) !== '-')
323
+ if (words[words.length-1] !== '-')
324
324
  words = words + ' '; // Just makes it easier to parse below, since every word has a divider after it.
325
325
  var word_list = [];
326
326
  // first make a list of words from the string we are passed. A word is divided on either a space or dash.
@@ -331,8 +331,8 @@ var Parse = function() {
331
331
  last_divider = i+1;
332
332
  if (word.length > 0) {
333
333
  if (replace)
334
- word = parseCommon.gsub(word, '~', ' ');
335
- var div = words.charAt(i);
334
+ word = word.replace(/~/g, ' ');
335
+ var div = words[i];
336
336
  if (div !== '_' && div !== '-')
337
337
  div = ' ';
338
338
  word_list.push({syllable: tokenizer.translateString(word), divider: div});
@@ -342,7 +342,7 @@ var Parse = function() {
342
342
  return false;
343
343
  };
344
344
  for (var i = 0; i < words.length; i++) {
345
- switch (words.charAt(i)) {
345
+ switch (words[i]) {
346
346
  case ' ':
347
347
  case '\x12':
348
348
  addWord(i);
@@ -372,7 +372,7 @@ var Parse = function() {
372
372
  }
373
373
 
374
374
  var inSlur = false;
375
- parseCommon.each(line, function(el) {
375
+ line.forEach(function(el) {
376
376
  if (word_list.length !== 0) {
377
377
  if (word_list[0].skip) {
378
378
  switch (word_list[0].to) {
@@ -416,7 +416,7 @@ var Parse = function() {
416
416
  addSymbols(tuneBuilder.getCurrentVoice(), line.substring(2));
417
417
  return
418
418
  }
419
- if (line.length < 2 || line.charAt(1) !== ':' || music.lineContinuation) {
419
+ if (line.length < 2 || line[1] !== ':' || music.lineContinuation) {
420
420
  music.parseMusic(line);
421
421
  return
422
422
  }
@@ -15,7 +15,7 @@ var bookParser = function(book) {
15
15
  // Keep track of the character position each tune starts with. If the string starts with white space, count that, too.
16
16
  var pos = initialWhiteSpace ? initialWhiteSpace[0].length : 0;
17
17
  var tunes = [];
18
- parseCommon.each(tuneStrings, function(tune) {
18
+ tuneStrings.forEach(function(tune) {
19
19
  tunes.push({ abc: tune, startPos: pos});
20
20
  pos += tune.length + 1; // We also lost a newline when splitting, so count that.
21
21
  });
@@ -25,7 +25,7 @@ var bookParser = function(book) {
25
25
  // the tune is parsed all at once. The directives will be seen before the engraver begins processing.
26
26
  var dir = tunes.shift();
27
27
  var arrDir = dir.abc.split('\n');
28
- parseCommon.each(arrDir, function(line) {
28
+ arrDir.forEach(function(line) {
29
29
  if (parseCommon.startsWith(line, '%%'))
30
30
  directives += line + '\n';
31
31
  });
@@ -33,7 +33,7 @@ var bookParser = function(book) {
33
33
  var header = directives;
34
34
 
35
35
  // Now, the tune ends at a blank line, so truncate it if needed. There may be "intertune" stuff.
36
- parseCommon.each(tunes, function(tune) {
36
+ tunes.forEach(function(tune) {
37
37
  var end = tune.abc.indexOf('\n\n');
38
38
  if (end > 0)
39
39
  tune.abc = tune.abc.substring(0, end);
@@ -330,7 +330,7 @@ var parseDirective = {};
330
330
 
331
331
  var setScale = function(cmd, tokens) {
332
332
  var scratch = "";
333
- parseCommon.each(tokens, function(tok) {
333
+ tokens.forEach(function(tok) {
334
334
  scratch += tok.token;
335
335
  });
336
336
  var num = parseFloat(scratch);
@@ -485,6 +485,8 @@ var parseDirective = {};
485
485
  var midiCmdParam1Integer = [
486
486
  "bassvol",
487
487
  "chordvol",
488
+ "bassprog",
489
+ "chordprog",
488
490
  "c",
489
491
  "channel",
490
492
  "beatmod",
@@ -686,15 +688,15 @@ var parseDirective = {};
686
688
  if (textParts.length > 1 && multilineVars.setfont) {
687
689
  var textarr = [ { text: textParts[0] }];
688
690
  for (var i = 1; i < textParts.length; i++) {
689
- if (textParts[i].charAt(0) === '0')
691
+ if (textParts[i][0] === '0')
690
692
  textarr.push({ text: textParts[i].substring(1) });
691
- else if (textParts[i].charAt(0) === '1' && multilineVars.setfont[1])
693
+ else if (textParts[i][0] === '1' && multilineVars.setfont[1])
692
694
  textarr.push({font: multilineVars.setfont[1], text: textParts[i].substring(1) });
693
- else if (textParts[i].charAt(0) === '2' && multilineVars.setfont[2])
695
+ else if (textParts[i][0] === '2' && multilineVars.setfont[2])
694
696
  textarr.push({font: multilineVars.setfont[2], text: textParts[i].substring(1) });
695
- else if (textParts[i].charAt(0) === '3' && multilineVars.setfont[3])
697
+ else if (textParts[i][0] === '3' && multilineVars.setfont[3])
696
698
  textarr.push({font: multilineVars.setfont[3], text: textParts[i].substring(1) });
697
- else if (textParts[i].charAt(0) === '4' && multilineVars.setfont[4])
699
+ else if (textParts[i][0] === '4' && multilineVars.setfont[4])
698
700
  textarr.push({font: multilineVars.setfont[4], text: textParts[i].substring(1) });
699
701
  else
700
702
  textarr[textarr.length-1].text += '$' + textParts[i];
@@ -746,6 +748,7 @@ var parseDirective = {};
746
748
  case "bagpipes":tune.formatting.bagpipes = true;break;
747
749
  case "flatbeams":tune.formatting.flatbeams = true;break;
748
750
  case "jazzchords":tune.formatting.jazzchords = true;break;
751
+ case "germanAlphabet":tune.formatting.germanAlphabet = true;break;
749
752
  case "landscape":multilineVars.landscape = true;break;
750
753
  case "papersize":multilineVars.papersize = restOfString;break;
751
754
  case "graceslurs":
@@ -758,6 +761,13 @@ var parseDirective = {};
758
761
  else
759
762
  return "Directive graceslurs requires one parameter: 0 or 1 (received " + tokens[0].token + ')';
760
763
  break;
764
+ case "lineThickness":
765
+ var lt = parseStretchLast(tokens);
766
+ if (lt.value !== undefined)
767
+ tune.formatting.lineThickness = lt.value;
768
+ if (lt.error)
769
+ return lt.error;
770
+ break;
761
771
  case "stretchlast":
762
772
  var sl = parseStretchLast(tokens);
763
773
  if (sl.value !== undefined)
@@ -807,6 +817,15 @@ var parseDirective = {};
807
817
  tuneBuilder.changeVoiceScale(multilineVars.currentVoice.scale);
808
818
  }
809
819
  return null;
820
+ case "voicecolor":
821
+ if (tokens.length !== 1) // this could either be of type alpha or quote, but it's ok if it is a number
822
+ return "voicecolor requires one string as a parameter";
823
+ var voiceColor = tokens.shift();
824
+ if (multilineVars.currentVoice) {
825
+ multilineVars.currentVoice.color = voiceColor.token;
826
+ tuneBuilder.changeVoiceColor(multilineVars.currentVoice.color);
827
+ }
828
+ return null;
810
829
  case "vskip":
811
830
  var vskip = Math.round(getRequiredMeasurement(cmd, tokens));
812
831
  if (vskip.error)
@@ -1081,7 +1100,7 @@ var parseDirective = {};
1081
1100
  case "footer":
1082
1101
  var footerStr = tokenizer.getMeat(restOfString, 0, restOfString.length);
1083
1102
  footerStr = restOfString.substring(footerStr.start, footerStr.end);
1084
- if (footerStr.charAt(0) === '"' && footerStr.charAt(footerStr.length-1) === '"' )
1103
+ if (footerStr[0] === '"' && footerStr[footerStr.length-1] === '"' )
1085
1104
  footerStr = footerStr.substring(1, footerStr.length-1);
1086
1105
  var footerArr = footerStr.split('\t');
1087
1106
  var footer = {};
@@ -189,7 +189,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
189
189
  };
190
190
 
191
191
  this.setDefaultLength = function(line, start, end) {
192
- var len = parseCommon.gsub(line.substring(start, end), " ", "");
192
+ var len = line.substring(start, end).replace(/ /g, "");
193
193
  var len_arr = len.split('/');
194
194
  if (len_arr.length === 2) {
195
195
  var n = parseInt(len_arr[0]);
@@ -346,7 +346,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
346
346
  {
347
347
  var ws = tokenizer.eatWhiteSpace(line, i);
348
348
  i +=ws;
349
- if (line.length >= i+5 && line.charAt(i) === '[' && line.charAt(i+2) === ':') {
349
+ if (line.length >= i+5 && line[i] === '[' && line[i+2] === ':') {
350
350
  var e = line.indexOf(']', i);
351
351
  var startChar = multilineVars.iChar + i;
352
352
  var endChar = multilineVars.iChar + e + 1;
@@ -393,16 +393,18 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
393
393
  else
394
394
  multilineVars.tempoForNextLine = ['tempo', startChar, endChar, tempo.tempo]
395
395
  }
396
- return [ e-i+1+ws, line.charAt(i+1), line.substring(i+3, e)];
396
+ return [ e-i+1+ws, line[i+1], line.substring(i+3, e)];
397
397
  }
398
398
  break;
399
399
  case "[V:":
400
400
  if (e > 0) {
401
401
  parseKeyVoice.parseVoice(line, i+3, e);
402
402
  //startNewLine();
403
- return [ e-i+1+ws, line.charAt(i+1), line.substring(i+3, e)];
403
+ return [ e-i+1+ws, line[i+1], line.substring(i+3, e)];
404
404
  }
405
405
  break;
406
+ case "[r:":
407
+ return [ e-i+1+ws ];
406
408
 
407
409
  default:
408
410
  // TODO: complain about unhandled header
@@ -445,11 +447,11 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
445
447
  var tempo = this.setTempo(line, i+2, e, multilineVars.iChar);
446
448
  if (tempo.type === 'delaySet') tuneBuilder.appendElement('tempo', multilineVars.iChar + i, multilineVars.iChar + line.length, this.calcTempo(tempo.tempo));
447
449
  else if (tempo.type === 'immediate') tuneBuilder.appendElement('tempo', multilineVars.iChar + i, multilineVars.iChar + line.length, tempo.tempo);
448
- return [ e, line.charAt(i), parseCommon.strip(line.substring(i+2))];
450
+ return [ e, line[i], parseCommon.strip(line.substring(i+2))];
449
451
  case "V:":
450
452
  parseKeyVoice.parseVoice(line, i+2, line.length);
451
453
  // startNewLine();
452
- return [ line.length, line.charAt(i), parseCommon.strip(line.substring(i+2))];
454
+ return [ line.length, line[i], parseCommon.strip(line.substring(i+2))];
453
455
  default:
454
456
  // TODO: complain about unhandled header
455
457
  }
@@ -474,7 +476,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
474
476
  };
475
477
 
476
478
  this.parseHeader = function(line) {
477
- var field = metaTextHeaders[line.charAt(0)];
479
+ var field = metaTextHeaders[line[0]];
478
480
  if (field !== undefined) {
479
481
  if (field === 'unalignedWords')
480
482
  tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line.substring(2)))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
@@ -484,12 +486,12 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
484
486
  } else {
485
487
  var startChar = multilineVars.iChar;
486
488
  var endChar = startChar + line.length;
487
- switch(line.charAt(0))
489
+ switch(line[0])
488
490
  {
489
491
  case 'H':
490
492
  tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
491
493
  line = tokenizer.peekLine()
492
- while (line && line.charAt(1) !== ':') {
494
+ while (line && line[1] !== ':') {
493
495
  tokenizer.nextLine()
494
496
  tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line)), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
495
497
  line = tokenizer.peekLine()
@@ -83,7 +83,7 @@ var parseKeyVoice = {};
83
83
 
84
84
  parseKeyVoice.deepCopyKey = function(key) {
85
85
  var ret = { accidentals: [], root: key.root, acc: key.acc, mode: key.mode };
86
- parseCommon.each(key.accidentals, function(k) {
86
+ key.accidentals.forEach(function(k) {
87
87
  ret.accidentals.push(parseCommon.clone(k));
88
88
  });
89
89
  return ret;
@@ -95,20 +95,20 @@ var parseKeyVoice = {};
95
95
  // Shift the key signature from the treble positions to whatever position is needed for the clef.
96
96
  // This may put the key signature unnaturally high or low, so if it does, then shift it.
97
97
  var mid = clef.verticalPos;
98
- parseCommon.each(key.accidentals, function(acc) {
98
+ key.accidentals.forEach(function(acc) {
99
99
  var pitch = pitches[acc.note];
100
100
  pitch = pitch - mid;
101
101
  acc.verticalPos = pitch;
102
102
  });
103
103
  if (key.impliedNaturals)
104
- parseCommon.each(key.impliedNaturals, function(acc) {
104
+ key.impliedNaturals.forEach(function(acc) {
105
105
  var pitch = pitches[acc.note];
106
106
  pitch = pitch - mid;
107
107
  acc.verticalPos = pitch;
108
108
  });
109
109
 
110
110
  if (mid < -10) {
111
- parseCommon.each(key.accidentals, function(acc) {
111
+ key.accidentals.forEach(function(acc) {
112
112
  acc.verticalPos -= 7;
113
113
  if (acc.verticalPos >= 11 || (acc.verticalPos === 10 && acc.acc === 'flat'))
114
114
  acc.verticalPos -= 7;
@@ -118,7 +118,7 @@ var parseKeyVoice = {};
118
118
  acc.verticalPos -=7;
119
119
  });
120
120
  if (key.impliedNaturals)
121
- parseCommon.each(key.impliedNaturals, function(acc) {
121
+ key.impliedNaturals.forEach(function(acc) {
122
122
  acc.verticalPos -= 7;
123
123
  if (acc.verticalPos >= 11 || (acc.verticalPos === 10 && acc.acc === 'flat'))
124
124
  acc.verticalPos -= 7;
@@ -128,23 +128,23 @@ var parseKeyVoice = {};
128
128
  acc.verticalPos -=7;
129
129
  });
130
130
  } else if (mid < -4) {
131
- parseCommon.each(key.accidentals, function(acc) {
131
+ key.accidentals.forEach(function(acc) {
132
132
  acc.verticalPos -= 7;
133
133
  if (mid === -8 && (acc.note === 'f' || acc.note === 'g') && acc.acc === 'sharp' )
134
134
  acc.verticalPos -=7;
135
135
  });
136
136
  if (key.impliedNaturals)
137
- parseCommon.each(key.impliedNaturals, function(acc) {
137
+ key.impliedNaturals.forEach(function(acc) {
138
138
  acc.verticalPos -= 7;
139
139
  if (mid === -8 && (acc.note === 'f' || acc.note === 'g') && acc.acc === 'sharp' )
140
140
  acc.verticalPos -=7;
141
141
  });
142
142
  } else if (mid >= 7) {
143
- parseCommon.each(key.accidentals, function(acc) {
143
+ key.accidentals.forEach(function(acc) {
144
144
  acc.verticalPos += 7;
145
145
  });
146
146
  if (key.impliedNaturals)
147
- parseCommon.each(key.impliedNaturals, function(acc) {
147
+ key.impliedNaturals.forEach(function(acc) {
148
148
  acc.verticalPos += 7;
149
149
  });
150
150
  }
@@ -158,15 +158,15 @@ var parseKeyVoice = {};
158
158
 
159
159
  var parseMiddle = function(str) {
160
160
  var i = 0;
161
- var p = str.charAt(i++);
161
+ var p = str[i++];
162
162
  if (p === '^' || p === '_')
163
- p = str.charAt(i++);
163
+ p = str[i++];
164
164
  var mid = pitches[p];
165
165
  if (mid === undefined)
166
166
  mid = 6; // If a legal middle note wasn't received, just ignore it.
167
167
  for ( ; i < str.length; i++) {
168
- if (str.charAt(i) === ',') mid -= 7;
169
- else if (str.charAt(i) === "'") mid += 7;
168
+ if (str[i] === ',') mid -= 7;
169
+ else if (str[i] === "'") mid += 7;
170
170
  else break;
171
171
  }
172
172
  return { mid: mid - 6, str: str.substring(i) }; // We get the note in the middle of the staff. We want the note that appears as the first ledger line below the staff.
@@ -528,7 +528,7 @@ var parseKeyVoice = {};
528
528
  warn("Expected value for " + name + " in voice: " + attr.warn, line, start);
529
529
  else if (attr.err !== undefined)
530
530
  warn("Expected value for " + name + " in voice: " + attr.err, line, start);
531
- else if (attr.token.length === 0 && line.charAt(start) !== '"')
531
+ else if (attr.token.length === 0 && line[start] !== '"')
532
532
  warn("Expected value for " + name + " in voice", line, start);
533
533
  else
534
534
  staffInfo[name] = attr.token;
@@ -540,7 +540,7 @@ var parseKeyVoice = {};
540
540
  warn("Expected value for " + name + " in voice: " + attr.warn, line, start);
541
541
  else if (attr.err !== undefined)
542
542
  warn("Expected value for " + name + " in voice: " + attr.err, line, start);
543
- else if (attr.token.length === 0 && line.charAt(start) !== '"')
543
+ else if (attr.token.length === 0 && line[start] !== '"')
544
544
  warn("Expected value for " + name + " in voice", line, start);
545
545
  else {
546
546
  if (type === 'number')
@@ -555,7 +555,7 @@ var parseKeyVoice = {};
555
555
  warn("Expected value for " + name + " in voice: " + attr.warn, line, start);
556
556
  else if (attr.err !== undefined)
557
557
  warn("Expected value for " + name + " in voice: " + attr.err, line, start);
558
- else if (attr.token.length === 0 && line.charAt(start) !== '"')
558
+ else if (attr.token.length === 0 && line[start] !== '"')
559
559
  warn("Expected value for " + name + " in voice", line, start);
560
560
  else {
561
561
  if (type === 'number')
@@ -574,7 +574,7 @@ var parseKeyVoice = {};
574
574
  var attr = tokenizer.getVoiceToken(line, start, end);
575
575
  if (attr.warn !== undefined)
576
576
  warn("Expected one of (_B, _E, _b, _e) for " + name + " in voice: " + attr.warn, line, start);
577
- else if (attr.token.length === 0 && line.charAt(start) !== '"')
577
+ else if (attr.token.length === 0 && line[start] !== '"')
578
578
  warn("Expected one of (_B, _E, _b, _e) for " + name + " in voice", line, start);
579
579
  else {
580
580
  var t = noteToTransposition[attr.token];