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
@@ -209,6 +209,7 @@ var pitchesToPerc = require('./pitches-to-perc');
209
209
  case "chordprog":
210
210
  case "bassvol":
211
211
  case "chordvol":
212
+ case "gchordbars":
212
213
  chordTrack.paramChange(element)
213
214
  break
214
215
  default:
@@ -347,7 +348,8 @@ var pitchesToPerc = require('./pitches-to-perc');
347
348
  return 0;
348
349
 
349
350
  var volume;
350
- if (nextVolume) {
351
+ // MAE 21 Jun 2024 - This previously wasn't allowing zero volume to be applied
352
+ if (nextVolume != undefined) {
351
353
  volume = nextVolume;
352
354
  nextVolume = undefined;
353
355
  } else if (!doBeatAccents) {
@@ -315,7 +315,7 @@ var parseCommon = require("../parse/abc_common");
315
315
  if (!e) e = voices[voiceNumber].length; // If there wasn't a first ending marker, then we copy everything.
316
316
  // duplicate each of the elements - this has to be a deep copy.
317
317
  for (var z = s; z < e; z++) {
318
- var item = parseCommon.clone(voices[voiceNumber][z]);
318
+ var item = Object.assign({},voices[voiceNumber][z]);
319
319
  if (item.pitches)
320
320
  item.pitches = parseCommon.cloneArray(item.pitches);
321
321
  voices[voiceNumber].push(item);
@@ -389,12 +389,27 @@ var parseCommon = require("../parse/abc_common");
389
389
  break;
390
390
  case "swing":
391
391
  case "gchord":
392
- case "bassprog":
393
- case "chordprog":
394
392
  case "bassvol":
395
393
  case "chordvol":
396
394
  voices[voiceNumber].push({ el_type: elem.cmd, param: elem.params[0] });
397
395
  break;
396
+
397
+ case "bassprog": // MAE 22 May 2024
398
+ case "chordprog": // MAE 22 May 2024
399
+ voices[voiceNumber].push({
400
+ el_type: elem.cmd,
401
+ value: elem.params[0],
402
+ octaveShift: elem.params[1]
403
+ });
404
+ break;
405
+
406
+ // MAE 23 Jun 2024
407
+ case "gchordbars":
408
+ voices[voiceNumber].push({
409
+ el_type: elem.cmd,
410
+ param: elem.params[0]
411
+ });
412
+ break;
398
413
  default:
399
414
  console.log("MIDI seq: midi cmd not handled: ", elem.cmd, elem);
400
415
  }
@@ -17,8 +17,6 @@
17
17
  //
18
18
  // If there is any note in the melody that has a rhythm head, then assume the melody controls the rhythm, so there is no chord added for that entire measure.
19
19
 
20
- var parseCommon = require("../parse/abc_common");
21
-
22
20
  var ChordTrack = function ChordTrack(numVoices, chordsOff, midiOptions, meter) {
23
21
  this.chordTrack = [];
24
22
  this.chordTrackFinished = false;
@@ -34,12 +32,24 @@ var ChordTrack = function ChordTrack(numVoices, chordsOff, midiOptions, meter) {
34
32
  this.meter = meter;
35
33
  this.tempoChangeFactor = 1;
36
34
 
37
- this.bassInstrument = midiOptions.bassprog && midiOptions.bassprog.length === 1 ? midiOptions.bassprog[0] : 0;
38
- this.chordInstrument = midiOptions.chordprog && midiOptions.chordprog.length === 1 ? midiOptions.chordprog[0] : 0;
35
+ // MAE 17 Jun 2024 - To allow for bass and chord instrument octave shifts
36
+ this.bassInstrument = midiOptions.bassprog && midiOptions.bassprog.length >= 1 ? midiOptions.bassprog[0] : 0;
37
+ this.chordInstrument = midiOptions.chordprog && midiOptions.chordprog.length >= 1 ? midiOptions.chordprog[0] : 0;
38
+
39
+ // MAE For octave shifted bass and chords
40
+ this.bassOctaveShift = midiOptions.bassprog && midiOptions.bassprog.length === 2 ? midiOptions.bassprog[1] : 0;
41
+ this.chordOctaveShift = midiOptions.chordprog && midiOptions.chordprog.length === 2 ? midiOptions.chordprog[1] : 0;
42
+
39
43
  this.boomVolume = midiOptions.bassvol && midiOptions.bassvol.length === 1 ? midiOptions.bassvol[0] : 64;
40
44
  this.chickVolume = midiOptions.chordvol && midiOptions.chordvol.length === 1 ? midiOptions.chordvol[0] : 48;
41
45
 
42
- this.overridePattern = midiOptions.gchord ? parseGChord(midiOptions.gchord[0]) : undefined
46
+ // This allows for an initial %%MIDI gchord with no string
47
+ if (midiOptions.gchord && (midiOptions.gchord.length > 0)) {
48
+ this.overridePattern = parseGChord(midiOptions.gchord[0])
49
+ }
50
+ else {
51
+ this.overridePattern = undefined;
52
+ }
43
53
  };
44
54
 
45
55
  ChordTrack.prototype.setMeter = function (meter) {
@@ -64,7 +74,7 @@ ChordTrack.prototype.setRhythmHead = function (isRhythmHead, elem) {
64
74
  if (isRhythmHead) {
65
75
  if (this.lastChord && this.lastChord.chick) {
66
76
  for (var i2 = 0; i2 < this.lastChord.chick.length; i2++) {
67
- var note2 = parseCommon.clone(elem.pitches[0]);
77
+ var note2 = Object.assign({},elem.pitches[0]);
68
78
  note2.actualPitch = this.lastChord.chick[i2];
69
79
  ePitches.push(note2);
70
80
  }
@@ -89,13 +99,32 @@ ChordTrack.prototype.gChordOn = function (element) {
89
99
  ChordTrack.prototype.paramChange = function (element) {
90
100
  switch (element.el_type) {
91
101
  case "gchord":
92
- this.overridePattern = parseGChord(element.param);
102
+ // Skips gchord elements that don't have pattern strings
103
+ if (element.param && element.param.length > 0) {
104
+ this.overridePattern = parseGChord(element.param);
105
+
106
+ // Generate a default duration scale based on the pattern
107
+ //this.gchordduration = generateDefaultDurationScale(element.param);
108
+ } else
109
+ this.overridePattern = undefined;
93
110
  break;
94
111
  case "bassprog":
95
- this.bassInstrument = element.param;
112
+ this.bassInstrument = element.value;
113
+ if ((element.octaveShift != undefined) && (element.octaveShift != null)) {
114
+ this.bassOctaveShift = element.octaveShift;
115
+ }
116
+ else {
117
+ this.bassOctaveShift = 0;
118
+ }
96
119
  break;
97
120
  case "chordprog":
98
- this.chordInstrument = element.param;
121
+ this.chordInstrument = element.value;
122
+ if ((element.octaveShift != undefined) && (element.octaveShift != null)) {
123
+ this.chordOctaveShift = element.octaveShift;
124
+ }
125
+ else {
126
+ this.chordOctaveShift = 0;
127
+ }
99
128
  break;
100
129
  case "bassvol":
101
130
  this.boomVolume = element.param;
@@ -169,6 +198,21 @@ ChordTrack.prototype.interpretChord = function (name) {
169
198
  while (chordTranspose > 8)
170
199
  chordTranspose -= 12;
171
200
  bass += chordTranspose;
201
+
202
+ // MAE 31 Aug 2024 - For visual transpose backup range issue
203
+ // If transposed below A or above G, bring it back in the normal backup range
204
+ if (bass < 33){
205
+ bass += 12;
206
+ }
207
+ else if (bass > 44){
208
+ bass -= 12;
209
+ }
210
+
211
+ // MAE 17 Jun 2024 - Supporting octave shifted bass and chords
212
+ var unshiftedBass = bass;
213
+
214
+ bass += this.bassOctaveShift * 12;
215
+
172
216
  var bass2 = bass - 5; // The alternating bass is a 4th below
173
217
  var chick;
174
218
  if (name.length === 1)
@@ -176,16 +220,18 @@ ChordTrack.prototype.interpretChord = function (name) {
176
220
  var remaining = name.substring(1);
177
221
  var acc = remaining.substring(0, 1);
178
222
  if (acc === 'b' || acc === '♭') {
223
+ unshiftedBass--;
179
224
  bass--;
180
225
  bass2--;
181
226
  remaining = remaining.substring(1);
182
227
  } else if (acc === '#' || acc === '♯') {
228
+ unshiftedBass++;
183
229
  bass++;
184
230
  bass2++;
185
231
  remaining = remaining.substring(1);
186
232
  }
187
233
  var arr = remaining.split('/');
188
- chick = this.chordNotes(bass, arr[0]);
234
+ chick = this.chordNotes(unshiftedBass, arr[0]);
189
235
  // If the 5th is altered then the bass is altered. Normally the bass is 7 from the root, so adjust if it isn't.
190
236
  if (chick.length >= 3) {
191
237
  var fifth = chick[2] - chick[0];
@@ -198,6 +244,10 @@ ChordTrack.prototype.interpretChord = function (name) {
198
244
  var bassAcc = arr[1].substring(1);
199
245
  var bassShift = { '#': 1, '♯': 1, 'b': -1, '♭': -1 }[bassAcc] || 0;
200
246
  bass = this.basses[arr[1].substring(0, 1)] + bassShift + chordTranspose;
247
+
248
+ // MAE 22 May 2024 - Supporting octave shifted bass and chords
249
+ bass += this.bassOctaveShift * 12;
250
+
201
251
  bass2 = bass;
202
252
  }
203
253
  }
@@ -215,6 +265,10 @@ ChordTrack.prototype.chordNotes = function (bass, modifier) {
215
265
  intervals = this.chordIntervals.M;
216
266
  }
217
267
  bass += 12; // the chord is an octave above the bass note.
268
+
269
+ // MAE 22 May 2024 - For chick octave shift
270
+ bass += (this.chordOctaveShift * 12);
271
+
218
272
  var notes = [];
219
273
  for (var i = 0; i < intervals.length; i++) {
220
274
  notes.push(bass + intervals[i]);
@@ -338,25 +392,34 @@ function resolvePitch(currentChord, type, firstBoom, newBass) {
338
392
  ret.push(firstBoom ? currentChord.boom : currentChord.boom2)
339
393
  else if (newBass)
340
394
  ret.push(currentChord.boom)
395
+ var numChordNotes = currentChord.chick.length
341
396
  if (type.indexOf('chick') >= 0) {
342
- for (var i = 0; i < currentChord.chick.length; i++)
397
+ for (var i = 0; i < numChordNotes; i++)
343
398
  ret.push(currentChord.chick[i])
344
399
  }
345
400
  switch (type) {
346
401
  case 'DO': ret.push(currentChord.chick[0]); break;
347
402
  case 'MI': ret.push(currentChord.chick[1]); break;
348
- case 'SOL': ret.push(currentChord.chick[2]); break;
349
- case 'TI': currentChord.chick.length > 3 ? ret.push(currentChord.chick[2]) : ret.push(currentChord.chick[0]+12); break;
350
- case 'TOP': currentChord.chick.length > 4 ? ret.push(currentChord.chick[2]) : ret.push(currentChord.chick[1]+12); break;
403
+ case 'SOL': ret.push(extractNote(currentChord,2)); break;
404
+ case 'TI': ret.push(extractNote(currentChord,3)); break;
405
+ case 'TOP': ret.push(extractNote(currentChord,4)); break;
351
406
  case 'do': ret.push(currentChord.chick[0]+12); break;
352
407
  case 'mi': ret.push(currentChord.chick[1]+12); break;
353
- case 'sol': ret.push(currentChord.chick[2]+12); break;
354
- case 'ti': currentChord.chick.length > 3 ? ret.push(currentChord.chick[2]+12) : ret.push(currentChord.chick[0]+24); break;
355
- case 'top': currentChord.chick.length > 4 ? ret.push(currentChord.chick[2]+12) : ret.push(currentChord.chick[1]+24); break;
408
+ case 'sol': ret.push(extractNote(currentChord,2)+12); break;
409
+ case 'ti': ret.push(extractNote(currentChord,3)+12); break;
410
+ case 'top': ret.push(extractNote(currentChord,4)+12); break;
356
411
  }
357
412
  return ret
358
413
  }
359
414
 
415
+ function extractNote(chord, index) {
416
+ // This creates an arpeggio note no matter how many notes are in the chord - if it runs out of notes it continues in the next octave
417
+ var octave = Math.floor(index / chord.chick.length)
418
+ var note = chord.chick[index % chord.chick.length]
419
+ //console.log(chord.chick, {index, octave, note}, index % chord.chick.length)
420
+ return note + octave * 12
421
+ }
422
+
360
423
  function parseGChord(gchord) {
361
424
  // TODO-PER: The spec is more complicated than this but for now this will not try to do anything with error cases like the wrong number of beats.
362
425
  var pattern = []
@@ -532,7 +595,12 @@ ChordTrack.prototype.chordIntervals = {
532
595
  'maj7#5#11': [0, 4, 8, 11, 18],
533
596
  '9(#5)': [0, 4, 8, 10, 14],
534
597
  '13(#5)': [0, 4, 8, 10, 14, 21],
535
- '13#5': [0, 4, 8, 10, 14, 21]
598
+ '13#5': [0, 4, 8, 10, 14, 21],
599
+ // MAE Power chords added 10 April 2024
600
+ '5': [0, 7],
601
+ '5(8)': [0, 7, 12],
602
+ '5add8': [0, 7, 12]
603
+
536
604
  };
537
605
 
538
606
  ChordTrack.prototype.rhythmPatterns = {
@@ -547,8 +615,12 @@ ChordTrack.prototype.rhythmPatterns = {
547
615
  "6/4": ['boom', '', 'chick', '', 'boom', '', 'chick', '', 'boom', '', 'chick', ''],
548
616
 
549
617
  "3/8": ['boom', '', 'chick'],
618
+ "5/8": ['boom', 'chick', 'chick', 'boom', 'chick'],
550
619
  "6/8": ['boom', '', 'chick', 'boom', '', 'chick'],
620
+ "7/8": ['boom', 'chick', 'chick', 'boom', 'chick', 'boom', 'chick'],
551
621
  "9/8": ['boom', '', 'chick', 'boom', '', 'chick', 'boom', '', 'chick'],
622
+ "10/8": ['boom', 'chick', 'chick', 'boom', 'chick', 'chick', 'boom', 'chick', 'boom', 'chick'],
623
+ "11/8": ['boom', 'chick', 'chick', 'boom', 'chick', 'chick', 'boom', 'chick', 'boom', 'chick', 'chick'],
552
624
  "12/8": ['boom', '', 'chick', 'boom', '', 'chick', 'boom', '', 'chick', 'boom', '', 'chick'],
553
625
  };
554
626
 
@@ -1,7 +1,6 @@
1
1
  var supportsAudio = require('./supports-audio');
2
2
  var registerAudioContext = require('./register-audio-context');
3
3
  var activeAudioContext = require('./active-audio-context');
4
- var parseCommon = require('../parse/abc_common');
5
4
 
6
5
  var loopImage = require('./images/loop.svg.js');
7
6
  var playImage = require('./images/play.svg.js');
@@ -23,7 +22,7 @@ function CreateSynthControl(parent, options) {
23
22
  self.parent = parent;
24
23
  self.options = {};
25
24
  if (options)
26
- self.options = parseCommon.clone(options);
25
+ self.options = Object.assign({},options);
27
26
 
28
27
  // This can be called in the following cases:
29
28
  // AC already registered and not suspended
@@ -0,0 +1,184 @@
1
+ /*
2
+ * Tablature Plugins
3
+ * tablature are defined dynamically and registered inside abcjs
4
+ * by calling abcTablatures.register(plugin)
5
+ * where plugin represents a plugin instance
6
+ *
7
+ */
8
+
9
+ // This is the only entry point to the tablatures. It is called both after parsing a tune and just before engraving
10
+
11
+ var TabString = require('./instruments/tab-string');
12
+
13
+ /* extend the table below when adding a new instrument plugin */
14
+
15
+ // Existing tab classes
16
+ var pluginTab = {
17
+ 'violin': { name: 'StringTab', defaultTuning: ['G,', 'D', 'A', 'e'], isTabBig: false, tabSymbolOffset: 0 },
18
+ 'fiddle': { name: 'StringTab', defaultTuning: ['G,', 'D', 'A', 'e'], isTabBig: false, tabSymbolOffset: 0 },
19
+ 'mandolin': { name: 'StringTab', defaultTuning: ['G,', 'D', 'A', 'e'], isTabBig: false, tabSymbolOffset: 0 },
20
+ 'guitar': { name: 'StringTab', defaultTuning: ['E,', 'A,', 'D', 'G', 'B', 'e'], isTabBig: true, tabSymbolOffset: 0 },
21
+ 'fiveString': { name: 'StringTab', defaultTuning: ['C,', 'G,', 'D', 'A', 'e'], isTabBig: false, tabSymbolOffset: -.95 },
22
+ };
23
+
24
+ var abcTablatures = {
25
+
26
+ inited: false,
27
+ plugins: {},
28
+
29
+
30
+ /**
31
+ * to be called once per plugin for registration
32
+ * @param {*} plugin
33
+ */
34
+ register: function (plugin) {
35
+ var name = plugin.name;
36
+ var tablature = plugin.tablature;
37
+ this.plugins[name] = tablature;
38
+ },
39
+
40
+ setError: function (tune, msg) {
41
+ if (tune.warnings) {
42
+ tune.warning.push(msg);
43
+ } else {
44
+ tune.warnings = [msg];
45
+ }
46
+ },
47
+
48
+ /**
49
+ * handle params for current processed score
50
+ * @param {*} tune current tune
51
+ * @param {*} tuneNumber number in tune list
52
+ * @param {*} params params to be processed for tablature
53
+ * @return prepared tablatures plugin instances for current tune
54
+ */
55
+ preparePlugins: function (tune, tuneNumber, params) {
56
+ // Called after parsing a tune and before engraving it
57
+ if (!this.inited) {
58
+ // TODO-PER: I don't think this is needed - the plugin array can be hard coded, right?
59
+ this.register(new TabString());
60
+ this.inited = true;
61
+ }
62
+ var returned = null;
63
+ var nbPlugins = 0;
64
+ if (params.tablature) {
65
+ // validate requested plugins
66
+ var tabs = params.tablature;
67
+ returned = [];
68
+ for (var ii = 0; ii < tabs.length; ii++) {
69
+ var args = tabs[ii];
70
+ var instrument = args['instrument'];
71
+ if (instrument == null) {
72
+ this.setError(tune, "tablature 'instrument' is missing");
73
+ return returned;
74
+ }
75
+ var tabName = pluginTab[instrument];
76
+ var plugin = null;
77
+ if (tabName) {
78
+ plugin = this.plugins[tabName.name];
79
+ }
80
+ if (plugin) {
81
+ if (params.visualTranspose != 0) {
82
+ // populate transposition request to tabs
83
+ args.visualTranspose = params.visualTranspose;
84
+ }
85
+ args.abcSrc = params.tablature.abcSrc;
86
+ var pluginInstance = {
87
+ classz: plugin,
88
+ tuneNumber: tuneNumber,
89
+ params: args,
90
+ instance: null,
91
+ tabType: tabName,
92
+ };
93
+ // proceed with tab plugin init
94
+ // plugin.init(tune, tuneNumber, args, ii);
95
+ returned.push(pluginInstance);
96
+ nbPlugins++;
97
+ } else if (instrument === '') {
98
+ // create a placeholder - there is no tab for this staff
99
+ returned.push(null)
100
+ } else {
101
+ // unknown tab plugin
102
+ //this.emit_error('Undefined tablature plugin: ' + tabName)
103
+ this.setError(tune, 'Undefined tablature plugin: ' + instrument);
104
+ return returned;
105
+ }
106
+ }
107
+ }
108
+ return returned;
109
+ },
110
+
111
+ /**
112
+ * Call requested plugin
113
+ * @param {*} renderer
114
+ * @param {*} abcTune
115
+ */
116
+ layoutTablatures: function layoutTablatures(renderer, abcTune) {
117
+ var tabs = abcTune.tablatures;
118
+
119
+ // chack tabs request for each staffs
120
+ var staffLineCount = 0;
121
+
122
+ // Clear the suppression flag
123
+ if (tabs && (tabs.length > 0)) {
124
+ var nTabs = tabs.length;
125
+ for (var kk = 0; kk < nTabs; ++kk) {
126
+ if (tabs[kk] && tabs[kk].params.firstStaffOnly) {
127
+ tabs[kk].params.suppress = false;
128
+ }
129
+ }
130
+ }
131
+
132
+ for (var ii = 0; ii < abcTune.lines.length; ii++) {
133
+ var line = abcTune.lines[ii];
134
+
135
+ if (line.staff) {
136
+ staffLineCount++;
137
+ }
138
+
139
+ // MAE 27Nov2023
140
+ // If tab param "firstStaffOnly", remove the tab label after the first staff
141
+ if (staffLineCount > 1) {
142
+ if (tabs && (tabs.length > 0)) {
143
+ var nTabs = tabs.length;
144
+ for (var kk = 0; kk < nTabs; ++kk) {
145
+ if (tabs[kk].params.firstStaffOnly) {
146
+ // Set the staff draw suppression flag
147
+ tabs[kk].params.suppress = true;
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ var curStaff = line.staff;
154
+ if (curStaff) {
155
+ var maxStaves = curStaff.length
156
+ for (var jj = 0; jj < curStaff.length; jj++) {
157
+
158
+ if (tabs[jj] && jj < maxStaves) {
159
+ // tablature requested for staff
160
+ var tabPlugin = tabs[jj];
161
+ if (tabPlugin.instance == null) {
162
+ //console.log("★★★★ Tab Init line: " + ii + " staff: " + jj)
163
+ tabPlugin.instance = new tabPlugin.classz();
164
+ // plugin.init(tune, tuneNumber, args, ii);
165
+ // call initer first
166
+ tabPlugin.instance.init(abcTune,
167
+ tabPlugin.tuneNumber,
168
+ tabPlugin.params,
169
+ tabPlugin.tabType
170
+ );
171
+ }
172
+ // render next
173
+ //console.log("★★★★ Tab Render line: " + ii + " staff: " + jj)
174
+ tabPlugin.instance.render(renderer, line, jj);
175
+ }
176
+ }
177
+ }
178
+ }
179
+ },
180
+
181
+ };
182
+
183
+
184
+ module.exports = abcTablatures;