abcjs 6.1.8 → 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 (132) hide show
  1. package/RELEASE.md +66 -0
  2. package/dist/abcjs-basic-min.js +2 -2
  3. package/dist/abcjs-basic.js +4373 -4424
  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 +6 -6
  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/midi/abc_midi_create.js +8 -2
  13. package/src/parse/abc_common.js +0 -47
  14. package/src/parse/abc_parse.js +16 -16
  15. package/src/parse/abc_parse_book.js +3 -3
  16. package/src/parse/abc_parse_directive.js +26 -7
  17. package/src/parse/abc_parse_header.js +11 -9
  18. package/src/parse/abc_parse_key_voice.js +17 -17
  19. package/src/parse/abc_parse_music.js +89 -106
  20. package/src/parse/abc_tokenizer.js +60 -60
  21. package/src/parse/abc_transpose.js +14 -3
  22. package/src/parse/tune-builder.js +19 -14
  23. package/src/synth/abc_midi_flattener.js +25 -9
  24. package/src/synth/abc_midi_sequencer.js +1 -1
  25. package/src/synth/create-synth.js +41 -0
  26. package/src/synth/note-to-midi.js +50 -0
  27. package/src/tablatures/instruments/guitar/tab-guitar.js +0 -2
  28. package/src/tablatures/instruments/string-patterns.js +46 -28
  29. package/src/tablatures/instruments/tab-note.js +26 -103
  30. package/src/tablatures/instruments/violin/tab-violin.js +0 -2
  31. package/src/tablatures/tab-absolute-elements.js +9 -31
  32. package/src/tablatures/tab-renderer.js +2 -2
  33. package/src/test/abc_parser_lint.js +8 -5
  34. package/src/write/README.md +31 -0
  35. package/src/write/creation/abstract-engraver.js +1036 -0
  36. package/src/write/creation/add-chord.js +102 -0
  37. package/src/write/{add-text-if.js → creation/add-text-if.js} +6 -6
  38. package/src/write/{calcHeight.js → creation/calc-height.js} +2 -2
  39. package/src/write/creation/create-clef.js +72 -0
  40. package/src/write/creation/create-key-signature.js +31 -0
  41. package/src/write/creation/create-note-head.js +107 -0
  42. package/src/write/creation/create-time-signature.js +55 -0
  43. package/src/write/creation/decoration.js +357 -0
  44. package/src/write/{abc_absolute_element.js → creation/elements/absolute-element.js} +14 -15
  45. package/src/write/creation/elements/beam-element.js +113 -0
  46. package/src/write/{bottom-text.js → creation/elements/bottom-text.js} +14 -15
  47. package/src/write/{abc_brace_element.js → creation/elements/brace-element.js} +5 -5
  48. package/src/write/creation/elements/free-text.js +41 -0
  49. package/src/write/{abc_relative_element.js → creation/elements/relative-element.js} +8 -7
  50. package/src/write/{separator.js → creation/elements/separator.js} +2 -2
  51. package/src/write/{abc_staff_group_element.js → creation/elements/staff-group-element.js} +4 -4
  52. package/src/write/{subtitle.js → creation/elements/subtitle.js} +3 -3
  53. package/src/write/creation/elements/tempo-element.js +63 -0
  54. package/src/write/{abc_tie_element.js → creation/elements/tie-element.js} +15 -11
  55. package/src/write/{top-text.js → creation/elements/top-text.js} +12 -12
  56. package/src/write/creation/elements/triplet-element.js +28 -0
  57. package/src/write/{abc_voice_element.js → creation/elements/voice-element.js} +3 -3
  58. package/src/write/creation/glyphs.js +226 -0
  59. package/src/write/creation/translate-chord.js +37 -0
  60. package/src/write/draw/absolute.js +5 -5
  61. package/src/write/draw/beam.js +8 -8
  62. package/src/write/draw/brace.js +33 -33
  63. package/src/write/draw/crescendo.js +4 -4
  64. package/src/write/draw/debug-box.js +1 -1
  65. package/src/write/draw/draw.js +7 -7
  66. package/src/write/draw/dynamics.js +2 -2
  67. package/src/write/draw/ending.js +6 -6
  68. package/src/write/draw/glissando.js +17 -17
  69. package/src/write/draw/group-elements.js +51 -51
  70. package/src/write/draw/horizontal-line.js +9 -9
  71. package/src/write/draw/non-music.js +1 -1
  72. package/src/write/draw/print-line.js +15 -16
  73. package/src/write/draw/print-stem.js +5 -5
  74. package/src/write/draw/print-symbol.js +12 -12
  75. package/src/write/draw/print-vertical-line.js +8 -8
  76. package/src/write/draw/relative.js +17 -16
  77. package/src/write/draw/selectables.js +5 -5
  78. package/src/write/draw/separator.js +4 -4
  79. package/src/write/draw/set-paper-size.js +2 -2
  80. package/src/write/draw/sprintf.js +31 -31
  81. package/src/write/draw/staff-group.js +36 -30
  82. package/src/write/draw/staff-line.js +2 -2
  83. package/src/write/draw/staff.js +4 -4
  84. package/src/write/draw/tab-line.js +26 -26
  85. package/src/write/draw/tempo.js +30 -30
  86. package/src/write/draw/text.js +5 -5
  87. package/src/write/draw/tie.js +18 -18
  88. package/src/write/draw/triplet.js +6 -6
  89. package/src/write/draw/voice.js +16 -17
  90. package/src/write/{abc_engraver_controller.js → engraver-controller.js} +58 -51
  91. package/src/write/{classes.js → helpers/classes.js} +6 -6
  92. package/src/write/{get-font-and-attr.js → helpers/get-font-and-attr.js} +9 -7
  93. package/src/write/{get-text-size.js → helpers/get-text-size.js} +5 -5
  94. package/src/write/{set-class.js → helpers/set-class.js} +1 -1
  95. package/src/write/{abc_spacing.js → helpers/spacing.js} +1 -1
  96. package/src/write/{highlight.js → interactive/highlight.js} +1 -1
  97. package/src/write/{selection.js → interactive/selection.js} +27 -27
  98. package/src/write/{unhighlight.js → interactive/unhighlight.js} +1 -1
  99. package/src/write/layout/beam.js +13 -13
  100. package/src/write/layout/get-left-edge-of-staff.js +4 -4
  101. package/src/write/layout/layout.js +74 -74
  102. package/src/write/layout/{setUpperAndLowerElements.js → set-upper-and-lower-elements.js} +8 -8
  103. package/src/write/layout/{staffGroup.js → staff-group.js} +32 -32
  104. package/src/write/layout/triplet.js +4 -4
  105. package/src/write/layout/{VoiceElements.js → voice-elements.js} +23 -23
  106. package/src/write/layout/voice.js +6 -6
  107. package/src/write/{abc_renderer.js → renderer.js} +35 -32
  108. package/src/write/svg.js +35 -35
  109. package/test.js +1 -1
  110. package/types/index.d.ts +99 -22
  111. package/version.js +1 -1
  112. package/src/tablatures/instruments/guitar/guitar-fonts.js +0 -19
  113. package/src/tablatures/instruments/violin/violin-fonts.js +0 -19
  114. package/src/tablatures/transposer.js +0 -110
  115. package/src/write/abc_abstract_engraver.js +0 -1026
  116. package/src/write/abc_beam_element.js +0 -113
  117. package/src/write/abc_create_clef.js +0 -72
  118. package/src/write/abc_create_key_signature.js +0 -33
  119. package/src/write/abc_create_note_head.js +0 -107
  120. package/src/write/abc_create_time_signature.js +0 -55
  121. package/src/write/abc_decoration.js +0 -341
  122. package/src/write/abc_glyphs.js +0 -224
  123. package/src/write/abc_tempo_element.js +0 -63
  124. package/src/write/abc_triplet_element.js +0 -28
  125. package/src/write/add-chord.js +0 -103
  126. package/src/write/format-jazz-chord.js +0 -15
  127. package/src/write/free-text.js +0 -41
  128. /package/src/write/{abc_crescendo_element.js → creation/elements/crescendo-element.js} +0 -0
  129. /package/src/write/{abc_dynamic_decoration.js → creation/elements/dynamic-decoration.js} +0 -0
  130. /package/src/write/{abc_ending_element.js → creation/elements/ending-element.js} +0 -0
  131. /package/src/write/{abc_glissando_element.js → creation/elements/glissando-element.js} +0 -0
  132. /package/src/write/layout/{getBarYAt.js → get-bar-y-at.js} +0 -0
@@ -82,7 +82,7 @@ var isInTie = function(multilineVars, overlayLevel, el) {
82
82
  if (multilineVars.inTie[overlayLevel] === undefined)
83
83
  return false;
84
84
  // If this is single voice music then the voice index isn't set, so we use the first voice.
85
- var voiceIndex = multilineVars.currentVoice ? multilineVars.currentVoice.index : 0;
85
+ var voiceIndex = multilineVars.currentVoice ? multilineVars.currentVoice.staffNum * 100 + multilineVars.currentVoice.index : 0;
86
86
  if (multilineVars.inTie[overlayLevel][voiceIndex]) {
87
87
  if (el.pitches !== undefined || el.rest.type !== 'spacer')
88
88
  return true;
@@ -98,9 +98,9 @@ MusicParser.prototype.parseMusic = function(line) {
98
98
  var i = 0;
99
99
  var startOfLine = multilineVars.iChar;
100
100
  // see if there is nothing but a comment on this line. If so, just ignore it. A full line comment is optional white space followed by %
101
- while (tokenizer.isWhiteSpace(line.charAt(i)) && i < line.length)
101
+ while (tokenizer.isWhiteSpace(line[i]) && i < line.length)
102
102
  i++;
103
- if (i === line.length || line.charAt(i) === '%')
103
+ if (i === line.length || line[i] === '%')
104
104
  return;
105
105
 
106
106
  // Start with the standard staff, clef and key symbols on each line
@@ -126,7 +126,7 @@ MusicParser.prototype.parseMusic = function(line) {
126
126
  while (i < line.length)
127
127
  {
128
128
  var startI = i;
129
- if (line.charAt(i) === '%')
129
+ if (line[i] === '%')
130
130
  break;
131
131
 
132
132
  var retInlineHeader = header.letter_to_inline_header(line, i, delayStartNewLine);
@@ -158,7 +158,7 @@ MusicParser.prototype.parseMusic = function(line) {
158
158
  if (ret > 0) {
159
159
  i += ret;
160
160
  }
161
- if (i > 0 && line.charAt(i-1) === '\x12') {
161
+ if (i > 0 && line[i-1] === '\x12') {
162
162
  // there is one case where a line continuation isn't the same as being on the same line, and that is if the next character after it is a header.
163
163
  ret = header.letter_to_body_header(line, i);
164
164
  if (ret[0] > 0) {
@@ -203,7 +203,7 @@ MusicParser.prototype.parseMusic = function(line) {
203
203
  el.force_end_beam_last = true;
204
204
  i += ii;
205
205
  } else {
206
- if (nonDecorations.indexOf(line.charAt(i)) === -1)
206
+ if (nonDecorations.indexOf(line[i]) === -1)
207
207
  ret = letter_to_accent(line, i);
208
208
  else ret = [ 0 ];
209
209
  if (ret[0] > 0) {
@@ -326,7 +326,7 @@ MusicParser.prototype.parseMusic = function(line) {
326
326
  }
327
327
 
328
328
  // handle chords.
329
- if (line.charAt(i) === '[') {
329
+ if (line[i] === '[') {
330
330
  var chordStartChar = i;
331
331
  i++;
332
332
  var chordDuration = null;
@@ -373,12 +373,12 @@ MusicParser.prototype.parseMusic = function(line) {
373
373
 
374
374
  i = chordNote.endChar;
375
375
  delete chordNote.endChar;
376
- } else if (line.charAt(i) === ' ') {
376
+ } else if (line[i] === ' ') {
377
377
  // Spaces are not allowed in chords, but we can recover from it by ignoring it.
378
378
  warn("Spaces are not allowed in chords", line, i);
379
379
  i++;
380
380
  } else {
381
- if (i < line.length && line.charAt(i) === ']') {
381
+ if (i < line.length && line[i] === ']') {
382
382
  // consume the close bracket
383
383
  i++;
384
384
 
@@ -388,7 +388,7 @@ MusicParser.prototype.parseMusic = function(line) {
388
388
  }
389
389
 
390
390
  if (isInTie(multilineVars, overlayLevel, el)) {
391
- parseCommon.each(el.pitches, function(pitch) { pitch.endTie = true; });
391
+ el.pitches.forEach(function(pitch) { pitch.endTie = true; });
392
392
  setIsInTie(multilineVars, overlayLevel, false);
393
393
  }
394
394
 
@@ -401,7 +401,7 @@ MusicParser.prototype.parseMusic = function(line) {
401
401
 
402
402
  var postChordDone = false;
403
403
  while (i < line.length && !postChordDone) {
404
- switch (line.charAt(i)) {
404
+ switch (line[i]) {
405
405
  case ' ':
406
406
  case '\t':
407
407
  addEndBeam(el);
@@ -410,7 +410,7 @@ MusicParser.prototype.parseMusic = function(line) {
410
410
  if (el.endSlur === undefined) el.endSlur = 1; else el.endSlur++;
411
411
  break;
412
412
  case '-':
413
- parseCommon.each(el.pitches, function(pitch) { pitch.startTie = {}; });
413
+ el.pitches.forEach(function(pitch) { pitch.startTie = {}; });
414
414
  setIsInTie(multilineVars, overlayLevel, true);
415
415
  break;
416
416
  case '>':
@@ -436,9 +436,10 @@ MusicParser.prototype.parseMusic = function(line) {
436
436
  var fraction = tokenizer.getFraction(line, i);
437
437
  chordDuration = fraction.value;
438
438
  i = fraction.index;
439
- if (line.charAt(i) === ' ')
439
+ var ch = line[i]
440
+ if (ch === ' ')
440
441
  rememberEndBeam = true;
441
- if (line.charAt(i) === '-' || line.charAt(i) === ')' || line.charAt(i) === ' ' || line.charAt(i) === '<' || line.charAt(i) === '>')
442
+ if (ch === '-' || ch === ')' || ch === ' ' || ch === '<' || ch === '>')
442
443
  i--; // Subtracting one because one is automatically added below
443
444
  else
444
445
  postChordDone = true;
@@ -560,7 +561,7 @@ MusicParser.prototype.parseMusic = function(line) {
560
561
  }
561
562
 
562
563
  if (i === startI) { // don't know what this is, so ignore it.
563
- if (line.charAt(i) !== ' ' && line.charAt(i) !== '`')
564
+ if (line[i] !== ' ' && line[i] !== '`')
564
565
  warn("Unknown character ignored", line, i);
565
566
  i++;
566
567
  }
@@ -573,14 +574,14 @@ MusicParser.prototype.parseMusic = function(line) {
573
574
 
574
575
  var setIsInTie =function(multilineVars, overlayLevel, value) {
575
576
  // If this is single voice music then the voice index isn't set, so we use the first voice.
576
- var voiceIndex = multilineVars.currentVoice ? multilineVars.currentVoice.index : 0;
577
+ var voiceIndex = multilineVars.currentVoice ? multilineVars.currentVoice.staffNum * 100 + multilineVars.currentVoice.index : 0;
577
578
  if (multilineVars.inTie[overlayLevel] === undefined)
578
579
  multilineVars.inTie[overlayLevel] = [];
579
580
  multilineVars.inTie[overlayLevel][voiceIndex] = value;
580
581
  };
581
582
 
582
583
  var letter_to_chord = function(line, i) {
583
- if (line.charAt(i) === '"')
584
+ if (line[i] === '"')
584
585
  {
585
586
  var chord = tokenizer.getBrackettedSubstring(line, i, 5);
586
587
  if (!chord[2])
@@ -588,19 +589,19 @@ var letter_to_chord = function(line, i) {
588
589
  // If it starts with ^, then the chord appears above.
589
590
  // If it starts with _ then the chord appears below.
590
591
  // (note that the 2.0 draft standard defines them as not chords, but annotations and also defines @.)
591
- if (chord[0] > 0 && chord[1].length > 0 && chord[1].charAt(0) === '^') {
592
+ if (chord[0] > 0 && chord[1].length > 0 && chord[1][0] === '^') {
592
593
  chord[1] = chord[1].substring(1);
593
594
  chord[2] = 'above';
594
- } else if (chord[0] > 0 && chord[1].length > 0 && chord[1].charAt(0) === '_') {
595
+ } else if (chord[0] > 0 && chord[1].length > 0 && chord[1][0] === '_') {
595
596
  chord[1] = chord[1].substring(1);
596
597
  chord[2] = 'below';
597
- } else if (chord[0] > 0 && chord[1].length > 0 && chord[1].charAt(0) === '<') {
598
+ } else if (chord[0] > 0 && chord[1].length > 0 && chord[1][0] === '<') {
598
599
  chord[1] = chord[1].substring(1);
599
600
  chord[2] = 'left';
600
- } else if (chord[0] > 0 && chord[1].length > 0 && chord[1].charAt(0) === '>') {
601
+ } else if (chord[0] > 0 && chord[1].length > 0 && chord[1][0] === '>') {
601
602
  chord[1] = chord[1].substring(1);
602
603
  chord[2] = 'right';
603
- } else if (chord[0] > 0 && chord[1].length > 0 && chord[1].charAt(0) === '@') {
604
+ } else if (chord[0] > 0 && chord[1].length > 0 && chord[1][0] === '@') {
604
605
  // @-15,5.7
605
606
  chord[1] = chord[1].substring(1);
606
607
  var x = tokenizer.getFloat(chord[1]);
@@ -637,7 +638,7 @@ var letter_to_chord = function(line, i) {
637
638
 
638
639
  var letter_to_grace = function(line, i) {
639
640
  // Grace notes are an array of: startslur, note, endslur, space; where note is accidental, pitch, duration
640
- if (line.charAt(i) === '{') {
641
+ if (line[i] === '{') {
641
642
  // fetch the gracenotes string and consume that into the array
642
643
  var gra = tokenizer.getBrackettedSubstring(line, i, 1, '}');
643
644
  if (!gra[2])
@@ -653,7 +654,7 @@ var letter_to_grace = function(line, i) {
653
654
  var inTie = false;
654
655
  while (ii < gra[1].length) {
655
656
  var acciaccatura = false;
656
- if (gra[1].charAt(ii) === '/') {
657
+ if (gra[1][ii] === '/') {
657
658
  acciaccatura = true;
658
659
  ii++;
659
660
  }
@@ -682,11 +683,11 @@ var letter_to_grace = function(line, i) {
682
683
  }
683
684
  else {
684
685
  // We shouldn't get anything but notes or a space here, so report an error
685
- if (gra[1].charAt(ii) === ' ') {
686
+ if (gra[1][ii] === ' ') {
686
687
  if (gracenotes.length > 0)
687
688
  gracenotes[gracenotes.length-1].endBeam = true;
688
689
  } else
689
- warn("Unknown character '" + gra[1].charAt(ii) + "' while parsing grace note", line, i);
690
+ warn("Unknown character '" + gra[1][ii] + "' while parsing grace note", line, i);
690
691
  ii++;
691
692
  }
692
693
  }
@@ -697,9 +698,9 @@ var letter_to_grace = function(line, i) {
697
698
  };
698
699
 
699
700
  function letter_to_overlay(line, i) {
700
- if (line.charAt(i) === '&') {
701
+ if (line[i] === '&') {
701
702
  var start = i;
702
- while (line.charAt(i) && line.charAt(i) !== ':' && line.charAt(i) !== '|')
703
+ while (line[i] && line[i] !== ':' && line[i] !== '|')
703
704
  i++;
704
705
  return [ i-start, line.substring(start+1, i) ];
705
706
  }
@@ -724,7 +725,7 @@ var legalAccents = [
724
725
  "slide", "marcato",
725
726
  "upbow", "downbow", "/", "//", "///", "////", "trem1", "trem2", "trem3", "trem4",
726
727
  "turnx", "invertedturn", "invertedturnx", "trill(", "trill)", "arpeggio", "xstem", "mark", "umarcato",
727
- "style=normal", "style=harmonic", "style=rhythm", "style=x", "style=triangle"
728
+ "style=normal", "style=harmonic", "style=rhythm", "style=x", "style=triangle", "D.C.alcoda", "D.C.alfine", "D.S.alcoda", "D.S.alfine", "editorial", "courtesy"
728
729
  ];
729
730
 
730
731
  var volumeDecorations = [
@@ -747,38 +748,30 @@ var accentDynamicPseudonyms = [
747
748
  ];
748
749
 
749
750
  var letter_to_accent = function(line, i) {
750
- var macro = multilineVars.macros[line.charAt(i)];
751
+ var macro = multilineVars.macros[line[i]];
751
752
 
752
753
  if (macro !== undefined) {
753
- if (macro.charAt(0) === '!' || macro.charAt(0) === '+')
754
+ if (macro[0] === '!' || macro[0] === '+')
754
755
  macro = macro.substring(1);
755
- if (macro.charAt(macro.length-1) === '!' || macro.charAt(macro.length-1) === '+')
756
+ if (macro[macro.length-1] === '!' || macro[macro.length-1] === '+')
756
757
  macro = macro.substring(0, macro.length-1);
757
- if (parseCommon.detect(legalAccents, function(acc) {
758
- return (macro === acc);
759
- }))
758
+ if (legalAccents.includes(macro))
760
759
  return [ 1, macro ];
761
- else if (parseCommon.detect(volumeDecorations, function(acc) {
762
- return (macro === acc);
763
- })) {
760
+ else if (volumeDecorations.includes(macro)) {
764
761
  if (multilineVars.volumePosition === 'hidden')
765
762
  macro = "";
766
763
  return [1, macro];
767
- } else if (parseCommon.detect(dynamicDecorations, function(acc) {
764
+ } else if (dynamicDecorations.includes(macro)) {
768
765
  if (multilineVars.dynamicPosition === 'hidden')
769
766
  macro = "";
770
- return (macro === acc);
771
- })) {
772
767
  return [1, macro];
773
768
  } else {
774
- if (!parseCommon.detect(multilineVars.ignoredDecorations, function(dec) {
775
- return (macro === dec);
776
- }))
769
+ if (!multilineVars.ignoredDecorations.includes(macro))
777
770
  warn("Unknown macro: " + macro, line, i);
778
771
  return [1, '' ];
779
772
  }
780
773
  }
781
- switch (line.charAt(i))
774
+ switch (line[i])
782
775
  {
783
776
  case '.':
784
777
  if (line[i+1] === '(' || line[i+1] === '-') // a dot then open paren is a dotted slur; likewise dot dash is dotted tie.
@@ -791,50 +784,38 @@ var letter_to_accent = function(line, i) {
791
784
  case '+':
792
785
  var ret = tokenizer.getBrackettedSubstring(line, i, 5);
793
786
  // Be sure that the accent is recognizable.
794
- if (ret[1].length > 1 && (ret[1].charAt(0) === '^' || ret[1].charAt(0) ==='_'))
787
+ if (ret[1].length > 1 && (ret[1][0] === '^' || ret[1][0] ==='_'))
795
788
  ret[1] = ret[1].substring(1); // TODO-PER: The test files have indicators forcing the ornament to the top or bottom, but that isn't in the standard. We'll just ignore them.
796
- if (parseCommon.detect(legalAccents, function(acc) {
797
- return (ret[1] === acc);
798
- }))
789
+ if (legalAccents.includes(ret[1]))
799
790
  return ret;
800
- if (parseCommon.detect(volumeDecorations, function(acc) {
801
- return (ret[1] === acc);
802
- })) {
791
+ if (volumeDecorations.includes(ret[1])) {
803
792
  if (multilineVars.volumePosition === 'hidden' )
804
793
  ret[1] = '';
805
794
  return ret;
806
795
  }
807
- if (parseCommon.detect(dynamicDecorations, function(acc) {
808
- return (ret[1] === acc);
809
- })) {
796
+ if (dynamicDecorations.includes(ret[1])) {
810
797
  if (multilineVars.dynamicPosition === 'hidden' )
811
798
  ret[1] = '';
812
799
  return ret;
813
800
  }
814
801
 
815
- if (parseCommon.detect(accentPseudonyms, function(acc) {
816
- if (ret[1] === acc[0]) {
817
- ret[1] = acc[1];
818
- return true;
819
- } else
820
- return false;
821
- }))
802
+ var ind = accentPseudonyms.findIndex(function (acc) { return ret[1] === acc[0]})
803
+ if (ind >= 0) {
804
+ ret[1] = accentPseudonyms[ind][1];
822
805
  return ret;
806
+ }
823
807
 
824
- if (parseCommon.detect(accentDynamicPseudonyms, function(acc) {
825
- if (ret[1] === acc[0]) {
826
- ret[1] = acc[1];
827
- return true;
828
- } else
829
- return false;
830
- })) {
808
+ ind = accentDynamicPseudonyms.findIndex(function (acc) { return ret[1] === acc[0]})
809
+ if (ind >= 0) {
810
+ ret[1] = accentDynamicPseudonyms[ind][1];
831
811
  if (multilineVars.dynamicPosition === 'hidden' )
832
812
  ret[1] = '';
833
813
  return ret;
834
814
  }
815
+
835
816
  // We didn't find the accent in the list, so consume the space, but don't return an accent.
836
817
  // Although it is possible that ! was used as a line break, so accept that.
837
- if (line.charAt(i) === '!' && (ret[0] === 1 || line.charAt(i+ret[0]-1) !== '!'))
818
+ if (line[i] === '!' && (ret[0] === 1 || line[i+ret[0]-1] !== '!'))
838
819
  return [1, null ];
839
820
  warn("Unknown decoration: " + ret[1], line, i);
840
821
  ret[1] = "";
@@ -854,7 +835,7 @@ var letter_to_accent = function(line, i) {
854
835
 
855
836
  var letter_to_spacer = function(line, i) {
856
837
  var start = i;
857
- while (tokenizer.isWhiteSpace(line.charAt(i)))
838
+ while (tokenizer.isWhiteSpace(line[i]))
858
839
  i++;
859
840
  return [ i-start ];
860
841
  };
@@ -877,15 +858,15 @@ var letter_to_bar = function(line, curr_pos) {
877
858
  // It can also optionally start with '[', which is ignored.
878
859
  // Also, it can have white space before the '['.
879
860
  for (var ws = 0; ws < line.length; ws++)
880
- if (line.charAt(curr_pos + ret.len + ws) !== ' ')
861
+ if (line[curr_pos + ret.len + ws] !== ' ')
881
862
  break;
882
863
  var orig_bar_len = ret.len;
883
- if (line.charAt(curr_pos+ret.len+ws) === '[') {
864
+ if (line[curr_pos+ret.len+ws] === '[') {
884
865
  ret.len += ws + 1;
885
866
  }
886
867
 
887
868
  // It can also be a quoted string. It is unclear whether that construct requires '[', but it seems like it would. otherwise it would be confused with a regular chord.
888
- if (line.charAt(curr_pos+ret.len) === '"' && line.charAt(curr_pos+ret.len-1) === '[') {
869
+ if (line[curr_pos+ret.len] === '"' && line[curr_pos+ret.len-1] === '[') {
889
870
  var ending = tokenizer.getBrackettedSubstring(line, curr_pos+ret.len, 5);
890
871
  return [ret.len+ending[0], ret.token, ending[1]];
891
872
  }
@@ -916,16 +897,16 @@ var letter_to_open_slurs_and_triplets = function(line, i) {
916
897
  ret.dottedSlur = true;
917
898
  i++;
918
899
  }
919
- while (line.charAt(i) === '(' || tokenizer.isWhiteSpace(line.charAt(i))) {
920
- if (line.charAt(i) === '(') {
921
- if (i+1 < line.length && (line.charAt(i+1) >= '2' && line.charAt(i+1) <= '9')) {
900
+ while (line[i] === '(' || tokenizer.isWhiteSpace(line[i])) {
901
+ if (line[i] === '(') {
902
+ if (i+1 < line.length && (line[i+1] >= '2' && line[i+1] <= '9')) {
922
903
  if (ret.triplet !== undefined)
923
904
  warn("Can't nest triplets", line, i);
924
905
  else {
925
- ret.triplet = line.charAt(i+1) - '0';
906
+ ret.triplet = line[i+1] - '0';
926
907
  ret.tripletQ = tripletQ[ret.triplet];
927
908
  ret.num_notes = ret.triplet;
928
- if (i+2 < line.length && line.charAt(i+2) === ':') {
909
+ if (i+2 < line.length && line[i+2] === ':') {
929
910
  // We are expecting "(p:q:r" or "(p:q" or "(p::r"
930
911
  // That is: "put p notes into the time of q for the next r notes"
931
912
  // if r is missing, then it is equal to p.
@@ -938,18 +919,18 @@ var letter_to_open_slurs_and_triplets = function(line, i) {
938
919
  // (7 notes in the time of n
939
920
  // (8 notes in the time of 3
940
921
  // (9 notes in the time of n
941
- if (i+3 < line.length && line.charAt(i+3) === ':') {
922
+ if (i+3 < line.length && line[i+3] === ':') {
942
923
  // The second number, 'q', is not present.
943
- if (i+4 < line.length && (line.charAt(i+4) >= '1' && line.charAt(i+4) <= '9')) {
944
- ret.num_notes = line.charAt(i+4) - '0';
924
+ if (i+4 < line.length && (line[i+4] >= '1' && line[i+4] <= '9')) {
925
+ ret.num_notes = line[i+4] - '0';
945
926
  i += 3;
946
927
  } else
947
928
  warn("expected number after the two colons after the triplet to mark the duration", line, i);
948
- } else if (i+3 < line.length && (line.charAt(i+3) >= '1' && line.charAt(i+3) <= '9')) {
949
- ret.tripletQ = line.charAt(i+3) - '0';
950
- if (i+4 < line.length && line.charAt(i+4) === ':') {
951
- if (i+5 < line.length && (line.charAt(i+5) >= '1' && line.charAt(i+5) <= '9')) {
952
- ret.num_notes = line.charAt(i+5) - '0';
929
+ } else if (i+3 < line.length && (line[i+3] >= '1' && line[i+3] <= '9')) {
930
+ ret.tripletQ = line[i+3] - '0';
931
+ if (i+4 < line.length && line[i+4] === ':') {
932
+ if (i+5 < line.length && (line[i+5] >= '1' && line[i+5] <= '9')) {
933
+ ret.num_notes = line[i+5] - '0';
953
934
  i += 4;
954
935
  }
955
936
  } else {
@@ -1004,7 +985,7 @@ MusicParser.prototype.startNewLine = function() {
1004
985
  parseKeyVoice.addPosToKey(params.clef, params.key);
1005
986
  if (multilineVars.meter !== null) {
1006
987
  if (multilineVars.currentVoice) {
1007
- parseCommon.each(multilineVars.staves, function(st) {
988
+ multilineVars.staves.forEach(function(st) {
1008
989
  st.meter = multilineVars.meter;
1009
990
  });
1010
991
  params.meter = multilineVars.staves[multilineVars.currentVoice.staffNum].meter;
@@ -1042,6 +1023,8 @@ MusicParser.prototype.startNewLine = function() {
1042
1023
  params.staffscale = multilineVars.currentVoice.staffscale;
1043
1024
  if (multilineVars.currentVoice.scale)
1044
1025
  params.scale = multilineVars.currentVoice.scale;
1026
+ if (multilineVars.currentVoice.color)
1027
+ params.color = multilineVars.currentVoice.color;
1045
1028
  if (multilineVars.currentVoice.style)
1046
1029
  params.style = multilineVars.currentVoice.style;
1047
1030
  if (multilineVars.currentVoice.transpose)
@@ -1083,7 +1066,7 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
1083
1066
  var state = 'startSlur';
1084
1067
  var durationSetByPreviousNote = false;
1085
1068
  while (1) {
1086
- switch(line.charAt(index)) {
1069
+ switch(line[index]) {
1087
1070
  case '(':
1088
1071
  if (state === 'startSlur') {
1089
1072
  if (el.startSlur === undefined) el.startSlur = 1; else el.startSlur++;
@@ -1127,9 +1110,9 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
1127
1110
  case 'f':
1128
1111
  case 'g':
1129
1112
  if (state === 'startSlur' || state === 'sharp2' || state === 'flat2' || state === 'pitch') {
1130
- el.pitch = pitches[line.charAt(index)];
1113
+ el.pitch = pitches[line[index]];
1131
1114
  el.pitch += 7 * (multilineVars.currentVoice && multilineVars.currentVoice.octave !== undefined ? multilineVars.currentVoice.octave : multilineVars.octave);
1132
- el.name = line.charAt(index);
1115
+ el.name = line[index];
1133
1116
  if (el.accidental)
1134
1117
  el.name = accMap[el.accidental] + el.name;
1135
1118
  transpose.note(multilineVars, el);
@@ -1144,7 +1127,7 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
1144
1127
  // If the clef is percussion, there is probably some translation of the pitch to a particular drum kit item.
1145
1128
  if ((multilineVars.clef && multilineVars.clef.type === "perc") ||
1146
1129
  (multilineVars.currentVoice && multilineVars.currentVoice.clef === "perc")) {
1147
- var key = line.charAt(index);
1130
+ var key = line[index];
1148
1131
  if (el.accidental) {
1149
1132
  key = accMap[el.accidental] + key;
1150
1133
  }
@@ -1170,7 +1153,7 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
1170
1153
  case 'z':
1171
1154
  case 'Z':
1172
1155
  if (state === 'startSlur') {
1173
- el.rest = { type: rests[line.charAt(index)] };
1156
+ el.rest = { type: rests[line[index]] };
1174
1157
  // There shouldn't be some of the properties that notes have. If some sneak in due to bad syntax in the abc file,
1175
1158
  // just nix them here.
1176
1159
  delete el.accidental;
@@ -1214,8 +1197,8 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
1214
1197
  el.duration = el.duration * fraction.value;
1215
1198
  // TODO-PER: We can test the returned duration here and give a warning if it isn't the one expected.
1216
1199
  el.endChar = fraction.index;
1217
- while (fraction.index < line.length && (tokenizer.isWhiteSpace(line.charAt(fraction.index)) || line.charAt(fraction.index) === '-')) {
1218
- if (line.charAt(fraction.index) === '-')
1200
+ while (fraction.index < line.length && (tokenizer.isWhiteSpace(line[fraction.index]) || line[fraction.index] === '-')) {
1201
+ if (line[fraction.index] === '-')
1219
1202
  el.startTie = {};
1220
1203
  else
1221
1204
  el = addEndBeam(el);
@@ -1246,7 +1229,7 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
1246
1229
  state = 'broken_rhythm';
1247
1230
  else {
1248
1231
  // Peek ahead to the next character. If it is a space, then we have an end beam.
1249
- if (tokenizer.isWhiteSpace(line.charAt(index + 1)))
1232
+ if (tokenizer.isWhiteSpace(line[index + 1]))
1250
1233
  addEndBeam(el);
1251
1234
  el.endChar = index+1;
1252
1235
  return el;
@@ -1261,21 +1244,21 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
1261
1244
  // look ahead to see if there is a tie
1262
1245
  dottedTie = false;
1263
1246
  do {
1264
- if (line.charAt(index) === '.' && line.charAt(index+1) === '-') {
1247
+ if (line[index] === '.' && line[index+1] === '-') {
1265
1248
  dottedTie = true;
1266
1249
  index++;
1267
1250
  }
1268
- if (line.charAt(index) === '-') {
1251
+ if (line[index] === '-') {
1269
1252
  el.startTie = {};
1270
1253
  if (dottedTie)
1271
1254
  el.startTie.style = "dotted";
1272
1255
  }
1273
1256
  index++;
1274
1257
  } while (index < line.length &&
1275
- (tokenizer.isWhiteSpace(line.charAt(index)) || line.charAt(index) === '-') ||
1276
- (line.charAt(index) === '.' && line.charAt(index+1) === '-'));
1258
+ (tokenizer.isWhiteSpace(line[index]) || line[index] === '-') ||
1259
+ (line[index] === '.' && line[index+1] === '-'));
1277
1260
  el.endChar = index;
1278
- if (!durationSetByPreviousNote && canHaveBrokenRhythm && (line.charAt(index) === '<' || line.charAt(index) === '>')) { // TODO-PER: Don't need the test for < and >, but that makes the endChar work out for the regression test.
1261
+ if (!durationSetByPreviousNote && canHaveBrokenRhythm && (line[index] === '<' || line[index] === '>')) { // TODO-PER: Don't need the test for < and >, but that makes the endChar work out for the regression test.
1279
1262
  index--;
1280
1263
  state = 'broken_rhythm';
1281
1264
  } else
@@ -1316,18 +1299,18 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
1316
1299
  };
1317
1300
 
1318
1301
  var getBrokenRhythm = function(line, index) {
1319
- switch (line.charAt(index)) {
1302
+ switch (line[index]) {
1320
1303
  case '>':
1321
- if (index < line.length - 2 && line.charAt(index + 1) === '>' && line.charAt(index + 2) === '>') // triple >>>
1304
+ if (index < line.length - 2 && line[index + 1] === '>' && line[index + 2] === '>') // triple >>>
1322
1305
  return [3, 1.875, 0.125];
1323
- else if (index < line.length - 1 && line.charAt(index + 1) === '>') // double >>
1306
+ else if (index < line.length - 1 && line[index + 1] === '>') // double >>
1324
1307
  return [2, 1.75, 0.25];
1325
1308
  else
1326
1309
  return [1, 1.5, 0.5];
1327
1310
  case '<':
1328
- if (index < line.length - 2 && line.charAt(index + 1) === '<' && line.charAt(index + 2) === '<') // triple <<<
1311
+ if (index < line.length - 2 && line[index + 1] === '<' && line[index + 2] === '<') // triple <<<
1329
1312
  return [3, 0.125, 1.875];
1330
- else if (index < line.length - 1 && line.charAt(index + 1) === '<') // double <<
1313
+ else if (index < line.length - 1 && line[index + 1] === '<') // double <<
1331
1314
  return [2, 0.25, 1.75];
1332
1315
  else
1333
1316
  return [1, 0.5, 1.5];