abcjs 6.4.0 → 6.4.2
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.
- package/RELEASE.md +42 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +1252 -1139
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/package.json +1 -1
- package/src/api/abc_tunebook.js +1 -2
- package/src/api/abc_tunebook_svg.js +0 -1
- package/src/midi/abc_midi_create.js +22 -7
- package/src/parse/abc_common.js +3 -11
- package/src/parse/abc_parse.js +1 -1
- package/src/parse/abc_parse_directive.js +44 -3
- package/src/parse/abc_parse_header.js +6 -4
- package/src/parse/abc_parse_key_voice.js +10 -6
- package/src/parse/abc_parse_music.js +22 -5
- package/src/parse/tune-builder.js +675 -643
- package/src/synth/abc_midi_flattener.js +3 -1
- package/src/synth/abc_midi_sequencer.js +18 -3
- package/src/synth/chord-track.js +86 -20
- package/src/synth/create-synth-control.js +1 -2
- package/src/synth/create-synth.js +1 -1
- package/src/tablatures/abc_tablatures.js +184 -0
- package/src/tablatures/instruments/string-patterns.js +266 -268
- package/src/tablatures/instruments/string-tablature.js +38 -35
- package/src/tablatures/instruments/tab-note.js +186 -181
- package/src/tablatures/instruments/tab-notes.js +30 -35
- package/src/tablatures/instruments/tab-string.js +43 -25
- package/src/tablatures/render/tab-absolute-elements.js +303 -0
- package/src/tablatures/render/tab-renderer.js +244 -0
- package/src/test/abc_parser_lint.js +2 -3
- package/src/write/creation/abstract-engraver.js +1 -1
- package/src/write/engraver-controller.js +1 -1
- package/temp.txt +9 -0
- package/types/index.d.ts +1 -1
- package/version.js +1 -1
- package/src/api/abc_tablatures.js +0 -184
- package/src/tablatures/instruments/tab-string-patterns.js +0 -23
- package/src/tablatures/tab-absolute-elements.js +0 -301
- package/src/tablatures/tab-common.js +0 -29
- 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
|
-
|
|
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 =
|
|
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
|
}
|
package/src/synth/chord-track.js
CHANGED
|
@@ -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
|
-
|
|
38
|
-
this.
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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,12 @@ ChordTrack.prototype.interpretChord = function (name) {
|
|
|
169
198
|
while (chordTranspose > 8)
|
|
170
199
|
chordTranspose -= 12;
|
|
171
200
|
bass += chordTranspose;
|
|
201
|
+
|
|
202
|
+
// MAE 17 Jun 2024 - Supporting octave shifted bass and chords
|
|
203
|
+
var unshiftedBass = bass;
|
|
204
|
+
|
|
205
|
+
bass += this.bassOctaveShift * 12;
|
|
206
|
+
|
|
172
207
|
var bass2 = bass - 5; // The alternating bass is a 4th below
|
|
173
208
|
var chick;
|
|
174
209
|
if (name.length === 1)
|
|
@@ -176,16 +211,18 @@ ChordTrack.prototype.interpretChord = function (name) {
|
|
|
176
211
|
var remaining = name.substring(1);
|
|
177
212
|
var acc = remaining.substring(0, 1);
|
|
178
213
|
if (acc === 'b' || acc === '♭') {
|
|
214
|
+
unshiftedBass--;
|
|
179
215
|
bass--;
|
|
180
216
|
bass2--;
|
|
181
217
|
remaining = remaining.substring(1);
|
|
182
218
|
} else if (acc === '#' || acc === '♯') {
|
|
219
|
+
unshiftedBass++;
|
|
183
220
|
bass++;
|
|
184
221
|
bass2++;
|
|
185
222
|
remaining = remaining.substring(1);
|
|
186
223
|
}
|
|
187
224
|
var arr = remaining.split('/');
|
|
188
|
-
chick = this.chordNotes(
|
|
225
|
+
chick = this.chordNotes(unshiftedBass, arr[0]);
|
|
189
226
|
// 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
227
|
if (chick.length >= 3) {
|
|
191
228
|
var fifth = chick[2] - chick[0];
|
|
@@ -198,6 +235,10 @@ ChordTrack.prototype.interpretChord = function (name) {
|
|
|
198
235
|
var bassAcc = arr[1].substring(1);
|
|
199
236
|
var bassShift = { '#': 1, '♯': 1, 'b': -1, '♭': -1 }[bassAcc] || 0;
|
|
200
237
|
bass = this.basses[arr[1].substring(0, 1)] + bassShift + chordTranspose;
|
|
238
|
+
|
|
239
|
+
// MAE 22 May 2024 - Supporting octave shifted bass and chords
|
|
240
|
+
bass += this.bassOctaveShift * 12;
|
|
241
|
+
|
|
201
242
|
bass2 = bass;
|
|
202
243
|
}
|
|
203
244
|
}
|
|
@@ -215,6 +256,10 @@ ChordTrack.prototype.chordNotes = function (bass, modifier) {
|
|
|
215
256
|
intervals = this.chordIntervals.M;
|
|
216
257
|
}
|
|
217
258
|
bass += 12; // the chord is an octave above the bass note.
|
|
259
|
+
|
|
260
|
+
// MAE 22 May 2024 - For chick octave shift
|
|
261
|
+
bass += (this.chordOctaveShift * 12);
|
|
262
|
+
|
|
218
263
|
var notes = [];
|
|
219
264
|
for (var i = 0; i < intervals.length; i++) {
|
|
220
265
|
notes.push(bass + intervals[i]);
|
|
@@ -284,8 +329,11 @@ ChordTrack.prototype.resolveChords = function (startTime, endTime) {
|
|
|
284
329
|
firstBoom = true
|
|
285
330
|
var type = thisPattern[p]
|
|
286
331
|
var isBoom = type.indexOf('boom') >= 0
|
|
287
|
-
// If we changed chords at a time when we're not expecting a bass note, then add an extra bass note in.
|
|
288
|
-
var newBass = !isBoom &&
|
|
332
|
+
// If we changed chords at a time when we're not expecting a bass note, then add an extra bass note in if the first thing in the pattern is a bass note.
|
|
333
|
+
var newBass = !isBoom &&
|
|
334
|
+
p !== 0 &&
|
|
335
|
+
thisPattern[0].indexOf('boom') >= 0 &&
|
|
336
|
+
(!currentChordsExpanded[p-1] || currentChordsExpanded[p-1].boom !== currentChordsExpanded[p].boom)
|
|
289
337
|
var pitches = resolvePitch(currentChordsExpanded[p], type, firstBoom, newBass)
|
|
290
338
|
if (isBoom)
|
|
291
339
|
firstBoom = false
|
|
@@ -335,25 +383,34 @@ function resolvePitch(currentChord, type, firstBoom, newBass) {
|
|
|
335
383
|
ret.push(firstBoom ? currentChord.boom : currentChord.boom2)
|
|
336
384
|
else if (newBass)
|
|
337
385
|
ret.push(currentChord.boom)
|
|
386
|
+
var numChordNotes = currentChord.chick.length
|
|
338
387
|
if (type.indexOf('chick') >= 0) {
|
|
339
|
-
for (var i = 0; i <
|
|
388
|
+
for (var i = 0; i < numChordNotes; i++)
|
|
340
389
|
ret.push(currentChord.chick[i])
|
|
341
390
|
}
|
|
342
391
|
switch (type) {
|
|
343
392
|
case 'DO': ret.push(currentChord.chick[0]); break;
|
|
344
393
|
case 'MI': ret.push(currentChord.chick[1]); break;
|
|
345
|
-
case 'SOL': ret.push(currentChord
|
|
346
|
-
case 'TI':
|
|
347
|
-
case 'TOP':
|
|
394
|
+
case 'SOL': ret.push(extractNote(currentChord,2)); break;
|
|
395
|
+
case 'TI': ret.push(extractNote(currentChord,3)); break;
|
|
396
|
+
case 'TOP': ret.push(extractNote(currentChord,4)); break;
|
|
348
397
|
case 'do': ret.push(currentChord.chick[0]+12); break;
|
|
349
398
|
case 'mi': ret.push(currentChord.chick[1]+12); break;
|
|
350
|
-
case 'sol': ret.push(currentChord
|
|
351
|
-
case 'ti':
|
|
352
|
-
case 'top':
|
|
399
|
+
case 'sol': ret.push(extractNote(currentChord,2)+12); break;
|
|
400
|
+
case 'ti': ret.push(extractNote(currentChord,3)+12); break;
|
|
401
|
+
case 'top': ret.push(extractNote(currentChord,4)+12); break;
|
|
353
402
|
}
|
|
354
403
|
return ret
|
|
355
404
|
}
|
|
356
405
|
|
|
406
|
+
function extractNote(chord, index) {
|
|
407
|
+
// 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
|
|
408
|
+
var octave = Math.floor(index / chord.chick.length)
|
|
409
|
+
var note = chord.chick[index % chord.chick.length]
|
|
410
|
+
//console.log(chord.chick, {index, octave, note}, index % chord.chick.length)
|
|
411
|
+
return note + octave * 12
|
|
412
|
+
}
|
|
413
|
+
|
|
357
414
|
function parseGChord(gchord) {
|
|
358
415
|
// 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.
|
|
359
416
|
var pattern = []
|
|
@@ -529,7 +586,12 @@ ChordTrack.prototype.chordIntervals = {
|
|
|
529
586
|
'maj7#5#11': [0, 4, 8, 11, 18],
|
|
530
587
|
'9(#5)': [0, 4, 8, 10, 14],
|
|
531
588
|
'13(#5)': [0, 4, 8, 10, 14, 21],
|
|
532
|
-
'13#5': [0, 4, 8, 10, 14, 21]
|
|
589
|
+
'13#5': [0, 4, 8, 10, 14, 21],
|
|
590
|
+
// MAE Power chords added 10 April 2024
|
|
591
|
+
'5': [0, 7],
|
|
592
|
+
'5(8)': [0, 7, 12],
|
|
593
|
+
'5add8': [0, 7, 12]
|
|
594
|
+
|
|
533
595
|
};
|
|
534
596
|
|
|
535
597
|
ChordTrack.prototype.rhythmPatterns = {
|
|
@@ -544,8 +606,12 @@ ChordTrack.prototype.rhythmPatterns = {
|
|
|
544
606
|
"6/4": ['boom', '', 'chick', '', 'boom', '', 'chick', '', 'boom', '', 'chick', ''],
|
|
545
607
|
|
|
546
608
|
"3/8": ['boom', '', 'chick'],
|
|
609
|
+
"5/8": ['boom', 'chick', 'chick', 'boom', 'chick'],
|
|
547
610
|
"6/8": ['boom', '', 'chick', 'boom', '', 'chick'],
|
|
611
|
+
"7/8": ['boom', 'chick', 'chick', 'boom', 'chick', 'boom', 'chick'],
|
|
548
612
|
"9/8": ['boom', '', 'chick', 'boom', '', 'chick', 'boom', '', 'chick'],
|
|
613
|
+
"10/8": ['boom', 'chick', 'chick', 'boom', 'chick', 'chick', 'boom', 'chick', 'boom', 'chick'],
|
|
614
|
+
"11/8": ['boom', 'chick', 'chick', 'boom', 'chick', 'chick', 'boom', 'chick', 'boom', 'chick', 'chick'],
|
|
549
615
|
"12/8": ['boom', '', 'chick', 'boom', '', 'chick', 'boom', '', 'chick', 'boom', '', 'chick'],
|
|
550
616
|
};
|
|
551
617
|
|
|
@@ -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 =
|
|
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
|
|
@@ -127,7 +127,7 @@ function CreateSynth() {
|
|
|
127
127
|
self.sequenceCallback = params.sequenceCallback;
|
|
128
128
|
self.callbackContext = params.callbackContext;
|
|
129
129
|
self.onEnded = params.onEnded;
|
|
130
|
-
self.meterFraction = options.visualObj.getMeterFraction()
|
|
130
|
+
self.meterFraction = options.visualObj ? options.visualObj.getMeterFraction() : {den: 1} // If we are given a sequence instead of a regular visual obj, then don't do the swing
|
|
131
131
|
|
|
132
132
|
var allNotes = {};
|
|
133
133
|
var cached = [];
|
|
@@ -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;
|