abcjs 6.4.1 → 6.4.3

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 (48) hide show
  1. package/LICENSE.md +1 -1
  2. package/RELEASE.md +46 -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 +1271 -1179
  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 +1 -1
  10. package/license.js +1 -1
  11. package/package.json +1 -1
  12. package/plugin.js +1 -1
  13. package/src/api/abc_tunebook.js +1 -2
  14. package/src/api/abc_tunebook_svg.js +0 -1
  15. package/src/data/abc_tune.js +2 -0
  16. package/src/midi/abc_midi_create.js +22 -7
  17. package/src/parse/abc_common.js +3 -11
  18. package/src/parse/abc_parse.js +1 -1
  19. package/src/parse/abc_parse_directive.js +44 -3
  20. package/src/parse/abc_parse_header.js +6 -4
  21. package/src/parse/abc_parse_key_voice.js +10 -6
  22. package/src/parse/abc_parse_music.js +54 -22
  23. package/src/parse/tune-builder.js +675 -643
  24. package/src/synth/abc_midi_flattener.js +3 -1
  25. package/src/synth/abc_midi_sequencer.js +18 -3
  26. package/src/synth/chord-track.js +90 -18
  27. package/src/synth/create-synth-control.js +1 -2
  28. package/src/tablatures/abc_tablatures.js +184 -0
  29. package/src/tablatures/instruments/string-patterns.js +266 -268
  30. package/src/tablatures/instruments/string-tablature.js +38 -35
  31. package/src/tablatures/instruments/tab-note.js +186 -181
  32. package/src/tablatures/instruments/tab-notes.js +30 -35
  33. package/src/tablatures/instruments/tab-string.js +43 -25
  34. package/src/tablatures/render/tab-absolute-elements.js +303 -0
  35. package/src/tablatures/render/tab-renderer.js +244 -0
  36. package/src/test/abc_parser_lint.js +2 -3
  37. package/src/write/creation/abstract-engraver.js +1 -1
  38. package/src/write/creation/elements/tie-element.js +26 -0
  39. package/src/write/engraver-controller.js +1 -1
  40. package/src/write/layout/set-upper-and-lower-elements.js +8 -0
  41. package/test.js +1 -1
  42. package/types/index.d.ts +2 -2
  43. package/version.js +1 -1
  44. package/src/api/abc_tablatures.js +0 -184
  45. package/src/tablatures/instruments/tab-string-patterns.js +0 -23
  46. package/src/tablatures/tab-absolute-elements.js +0 -301
  47. package/src/tablatures/tab-common.js +0 -29
  48. package/src/tablatures/tab-renderer.js +0 -259
@@ -1,5 +1,5 @@
1
1
  /**!
2
- Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
2
+ Copyright (c) 2009-2024 Paul Rosen and Gregory Dyke
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**!
2
- Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
2
+ Copyright (c) 2009-2024 Paul Rosen and Gregory Dyke
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
package/license.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**!
2
- Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
2
+ Copyright (c) 2009-2024 Paul Rosen and Gregory Dyke
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abcjs",
3
- "version": "6.4.1",
3
+ "version": "6.4.3",
4
4
  "description": "Renderer for abc music notation",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
package/plugin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**!
2
- Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
2
+ Copyright (c) 2009-2024 Paul Rosen and Gregory Dyke
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
@@ -2,7 +2,7 @@
2
2
 
3
3
  var Parse = require('../parse/abc_parse');
4
4
  var bookParser = require('../parse/abc_parse_book');
5
- var tablatures = require('./abc_tablatures');
5
+ var tablatures = require('../tablatures/abc_tablatures');
6
6
 
7
7
 
8
8
  var tunebook = {};
@@ -87,7 +87,6 @@ var tunebook = {};
87
87
  // Init tablatures plugins
88
88
  //
89
89
  if (params.tablature) {
90
- tablatures.init();
91
90
  tune.tablatures = tablatures.preparePlugins(tune, currentTune, params);
92
91
  }
93
92
  var warnings = abcParser.getWarnings();
@@ -4,7 +4,6 @@ var Tune = require('../data/abc_tune');
4
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 tablatures = require('./abc_tablatures');
8
7
 
9
8
 
10
9
  var resizeDivs = {};
@@ -551,6 +551,8 @@ var Tune = function() {
551
551
 
552
552
  this.getBpm = function(tempo) {
553
553
  var bpm;
554
+ if (!tempo)
555
+ tempo = this.metaText ? this.metaText.tempo : null;
554
556
  if (tempo) {
555
557
  bpm = tempo.bpm;
556
558
  var beatLength = this.getBeatLength();
@@ -9,20 +9,35 @@ var create;
9
9
 
10
10
  var baseDuration = 480*4; // nice and divisible, equals 1 whole note
11
11
 
12
- create = function(abcTune, options) {
12
+ create = function create(abcTune, options) {
13
13
  if (options === undefined) options = {};
14
14
  var commands = abcTune.setUpAudio(options);
15
15
  var midi = rendererFactory();
16
-
17
16
  var title = abcTune.metaText ? abcTune.metaText.title : undefined;
18
- if (title && title.length > 128)
19
- title = title.substring(0,124) + '...';
17
+ if (title && title.length > 128) title = title.substring(0, 124) + '...';
20
18
  var key = abcTune.getKeySignature();
21
19
  var time = abcTune.getMeterFraction();
22
- var beatsPerSecond = commands.tempo / 60;
23
- //var beatLength = abcTune.getBeatLength();
24
- midi.setGlobalInfo(commands.tempo, title, key, time);
25
20
 
21
+ // MAE 7 July 2024 - Fix for */8 meter tempos
22
+ var tempo = commands.tempo;
23
+
24
+ var beatsPerSecond = tempo / 60;
25
+
26
+ // Fix tempo for */8 meters
27
+ if (time.den == 8){
28
+
29
+ // Compute the tempo based on the actual milliseconds per measure, scaled by the number of eight notes and halved to get tempo in bpm.
30
+ var msPerMeasure = abcTune.millisecondsPerMeasure();
31
+
32
+ tempo = (60000 / (msPerMeasure/time.num)) / 2;
33
+
34
+ beatsPerSecond = tempo/60;
35
+
36
+ }
37
+
38
+ //var beatLength = abcTune.getBeatLength();
39
+ midi.setGlobalInfo(tempo, title, key, time);
40
+
26
41
  for (var i = 0; i < commands.tracks.length; i++) {
27
42
  midi.startTrack();
28
43
  var notePlacement = {};
@@ -1,19 +1,11 @@
1
- // abc_parse.js: parses a string representing ABC Music Notation into a usable internal structure.
1
+ // abc_common.js: Some common utility functions.
2
2
 
3
3
  var parseCommon = {};
4
4
 
5
- parseCommon.clone = function(source) {
6
- var destination = {};
7
- for (var property in source)
8
- if (source.hasOwnProperty(property))
9
- destination[property] = source[property];
10
- return destination;
11
- };
12
-
13
5
  parseCommon.cloneArray = function(source) {
14
6
  var destination = [];
15
7
  for (var i = 0; i < source.length; i++) {
16
- destination.push(parseCommon.clone(source[i]));
8
+ destination.push(Object.assign({},source[i]));
17
9
  }
18
10
  return destination;
19
11
  };
@@ -22,7 +14,7 @@ parseCommon.cloneHashOfHash = function(source) {
22
14
  var destination = {};
23
15
  for (var property in source)
24
16
  if (source.hasOwnProperty(property))
25
- destination[property] = parseCommon.clone(source[property]);
17
+ destination[property] = Object.assign({},source[property]);
26
18
  return destination;
27
19
  };
28
20
 
@@ -440,7 +440,7 @@ var Parse = function() {
440
440
  });
441
441
  for (var i = 0; i < nextVoice.length; i++) {
442
442
  var element = nextVoice[i];
443
- var hint = parseCommon.clone(element);
443
+ var hint = Object.assign({},element);
444
444
  voice.push(hint);
445
445
  if (element.el_type === 'bar')
446
446
  return;
@@ -485,8 +485,6 @@ var parseDirective = {};
485
485
  var midiCmdParam1Integer = [
486
486
  "bassvol",
487
487
  "chordvol",
488
- "bassprog",
489
- "chordprog",
490
488
  "c",
491
489
  "channel",
492
490
  "beatmod",
@@ -500,7 +498,8 @@ var parseDirective = {};
500
498
  "transpose",
501
499
  "rtranspose",
502
500
  "vol",
503
- "volinc"
501
+ "volinc",
502
+ "gchordbars"
504
503
  ];
505
504
  var midiCmdParam1Integer1OptionalInteger = [
506
505
  "program"
@@ -531,6 +530,10 @@ var parseDirective = {};
531
530
  "drum",
532
531
  "chordname"
533
532
  ];
533
+ var midiCmdParam1Integer1OptionalString = [
534
+ "bassprog", "chordprog"
535
+ ];
536
+
534
537
 
535
538
  var parseMidiCommand = function(midi, tune, restOfString) {
536
539
  var midi_cmd = midi.shift().token;
@@ -673,6 +676,44 @@ var parseDirective = {};
673
676
  }
674
677
  }
675
678
  }
679
+ else if (midiCmdParam1Integer1OptionalString.indexOf(midi_cmd) >= 0){
680
+
681
+ // ONE INT PARAMETER, ONE OPTIONAL string
682
+ if (midi.length !== 1 && midi.length !== 2)
683
+ warn("Expected one or two parameters in MIDI " + midi_cmd, restOfString, 0);
684
+ else if (midi[0].type !== "number")
685
+ warn("Expected integer parameter in MIDI " + midi_cmd, restOfString, 0);
686
+ else if (midi.length === 2 && midi[1].type !== "alpha")
687
+ warn("Expected alpha parameter in MIDI " + midi_cmd, restOfString, 0);
688
+ else {
689
+ midi_params.push(midi[0].intt);
690
+
691
+ // Currently only bassprog and chordprog with optional octave shifts use this path
692
+ if (midi.length === 2){
693
+ var cmd = midi[1].token;
694
+ if (cmd.indexOf("octave=") != -1){
695
+ cmd = cmd.replace("octave=","");
696
+ cmd = parseInt(cmd);
697
+ if (!isNaN(cmd)){
698
+ // Limit range from -1 to 3 octaves
699
+ if (cmd < -1){
700
+ warn("Expected octave= in MIDI " + midi_cmd + ' to be >= -1 (recv:'+cmd+')');
701
+ cmd = -1;
702
+ }
703
+ if (cmd > 3){
704
+ warn("Expected octave= in MIDI " + midi_cmd + ' to be <= 3 (recv:'+cmd+')');
705
+ cmd = 3;
706
+ }
707
+ midi_params.push(cmd);
708
+ } else
709
+ warn("Expected octave value in MIDI" + midi_cmd);
710
+ }
711
+ else{
712
+ warn("Expected octave= in MIDI" + midi_cmd);
713
+ }
714
+ }
715
+ }
716
+ }
676
717
 
677
718
  if (tuneBuilder.hasBeginMusic())
678
719
  tuneBuilder.appendElement('midi', -1, -1, { cmd: midi_cmd, params: midi_params });
@@ -341,6 +341,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
341
341
 
342
342
  this.letter_to_inline_header = function(line, i, startLine)
343
343
  {
344
+ var needsNewLine = false
344
345
  var ws = tokenizer.eatWhiteSpace(line, i);
345
346
  i +=ws;
346
347
  if (line.length >= i+5 && line[i] === '[' && line[i+2] === ':') {
@@ -396,9 +397,9 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
396
397
  break;
397
398
  case "[V:":
398
399
  if (e > 0) {
399
- parseKeyVoice.parseVoice(line, i+3, e);
400
+ needsNewLine = parseKeyVoice.parseVoice(line, i+3, e);
400
401
  //startNewLine();
401
- return [ e-i+1+ws, line[i+1], line.substring(i+3, e)];
402
+ return [ e-i+1+ws, line[i+1], line.substring(i+3, e), needsNewLine];
402
403
  }
403
404
  break;
404
405
  case "[r:":
@@ -413,6 +414,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
413
414
 
414
415
  this.letter_to_body_header = function(line, i)
415
416
  {
417
+ var needsNewLine = false
416
418
  if (line.length >= i+3) {
417
419
  switch(line.substring(i, i+2))
418
420
  {
@@ -447,9 +449,9 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
447
449
  else if (tempo.type === 'immediate') tuneBuilder.appendElement('tempo', multilineVars.iChar + i, multilineVars.iChar + line.length, tempo.tempo);
448
450
  return [ e, line[i], parseCommon.strip(line.substring(i+2))];
449
451
  case "V:":
450
- parseKeyVoice.parseVoice(line, i+2, line.length);
452
+ needsNewLine = parseKeyVoice.parseVoice(line, i+2, line.length);
451
453
  // startNewLine();
452
- return [ line.length, line[i], parseCommon.strip(line.substring(i+2))];
454
+ return [ line.length, line[i], parseCommon.strip(line.substring(i+2)), needsNewLine];
453
455
  default:
454
456
  // TODO: complain about unhandled header
455
457
  }
@@ -1,4 +1,3 @@
1
- var parseCommon = require('./abc_common');
2
1
  var parseDirective = require('./abc_parse_directive');
3
2
  var transpose = require('./abc_transpose');
4
3
 
@@ -84,7 +83,7 @@ var parseKeyVoice = {};
84
83
  parseKeyVoice.deepCopyKey = function(key) {
85
84
  var ret = { accidentals: [], root: key.root, acc: key.acc, mode: key.mode };
86
85
  key.accidentals.forEach(function(k) {
87
- ret.accidentals.push(parseCommon.clone(k));
86
+ ret.accidentals.push(Object.assign({},k));
88
87
  });
89
88
  return ret;
90
89
  };
@@ -151,7 +150,7 @@ var parseKeyVoice = {};
151
150
  };
152
151
 
153
152
  parseKeyVoice.fixKey = function(clef, key) {
154
- var fixedKey = parseCommon.clone(key);
153
+ var fixedKey = Object.assign({},key);
155
154
  parseKeyVoice.addPosToKey(clef, fixedKey);
156
155
  return fixedKey;
157
156
  };
@@ -494,8 +493,13 @@ var parseKeyVoice = {};
494
493
  };
495
494
 
496
495
  var setCurrentVoice = function(id) {
497
- multilineVars.currentVoice = multilineVars.voices[id];
498
- tuneBuilder.setCurrentVoice(multilineVars.currentVoice.staffNum, multilineVars.currentVoice.index);
496
+ var currentVoice = multilineVars.voices[id]
497
+ if (multilineVars.currentVoice) {
498
+ if (multilineVars.currentVoice.index === currentVoice.index && multilineVars.currentVoice.staffNum === currentVoice.staffNum)
499
+ return // there was no change so don't reset it.
500
+ }
501
+ multilineVars.currentVoice = currentVoice;
502
+ return tuneBuilder.setCurrentVoice(currentVoice.staffNum, currentVoice.index, id);
499
503
  };
500
504
 
501
505
  parseKeyVoice.parseVoice = function(line, i, e) {
@@ -788,7 +792,7 @@ var parseKeyVoice = {};
788
792
  if (staffInfo.name) {if (s.name) s.name.push(staffInfo.name); else s.name = [ staffInfo.name ];}
789
793
  if (staffInfo.subname) {if (s.subname) s.subname.push(staffInfo.subname); else s.subname = [ staffInfo.subname ];}
790
794
 
791
- setCurrentVoice(id);
795
+ return setCurrentVoice(id);
792
796
  };
793
797
 
794
798
  })();
@@ -1,4 +1,3 @@
1
- var parseCommon = require('./abc_common');
2
1
  var parseKeyVoice = require('./abc_parse_key_voice');
3
2
  var transpose = require('./abc_transpose');
4
3
 
@@ -145,6 +144,7 @@ MusicParser.prototype.parseMusic = function(line) {
145
144
  var retInlineHeader = header.letter_to_inline_header(line, i, delayStartNewLine);
146
145
  if (retInlineHeader[0] > 0) {
147
146
  i += retInlineHeader[0];
147
+ //console.log("inline header", retInlineHeader)
148
148
  if (retInlineHeader[1] === 'V')
149
149
  delayStartNewLine = true; // fixes bug on this: c[V:2]d
150
150
  // TODO-PER: Handle inline headers
@@ -457,6 +457,9 @@ MusicParser.prototype.parseMusic = function(line) {
457
457
  else
458
458
  postChordDone = true;
459
459
  break;
460
+ case '0':
461
+ chordDuration = 0;
462
+ break;
460
463
  default:
461
464
  postChordDone = true;
462
465
  break;
@@ -554,13 +557,17 @@ MusicParser.prototype.parseMusic = function(line) {
554
557
  // The first item on a line is a regular note value, each item after that represents a dot placed after the previous note.
555
558
  // Only durations less than a whole note are tested because whole note durations have some tricky rules.
556
559
 
557
- if (el.duration < 1 && durations.indexOf(el.duration) === -1 && el.duration !== 0) {
560
+ if (el.duration < 1 && durations.indexOf(el.duration) === -1 && el.duration !== 0) {
558
561
  if (!el.rest || el.rest.type !== 'spacer')
559
562
  warn("Duration not representable: " + line.substring(startI, i), line, i);
560
563
  }
561
564
 
562
565
  multilineVars.addFormattingOptions(el, tune.formatting, 'note');
563
- tuneBuilder.appendElement('note', startOfLine+startI, startOfLine+i, el);
566
+ var succeeded = tuneBuilder.appendElement('note', startOfLine+startI, startOfLine+i, el);
567
+ if (!succeeded) {
568
+ this.startNewLine()
569
+ tuneBuilder.appendElement('note', startOfLine+startI, startOfLine+i, el);
570
+ }
564
571
  multilineVars.measureNotEmpty = true;
565
572
  el = {};
566
573
  }
@@ -608,23 +615,38 @@ var letter_to_chord = function(line, i) {
608
615
  chord[1] = chord[1].substring(1);
609
616
  chord[2] = 'right';
610
617
  } else if (chord[0] > 0 && chord[1].length > 0 && chord[1][0] === '@') {
611
- // @-15,5.7
612
- chord[1] = chord[1].substring(1);
613
- var x = tokenizer.getFloat(chord[1]);
614
- if (x.digits === 0)
615
- warn("Missing first position in absolutely positioned annotation.", line , i);
616
- chord[1] = chord[1].substring(x.digits);
617
- if (chord[1][0] !== ',')
618
- warn("Missing comma absolutely positioned annotation.", line , i);
619
- chord[1] = chord[1].substring(1);
620
- var y = tokenizer.getFloat(chord[1]);
621
- if (y.digits === 0)
622
- warn("Missing second position in absolutely positioned annotation.", line , i);
623
- chord[1] = chord[1].substring(y.digits);
624
- var ws = tokenizer.skipWhiteSpace(chord[1]);
625
- chord[1] = chord[1].substring(ws);
626
- chord[2] = null;
627
- chord[3] = { x: x.value, y: y.value };
618
+ // @-15,5.7
619
+ chord[1] = chord[1].substring(1);
620
+ var x = tokenizer.getFloat(chord[1]);
621
+ if (x.digits === 0){
622
+ warn("Missing first position in absolutely positioned annotation.", line, i);
623
+ chord[1] = chord[1].replace("@","");
624
+ chord[2] = 'above';
625
+ return chord;
626
+ }
627
+ chord[1] = chord[1].substring(x.digits);
628
+ if (chord[1][0] !== ','){
629
+ warn("Missing comma absolutely positioned annotation.", line, i);
630
+ chord[1] = chord[1].replace("@","");
631
+ chord[2] = 'above';
632
+ return chord;
633
+ }
634
+ chord[1] = chord[1].substring(1);
635
+ var y = tokenizer.getFloat(chord[1]);
636
+ if (y.digits === 0){
637
+ warn("Missing second position in absolutely positioned annotation.", line, i);
638
+ chord[1] = chord[1].replace("@","");
639
+ chord[2] = 'above';
640
+ return chord;
641
+ }
642
+ chord[1] = chord[1].substring(y.digits);
643
+ var ws = tokenizer.skipWhiteSpace(chord[1]);
644
+ chord[1] = chord[1].substring(ws);
645
+ chord[2] = null;
646
+ chord[3] = {
647
+ x: x.value,
648
+ y: y.value
649
+ };
628
650
  } else {
629
651
  if (multilineVars.freegchord !== true) {
630
652
  chord[1] = chord[1].replace(/([ABCDEFG0-9])b/g, "$1♭");
@@ -670,7 +692,11 @@ var letter_to_grace = function(line, i) {
670
692
  note.duration = note.duration / (multilineVars.default_length * 8);
671
693
  if (acciaccatura)
672
694
  note.acciaccatura = true;
673
- gracenotes.push(note);
695
+ if (note.rest) {
696
+ // don't allow rests inside gracenotes
697
+ warn("Rests not allowed as grace notes '" + gra[1][ii] + "' while parsing grace note", line, i);
698
+ } else
699
+ gracenotes.push(note);
674
700
 
675
701
  if (inTie) {
676
702
  note.endTie = true;
@@ -927,7 +953,7 @@ MusicParser.prototype.startNewLine = function() {
927
953
  var params = { startChar: -1, endChar: -1};
928
954
  if (multilineVars.partForNextLine.title)
929
955
  params.part = multilineVars.partForNextLine;
930
- params.clef = multilineVars.currentVoice && multilineVars.staves[multilineVars.currentVoice.staffNum].clef !== undefined ? parseCommon.clone(multilineVars.staves[multilineVars.currentVoice.staffNum].clef) : parseCommon.clone(multilineVars.clef);
956
+ params.clef = multilineVars.currentVoice && multilineVars.staves[multilineVars.currentVoice.staffNum].clef !== undefined ? Object.assign({},multilineVars.staves[multilineVars.currentVoice.staffNum].clef) : Object.assign({},multilineVars.clef);
931
957
  var scoreTranspose = multilineVars.currentVoice ? multilineVars.currentVoice.scoreTranspose : 0;
932
958
  params.key = parseKeyVoice.standardKey(multilineVars.key.root+multilineVars.key.acc+multilineVars.key.mode, multilineVars.key.root, multilineVars.key.acc, scoreTranspose);
933
959
  params.key.mode = multilineVars.key.mode;
@@ -997,6 +1023,12 @@ MusicParser.prototype.startNewLine = function() {
997
1023
  params.style = multilineVars.currentVoice.style;
998
1024
  if (multilineVars.currentVoice.transpose)
999
1025
  params.clef.transpose = multilineVars.currentVoice.transpose;
1026
+ params.currentVoice = multilineVars.currentVoice
1027
+ var voices = Object.keys(multilineVars.voices)
1028
+ for (var mv = 0; mv < voices.length; mv++) {
1029
+ if (params.currentVoice.staffNum === multilineVars.voices[voices[mv]].staffNum && params.currentVoice.index === multilineVars.voices[voices[mv]].index)
1030
+ params.currentVoiceName = voices[mv]
1031
+ }
1000
1032
  }
1001
1033
  var isFirstVoice = multilineVars.currentVoice === undefined || (multilineVars.currentVoice.staffNum === 0 && multilineVars.currentVoice.index === 0);
1002
1034
  if (multilineVars.barNumbers === 0 && isFirstVoice && multilineVars.currBarNumber !== 1)