abcjs 6.1.9 → 6.2.1

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 (135) hide show
  1. package/LICENSE.md +1 -1
  2. package/RELEASE.md +58 -0
  3. package/dist/abcjs-basic-min.js +2 -2
  4. package/dist/abcjs-basic-min.js.LICENSE +1 -1
  5. package/dist/abcjs-basic.js +4505 -4556
  6. package/dist/abcjs-basic.js.map +1 -1
  7. package/dist/abcjs-plugin-min.js +2 -2
  8. package/dist/abcjs-plugin-min.js.LICENSE +1 -1
  9. package/index.js +2 -2
  10. package/license.js +1 -1
  11. package/package.json +1 -1
  12. package/plugin.js +2 -2
  13. package/src/api/abc_timing_callbacks.js +8 -4
  14. package/src/api/abc_tunebook_svg.js +1 -2
  15. package/src/data/abc_tune.js +3 -3
  16. package/src/parse/abc_common.js +0 -47
  17. package/src/parse/abc_parse.js +16 -16
  18. package/src/parse/abc_parse_book.js +3 -3
  19. package/src/parse/abc_parse_directive.js +26 -7
  20. package/src/parse/abc_parse_header.js +11 -9
  21. package/src/parse/abc_parse_key_voice.js +17 -17
  22. package/src/parse/abc_parse_music.js +88 -105
  23. package/src/parse/abc_tokenizer.js +60 -60
  24. package/src/parse/tune-builder.js +19 -14
  25. package/src/synth/abc_midi_flattener.js +25 -9
  26. package/src/synth/create-synth.js +71 -4
  27. package/src/synth/load-note.js +1 -1
  28. package/src/synth/note-to-midi.js +50 -0
  29. package/src/synth/place-note.js +10 -2
  30. package/src/tablatures/instruments/guitar/tab-guitar.js +0 -2
  31. package/src/tablatures/instruments/string-patterns.js +47 -29
  32. package/src/tablatures/instruments/tab-note.js +26 -103
  33. package/src/tablatures/instruments/violin/tab-violin.js +0 -2
  34. package/src/tablatures/tab-absolute-elements.js +9 -31
  35. package/src/tablatures/tab-renderer.js +2 -2
  36. package/src/test/abc_parser_lint.js +7 -4
  37. package/src/write/README.md +31 -0
  38. package/src/write/creation/abstract-engraver.js +1036 -0
  39. package/src/write/creation/add-chord.js +102 -0
  40. package/src/write/{add-text-if.js → creation/add-text-if.js} +6 -6
  41. package/src/write/{calcHeight.js → creation/calc-height.js} +2 -2
  42. package/src/write/creation/create-clef.js +72 -0
  43. package/src/write/creation/create-key-signature.js +31 -0
  44. package/src/write/creation/create-note-head.js +107 -0
  45. package/src/write/creation/create-time-signature.js +55 -0
  46. package/src/write/creation/decoration.js +357 -0
  47. package/src/write/{abc_absolute_element.js → creation/elements/absolute-element.js} +14 -15
  48. package/src/write/creation/elements/beam-element.js +113 -0
  49. package/src/write/{bottom-text.js → creation/elements/bottom-text.js} +14 -15
  50. package/src/write/{abc_brace_element.js → creation/elements/brace-element.js} +5 -5
  51. package/src/write/creation/elements/free-text.js +41 -0
  52. package/src/write/{abc_relative_element.js → creation/elements/relative-element.js} +7 -7
  53. package/src/write/{separator.js → creation/elements/separator.js} +2 -2
  54. package/src/write/{abc_staff_group_element.js → creation/elements/staff-group-element.js} +4 -4
  55. package/src/write/{subtitle.js → creation/elements/subtitle.js} +3 -3
  56. package/src/write/creation/elements/tempo-element.js +63 -0
  57. package/src/write/{abc_tie_element.js → creation/elements/tie-element.js} +15 -11
  58. package/src/write/{top-text.js → creation/elements/top-text.js} +12 -12
  59. package/src/write/creation/elements/triplet-element.js +28 -0
  60. package/src/write/{abc_voice_element.js → creation/elements/voice-element.js} +3 -3
  61. package/src/write/creation/glyphs.js +226 -0
  62. package/src/write/creation/translate-chord.js +37 -0
  63. package/src/write/draw/absolute.js +5 -5
  64. package/src/write/draw/beam.js +8 -8
  65. package/src/write/draw/brace.js +33 -33
  66. package/src/write/draw/crescendo.js +4 -4
  67. package/src/write/draw/debug-box.js +1 -1
  68. package/src/write/draw/draw.js +7 -7
  69. package/src/write/draw/dynamics.js +2 -2
  70. package/src/write/draw/ending.js +6 -6
  71. package/src/write/draw/glissando.js +17 -17
  72. package/src/write/draw/group-elements.js +51 -51
  73. package/src/write/draw/horizontal-line.js +9 -9
  74. package/src/write/draw/non-music.js +1 -1
  75. package/src/write/draw/print-line.js +25 -16
  76. package/src/write/draw/print-stem.js +15 -5
  77. package/src/write/draw/print-symbol.js +12 -12
  78. package/src/write/draw/print-vertical-line.js +8 -8
  79. package/src/write/draw/relative.js +17 -16
  80. package/src/write/draw/selectables.js +5 -5
  81. package/src/write/draw/separator.js +4 -4
  82. package/src/write/draw/set-paper-size.js +2 -2
  83. package/src/write/draw/sprintf.js +31 -31
  84. package/src/write/draw/staff-group.js +36 -30
  85. package/src/write/draw/staff-line.js +2 -2
  86. package/src/write/draw/staff.js +4 -4
  87. package/src/write/draw/tab-line.js +26 -26
  88. package/src/write/draw/tempo.js +30 -30
  89. package/src/write/draw/text.js +5 -5
  90. package/src/write/draw/tie.js +18 -18
  91. package/src/write/draw/triplet.js +6 -6
  92. package/src/write/draw/voice.js +16 -17
  93. package/src/write/{abc_engraver_controller.js → engraver-controller.js} +58 -51
  94. package/src/write/{classes.js → helpers/classes.js} +6 -6
  95. package/src/write/{get-font-and-attr.js → helpers/get-font-and-attr.js} +9 -7
  96. package/src/write/{get-text-size.js → helpers/get-text-size.js} +5 -5
  97. package/src/write/{set-class.js → helpers/set-class.js} +1 -1
  98. package/src/write/{abc_spacing.js → helpers/spacing.js} +1 -1
  99. package/src/write/{highlight.js → interactive/highlight.js} +1 -1
  100. package/src/write/{selection.js → interactive/selection.js} +34 -31
  101. package/src/write/{unhighlight.js → interactive/unhighlight.js} +1 -1
  102. package/src/write/layout/beam.js +13 -13
  103. package/src/write/layout/get-left-edge-of-staff.js +4 -4
  104. package/src/write/layout/layout.js +74 -74
  105. package/src/write/layout/{setUpperAndLowerElements.js → set-upper-and-lower-elements.js} +8 -8
  106. package/src/write/layout/{staffGroup.js → staff-group.js} +32 -32
  107. package/src/write/layout/triplet.js +4 -4
  108. package/src/write/layout/{VoiceElements.js → voice-elements.js} +23 -23
  109. package/src/write/layout/voice.js +6 -6
  110. package/src/write/{abc_renderer.js → renderer.js} +36 -32
  111. package/src/write/svg.js +35 -35
  112. package/test.js +2 -2
  113. package/types/index.d.ts +37 -8
  114. package/version.js +1 -1
  115. package/src/tablatures/instruments/guitar/guitar-fonts.js +0 -19
  116. package/src/tablatures/instruments/violin/violin-fonts.js +0 -19
  117. package/src/tablatures/transposer.js +0 -110
  118. package/src/write/abc_abstract_engraver.js +0 -1026
  119. package/src/write/abc_beam_element.js +0 -113
  120. package/src/write/abc_create_clef.js +0 -72
  121. package/src/write/abc_create_key_signature.js +0 -33
  122. package/src/write/abc_create_note_head.js +0 -107
  123. package/src/write/abc_create_time_signature.js +0 -55
  124. package/src/write/abc_decoration.js +0 -357
  125. package/src/write/abc_glyphs.js +0 -224
  126. package/src/write/abc_tempo_element.js +0 -63
  127. package/src/write/abc_triplet_element.js +0 -28
  128. package/src/write/add-chord.js +0 -103
  129. package/src/write/format-jazz-chord.js +0 -15
  130. package/src/write/free-text.js +0 -41
  131. /package/src/write/{abc_crescendo_element.js → creation/elements/crescendo-element.js} +0 -0
  132. /package/src/write/{abc_dynamic_decoration.js → creation/elements/dynamic-decoration.js} +0 -0
  133. /package/src/write/{abc_ending_element.js → creation/elements/ending-element.js} +0 -0
  134. /package/src/write/{abc_glissando_element.js → creation/elements/glissando-element.js} +0 -0
  135. /package/src/write/layout/{getBarYAt.js → get-bar-y-at.js} +0 -0
@@ -13,7 +13,7 @@ var Tokenizer = function(lines, multilineVars) {
13
13
 
14
14
  this.skipWhiteSpace = function(str) {
15
15
  for (var i = 0; i < str.length; i++) {
16
- if (!this.isWhiteSpace(str.charAt(i)))
16
+ if (!this.isWhiteSpace(str[i]))
17
17
  return i;
18
18
  }
19
19
  return str.length; // It must have been all white space
@@ -23,7 +23,7 @@ var Tokenizer = function(lines, multilineVars) {
23
23
  };
24
24
  this.eatWhiteSpace = function(line, index) {
25
25
  for (var i = index; i < line.length; i++) {
26
- if (!this.isWhiteSpace(line.charAt(i)))
26
+ if (!this.isWhiteSpace(line[i]))
27
27
  return i-index;
28
28
  }
29
29
  return i-index;
@@ -34,7 +34,7 @@ var Tokenizer = function(lines, multilineVars) {
34
34
  var i = this.skipWhiteSpace(str);
35
35
  if (finished(str, i))
36
36
  return {len: 0};
37
- switch (str.charAt(i)) {
37
+ switch (str[i]) {
38
38
  case 'A':return {len: i+1, token: 'A'};
39
39
  case 'B':return {len: i+1, token: 'B'};
40
40
  case 'C':return {len: i+1, token: 'C'};
@@ -57,7 +57,7 @@ var Tokenizer = function(lines, multilineVars) {
57
57
  this.getSharpFlat = function(str) {
58
58
  if (str === 'bass')
59
59
  return {len: 0};
60
- switch (str.charAt(0)) {
60
+ switch (str[0]) {
61
61
  case '#':return {len: 1, token: '#'};
62
62
  case 'b':return {len: 1, token: 'b'};
63
63
  }
@@ -67,7 +67,7 @@ var Tokenizer = function(lines, multilineVars) {
67
67
  this.getMode = function(str) {
68
68
  var skipAlpha = function(str, start) {
69
69
  // This returns the index of the next non-alphabetic char, or the entire length of the string if not found.
70
- while (start < str.length && ((str.charAt(start) >= 'a' && str.charAt(start) <= 'z') || (str.charAt(start) >= 'A' && str.charAt(start) <= 'Z')))
70
+ while (start < str.length && ((str[start] >= 'a' && str[start] <= 'z') || (str[start] >= 'A' && str[start] <= 'Z')))
71
71
  start++;
72
72
  return start;
73
73
  };
@@ -76,7 +76,7 @@ var Tokenizer = function(lines, multilineVars) {
76
76
  if (finished(str, i))
77
77
  return {len: 0};
78
78
  var firstThree = str.substring(i,i+3).toLowerCase();
79
- if (firstThree.length > 1 && firstThree.charAt(1) === ' ' || firstThree.charAt(1) === '^' || firstThree.charAt(1) === '_' || firstThree.charAt(1) === '=') firstThree = firstThree.charAt(0); // This will handle the case of 'm'
79
+ if (firstThree.length > 1 && firstThree[1] === ' ' || firstThree[1] === '^' || firstThree[1] === '_' || firstThree[1] === '=') firstThree = firstThree[0]; // This will handle the case of 'm'
80
80
  switch (firstThree) {
81
81
  case 'mix':return {len: skipAlpha(str, i), token: 'Mix'};
82
82
  case 'dor':return {len: skipAlpha(str, i), token: 'Dor'};
@@ -158,14 +158,14 @@ var Tokenizer = function(lines, multilineVars) {
158
158
  // This returns one of the legal bar lines
159
159
  // This is called alot and there is no obvious tokenable items, so this is broken apart.
160
160
  this.getBarLine = function(line, i) {
161
- switch (line.charAt(i)) {
161
+ switch (line[i]) {
162
162
  case ']':
163
163
  ++i;
164
- switch (line.charAt(i)) {
164
+ switch (line[i]) {
165
165
  case '|': return {len: 2, token: "bar_thick_thin"};
166
166
  case '[':
167
167
  ++i;
168
- if ((line.charAt(i) >= '1' && line.charAt(i) <= '9') || line.charAt(i) === '"')
168
+ if ((line[i] >= '1' && line[i] <= '9') || line[i] === '"')
169
169
  return {len: 2, token: "bar_invisible"};
170
170
  return {len: 1, warn: "Unknown bar symbol"};
171
171
  default:
@@ -174,17 +174,17 @@ var Tokenizer = function(lines, multilineVars) {
174
174
  break;
175
175
  case ':':
176
176
  ++i;
177
- switch (line.charAt(i)) {
177
+ switch (line[i]) {
178
178
  case ':': return {len: 2, token: "bar_dbl_repeat"};
179
179
  case '|': // :|
180
180
  ++i;
181
- switch (line.charAt(i)) {
181
+ switch (line[i]) {
182
182
  case ']': // :|]
183
183
  ++i;
184
- switch (line.charAt(i)) {
184
+ switch (line[i]) {
185
185
  case '|': // :|]|
186
186
  ++i;
187
- if (line.charAt(i) === ':') return {len: 5, token: "bar_dbl_repeat"};
187
+ if (line[i] === ':') return {len: 5, token: "bar_dbl_repeat"};
188
188
  return {len: 3, token: "bar_right_repeat"};
189
189
  default:
190
190
  return {len: 3, token: "bar_right_repeat"};
@@ -192,7 +192,7 @@ var Tokenizer = function(lines, multilineVars) {
192
192
  break;
193
193
  case '|': // :||
194
194
  ++i;
195
- if (line.charAt(i) === ':') return {len: 4, token: "bar_dbl_repeat"};
195
+ if (line[i] === ':') return {len: 4, token: "bar_dbl_repeat"};
196
196
  return {len: 3, token: "bar_right_repeat"};
197
197
  default:
198
198
  return {len: 2, token: "bar_right_repeat"};
@@ -204,30 +204,30 @@ var Tokenizer = function(lines, multilineVars) {
204
204
  break;
205
205
  case '[': // [
206
206
  ++i;
207
- if (line.charAt(i) === '|') { // [|
207
+ if (line[i] === '|') { // [|
208
208
  ++i;
209
- switch (line.charAt(i)) {
209
+ switch (line[i]) {
210
210
  case ':': return {len: 3, token: "bar_left_repeat"};
211
211
  case ']': return {len: 3, token: "bar_invisible"};
212
212
  default: return {len: 2, token: "bar_thick_thin"};
213
213
  }
214
214
  } else {
215
- if ((line.charAt(i) >= '1' && line.charAt(i) <= '9') || line.charAt(i) === '"')
215
+ if ((line[i] >= '1' && line[i] <= '9') || line[i] === '"')
216
216
  return {len: 1, token: "bar_invisible"};
217
217
  return {len: 0};
218
218
  }
219
219
  break;
220
220
  case '|': // |
221
221
  ++i;
222
- switch (line.charAt(i)) {
222
+ switch (line[i]) {
223
223
  case ']': return {len: 2, token: "bar_thin_thick"};
224
224
  case '|': // ||
225
225
  ++i;
226
- if (line.charAt(i) === ':') return {len: 3, token: "bar_left_repeat"};
226
+ if (line[i] === ':') return {len: 3, token: "bar_left_repeat"};
227
227
  return {len: 2, token: "bar_thin_thin"};
228
228
  case ':': // |:
229
229
  var colons = 0;
230
- while (line.charAt(i+colons) === ':') colons++;
230
+ while (line[i+colons] === ':') colons++;
231
231
  return { len: 1+colons, token: "bar_left_repeat"};
232
232
  default: return {len: 1, token: "bar_thin"};
233
233
  }
@@ -239,7 +239,7 @@ var Tokenizer = function(lines, multilineVars) {
239
239
  // this returns all the characters in the string that match one of the characters in the legalChars string
240
240
  this.getTokenOf = function(str, legalChars) {
241
241
  for (var i = 0; i < str.length; i++) {
242
- if (legalChars.indexOf(str.charAt(i)) < 0)
242
+ if (legalChars.indexOf(str[i]) < 0)
243
243
  return {len: i, token: str.substring(0, i)};
244
244
  }
245
245
  return {len: i, token: str};
@@ -248,7 +248,7 @@ var Tokenizer = function(lines, multilineVars) {
248
248
  this.getToken = function(str, start, end) {
249
249
  // This returns the next set of chars that doesn't contain spaces
250
250
  var i = start;
251
- while (i < end && !this.isWhiteSpace(str.charAt(i)))
251
+ while (i < end && !this.isWhiteSpace(str[i]))
252
252
  i++;
253
253
  return str.substring(start, i);
254
254
  };
@@ -309,7 +309,7 @@ var Tokenizer = function(lines, multilineVars) {
309
309
  return { accs: accs };
310
310
  }
311
311
  if (tokens.length === 0) return {accs: accs, warn: 'Expected note name after ' + acc};
312
- switch (tokens[0].token.charAt(0))
312
+ switch (tokens[0].token[0])
313
313
  {
314
314
  case 'a':
315
315
  case 'b':
@@ -327,7 +327,7 @@ var Tokenizer = function(lines, multilineVars) {
327
327
  case 'G':
328
328
  if (accs === undefined)
329
329
  accs = [];
330
- accs.push({ acc: acc, note: tokens[0].token.charAt(0) });
330
+ accs.push({ acc: acc, note: tokens[0].token[0] });
331
331
  if (tokens[0].token.length === 1)
332
332
  tokens.shift();
333
333
  else
@@ -355,19 +355,19 @@ var Tokenizer = function(lines, multilineVars) {
355
355
  if (finished(str, i))
356
356
  return {len: 0};
357
357
  var acc = null;
358
- switch (str.charAt(i))
358
+ switch (str[i])
359
359
  {
360
360
  case '^':
361
361
  case '_':
362
362
  case '=':
363
- acc = str.charAt(i);
363
+ acc = str[i];
364
364
  break;
365
365
  default:return {len: 0};
366
366
  }
367
367
  i++;
368
368
  if (finished(str, i))
369
369
  return {len: 1, warn: 'Expected note name after accidental'};
370
- switch (str.charAt(i))
370
+ switch (str[i])
371
371
  {
372
372
  case 'a':
373
373
  case 'b':
@@ -383,15 +383,15 @@ var Tokenizer = function(lines, multilineVars) {
383
383
  case 'E':
384
384
  case 'F':
385
385
  case 'G':
386
- return {len: i+1, token: {acc: accTranslation[acc], note: str.charAt(i)}};
386
+ return {len: i+1, token: {acc: accTranslation[acc], note: str[i]}};
387
387
  case '^':
388
388
  case '_':
389
389
  case '/':
390
- acc += str.charAt(i);
390
+ acc += str[i];
391
391
  i++;
392
392
  if (finished(str, i))
393
393
  return {len: 2, warn: 'Expected note name after accidental'};
394
- switch (str.charAt(i))
394
+ switch (str[i])
395
395
  {
396
396
  case 'a':
397
397
  case 'b':
@@ -407,7 +407,7 @@ var Tokenizer = function(lines, multilineVars) {
407
407
  case 'E':
408
408
  case 'F':
409
409
  case 'G':
410
- return {len: i+1, token: {acc: accTranslation[acc], note: str.charAt(i)}};
410
+ return {len: i+1, token: {acc: accTranslation[acc], note: str[i]}};
411
411
  default:
412
412
  return {len: 2, warn: 'Expected note name after accidental'};
413
413
  }
@@ -427,9 +427,9 @@ var Tokenizer = function(lines, multilineVars) {
427
427
  var comment = line.indexOf('%', start);
428
428
  if (comment >= 0 && comment < end)
429
429
  end = comment;
430
- while (start < end && (line.charAt(start) === ' ' || line.charAt(start) === '\t' || line.charAt(start) === '\x12'))
430
+ while (start < end && (line[start] === ' ' || line[start] === '\t' || line[start] === '\x12'))
431
431
  start++;
432
- while (start < end && (line.charAt(end-1) === ' ' || line.charAt(end-1) === '\t' || line.charAt(end-1) === '\x12'))
432
+ while (start < end && (line[end-1] === ' ' || line[end-1] === '\t' || line[end-1] === '\x12'))
433
433
  end--;
434
434
  return {start: start, end: end};
435
435
  };
@@ -456,46 +456,46 @@ var Tokenizer = function(lines, multilineVars) {
456
456
  var tokens = [];
457
457
  var i;
458
458
  while (start < end) {
459
- if (line.charAt(start) === '"') {
459
+ if (line[start] === '"') {
460
460
  i = start+1;
461
- while (i < end && line.charAt(i) !== '"') i++;
461
+ while (i < end && line[i] !== '"') i++;
462
462
  tokens.push({ type: 'quote', token: line.substring(start+1, i), start: start+1, end: i});
463
463
  i++;
464
- } else if (isLetter(line.charAt(start))) {
464
+ } else if (isLetter(line[start])) {
465
465
  i = start+1;
466
466
  if (alphaUntilWhiteSpace)
467
- while (i < end && !this.isWhiteSpace(line.charAt(i))) i++;
467
+ while (i < end && !this.isWhiteSpace(line[i])) i++;
468
468
  else
469
- while (i < end && isLetter(line.charAt(i))) i++;
470
- tokens.push({ type: 'alpha', token: line.substring(start, i), continueId: isNumber(line.charAt(i)), start: start, end: i});
469
+ while (i < end && isLetter(line[i])) i++;
470
+ tokens.push({ type: 'alpha', token: line.substring(start, i), continueId: isNumber(line[i]), start: start, end: i});
471
471
  start = i + 1;
472
- } else if (line.charAt(start) === '.' && isNumber(line.charAt(i+1))) {
472
+ } else if (line[start] === '.' && isNumber(line[i+1])) {
473
473
  i = start+1;
474
474
  var int2 = null;
475
475
  var float2 = null;
476
- while (i < end && isNumber(line.charAt(i))) i++;
476
+ while (i < end && isNumber(line[i])) i++;
477
477
 
478
478
  float2 = parseFloat(line.substring(start, i));
479
- tokens.push({ type: 'number', token: line.substring(start, i), intt: int2, floatt: float2, continueId: isLetter(line.charAt(i)), start: start, end: i});
479
+ tokens.push({ type: 'number', token: line.substring(start, i), intt: int2, floatt: float2, continueId: isLetter(line[i]), start: start, end: i});
480
480
  start = i + 1;
481
- } else if (isNumber(line.charAt(start)) || (line.charAt(start) === '-' && isNumber(line.charAt(i+1)))) {
481
+ } else if (isNumber(line[start]) || (line[start] === '-' && isNumber(line[i+1]))) {
482
482
  i = start+1;
483
483
  var intt = null;
484
484
  var floatt = null;
485
- while (i < end && isNumber(line.charAt(i))) i++;
486
- if (line.charAt(i) === '.' && isNumber(line.charAt(i+1))) {
485
+ while (i < end && isNumber(line[i])) i++;
486
+ if (line[i] === '.' && isNumber(line[i+1])) {
487
487
  i++;
488
- while (i < end && isNumber(line.charAt(i))) i++;
488
+ while (i < end && isNumber(line[i])) i++;
489
489
  } else
490
490
  intt = parseInt(line.substring(start, i));
491
491
 
492
492
  floatt = parseFloat(line.substring(start, i));
493
- tokens.push({ type: 'number', token: line.substring(start, i), intt: intt, floatt: floatt, continueId: isLetter(line.charAt(i)), start: start, end: i});
493
+ tokens.push({ type: 'number', token: line.substring(start, i), intt: intt, floatt: floatt, continueId: isLetter(line[i]), start: start, end: i});
494
494
  start = i + 1;
495
- } else if (line.charAt(start) === ' ' || line.charAt(start) === '\t') {
495
+ } else if (line[start] === ' ' || line[start] === '\t') {
496
496
  i = start+1;
497
497
  } else {
498
- tokens.push({ type: 'punct', token: line.charAt(start), start: start, end: start+1});
498
+ tokens.push({ type: 'punct', token: line[start], start: start, end: start+1});
499
499
  i = start+1;
500
500
  }
501
501
  start = i;
@@ -506,17 +506,17 @@ var Tokenizer = function(lines, multilineVars) {
506
506
  this.getVoiceToken = function(line, start, end) {
507
507
  // This finds the next token. A token is delimited by a space or an equal sign. If it starts with a quote, then the portion between the quotes is returned.
508
508
  var i = start;
509
- while (i < end && this.isWhiteSpace(line.charAt(i)) || line.charAt(i) === '=')
509
+ while (i < end && this.isWhiteSpace(line[i]) || line[i] === '=')
510
510
  i++;
511
511
 
512
- if (line.charAt(i) === '"') {
512
+ if (line[i] === '"') {
513
513
  var close = line.indexOf('"', i+1);
514
514
  if (close === -1 || close >= end)
515
515
  return {len: 1, err: "Missing close quote"};
516
516
  return {len: close-start+1, token: this.translateString(line.substring(i+1, close))};
517
517
  } else {
518
518
  var ii = i;
519
- while (ii < end && !this.isWhiteSpace(line.charAt(ii)) && line.charAt(ii) !== '=')
519
+ while (ii < end && !this.isWhiteSpace(line[ii]) && line[ii] !== '=')
520
520
  ii++;
521
521
  return {len: ii-start+1, token: line.substring(i, ii)};
522
522
  }
@@ -566,7 +566,7 @@ var Tokenizer = function(lines, multilineVars) {
566
566
  var arr = str.split('\\');
567
567
  if (arr.length === 1) return str;
568
568
  var out = null;
569
- parseCommon.each(arr, function(s) {
569
+ arr.forEach(function(s) {
570
570
  if (out === null)
571
571
  out = s;
572
572
  else {
@@ -592,7 +592,7 @@ var Tokenizer = function(lines, multilineVars) {
592
592
  this.getNumber = function(line, index) {
593
593
  var num = 0;
594
594
  while (index < line.length) {
595
- switch (line.charAt(index)) {
595
+ switch (line[index]) {
596
596
  case '0':num = num*10;index++;break;
597
597
  case '1':num = num*10+1;index++;break;
598
598
  case '2':num = num*10+2;index++;break;
@@ -613,16 +613,16 @@ var Tokenizer = function(lines, multilineVars) {
613
613
  this.getFraction = function(line, index) {
614
614
  var num = 1;
615
615
  var den = 1;
616
- if (line.charAt(index) !== '/') {
616
+ if (line[index] !== '/') {
617
617
  var ret = this.getNumber(line, index);
618
618
  num = ret.num;
619
619
  index = ret.index;
620
620
  }
621
- if (line.charAt(index) === '/') {
621
+ if (line[index] === '/') {
622
622
  index++;
623
- if (line.charAt(index) === '/') {
623
+ if (line[index] === '/') {
624
624
  var div = 0.5;
625
- while (line.charAt(index++) === '/')
625
+ while (line[index++] === '/')
626
626
  div = div /2;
627
627
  return {value: num * div, index: index-1};
628
628
  } else {
@@ -723,14 +723,14 @@ var Tokenizer = function(lines, multilineVars) {
723
723
  // It returns the substring and the number of characters consumed.
724
724
  // The number of characters consumed is normally two more than the size of the substring,
725
725
  // but in the error case it might not be.
726
- var matchChar = _matchChar || line.charAt(i);
726
+ var matchChar = _matchChar || line[i];
727
727
  var pos = i+1;
728
728
  var esc = false;
729
729
  while ((pos < line.length) && (esc || line[pos] !== matchChar)) {
730
730
  esc = line[pos] === '\\';
731
731
  ++pos;
732
732
  }
733
- if (line.charAt(pos) === matchChar)
733
+ if (line[pos] === matchChar)
734
734
  return [pos-i+1,substInChord(line.substring(i+1, pos)), true];
735
735
  else // we hit the end of line, so we'll just pick an arbitrary num of chars so the line doesn't disappear.
736
736
  {
@@ -68,7 +68,7 @@ var TuneBuilder = function(tune) {
68
68
  durationThisBar += event.duration;
69
69
  durationsPerLines[i] += event.duration;
70
70
  }
71
- } else if (event.el_type === "scale" || event.el_type === "stem" || event.el_type === "overlay" || event.el_type === "style" || event.el_type === "transpose") {
71
+ } else if (event.el_type === "scale" || event.el_type === "stem" || event.el_type === "overlay" || event.el_type === "style" || event.el_type === "transpose" || event.el_type === "color") {
72
72
  // These types of events are duplicated on the overlay layer.
73
73
  overlayVoice[k].voice.push(event);
74
74
  }
@@ -178,10 +178,10 @@ var TuneBuilder = function(tune) {
178
178
  }
179
179
  }
180
180
  if (anyDeleted) {
181
- tune.lines = parseCommon.compact(tune.lines);
182
- parseCommon.each(tune.lines, function(line) {
181
+ tune.lines = tune.lines.filter(function (line) { return !!line });
182
+ tune.lines.forEach(function(line) {
183
183
  if (line.staff)
184
- line.staff = parseCommon.compact(line.staff);
184
+ line.staff = line.staff.filter(function (line) { return !!line });
185
185
  });
186
186
  }
187
187
 
@@ -212,9 +212,9 @@ var TuneBuilder = function(tune) {
212
212
  }
213
213
  }
214
214
  if (anyDeleted) {
215
- parseCommon.each(tune.lines, function(line) {
215
+ tune.lines.forEach(function(line) {
216
216
  if (line.staff)
217
- line.staff = parseCommon.compact(line.staff);
217
+ line.staff = line.staff.filter(function (staff) { return !!staff });
218
218
  });
219
219
  }
220
220
  }
@@ -253,7 +253,7 @@ var TuneBuilder = function(tune) {
253
253
  }
254
254
  if (currSlur[staffNum][voiceNum][chordPos] === undefined) {
255
255
  var offNum = chordPos*100+1;
256
- parseCommon.each(obj.endSlur, function(x) { if (offNum === x) --offNum; });
256
+ obj.endSlur.forEach(function(x) { if (offNum === x) --offNum; });
257
257
  currSlur[staffNum][voiceNum][chordPos] = [offNum];
258
258
  }
259
259
  }
@@ -276,12 +276,12 @@ var TuneBuilder = function(tune) {
276
276
  var nextNum = chordPos*100+1;
277
277
  for (var i = 0; i < num; i++) {
278
278
  if (usedNums) {
279
- parseCommon.each(usedNums, function(x) { if (nextNum === x) ++nextNum; });
280
- parseCommon.each(usedNums, function(x) { if (nextNum === x) ++nextNum; });
281
- parseCommon.each(usedNums, function(x) { if (nextNum === x) ++nextNum; });
279
+ usedNums.forEach(function(x) { if (nextNum === x) ++nextNum; });
280
+ usedNums.forEach(function(x) { if (nextNum === x) ++nextNum; });
281
+ usedNums.forEach(function(x) { if (nextNum === x) ++nextNum; });
282
282
  }
283
- parseCommon.each(currSlur[staffNum][voiceNum][chordPos], function(x) { if (nextNum === x) ++nextNum; });
284
- parseCommon.each(currSlur[staffNum][voiceNum][chordPos], function(x) { if (nextNum === x) ++nextNum; });
283
+ currSlur[staffNum][voiceNum][chordPos].forEach(function(x) { if (nextNum === x) ++nextNum; });
284
+ currSlur[staffNum][voiceNum][chordPos].forEach(function(x) { if (nextNum === x) ++nextNum; });
285
285
 
286
286
  currSlur[staffNum][voiceNum][chordPos].push(nextNum);
287
287
  obj.startSlur.push({ label: nextNum });
@@ -512,11 +512,11 @@ var TuneBuilder = function(tune) {
512
512
  }
513
513
  if (hp.pitches !== undefined) {
514
514
  var mid = currStaff.workingClef.verticalPos;
515
- parseCommon.each(hp.pitches, function(p) { p.verticalPos = p.pitch - mid; });
515
+ hp.pitches.forEach(function(p) { p.verticalPos = p.pitch - mid; });
516
516
  }
517
517
  if (hp.gracenotes !== undefined) {
518
518
  var mid2 = currStaff.workingClef.verticalPos;
519
- parseCommon.each(hp.gracenotes, function(p) { p.verticalPos = p.pitch - mid2; });
519
+ hp.gracenotes.forEach(function(p) { p.verticalPos = p.pitch - mid2; });
520
520
  }
521
521
  currStaff.voices[This.voiceNum].push(hp);
522
522
  };
@@ -702,6 +702,9 @@ var TuneBuilder = function(tune) {
702
702
  this.changeVoiceScale = function(scale) {
703
703
  self.appendElement('scale', null, null, { size: scale} );
704
704
  };
705
+ this.changeVoiceColor = function(color) {
706
+ self.appendElement('color', null, null, { color: color} );
707
+ };
705
708
 
706
709
  this.startNewLine = function(params) {
707
710
  // If the pointed to line doesn't exist, just create that. If the line does exist, but doesn't have any music on it, just use it.
@@ -734,6 +737,8 @@ var TuneBuilder = function(tune) {
734
737
  }
735
738
  if (params.scale)
736
739
  self.appendElement('scale', null, null, { size: params.scale} );
740
+ if (params.color)
741
+ self.appendElement('color', null, null, { color: params.color} );
737
742
  };
738
743
  var createStaff = function(params) {
739
744
  if (params.key && params.key.impliedNaturals) {
@@ -32,6 +32,7 @@ var pitchesToPerc = require('./pitches-to-perc');
32
32
  var chordSourceTrack;
33
33
  var chordTrackFinished;
34
34
  var chordChannel;
35
+ var bassInstrument = 0;
35
36
  var chordInstrument = 0;
36
37
  var drumInstrument = 128;
37
38
  var boomVolume = 64;
@@ -54,6 +55,7 @@ var pitchesToPerc = require('./pitches-to-perc');
54
55
  var drumTrack;
55
56
  var drumTrackFinished;
56
57
  var drumDefinition = {};
58
+ var drumBars;
57
59
 
58
60
  var pickupLength = 0;
59
61
  var percmap;
@@ -63,8 +65,9 @@ var pitchesToPerc = require('./pitches-to-perc');
63
65
  var slurredBreakBetweenNotes = -0.001; // make the slurred notes actually overlap
64
66
  var staccatoBreakBetweenNotes = 0.4; // some people say staccato is half duration, some say 3/4 so this splits it
65
67
 
66
- flatten = function(voices, options, percmap_) {
68
+ flatten = function(voices, options, percmap_, midiOptions) {
67
69
  if (!options) options = {};
70
+ if (!midiOptions) midiOptions = {};
68
71
  barAccidentals = [];
69
72
  accidentals = [0,0,0,0,0,0,0];
70
73
  bagpipes = false;
@@ -87,8 +90,10 @@ var pitchesToPerc = require('./pitches-to-perc');
87
90
  chordChannel = voices.length; // first free channel for chords
88
91
  chordTrackFinished = false;
89
92
  currentChords = [];
90
- boomVolume = 64;
91
- chickVolume = 48;
93
+ bassInstrument = midiOptions.bassprog && midiOptions.bassprog.length === 1 ? midiOptions.bassprog[0] : 0;
94
+ chordInstrument = midiOptions.chordprog && midiOptions.chordprog.length === 1 ? midiOptions.chordprog[0] : 0;
95
+ boomVolume = midiOptions.bassvol && midiOptions.bassvol.length === 1 ? midiOptions.bassvol[0] : 64;
96
+ chickVolume = midiOptions.chordvol && midiOptions.chordvol.length === 1 ? midiOptions.chordvol[0] : 48;
92
97
  lastChord = undefined;
93
98
  chordLastBar = undefined;
94
99
  gChordTacet = options.chordsOff ? true : false;
@@ -107,6 +112,7 @@ var pitchesToPerc = require('./pitches-to-perc');
107
112
  drumTrack = [];
108
113
  drumTrackFinished = false;
109
114
  drumDefinition = {};
115
+ drumBars = 1;
110
116
 
111
117
  if (voices.length > 0 && voices[0].length > 0)
112
118
  pickupLength = voices[0][0].pickupLength;
@@ -145,6 +151,7 @@ var pitchesToPerc = require('./pitches-to-perc');
145
151
  startingMeter = element;
146
152
  meter = element;
147
153
  beatFraction = getBeatFraction(meter);
154
+ alignDrumToMeter();
148
155
  break;
149
156
  case "tempo":
150
157
  if (!startingTempo)
@@ -189,6 +196,7 @@ var pitchesToPerc = require('./pitches-to-perc');
189
196
  break;
190
197
  case "drum":
191
198
  drumDefinition = normalizeDrumDefinition(element.params);
199
+ alignDrumToMeter();
192
200
  break;
193
201
  case "gchord":
194
202
  if (!options.chordsOff)
@@ -995,9 +1003,9 @@ var pitchesToPerc = require('./pitches-to-perc');
995
1003
  function chordNotes(bass, modifier) {
996
1004
  var intervals = chordIntervals[modifier];
997
1005
  if (!intervals) {
998
- if (modifier.slice(0,2).toLowerCase() === 'ma' || modifier.charAt(0) === 'M')
1006
+ if (modifier.slice(0,2).toLowerCase() === 'ma' || modifier[0] === 'M')
999
1007
  intervals = chordIntervals.M;
1000
- else if (modifier.charAt(0) === 'm' || modifier.charAt(0) === '-')
1008
+ else if (modifier[0] === 'm' || modifier[0] === '-')
1001
1009
  intervals = chordIntervals.m;
1002
1010
  else
1003
1011
  intervals = chordIntervals.M;
@@ -1013,7 +1021,7 @@ var pitchesToPerc = require('./pitches-to-perc');
1013
1021
  function writeBoom(boom, beatLength, volume, beat, noteLength) {
1014
1022
  // undefined means there is a stop time.
1015
1023
  if (boom !== undefined)
1016
- chordTrack.push({cmd: 'note', pitch: boom, volume: volume, start: lastBarTime+beat*durationRounded(beatLength), duration: durationRounded(noteLength), gap: 0, instrument: chordInstrument});
1024
+ chordTrack.push({cmd: 'note', pitch: boom, volume: volume, start: lastBarTime+beat*durationRounded(beatLength), duration: durationRounded(noteLength), gap: 0, instrument: bassInstrument});
1017
1025
  }
1018
1026
 
1019
1027
  function writeChick(chick, beatLength, volume, beat, noteLength) {
@@ -1208,16 +1216,24 @@ var pitchesToPerc = require('./pitches-to-perc');
1208
1216
  } else
1209
1217
  ret.pattern.push({ len: len * beatLength, pitch: null});
1210
1218
  }
1219
+ drumBars = params.bars ? params.bars : 1;
1220
+ return ret;
1221
+ }
1222
+
1223
+ function alignDrumToMeter() {
1224
+ if (!drumDefinition ||!drumDefinition.pattern) {
1225
+ return;
1226
+ }
1227
+ var ret = drumDefinition;
1211
1228
  // Now normalize the pattern to cover the correct number of measures. The note lengths passed are relative to each other and need to be scaled to fit a measure.
1212
1229
  var totalTime = 0;
1213
1230
  var measuresPerBeat = meter.num/meter.den;
1214
1231
  for (var ii = 0; ii < ret.pattern.length; ii++)
1215
1232
  totalTime += ret.pattern[ii].len;
1216
- var numBars = params.bars ? params.bars : 1;
1217
- var factor = totalTime / numBars / measuresPerBeat;
1233
+ var factor = totalTime / drumBars / measuresPerBeat;
1218
1234
  for (ii = 0; ii < ret.pattern.length; ii++)
1219
1235
  ret.pattern[ii].len = ret.pattern[ii].len / factor;
1220
- return ret;
1236
+ drumDefinition = ret;
1221
1237
  }
1222
1238
 
1223
1239
  function writeDrum(channel) {