abcjs 6.5.2 → 6.6.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.
- package/README.md +6 -0
- package/RELEASE.md +26 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +1177 -170
- 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_timing_callbacks.js +88 -13
- package/src/const/relative-major.js +24 -19
- package/src/data/abc_tune.js +12 -1
- package/src/data/deline-tune.js +1 -1
- package/src/midi/abc_midi_create.js +2 -2
- package/src/parse/abc_parse.js +20 -0
- package/src/parse/chord-grid.js +364 -0
- package/src/parse/tune-builder.js +1 -1
- package/src/str/output.js +98 -62
- package/src/synth/abc_midi_sequencer.js +20 -30
- package/src/synth/create-synth.js +1 -1
- package/src/synth/repeats.js +200 -0
- package/src/write/creation/abstract-engraver.js +4 -1
- package/src/write/draw/chord-grid.js +252 -0
- package/src/write/draw/draw.js +48 -35
- package/src/write/draw/text.js +1 -1
- package/src/write/engraver-controller.js +4 -2
- package/src/write/layout/beam.js +16 -1
- package/src/write/renderer.js +4 -0
- package/src/write/svg.js +8 -1
- package/types/index.d.ts +25 -0
- package/version.js +1 -1
package/dist/abcjs-basic.js
CHANGED
|
@@ -213,6 +213,7 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
|
|
|
213
213
|
self.lineEndCallback = params.lineEndCallback; // This is called when the end of a line is approaching.
|
|
214
214
|
self.lineEndAnticipation = params.lineEndAnticipation ? parseInt(params.lineEndAnticipation, 10) : 0; // How many milliseconds before the end should the call happen.
|
|
215
215
|
self.beatSubdivisions = params.beatSubdivisions ? parseInt(params.beatSubdivisions, 10) : 1; // how many callbacks per beat is desired.
|
|
216
|
+
if (!self.beatSubdivisions) self.beatSubdivisions = 1;
|
|
216
217
|
self.joggerTimer = null;
|
|
217
218
|
self.replaceTarget = function (newTarget) {
|
|
218
219
|
self.noteTimings = newTarget.setTiming(self.qpm, self.extraMeasuresAtBeginning);
|
|
@@ -235,18 +236,96 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
|
|
|
235
236
|
// noteTimings contains an array of events sorted by time. Events that happen at the same time are in the same element of the array.
|
|
236
237
|
self.millisecondsPerBeat = 1000 / (self.qpm / 60) / self.beatSubdivisions;
|
|
237
238
|
self.lastMoment = self.noteTimings[self.noteTimings.length - 1].milliseconds;
|
|
238
|
-
|
|
239
|
+
|
|
240
|
+
// For irregular time sigs that specify the beat divisions (that is, something like `M: 2+3/8`)
|
|
241
|
+
// Then the beat is not a regular pulse. To keep most of this logic easy, the beat will be specified
|
|
242
|
+
// as half as long, but the callback will happen according to the pattern. That is,
|
|
243
|
+
// for bpm=60 at the above time signature, internally the beat callback will happen at 120 bpm, but
|
|
244
|
+
// the callback function will be called at 2 sub beats, then 3 sub beats, etc.
|
|
245
|
+
// The beat number will be an integer for all of them and count up by one each time.
|
|
246
|
+
var meter = newTarget.getMeter();
|
|
247
|
+
var irregularMeter = '';
|
|
248
|
+
if (meter && meter.type === "specified" && meter.value && meter.value.length > 0 && meter.value[0].num.indexOf('+') > 0) irregularMeter = meter.value[0].num;
|
|
249
|
+
// for subdivisions = 1, then this should contain only whole numbers and the callbacks are irregular
|
|
250
|
+
// for subdivisions = 2, then this should be 1/8 notes. The beats should be something like: 0, 0.5, 1, 1.33, 1.66 2 (For M:2+3/8)
|
|
251
|
+
// etc for more subdivisions - they are just multiplied
|
|
252
|
+
self.beatStarts = [];
|
|
253
|
+
if (irregularMeter) {
|
|
254
|
+
var measureLength = self.noteTimings[self.noteTimings.length - 1].millisecondsPerMeasure;
|
|
255
|
+
var numMeasures = self.lastMoment / measureLength;
|
|
256
|
+
var parts = irregularMeter.split("+");
|
|
257
|
+
for (var i = 0; i < parts.length; i++) {
|
|
258
|
+
parts[i] = parseInt(parts[i], 10) / 2;
|
|
259
|
+
} // since we count a beat as a quarter note, but these numbers refer to 1/8 notes, we convert the beat length
|
|
260
|
+
var currentTs = 0;
|
|
261
|
+
var beatNumber = 0;
|
|
262
|
+
// For the input: parts = [ 1, 1.5 ] and beatSubdivisions = 2
|
|
263
|
+
// beatNumbers = 0 0.5 1 1.33 1.67 2 2.5 3 3.33 3.67 ...
|
|
264
|
+
// when part=1 then there is 1 extra sub beat and it is 0.5
|
|
265
|
+
// when part=1.5 then there are 2 extra sub beats at 0.33 and 0.67
|
|
266
|
+
//
|
|
267
|
+
// beatSubdivision | numFor1 | numFor1.5
|
|
268
|
+
// 2 | 2 | 3
|
|
269
|
+
// 3 | 3 | 4.5
|
|
270
|
+
// 4 | 4 | 6
|
|
271
|
+
for (var measureNumber = 0; measureNumber < numMeasures; measureNumber++) {
|
|
272
|
+
var measureStartTs = measureNumber * measureLength;
|
|
273
|
+
var subBeatCounter = 0;
|
|
274
|
+
for (var kk = 0; kk < parts.length; kk++) {
|
|
275
|
+
var beatLength = parts[kk]; // This is either 1 or 1.5 (how many quarter notes in this beat)
|
|
276
|
+
if (self.beatSubdivisions === 1) {
|
|
277
|
+
if (self.beatSubdivisions === 1) if (currentTs < self.lastMoment) {
|
|
278
|
+
self.beatStarts.push({
|
|
279
|
+
b: beatNumber,
|
|
280
|
+
ts: currentTs
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
currentTs += beatLength * self.millisecondsPerBeat;
|
|
284
|
+
} else {
|
|
285
|
+
var numDivisions = beatLength * self.beatSubdivisions;
|
|
286
|
+
for (var k = 0; k < Math.floor(numDivisions); k++) {
|
|
287
|
+
var subBeat = k / numDivisions;
|
|
288
|
+
var ts = Math.round(measureStartTs + subBeatCounter * self.millisecondsPerBeat);
|
|
289
|
+
if (ts < self.lastMoment) {
|
|
290
|
+
self.beatStarts.push({
|
|
291
|
+
b: beatNumber + subBeat,
|
|
292
|
+
ts: ts
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
subBeatCounter++;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
beatNumber++;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
self.beatStarts.push({
|
|
302
|
+
b: numMeasures * parts.length,
|
|
303
|
+
ts: self.lastMoment
|
|
304
|
+
});
|
|
305
|
+
self.totalBeats = self.beatStarts.length;
|
|
306
|
+
} else {
|
|
307
|
+
self.totalBeats = Math.round(self.lastMoment / self.millisecondsPerBeat);
|
|
308
|
+
// Add one so the last beat is the last moment
|
|
309
|
+
for (var j = 0; j < self.totalBeats + 1; j++) {
|
|
310
|
+
self.beatStarts.push({
|
|
311
|
+
b: j / self.beatSubdivisions,
|
|
312
|
+
ts: Math.round(j * self.millisecondsPerBeat)
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
//console.log({lastMoment: self.lastMoment, beatStarts: self.beatStarts})
|
|
239
317
|
};
|
|
318
|
+
|
|
240
319
|
self.replaceTarget(target);
|
|
241
320
|
self.doTiming = function (timestamp) {
|
|
242
321
|
// This is called 60 times a second, that is, every 16 msecs.
|
|
243
322
|
//console.log("doTiming", timestamp, timestamp-self.lastTimestamp);
|
|
244
323
|
if (self.lastTimestamp === timestamp) return; // If there are multiple seeks or other calls, then we can easily get multiple callbacks for the same instant.
|
|
245
324
|
self.lastTimestamp = timestamp;
|
|
246
|
-
if (!self.startTime) {
|
|
247
|
-
self.startTime = timestamp;
|
|
248
|
-
}
|
|
249
325
|
if (!self.isPaused && self.isRunning) {
|
|
326
|
+
if (!self.startTime) {
|
|
327
|
+
self.startTime = timestamp;
|
|
328
|
+
}
|
|
250
329
|
self.currentTime = timestamp - self.startTime;
|
|
251
330
|
self.currentTime += 16; // Add a little slop because this function isn't called exactly.
|
|
252
331
|
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
|
|
@@ -270,14 +349,16 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
|
|
|
270
349
|
}
|
|
271
350
|
if (self.currentTime < self.lastMoment) {
|
|
272
351
|
requestAnimationFrame(self.doTiming);
|
|
273
|
-
if (self.currentBeat
|
|
352
|
+
if (self.currentBeat < self.beatStarts.length && self.beatStarts[self.currentBeat].ts <= self.currentTime) {
|
|
274
353
|
var ret = self.doBeatCallback(timestamp);
|
|
354
|
+
self.currentBeat++;
|
|
275
355
|
if (ret !== null) self.currentTime = ret;
|
|
276
356
|
}
|
|
277
357
|
} else if (self.currentBeat <= self.totalBeats) {
|
|
278
358
|
// Because of timing issues (for instance, if the browser tab isn't active), the beat callbacks might not have happened when they are supposed to. To keep the client programs from having to deal with that, this will keep calling the loop until all of them have been sent.
|
|
279
359
|
if (self.beatCallback) {
|
|
280
360
|
var ret2 = self.doBeatCallback(timestamp);
|
|
361
|
+
self.currentBeat++;
|
|
281
362
|
if (ret2 !== null) self.currentTime = ret2;
|
|
282
363
|
requestAnimationFrame(self.doTiming);
|
|
283
364
|
}
|
|
@@ -362,11 +443,13 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
|
|
|
362
443
|
};
|
|
363
444
|
}
|
|
364
445
|
var thisStartTime = self.startTime; // the beat callback can call seek and change the position from beneath us.
|
|
365
|
-
self.beatCallback(self.
|
|
446
|
+
self.beatCallback(self.beatStarts[self.currentBeat].b, self.totalBeats / self.beatSubdivisions, self.lastMoment, position, debugInfo);
|
|
366
447
|
if (thisStartTime !== self.startTime) {
|
|
367
448
|
return timestamp - self.startTime;
|
|
368
|
-
} else
|
|
449
|
+
} // else
|
|
450
|
+
// self.currentBeat++;
|
|
369
451
|
}
|
|
452
|
+
|
|
370
453
|
return null;
|
|
371
454
|
};
|
|
372
455
|
|
|
@@ -457,7 +540,6 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
|
|
|
457
540
|
if (!self.isRunning) self.pausedPercent = percent;
|
|
458
541
|
var now = performance.now();
|
|
459
542
|
self.startTime = now - self.currentTime;
|
|
460
|
-
var oldEvent = self.currentEvent;
|
|
461
543
|
self.currentEvent = 0;
|
|
462
544
|
while (self.noteTimings.length > self.currentEvent && self.noteTimings[self.currentEvent].milliseconds < self.currentTime) {
|
|
463
545
|
self.currentEvent++;
|
|
@@ -468,11 +550,18 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
|
|
|
468
550
|
self.currentLine++;
|
|
469
551
|
}
|
|
470
552
|
}
|
|
553
|
+
|
|
554
|
+
//console.log({jump:self.currentTime})
|
|
471
555
|
var oldBeat = self.currentBeat;
|
|
472
|
-
self.currentBeat =
|
|
473
|
-
|
|
556
|
+
for (self.currentBeat = 0; self.currentBeat < self.beatStarts.length; self.currentBeat++) {
|
|
557
|
+
if (self.beatStarts[self.currentBeat].ts > self.currentTime) break;
|
|
558
|
+
}
|
|
559
|
+
self.currentBeat--;
|
|
560
|
+
if (self.beatCallback && oldBeat !== self.currentBeat) {
|
|
474
561
|
// If the movement caused the beat to change, then immediately report it to the client.
|
|
475
562
|
self.doBeatCallback(self.startTime + self.currentTime);
|
|
563
|
+
self.currentBeat++;
|
|
564
|
+
}
|
|
476
565
|
if (self.eventCallback && self.currentEvent >= 0 && self.noteTimings[self.currentEvent].type === 'event') self.eventCallback(self.noteTimings[self.currentEvent]);
|
|
477
566
|
if (self.lineEndCallback) self.lineEndCallback(self.lineEndTimings[self.currentLine], self.noteTimings[self.currentEvent], {
|
|
478
567
|
line: self.currentLine,
|
|
@@ -1024,67 +1113,71 @@ module.exports = keyAccidentals;
|
|
|
1024
1113
|
// All these keys have the same number of accidentals
|
|
1025
1114
|
var keys = {
|
|
1026
1115
|
'C': {
|
|
1027
|
-
modes: ['CMaj', 'Amin', 'Am', 'GMix', 'DDor', 'EPhr', 'FLyd', 'BLoc'],
|
|
1116
|
+
modes: ['CMaj', 'CIon', 'Amin', 'AAeo', 'Am', 'GMix', 'DDor', 'EPhr', 'FLyd', 'BLoc'],
|
|
1028
1117
|
stepsFromC: 0
|
|
1029
1118
|
},
|
|
1030
1119
|
'Db': {
|
|
1031
|
-
modes: ['DbMaj', 'Bbmin', 'Bbm', 'AbMix', 'EbDor', 'FPhr', 'GbLyd', 'CLoc'],
|
|
1120
|
+
modes: ['DbMaj', 'DbIon', 'Bbmin', 'BbAeo', 'Bbm', 'AbMix', 'EbDor', 'FPhr', 'GbLyd', 'CLoc'],
|
|
1032
1121
|
stepsFromC: 1
|
|
1033
1122
|
},
|
|
1034
1123
|
'D': {
|
|
1035
|
-
modes: ['DMaj', 'Bmin', 'Bm', 'AMix', 'EDor', 'F#Phr', 'GLyd', 'C#Loc'],
|
|
1124
|
+
modes: ['DMaj', 'DIon', 'Bmin', 'BAeo', 'Bm', 'AMix', 'EDor', 'F#Phr', 'GLyd', 'C#Loc'],
|
|
1036
1125
|
stepsFromC: 2
|
|
1037
1126
|
},
|
|
1038
1127
|
'Eb': {
|
|
1039
|
-
modes: ['EbMaj', 'Cmin', 'Cm', 'BbMix', 'FDor', 'GPhr', 'AbLyd', 'DLoc'],
|
|
1128
|
+
modes: ['EbMaj', 'EbIon', 'Cmin', 'CAeo', 'Cm', 'BbMix', 'FDor', 'GPhr', 'AbLyd', 'DLoc'],
|
|
1040
1129
|
stepsFromC: 3
|
|
1041
1130
|
},
|
|
1042
1131
|
'E': {
|
|
1043
|
-
modes: ['EMaj', 'C#min', 'C#m', 'BMix', 'F#Dor', 'G#Phr', 'ALyd', 'D#Loc'],
|
|
1132
|
+
modes: ['EMaj', 'EIon', 'C#min', 'C#Aeo', 'C#m', 'BMix', 'F#Dor', 'G#Phr', 'ALyd', 'D#Loc'],
|
|
1044
1133
|
stepsFromC: 4
|
|
1045
1134
|
},
|
|
1046
1135
|
'F': {
|
|
1047
|
-
modes: ['FMaj', 'Dmin', 'Dm', 'CMix', 'GDor', 'APhr', 'BbLyd', 'ELoc'],
|
|
1136
|
+
modes: ['FMaj', 'FIon', 'Dmin', 'DAeo', 'Dm', 'CMix', 'GDor', 'APhr', 'BbLyd', 'ELoc'],
|
|
1048
1137
|
stepsFromC: 5
|
|
1049
1138
|
},
|
|
1050
1139
|
'Gb': {
|
|
1051
|
-
modes: ['GbMaj', 'Ebmin', 'Ebm', 'DbMix', 'AbDor', 'BbPhr', 'CbLyd', 'FLoc'],
|
|
1140
|
+
modes: ['GbMaj', 'GbIon', 'Ebmin', 'EbAeo', 'Ebm', 'DbMix', 'AbDor', 'BbPhr', 'CbLyd', 'FLoc'],
|
|
1052
1141
|
stepsFromC: 6
|
|
1053
1142
|
},
|
|
1054
1143
|
'G': {
|
|
1055
|
-
modes: ['GMaj', 'Emin', 'Em', 'DMix', 'ADor', 'BPhr', 'CLyd', 'F#Loc'],
|
|
1144
|
+
modes: ['GMaj', 'GIon', 'Emin', 'EAeo', 'Em', 'DMix', 'ADor', 'BPhr', 'CLyd', 'F#Loc'],
|
|
1056
1145
|
stepsFromC: 7
|
|
1057
1146
|
},
|
|
1058
1147
|
'Ab': {
|
|
1059
|
-
modes: ['AbMaj', 'Fmin', 'Fm', 'EbMix', 'BbDor', 'CPhr', 'DbLyd', 'GLoc'],
|
|
1148
|
+
modes: ['AbMaj', 'AbIon', 'Fmin', 'FAeo', 'Fm', 'EbMix', 'BbDor', 'CPhr', 'DbLyd', 'GLoc'],
|
|
1060
1149
|
stepsFromC: 8
|
|
1061
1150
|
},
|
|
1062
1151
|
'A': {
|
|
1063
|
-
modes: ['AMaj', 'F#min', 'F#m', 'EMix', 'BDor', 'C#Phr', 'DLyd', 'G#Loc'],
|
|
1152
|
+
modes: ['AMaj', 'AIon', 'F#min', 'F#Aeo', 'F#m', 'EMix', 'BDor', 'C#Phr', 'DLyd', 'G#Loc'],
|
|
1064
1153
|
stepsFromC: 9
|
|
1065
1154
|
},
|
|
1066
1155
|
'Bb': {
|
|
1067
|
-
modes: ['BbMaj', 'Gmin', 'Gm', 'FMix', 'CDor', 'DPhr', 'EbLyd', 'ALoc'],
|
|
1156
|
+
modes: ['BbMaj', 'BbIon', 'Gmin', 'GAeo', 'Gm', 'FMix', 'CDor', 'DPhr', 'EbLyd', 'ALoc'],
|
|
1068
1157
|
stepsFromC: 10
|
|
1069
1158
|
},
|
|
1070
1159
|
'B': {
|
|
1071
|
-
modes: ['BMaj', 'G#min', 'G#m', 'F#Mix', 'C#Dor', 'D#Phr', 'ELyd', 'A#Loc'],
|
|
1160
|
+
modes: ['BMaj', 'BIon', 'G#min', 'G#Aeo', 'G#m', 'F#Mix', 'C#Dor', 'D#Phr', 'ELyd', 'A#Loc'],
|
|
1072
1161
|
stepsFromC: 11
|
|
1073
1162
|
},
|
|
1074
1163
|
// Enharmonic keys
|
|
1075
1164
|
'C#': {
|
|
1076
|
-
modes: ['C#Maj', 'A#min', 'A#m', 'G#Mix', 'D#Dor', 'E#Phr', 'F#Lyd', 'B#Loc'],
|
|
1165
|
+
modes: ['C#Maj', 'C#Ion', 'A#min', 'A#Aeo', 'A#m', 'G#Mix', 'D#Dor', 'E#Phr', 'F#Lyd', 'B#Loc'],
|
|
1077
1166
|
stepsFromC: 1
|
|
1078
1167
|
},
|
|
1079
1168
|
'F#': {
|
|
1080
|
-
modes: ['F#Maj', 'D#min', 'D#m', 'C#Mix', 'G#Dor', 'A#Phr', 'BLyd', 'E#Loc'],
|
|
1169
|
+
modes: ['F#Maj', 'F#Ion', 'D#min', 'D#Aeo', 'D#m', 'C#Mix', 'G#Dor', 'A#Phr', 'BLyd', 'E#Loc'],
|
|
1081
1170
|
stepsFromC: 6
|
|
1082
1171
|
},
|
|
1083
1172
|
'Cb': {
|
|
1084
|
-
modes: ['CbMaj', 'Abmin', 'Abm', 'GbMix', 'DbDor', 'EbPhr', 'FbLyd', 'BbLoc'],
|
|
1173
|
+
modes: ['CbMaj', 'CbIon', 'Abmin', 'AbAeo', 'Abm', 'GbMix', 'DbDor', 'EbPhr', 'FbLyd', 'BbLoc'],
|
|
1085
1174
|
stepsFromC: 11
|
|
1086
1175
|
}
|
|
1087
1176
|
};
|
|
1177
|
+
var modeNames = ['maj', 'ion', 'min', 'aeo', 'm', 'mix', 'dor', 'phr', 'lyd', 'loc'];
|
|
1178
|
+
function isLegalMode(mode) {
|
|
1179
|
+
return modeNames.indexOf(mode.toLowerCase()) >= 0;
|
|
1180
|
+
}
|
|
1088
1181
|
var keyReverse = null;
|
|
1089
1182
|
function createKeyReverse() {
|
|
1090
1183
|
keyReverse = {};
|
|
@@ -1107,7 +1200,7 @@ function relativeMajor(key) {
|
|
|
1107
1200
|
createKeyReverse();
|
|
1108
1201
|
}
|
|
1109
1202
|
// get the key portion itself - there might be other stuff, like extra sharps and flats, or the mode written out.
|
|
1110
|
-
var mode = key.toLowerCase().match(/([a-g][b#]?)(maj|min|mix|dor|phr|lyd|loc|m)?/);
|
|
1203
|
+
var mode = key.toLowerCase().match(/([a-g][b#]?)(maj|ion|min|aeo|mix|dor|phr|lyd|loc|m)?/);
|
|
1111
1204
|
if (!mode || !mode[2]) return key;
|
|
1112
1205
|
mode = mode[1] + mode[2];
|
|
1113
1206
|
var maj = keyReverse[mode];
|
|
@@ -1120,7 +1213,7 @@ function relativeMode(majorKey, mode) {
|
|
|
1120
1213
|
var group = keys[majorKey];
|
|
1121
1214
|
if (!group) return majorKey;
|
|
1122
1215
|
if (mode === '') return majorKey;
|
|
1123
|
-
var match = mode.toLowerCase().match(/^(maj|min|mix|dor|phr|lyd|loc|m)/);
|
|
1216
|
+
var match = mode.toLowerCase().match(/^(maj|ion|min|aeo|mix|dor|phr|lyd|loc|m)/);
|
|
1124
1217
|
if (!match) return majorKey;
|
|
1125
1218
|
var regMode = match[1];
|
|
1126
1219
|
for (var i = 0; i < group.modes.length; i++) {
|
|
@@ -1148,7 +1241,8 @@ function transposeKey(key, steps) {
|
|
|
1148
1241
|
module.exports = {
|
|
1149
1242
|
relativeMajor: relativeMajor,
|
|
1150
1243
|
relativeMode: relativeMode,
|
|
1151
|
-
transposeKey: transposeKey
|
|
1244
|
+
transposeKey: transposeKey,
|
|
1245
|
+
isLegalMode: isLegalMode
|
|
1152
1246
|
};
|
|
1153
1247
|
|
|
1154
1248
|
/***/ }),
|
|
@@ -1239,9 +1333,12 @@ var Tune = function Tune() {
|
|
|
1239
1333
|
this.getBeatLength = function () {
|
|
1240
1334
|
// This returns a fraction: for instance 1/4 for a quarter
|
|
1241
1335
|
// There are two types of meters: compound and regular. Compound meter has 3 beats counted as one.
|
|
1336
|
+
|
|
1337
|
+
// Irregular meters have the beat as an 1/8 note but the tempo as a 1/4.
|
|
1338
|
+
// That keeps it a similar tempo to 4/4 but that may or may not be generally intuitive, so that might need to change.
|
|
1242
1339
|
var meter = this.getMeterFraction();
|
|
1243
1340
|
var multiplier = 1;
|
|
1244
|
-
if (meter.num === 6 || meter.num === 9 || meter.num === 12) multiplier = 3;else if (meter.num === 3 && meter.den === 8) multiplier = 3;
|
|
1341
|
+
if (meter.num === 6 || meter.num === 9 || meter.num === 12) multiplier = 3;else if (meter.num === 3 && meter.den === 8) multiplier = 3;else if (meter.den === 8 && (meter.num === 5 || meter.num === 7)) multiplier = 2;
|
|
1245
1342
|
return multiplier / meter.den;
|
|
1246
1343
|
};
|
|
1247
1344
|
function computePickupLength(lines, barLength) {
|
|
@@ -1325,7 +1422,13 @@ var Tune = function Tune() {
|
|
|
1325
1422
|
var den = 4;
|
|
1326
1423
|
if (meter) {
|
|
1327
1424
|
if (meter.type === 'specified') {
|
|
1328
|
-
|
|
1425
|
+
if (meter.value && meter.value.length > 0 && meter.value[0].num.indexOf('+') > 0) {
|
|
1426
|
+
var parts = meter.value[0].num.split('+');
|
|
1427
|
+
num = 0;
|
|
1428
|
+
for (var i = 0; i < parts.length; i++) {
|
|
1429
|
+
num += parseInt(parts[i], 10);
|
|
1430
|
+
}
|
|
1431
|
+
} else num = parseInt(meter.value[0].num, 10);
|
|
1329
1432
|
den = parseInt(meter.value[0].den, 10);
|
|
1330
1433
|
} else if (meter.type === 'cut_time') {
|
|
1331
1434
|
num = 2;
|
|
@@ -1757,7 +1860,7 @@ function delineTune(inputLines, options) {
|
|
|
1757
1860
|
var currentTripletFont = [];
|
|
1758
1861
|
var currentAnnotationFont = [];
|
|
1759
1862
|
for (var i = 0; i < inputLines.length; i++) {
|
|
1760
|
-
var inputLine = inputLines[i];
|
|
1863
|
+
var inputLine = cloneLine(inputLines[i]);
|
|
1761
1864
|
if (inputLine.staff) {
|
|
1762
1865
|
if (inMusicLine && !inputLine.vskip) {
|
|
1763
1866
|
var outputLine = outputLines[outputLines.length - 1];
|
|
@@ -2451,8 +2554,8 @@ var create;
|
|
|
2451
2554
|
var tempo = commands.tempo;
|
|
2452
2555
|
var beatsPerSecond = tempo / 60;
|
|
2453
2556
|
|
|
2454
|
-
// Fix tempo for
|
|
2455
|
-
if (time.den
|
|
2557
|
+
// Fix tempo for compound meters
|
|
2558
|
+
if (time.den === 8 && time.num !== 5 && time.num !== 7) {
|
|
2456
2559
|
// Compute the tempo based on the actual milliseconds per measure, scaled by the number of eight notes and halved to get tempo in bpm.
|
|
2457
2560
|
var msPerMeasure = abcTune.millisecondsPerMeasure();
|
|
2458
2561
|
tempo = 60000 / (msPerMeasure / time.num) / 2;
|
|
@@ -2602,6 +2705,7 @@ var ParseHeader = __webpack_require__(/*! ./abc_parse_header */ "./src/parse/abc
|
|
|
2602
2705
|
var ParseMusic = __webpack_require__(/*! ./abc_parse_music */ "./src/parse/abc_parse_music.js");
|
|
2603
2706
|
var Tokenizer = __webpack_require__(/*! ./abc_tokenizer */ "./src/parse/abc_tokenizer.js");
|
|
2604
2707
|
var wrap = __webpack_require__(/*! ./wrap_lines */ "./src/parse/wrap_lines.js");
|
|
2708
|
+
var chordGrid = __webpack_require__(/*! ./chord-grid */ "./src/parse/chord-grid.js");
|
|
2605
2709
|
var Tune = __webpack_require__(/*! ../data/abc_tune */ "./src/data/abc_tune.js");
|
|
2606
2710
|
var TuneBuilder = __webpack_require__(/*! ../parse/tune-builder */ "./src/parse/tune-builder.js");
|
|
2607
2711
|
var Parse = function Parse() {
|
|
@@ -2644,6 +2748,7 @@ var Parse = function Parse() {
|
|
|
2644
2748
|
};
|
|
2645
2749
|
if (tune.lineBreaks) t.lineBreaks = tune.lineBreaks;
|
|
2646
2750
|
if (tune.visualTranspose) t.visualTranspose = tune.visualTranspose;
|
|
2751
|
+
if (tune.chordGrid) t.chordGrid = tune.chordGrid;
|
|
2647
2752
|
return t;
|
|
2648
2753
|
};
|
|
2649
2754
|
function addPositioning(el, type, value) {
|
|
@@ -3193,6 +3298,22 @@ var Parse = function Parse() {
|
|
|
3193
3298
|
addHintMeasures();
|
|
3194
3299
|
}
|
|
3195
3300
|
wrap.wrapLines(tune, multilineVars.lineBreaks, multilineVars.barNumbers);
|
|
3301
|
+
if (switches.chordGrid) {
|
|
3302
|
+
try {
|
|
3303
|
+
tune.chordGrid = chordGrid(tune);
|
|
3304
|
+
} catch (err) {
|
|
3305
|
+
switch (err.message) {
|
|
3306
|
+
case "notCommonTime":
|
|
3307
|
+
warn("Chord grid only works for 2/2 and 4/4 time.", 0, 0);
|
|
3308
|
+
break;
|
|
3309
|
+
case "noChords":
|
|
3310
|
+
warn("No chords are found in the tune.", 0, 0);
|
|
3311
|
+
break;
|
|
3312
|
+
default:
|
|
3313
|
+
warn(err.message, 0, 0);
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3196
3317
|
};
|
|
3197
3318
|
};
|
|
3198
3319
|
module.exports = Parse;
|
|
@@ -9219,6 +9340,344 @@ module.exports = allNotes;
|
|
|
9219
9340
|
|
|
9220
9341
|
/***/ }),
|
|
9221
9342
|
|
|
9343
|
+
/***/ "./src/parse/chord-grid.js":
|
|
9344
|
+
/*!*********************************!*\
|
|
9345
|
+
!*** ./src/parse/chord-grid.js ***!
|
|
9346
|
+
\*********************************/
|
|
9347
|
+
/***/ (function(module) {
|
|
9348
|
+
|
|
9349
|
+
// This takes a visual object and returns an object that can
|
|
9350
|
+
// be rotely turned into a chord grid.
|
|
9351
|
+
//
|
|
9352
|
+
// 1) It will always be 8 measures on a line, unless it is a 12 bar blues, then it will be 4 measures.
|
|
9353
|
+
// 2) If it is not in 4/4 it will return an error
|
|
9354
|
+
// 3) If there are no chords it will return an error
|
|
9355
|
+
// 4) It will be divided into parts with the part title and an array of measures
|
|
9356
|
+
// 5) |: and :| will be included in a measure
|
|
9357
|
+
// 6) If there are first and second endings and the chords are the same, then collapse them
|
|
9358
|
+
// 7) If there are first and second endings and the chords are different, use a separate line for the second ending and right justify it.
|
|
9359
|
+
// 8) If there is one chord per measure and it is repeated in the next measure use a % for the second measure.
|
|
9360
|
+
// 9) All lines are the same height, so they are tall enough to fit two lines if there lots of chords
|
|
9361
|
+
// 10) Chords will be printed as large as they can without overlapping, so different chords will be smaller if they are long.
|
|
9362
|
+
// 11) If there are two chords per measure then there is a slash between them.
|
|
9363
|
+
// 12) If there are three or four chords then there is a 2x2 grid with the chords reading right to left. For three chords, leave the repeated cell blank.
|
|
9364
|
+
// 13) Breaks are indicated by the word "break" or "N.C.". A break that extends to the next measure is indicated by three dots in the next measure.
|
|
9365
|
+
// 14) Ignore pickup notes
|
|
9366
|
+
// 15) if a part is not a multiple of 8 bars (and not 12 bars), the last line has
|
|
9367
|
+
// 4 squares on left and not any grid on the right.
|
|
9368
|
+
// 16) Annotations and some decorations get printed above the cells.
|
|
9369
|
+
|
|
9370
|
+
function chordGrid(visualObj) {
|
|
9371
|
+
var meter = visualObj.getMeterFraction();
|
|
9372
|
+
var isCommonTime = meter.num === 4 && meter.den === 4;
|
|
9373
|
+
var isCutTime = meter.num === 2 && meter.den === 2;
|
|
9374
|
+
if (!isCutTime && !isCommonTime) throw new Error("notCommonTime");
|
|
9375
|
+
var deline = visualObj.deline();
|
|
9376
|
+
var chartLines = [];
|
|
9377
|
+
var nonSubtitle = false;
|
|
9378
|
+
deline.forEach(function (section) {
|
|
9379
|
+
if (section.subtitle) {
|
|
9380
|
+
if (nonSubtitle) {
|
|
9381
|
+
// Don't do the subtitle if the first thing is the subtitle, but that is already printed on the top
|
|
9382
|
+
chartLines.push({
|
|
9383
|
+
type: "subtitle",
|
|
9384
|
+
subtitle: section.subtitle.text
|
|
9385
|
+
});
|
|
9386
|
+
}
|
|
9387
|
+
} else if (section.text) {
|
|
9388
|
+
nonSubtitle = true;
|
|
9389
|
+
chartLines.push({
|
|
9390
|
+
type: "text",
|
|
9391
|
+
text: section.text.text
|
|
9392
|
+
});
|
|
9393
|
+
} else if (section.staff) {
|
|
9394
|
+
nonSubtitle = true;
|
|
9395
|
+
// The first staff and the first voice in it drive everything.
|
|
9396
|
+
// Only part designations there will count. However, look for
|
|
9397
|
+
// chords in any other part. If there is not a chord defined in
|
|
9398
|
+
// the first part, use a chord defined in another part.
|
|
9399
|
+
var staves = section.staff;
|
|
9400
|
+
var parts = flattenVoices(staves);
|
|
9401
|
+
chartLines = chartLines.concat(parts);
|
|
9402
|
+
}
|
|
9403
|
+
});
|
|
9404
|
+
collapseIdenticalEndings(chartLines);
|
|
9405
|
+
addLineBreaks(chartLines);
|
|
9406
|
+
addPercents(chartLines);
|
|
9407
|
+
return chartLines;
|
|
9408
|
+
}
|
|
9409
|
+
var breakSynonyms = ['break', '(break)', 'no chord', 'n.c.', 'tacet'];
|
|
9410
|
+
function flattenVoices(staves) {
|
|
9411
|
+
var parts = [];
|
|
9412
|
+
var partName = "";
|
|
9413
|
+
var measures = [];
|
|
9414
|
+
var currentBar = {
|
|
9415
|
+
chord: ['', '', '', '']
|
|
9416
|
+
};
|
|
9417
|
+
var lastChord = "";
|
|
9418
|
+
var nextBarEnding = "";
|
|
9419
|
+
staves.forEach(function (staff, staffNum) {
|
|
9420
|
+
if (staff.voices) {
|
|
9421
|
+
staff.voices.forEach(function (voice, voiceNum) {
|
|
9422
|
+
var currentPartNum = 0;
|
|
9423
|
+
var beatNum = 0;
|
|
9424
|
+
var measureNum = 0;
|
|
9425
|
+
voice.forEach(function (element) {
|
|
9426
|
+
if (element.el_type === 'part') {
|
|
9427
|
+
if (measures.length > 0) {
|
|
9428
|
+
if (staffNum === 0 && voiceNum === 0) {
|
|
9429
|
+
parts.push({
|
|
9430
|
+
type: "part",
|
|
9431
|
+
name: partName,
|
|
9432
|
+
lines: [measures]
|
|
9433
|
+
});
|
|
9434
|
+
measures = [];
|
|
9435
|
+
// } else {
|
|
9436
|
+
// currentPartNum++
|
|
9437
|
+
// measureNum = 0
|
|
9438
|
+
// measures = parts[currentPartNum].lines[0]
|
|
9439
|
+
}
|
|
9440
|
+
}
|
|
9441
|
+
|
|
9442
|
+
partName = element.title;
|
|
9443
|
+
} else if (element.el_type === 'note') {
|
|
9444
|
+
addDecoration(element, currentBar);
|
|
9445
|
+
var intBeat = Math.floor(beatNum);
|
|
9446
|
+
if (element.chord && element.chord.length > 0) {
|
|
9447
|
+
var chord = element.chord[0]; // Use just the first chord specified - if there are multiple ones, then ignore them
|
|
9448
|
+
var chordName = chord.position === 'default' || breakSynonyms.indexOf(chord.name.toLowerCase()) >= 0 ? chord.name : '';
|
|
9449
|
+
if (chordName) {
|
|
9450
|
+
if (intBeat > 0 && !currentBar.chord[0])
|
|
9451
|
+
// Be sure there is a chord for the first beat in a measure
|
|
9452
|
+
currentBar.chord[0] = lastChord;
|
|
9453
|
+
lastChord = chordName;
|
|
9454
|
+
if (currentBar.chord[intBeat]) {
|
|
9455
|
+
// If there is already a chord on this beat put the next chord on the next beat, but don't overwrite anything.
|
|
9456
|
+
// This handles the case were a chord is misplaced slightly, for instance it is on the 1/8 before the beat.
|
|
9457
|
+
if (intBeat < 4 && !currentBar.chord[intBeat + 1]) currentBar.chord[intBeat + 1] = chordName;
|
|
9458
|
+
} else currentBar.chord[intBeat] = chordName;
|
|
9459
|
+
}
|
|
9460
|
+
element.chord.forEach(function (ch) {
|
|
9461
|
+
if (ch.position !== 'default' && breakSynonyms.indexOf(chord.name.toLowerCase()) < 0) {
|
|
9462
|
+
if (!currentBar.annotations) currentBar.annotations = [];
|
|
9463
|
+
currentBar.annotations.push(ch.name);
|
|
9464
|
+
}
|
|
9465
|
+
});
|
|
9466
|
+
}
|
|
9467
|
+
if (!element.rest || element.rest.type !== 'spacer') {
|
|
9468
|
+
var thisDuration = Math.floor(element.duration * 4);
|
|
9469
|
+
if (thisDuration > 4) {
|
|
9470
|
+
measureNum += Math.floor(thisDuration / 4);
|
|
9471
|
+
beatNum = 0;
|
|
9472
|
+
} else {
|
|
9473
|
+
var thisBeat = element.duration * 4;
|
|
9474
|
+
if (element.tripletMultiplier) thisBeat *= element.tripletMultiplier;
|
|
9475
|
+
beatNum += thisBeat;
|
|
9476
|
+
}
|
|
9477
|
+
}
|
|
9478
|
+
} else if (element.el_type === 'bar') {
|
|
9479
|
+
if (nextBarEnding) {
|
|
9480
|
+
currentBar.ending = nextBarEnding;
|
|
9481
|
+
nextBarEnding = "";
|
|
9482
|
+
}
|
|
9483
|
+
addDecoration(element, currentBar);
|
|
9484
|
+
if (element.type === 'bar_dbl_repeat' || element.type === 'bar_left_repeat') currentBar.hasStartRepeat = true;
|
|
9485
|
+
if (element.type === 'bar_dbl_repeat' || element.type === 'bar_right_repeat') currentBar.hasEndRepeat = true;
|
|
9486
|
+
if (element.startEnding) nextBarEnding = element.startEnding;
|
|
9487
|
+
if (beatNum >= 4) {
|
|
9488
|
+
if (currentBar.chord[0] === '') {
|
|
9489
|
+
// If there isn't a chord change at the beginning, repeat the last chord found
|
|
9490
|
+
if (currentBar.chord[1] || currentBar.chord[2] || currentBar.chord[3]) {
|
|
9491
|
+
currentBar.chord[0] = findLastChord(measures);
|
|
9492
|
+
}
|
|
9493
|
+
}
|
|
9494
|
+
if (staffNum === 0 && voiceNum === 0) measures.push(currentBar);else {
|
|
9495
|
+
// Add the found items of interest to the original array
|
|
9496
|
+
// We have the extra [0] in there because lines is an array of lines (but we just use the [0] for constructing, we split it apart at the end)
|
|
9497
|
+
var index = measureNum;
|
|
9498
|
+
var partIndex = 0;
|
|
9499
|
+
while (index >= parts[partIndex].lines[0].length && partIndex < parts.length) {
|
|
9500
|
+
index -= parts[partIndex].lines[0].length;
|
|
9501
|
+
partIndex++;
|
|
9502
|
+
}
|
|
9503
|
+
if (partIndex < parts.length && index < parts[partIndex].lines[0].length) {
|
|
9504
|
+
var bar = parts[partIndex].lines[0][index];
|
|
9505
|
+
if (!bar.chord[0] && currentBar.chord[0]) bar.chord[0] = currentBar.chord[0];
|
|
9506
|
+
if (!bar.chord[1] && currentBar.chord[1]) bar.chord[1] = currentBar.chord[1];
|
|
9507
|
+
if (!bar.chord[2] && currentBar.chord[2]) bar.chord[2] = currentBar.chord[2];
|
|
9508
|
+
if (!bar.chord[3] && currentBar.chord[3]) bar.chord[3] = currentBar.chord[3];
|
|
9509
|
+
if (currentBar.annotations) {
|
|
9510
|
+
if (!bar.annotations) bar.annotations = currentBar.annotations;else bar.annotations = bar.annotations.concat(currentBar.annotations);
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
measureNum++;
|
|
9514
|
+
}
|
|
9515
|
+
currentBar = {
|
|
9516
|
+
chord: ['', '', '', '']
|
|
9517
|
+
};
|
|
9518
|
+
} else currentBar.chord = ['', '', '', ''];
|
|
9519
|
+
beatNum = 0;
|
|
9520
|
+
} else if (element.el_type === 'tempo') {
|
|
9521
|
+
// TODO-PER: should probably report tempo, too
|
|
9522
|
+
}
|
|
9523
|
+
});
|
|
9524
|
+
if (staffNum === 0 && voiceNum === 0) {
|
|
9525
|
+
parts.push({
|
|
9526
|
+
type: "part",
|
|
9527
|
+
name: partName,
|
|
9528
|
+
lines: [measures]
|
|
9529
|
+
});
|
|
9530
|
+
}
|
|
9531
|
+
});
|
|
9532
|
+
}
|
|
9533
|
+
});
|
|
9534
|
+
if (!lastChord) throw new Error("noChords");
|
|
9535
|
+
return parts;
|
|
9536
|
+
}
|
|
9537
|
+
function findLastChord(measures) {
|
|
9538
|
+
for (var m = measures.length - 1; m >= 0; m--) {
|
|
9539
|
+
for (var c = measures[m].chord.length - 1; c >= 0; c--) {
|
|
9540
|
+
if (measures[m].chord[c]) return measures[m].chord[c];
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
}
|
|
9544
|
+
function collapseIdenticalEndings(chartLines) {
|
|
9545
|
+
chartLines.forEach(function (line) {
|
|
9546
|
+
if (line.type === "part") {
|
|
9547
|
+
var partLine = line.lines[0];
|
|
9548
|
+
var ending1 = partLine.findIndex(function (bar) {
|
|
9549
|
+
return !!bar.ending;
|
|
9550
|
+
});
|
|
9551
|
+
var ending2 = partLine.findIndex(function (bar, index) {
|
|
9552
|
+
return index > ending1 && !!bar.ending;
|
|
9553
|
+
});
|
|
9554
|
+
if (ending1 >= 0 && ending2 >= 0) {
|
|
9555
|
+
// If the endings are not the same length, don't collapse
|
|
9556
|
+
if (ending2 - ending1 === partLine.length - ending2) {
|
|
9557
|
+
var matches = true;
|
|
9558
|
+
for (var i = 0; i < ending2 - ending1 && matches; i++) {
|
|
9559
|
+
var measureLhs = partLine[ending1 + i];
|
|
9560
|
+
var measureRhs = partLine[ending2 + i];
|
|
9561
|
+
if (measureLhs.chord[0] !== measureRhs.chord[0]) matches = false;
|
|
9562
|
+
if (measureLhs.chord[1] !== measureRhs.chord[1]) matches = false;
|
|
9563
|
+
if (measureLhs.chord[2] !== measureRhs.chord[2]) matches = false;
|
|
9564
|
+
if (measureLhs.chord[3] !== measureRhs.chord[3]) matches = false;
|
|
9565
|
+
if (measureLhs.annotations && !measureRhs.annotations) matches = false;
|
|
9566
|
+
if (!measureLhs.annotations && measureRhs.annotations) matches = false;
|
|
9567
|
+
if (measureLhs.annotations && measureRhs.annotations) {
|
|
9568
|
+
if (measureLhs.annotations.length !== measureRhs.annotations.length) matches = false;else {
|
|
9569
|
+
for (var j = 0; j < measureLhs.annotations.length; j++) {
|
|
9570
|
+
if (measureLhs.annotations[j] !== measureRhs.annotations[j]) matches = false;
|
|
9571
|
+
}
|
|
9572
|
+
}
|
|
9573
|
+
}
|
|
9574
|
+
}
|
|
9575
|
+
if (matches) {
|
|
9576
|
+
delete partLine[ending1].ending;
|
|
9577
|
+
partLine.splice(ending2, partLine.length - ending2);
|
|
9578
|
+
}
|
|
9579
|
+
}
|
|
9580
|
+
}
|
|
9581
|
+
}
|
|
9582
|
+
});
|
|
9583
|
+
}
|
|
9584
|
+
function addLineBreaks(chartLines) {
|
|
9585
|
+
chartLines.forEach(function (line) {
|
|
9586
|
+
if (line.type === "part") {
|
|
9587
|
+
var newLines = [];
|
|
9588
|
+
var oldLines = line.lines[0];
|
|
9589
|
+
var is12bar = false;
|
|
9590
|
+
var firstEndRepeat = oldLines.findIndex(function (l) {
|
|
9591
|
+
return !!l.hasEndRepeat;
|
|
9592
|
+
});
|
|
9593
|
+
var length = firstEndRepeat >= 0 ? Math.min(firstEndRepeat + 1, oldLines.length) : oldLines.length;
|
|
9594
|
+
if (length === 12) is12bar = true;
|
|
9595
|
+
var barsPerLine = is12bar ? 4 : 8; // Only do 4 bars per line for 12-bar blues
|
|
9596
|
+
for (var i = 0; i < oldLines.length; i += barsPerLine) {
|
|
9597
|
+
var newLine = oldLines.slice(i, i + barsPerLine);
|
|
9598
|
+
var endRepeat = newLine.findIndex(function (l) {
|
|
9599
|
+
return !!l.hasEndRepeat;
|
|
9600
|
+
});
|
|
9601
|
+
if (endRepeat >= 0 && endRepeat < newLine.length - 1) {
|
|
9602
|
+
newLines.push(newLine.slice(0, endRepeat + 1));
|
|
9603
|
+
newLines.push(newLine.slice(endRepeat + 1));
|
|
9604
|
+
} else newLines.push(newLine);
|
|
9605
|
+
}
|
|
9606
|
+
// TODO-PER: The following probably doesn't handle all cases. Rethink it.
|
|
9607
|
+
for (var _i = 0; _i < newLines.length; _i++) {
|
|
9608
|
+
if (newLines[_i][0].ending) {
|
|
9609
|
+
var prevLine = Math.max(0, _i - 1);
|
|
9610
|
+
var toAdd = newLines[prevLine].length - newLines[_i].length;
|
|
9611
|
+
var thisLine = [];
|
|
9612
|
+
for (var j = 0; j < toAdd; j++) {
|
|
9613
|
+
thisLine.push({
|
|
9614
|
+
noBorder: true,
|
|
9615
|
+
chord: ['', '', '', '']
|
|
9616
|
+
});
|
|
9617
|
+
}
|
|
9618
|
+
newLines[_i] = thisLine.concat(newLines[_i]);
|
|
9619
|
+
}
|
|
9620
|
+
}
|
|
9621
|
+
line.lines = newLines;
|
|
9622
|
+
}
|
|
9623
|
+
});
|
|
9624
|
+
}
|
|
9625
|
+
function addPercents(chartLines) {
|
|
9626
|
+
chartLines.forEach(function (part) {
|
|
9627
|
+
if (part.lines) {
|
|
9628
|
+
var lastMeasureSingle = false;
|
|
9629
|
+
var lastChord = "";
|
|
9630
|
+
part.lines.forEach(function (line) {
|
|
9631
|
+
line.forEach(function (measure) {
|
|
9632
|
+
if (!measure.noBorder) {
|
|
9633
|
+
var chords = measure.chord;
|
|
9634
|
+
if (!chords[0] && !chords[1] && !chords[2] && !chords[3]) {
|
|
9635
|
+
// if there are no chords specified for this measure
|
|
9636
|
+
if (lastMeasureSingle) {
|
|
9637
|
+
if (lastChord) chords[0] = '%';
|
|
9638
|
+
} else chords[0] = lastChord;
|
|
9639
|
+
lastMeasureSingle = true;
|
|
9640
|
+
} else if (!chords[1] && !chords[2] && !chords[3]) {
|
|
9641
|
+
// if there is a single chord for this measure
|
|
9642
|
+
lastMeasureSingle = true;
|
|
9643
|
+
lastChord = chords[0];
|
|
9644
|
+
} else {
|
|
9645
|
+
// if the measure is complicated - in that case the next measure won't get %
|
|
9646
|
+
lastMeasureSingle = false;
|
|
9647
|
+
lastChord = chords[3] || chords[2] || chords[1];
|
|
9648
|
+
}
|
|
9649
|
+
}
|
|
9650
|
+
});
|
|
9651
|
+
});
|
|
9652
|
+
}
|
|
9653
|
+
});
|
|
9654
|
+
}
|
|
9655
|
+
function addDecoration(element, currentBar) {
|
|
9656
|
+
if (element.decoration) {
|
|
9657
|
+
// Some decorations are interesting to rhythm players
|
|
9658
|
+
for (var i = 0; i < element.decoration.length; i++) {
|
|
9659
|
+
switch (element.decoration[i]) {
|
|
9660
|
+
case 'fermata':
|
|
9661
|
+
case 'segno':
|
|
9662
|
+
case 'coda':
|
|
9663
|
+
case "D.C.":
|
|
9664
|
+
case "D.S.":
|
|
9665
|
+
case "D.C.alcoda":
|
|
9666
|
+
case "D.C.alfine":
|
|
9667
|
+
case "D.S.alcoda":
|
|
9668
|
+
case "D.S.alfine":
|
|
9669
|
+
case "fine":
|
|
9670
|
+
if (!currentBar.annotations) currentBar.annotations = [];
|
|
9671
|
+
currentBar.annotations.push(element.decoration[i]);
|
|
9672
|
+
break;
|
|
9673
|
+
}
|
|
9674
|
+
}
|
|
9675
|
+
}
|
|
9676
|
+
}
|
|
9677
|
+
module.exports = chordGrid;
|
|
9678
|
+
|
|
9679
|
+
/***/ }),
|
|
9680
|
+
|
|
9222
9681
|
/***/ "./src/parse/transpose-chord.js":
|
|
9223
9682
|
/*!**************************************!*\
|
|
9224
9683
|
!*** ./src/parse/transpose-chord.js ***!
|
|
@@ -9857,7 +10316,7 @@ function resolveOverlays(tune) {
|
|
|
9857
10316
|
} else if (event.el_type === "note") {
|
|
9858
10317
|
if (inOverlay) {
|
|
9859
10318
|
overlayVoice[k].voice.push(event);
|
|
9860
|
-
} else {
|
|
10319
|
+
} else if (!event.rest || event.rest.type !== 'spacer') {
|
|
9861
10320
|
durationThisBar += event.duration;
|
|
9862
10321
|
durationsPerLines[i] += event.duration;
|
|
9863
10322
|
}
|
|
@@ -10794,14 +11253,12 @@ module.exports = {
|
|
|
10794
11253
|
\***************************/
|
|
10795
11254
|
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
10796
11255
|
|
|
10797
|
-
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }
|
|
10798
|
-
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
10799
|
-
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
10800
11256
|
var keyAccidentals = __webpack_require__(/*! ../const/key-accidentals */ "./src/const/key-accidentals.js");
|
|
10801
11257
|
var _require = __webpack_require__(/*! ../const/relative-major */ "./src/const/relative-major.js"),
|
|
10802
11258
|
relativeMajor = _require.relativeMajor,
|
|
10803
11259
|
transposeKey = _require.transposeKey,
|
|
10804
|
-
relativeMode = _require.relativeMode
|
|
11260
|
+
relativeMode = _require.relativeMode,
|
|
11261
|
+
isLegalMode = _require.isLegalMode;
|
|
10805
11262
|
var transposeChordName = __webpack_require__(/*! ../parse/transpose-chord */ "./src/parse/transpose-chord.js");
|
|
10806
11263
|
var strTranspose;
|
|
10807
11264
|
(function () {
|
|
@@ -10864,11 +11321,12 @@ var strTranspose;
|
|
|
10864
11321
|
var match = segment.match(/^( *)([A-G])([#b]?)( ?)(\w*)/);
|
|
10865
11322
|
if (match) {
|
|
10866
11323
|
var start = count + 2 + match[1].length; // move past the 'K:' and optional white space
|
|
10867
|
-
var
|
|
11324
|
+
var mode = isLegalMode(match[5]) ? match[5] : '';
|
|
11325
|
+
var key = match[2] + match[3] + match[4] + mode; // key name, accidental, optional space, and mode
|
|
10868
11326
|
var destinationKey = newKey({
|
|
10869
11327
|
root: match[2],
|
|
10870
11328
|
acc: match[3],
|
|
10871
|
-
mode:
|
|
11329
|
+
mode: mode
|
|
10872
11330
|
}, steps);
|
|
10873
11331
|
var dest = destinationKey.root + destinationKey.acc + match[4] + destinationKey.mode;
|
|
10874
11332
|
changes.push({
|
|
@@ -10932,12 +11390,18 @@ var strTranspose;
|
|
|
10932
11390
|
}
|
|
10933
11391
|
}
|
|
10934
11392
|
if (el.el_type === 'note' && el.pitches) {
|
|
10935
|
-
|
|
10936
|
-
|
|
11393
|
+
var pitchArray = findNotes(abc, el.startChar, el.endChar);
|
|
11394
|
+
//console.log(pitchArray)
|
|
11395
|
+
for (var j = 0; j < pitchArray.length; j++) {
|
|
11396
|
+
var note = parseNote(pitchArray[j].note, keyRoot, keyAccidentals, measureAccidentals);
|
|
10937
11397
|
if (note.acc) measureAccidentals[note.name.toUpperCase()] = note.acc;
|
|
10938
11398
|
var newPitch = transposePitch(note, destinationKey, letterDistance, transposedMeasureAccidentals);
|
|
10939
11399
|
if (newPitch.acc) transposedMeasureAccidentals[newPitch.upper] = newPitch.acc;
|
|
10940
|
-
changes.push(
|
|
11400
|
+
changes.push({
|
|
11401
|
+
note: newPitch.acc + newPitch.name,
|
|
11402
|
+
start: pitchArray[j].index,
|
|
11403
|
+
end: pitchArray[j].index + pitchArray[j].note.length
|
|
11404
|
+
});
|
|
10941
11405
|
}
|
|
10942
11406
|
if (el.gracenotes) {
|
|
10943
11407
|
for (var g = 0; g < el.gracenotes.length; g++) {
|
|
@@ -11013,6 +11477,7 @@ var strTranspose;
|
|
|
11013
11477
|
break;
|
|
11014
11478
|
}
|
|
11015
11479
|
}
|
|
11480
|
+
var newNote;
|
|
11016
11481
|
switch (adj) {
|
|
11017
11482
|
case -2:
|
|
11018
11483
|
acc = "__";
|
|
@@ -11031,7 +11496,7 @@ var strTranspose;
|
|
|
11031
11496
|
break;
|
|
11032
11497
|
case -3:
|
|
11033
11498
|
// This requires a triple flat, so bump down the pitch and try again
|
|
11034
|
-
|
|
11499
|
+
newNote = {};
|
|
11035
11500
|
newNote.pitch = note.pitch - 1;
|
|
11036
11501
|
newNote.oct = note.oct;
|
|
11037
11502
|
newNote.name = letters[letters.indexOf(note.name) - 1];
|
|
@@ -11043,7 +11508,7 @@ var strTranspose;
|
|
|
11043
11508
|
return transposePitch(newNote, key, letterDistance + 1, measureAccidentals);
|
|
11044
11509
|
case 3:
|
|
11045
11510
|
// This requires a triple sharp, so bump up the pitch and try again
|
|
11046
|
-
|
|
11511
|
+
newNote = {};
|
|
11047
11512
|
newNote.pitch = note.pitch + 1;
|
|
11048
11513
|
newNote.oct = note.oct;
|
|
11049
11514
|
newNote.name = letters[letters.indexOf(note.name) + 1];
|
|
@@ -11092,8 +11557,8 @@ var strTranspose;
|
|
|
11092
11557
|
var regPitch = /([_^=]*)([A-Ga-g])([,']*)/;
|
|
11093
11558
|
var regNote = /([_^=]*[A-Ga-g][,']*)(\d*\/*\d*)([\>\<\-\)\.\s\\]*)/;
|
|
11094
11559
|
var regOptionalNote = /([_^=]*[A-Ga-g][,']*)?(\d*\/*\d*)?([\>\<\-\)]*)?/;
|
|
11095
|
-
var regSpace = /(\s*)
|
|
11096
|
-
var regOptionalSpace = /(\s*)
|
|
11560
|
+
//var regSpace = /(\s*)$/
|
|
11561
|
+
//var regOptionalSpace = /(\s*)/
|
|
11097
11562
|
|
|
11098
11563
|
// This the relationship of the note to the tonic and an octave. So what is returned is a distance in steps from the tonic and the amount of adjustment from
|
|
11099
11564
|
// a normal scale. That is - in the key of D an F# is two steps from the tonic and no adjustment. A G# is three steps from the tonic and one half-step higher.
|
|
@@ -11121,68 +11586,100 @@ var strTranspose;
|
|
|
11121
11586
|
courtesy: reg[1] === currentAcc
|
|
11122
11587
|
};
|
|
11123
11588
|
}
|
|
11124
|
-
function
|
|
11589
|
+
function findNotes(abc, start, end) {
|
|
11590
|
+
// TODO-PER: I thought this regex should have found all the notes and ignored the chords and decorations but it didn't: /(?:"[^"]+")*(?:![^!]+!)*([_^=]*)([A-Ga-g])([,']*)/g
|
|
11125
11591
|
var note = abc.substring(start, end);
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
11130
|
-
|
|
11131
|
-
|
|
11132
|
-
|
|
11133
|
-
|
|
11134
|
-
|
|
11135
|
-
|
|
11136
|
-
|
|
11137
|
-
|
|
11138
|
-
|
|
11139
|
-
|
|
11140
|
-
|
|
11141
|
-
|
|
11142
|
-
|
|
11143
|
-
|
|
11144
|
-
|
|
11145
|
-
|
|
11146
|
-
|
|
11147
|
-
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
|
|
11151
|
-
var m = _step.value;
|
|
11152
|
-
var noteText = m[0].trim();
|
|
11153
|
-
if (noteText !== "") {
|
|
11154
|
-
chordNotes.push({
|
|
11155
|
-
text: noteText,
|
|
11156
|
-
index: m.index
|
|
11157
|
-
});
|
|
11158
|
-
}
|
|
11159
|
-
}
|
|
11160
|
-
} catch (err) {
|
|
11161
|
-
_iterator.e(err);
|
|
11162
|
-
} finally {
|
|
11163
|
-
_iterator.f();
|
|
11164
|
-
}
|
|
11165
|
-
if (index >= chordNotes.length) {
|
|
11166
|
-
throw new Error("Chord index out of range for chord: " + note);
|
|
11167
|
-
}
|
|
11168
|
-
var chosen = chordNotes[index];
|
|
11169
|
-
// Preserve duration and tie
|
|
11170
|
-
var mDurTie = chosen.text.match(/^(.+?)(\d+\/?\d*)?(-)?$/);
|
|
11171
|
-
var pitchPart = mDurTie ? mDurTie[1] : chosen.text;
|
|
11172
|
-
var durationPart = mDurTie && mDurTie[2] ? mDurTie[2] : "";
|
|
11173
|
-
var tiePart = mDurTie && mDurTie[3] ? mDurTie[3] : "";
|
|
11174
|
-
// Replace note keeping duration and tie
|
|
11175
|
-
newPitch = newPitch + durationPart + tiePart;
|
|
11176
|
-
start += beforeChordLen + chosen.index;
|
|
11177
|
-
end = start + chosen.text.length;
|
|
11592
|
+
|
|
11593
|
+
// Since the regex will also find "c", "d", and "a" in `!coda!`, we need to filter them
|
|
11594
|
+
var array;
|
|
11595
|
+
var ignoreBlocks = [];
|
|
11596
|
+
var regChord = /("[^"]+")+/g;
|
|
11597
|
+
while ((array = regChord.exec(note)) !== null) {
|
|
11598
|
+
ignoreBlocks.push({
|
|
11599
|
+
start: regChord.lastIndex - array[0].length,
|
|
11600
|
+
end: regChord.lastIndex
|
|
11601
|
+
});
|
|
11602
|
+
}
|
|
11603
|
+
var regDec = /(![^!]+!)+/g;
|
|
11604
|
+
while ((array = regDec.exec(note)) !== null) {
|
|
11605
|
+
ignoreBlocks.push({
|
|
11606
|
+
start: regDec.lastIndex - array[0].length,
|
|
11607
|
+
end: regDec.lastIndex
|
|
11608
|
+
});
|
|
11609
|
+
}
|
|
11610
|
+
var ret = [];
|
|
11611
|
+
// Define the regex each time because it is stateful
|
|
11612
|
+
var regPitch = /([_^=]*)([A-Ga-g])([,']*)/g;
|
|
11613
|
+
while ((array = regPitch.exec(note)) !== null) {
|
|
11614
|
+
var found = false;
|
|
11615
|
+
for (var i = 0; i < ignoreBlocks.length; i++) {
|
|
11616
|
+
if (regPitch.lastIndex >= ignoreBlocks[i].start && regPitch.lastIndex <= ignoreBlocks[i].end) found = true;
|
|
11178
11617
|
}
|
|
11618
|
+
if (!found) ret.push({
|
|
11619
|
+
note: array[0],
|
|
11620
|
+
index: start + regPitch.lastIndex - array[0].length
|
|
11621
|
+
});
|
|
11179
11622
|
}
|
|
11180
|
-
return
|
|
11181
|
-
start: start,
|
|
11182
|
-
end: end,
|
|
11183
|
-
note: newPitch
|
|
11184
|
-
};
|
|
11623
|
+
return ret;
|
|
11185
11624
|
}
|
|
11625
|
+
|
|
11626
|
+
// function replaceNote(abc, start, end, newPitch, oldPitch, index) {
|
|
11627
|
+
// var note = abc.substring(start, end);
|
|
11628
|
+
// // Try single note first
|
|
11629
|
+
// var match = note.match(new RegExp(regNote.source + regSpace.source));
|
|
11630
|
+
// if (match) {
|
|
11631
|
+
// var noteLen = match[1].length;
|
|
11632
|
+
// var trailingLen = match[2].length + match[3].length + match[4].length;
|
|
11633
|
+
// var leadingLen = end - start - noteLen - trailingLen;
|
|
11634
|
+
// start += leadingLen;
|
|
11635
|
+
// end -= trailingLen;
|
|
11636
|
+
// } else {
|
|
11637
|
+
// // Match chord
|
|
11638
|
+
// var regPreBracket = /([^\[]*)/;
|
|
11639
|
+
// var regOpenBracket = /\[/;
|
|
11640
|
+
// var regCloseBracket = /\-?](\d*\/*\d*)?([\>\<\-\)]*)/;
|
|
11641
|
+
// var regChord = new RegExp(
|
|
11642
|
+
// regPreBracket.source +
|
|
11643
|
+
// regOpenBracket.source +
|
|
11644
|
+
// "(?:" + regOptionalNote.source + "\\s*){1,8}" +
|
|
11645
|
+
// regCloseBracket.source +
|
|
11646
|
+
// regSpace.source
|
|
11647
|
+
// );
|
|
11648
|
+
// match = note.match(regChord);
|
|
11649
|
+
// if (match) {
|
|
11650
|
+
// var beforeChordLen = match[1].length + 1; // text before + '['
|
|
11651
|
+
// var chordBody = note.slice(match[1].length + 1, note.lastIndexOf("]"));
|
|
11652
|
+
// // Collect notes inside chord
|
|
11653
|
+
// var chordNotes = [];
|
|
11654
|
+
// var regNoteWithSpace = new RegExp(regOptionalNote.source + "\\s*", "g");
|
|
11655
|
+
// for (const m of chordBody.matchAll(regNoteWithSpace)) {
|
|
11656
|
+
// let noteText = m[0].trim();
|
|
11657
|
+
// if (noteText !== "") {
|
|
11658
|
+
// chordNotes.push({ text: noteText, index: m.index });
|
|
11659
|
+
// }
|
|
11660
|
+
// }
|
|
11661
|
+
// if (index >= chordNotes.length) {
|
|
11662
|
+
// throw new Error("Chord index out of range for chord: " + note);
|
|
11663
|
+
// }
|
|
11664
|
+
// var chosen = chordNotes[index];
|
|
11665
|
+
// // Preserve duration and tie
|
|
11666
|
+
// let mDurTie = chosen.text.match(/^(.+?)(\d+\/?\d*)?(-)?$/);
|
|
11667
|
+
// let pitchPart = mDurTie ? mDurTie[1] : chosen.text;
|
|
11668
|
+
// let durationPart = mDurTie && mDurTie[2] ? mDurTie[2] : "";
|
|
11669
|
+
// let tiePart = mDurTie && mDurTie[3] ? mDurTie[3] : "";
|
|
11670
|
+
// // Replace note keeping duration and tie
|
|
11671
|
+
// newPitch = newPitch + durationPart + tiePart;
|
|
11672
|
+
// start += beforeChordLen + chosen.index;
|
|
11673
|
+
// end = start + chosen.text.length;
|
|
11674
|
+
// }
|
|
11675
|
+
// }
|
|
11676
|
+
// return {
|
|
11677
|
+
// start: start,
|
|
11678
|
+
// end: end,
|
|
11679
|
+
// note: newPitch
|
|
11680
|
+
// };
|
|
11681
|
+
// }
|
|
11682
|
+
|
|
11186
11683
|
function replaceGrace(abc, start, end, newGrace, index) {
|
|
11187
11684
|
var note = abc.substring(start, end);
|
|
11188
11685
|
// I don't know how to capture more than one note, so I'm separating them. There is a limit of the number of notes in a chord depending on the repeats I have here, but it is unlikely to happen in real music.
|
|
@@ -12591,11 +13088,12 @@ module.exports = rendererFactory;
|
|
|
12591
13088
|
|
|
12592
13089
|
var sequence;
|
|
12593
13090
|
var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/abc_common.js");
|
|
13091
|
+
var Repeats = __webpack_require__(/*! ./repeats */ "./src/synth/repeats.js");
|
|
12594
13092
|
(function () {
|
|
12595
13093
|
"use strict";
|
|
12596
13094
|
|
|
12597
13095
|
var measureLength = 1; // This should be set by the meter, but just in case that is missing, we'll take a guess.
|
|
12598
|
-
// The abc is provided to us line by line. It might have repeats in it. We want to
|
|
13096
|
+
// The abc is provided to us line by line. It might have repeats in it. We want to rearrange the elements to
|
|
12599
13097
|
// be an array of voices with all the repeats embedded, and no lines. Then it is trivial to go through the events
|
|
12600
13098
|
// one at a time and turn it into midi.
|
|
12601
13099
|
|
|
@@ -12732,8 +13230,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12732
13230
|
timing: 0
|
|
12733
13231
|
};
|
|
12734
13232
|
var currentVolume;
|
|
12735
|
-
var
|
|
12736
|
-
var skipEndingPlaceholder = []; // This is the place where the first ending starts.
|
|
13233
|
+
var repeats = [];
|
|
12737
13234
|
var startingDrumSet = false;
|
|
12738
13235
|
var lines = abctune.lines; //abctune.deline(); TODO-PER: can switch to this, then simplify the loops below.
|
|
12739
13236
|
for (var i = 0; i < lines.length; i++) {
|
|
@@ -12812,6 +13309,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12812
13309
|
el_type: "name",
|
|
12813
13310
|
trackName: voiceName
|
|
12814
13311
|
});
|
|
13312
|
+
repeats[voiceNumber] = new Repeats(voices[voiceNumber]);
|
|
12815
13313
|
}
|
|
12816
13314
|
// Negate any transposition for the percussion staff.
|
|
12817
13315
|
if (transpose && staff.clef.type === "perc") voices[voiceNumber].push({
|
|
@@ -13010,28 +13508,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13010
13508
|
}); // We need the bar marking to reset the accidentals.
|
|
13011
13509
|
setDynamics(elem);
|
|
13012
13510
|
noteEventsInBar = 0;
|
|
13013
|
-
|
|
13014
|
-
// The important part is where there is a start repeat, and end repeat, or a first ending.
|
|
13015
|
-
var endRepeat = elem.type === "bar_right_repeat" || elem.type === "bar_dbl_repeat";
|
|
13016
|
-
var startEnding = elem.startEnding === '1';
|
|
13017
|
-
var startRepeat = elem.type === "bar_left_repeat" || elem.type === "bar_dbl_repeat" || elem.type === "bar_right_repeat";
|
|
13018
|
-
if (endRepeat) {
|
|
13019
|
-
var s = startRepeatPlaceholder[voiceNumber];
|
|
13020
|
-
if (!s) s = 0; // If there wasn't a left repeat, then we repeat from the beginning.
|
|
13021
|
-
var e = skipEndingPlaceholder[voiceNumber];
|
|
13022
|
-
if (!e) e = voices[voiceNumber].length; // If there wasn't a first ending marker, then we copy everything.
|
|
13023
|
-
// duplicate each of the elements - this has to be a deep copy.
|
|
13024
|
-
for (var z = s; z < e; z++) {
|
|
13025
|
-
var item = Object.assign({}, voices[voiceNumber][z]);
|
|
13026
|
-
if (item.pitches) item.pitches = parseCommon.cloneArray(item.pitches);
|
|
13027
|
-
voices[voiceNumber].push(item);
|
|
13028
|
-
}
|
|
13029
|
-
// reset these in case there is a second repeat later on.
|
|
13030
|
-
skipEndingPlaceholder[voiceNumber] = undefined;
|
|
13031
|
-
startRepeatPlaceholder[voiceNumber] = undefined;
|
|
13032
|
-
}
|
|
13033
|
-
if (startEnding) skipEndingPlaceholder[voiceNumber] = voices[voiceNumber].length;
|
|
13034
|
-
if (startRepeat) startRepeatPlaceholder[voiceNumber] = voices[voiceNumber].length;
|
|
13511
|
+
repeats[voiceNumber].addBar(elem, voiceNumber);
|
|
13035
13512
|
rhythmHeadThisBar = false;
|
|
13036
13513
|
break;
|
|
13037
13514
|
case 'style':
|
|
@@ -13181,6 +13658,10 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13181
13658
|
}
|
|
13182
13659
|
}
|
|
13183
13660
|
}
|
|
13661
|
+
for (var r = 0; r < repeats.length; r++) {
|
|
13662
|
+
voices[r] = repeats[r].resolveRepeats();
|
|
13663
|
+
}
|
|
13664
|
+
|
|
13184
13665
|
// If there are tempo changes, make sure they are in all the voices. This must be done post process because all the elements in all the voices need to be created first.
|
|
13185
13666
|
insertTempoChanges(voices, tempoChanges);
|
|
13186
13667
|
if (drumIntro) {
|
|
@@ -13320,6 +13801,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13320
13801
|
num: 4,
|
|
13321
13802
|
den: 4
|
|
13322
13803
|
};
|
|
13804
|
+
measureLength = 4 / 4;
|
|
13323
13805
|
break;
|
|
13324
13806
|
case "cut_time":
|
|
13325
13807
|
meter = {
|
|
@@ -13327,22 +13809,31 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13327
13809
|
num: 2,
|
|
13328
13810
|
den: 2
|
|
13329
13811
|
};
|
|
13812
|
+
measureLength = 2 / 2;
|
|
13330
13813
|
break;
|
|
13331
13814
|
case "specified":
|
|
13332
13815
|
// TODO-PER: only taking the first meter, so the complex meters are not handled.
|
|
13816
|
+
var num = 0;
|
|
13817
|
+
if (element.value && element.value.length > 0 && element.value[0].num.indexOf('+') > 0) {
|
|
13818
|
+
var parts = element.value[0].num.split('+');
|
|
13819
|
+
for (var i = 0; i < parts.length; i++) {
|
|
13820
|
+
num += parseInt(parts[i], 10);
|
|
13821
|
+
}
|
|
13822
|
+
} else num = parseInt(element.value[0].num, 10);
|
|
13333
13823
|
meter = {
|
|
13334
13824
|
el_type: 'meter',
|
|
13335
|
-
num:
|
|
13825
|
+
num: num,
|
|
13336
13826
|
den: element.value[0].den
|
|
13337
13827
|
};
|
|
13828
|
+
measureLength = num / parseInt(element.value[0].den, 10);
|
|
13338
13829
|
break;
|
|
13339
13830
|
default:
|
|
13340
13831
|
// This should never happen.
|
|
13341
13832
|
meter = {
|
|
13342
13833
|
el_type: 'meter'
|
|
13343
13834
|
};
|
|
13835
|
+
measureLength = 1;
|
|
13344
13836
|
}
|
|
13345
|
-
measureLength = meter.num / meter.den;
|
|
13346
13837
|
return meter;
|
|
13347
13838
|
}
|
|
13348
13839
|
function removeNaturals(accidentals) {
|
|
@@ -14438,7 +14929,7 @@ function CreateSynth() {
|
|
|
14438
14929
|
if (options.visualObj) {
|
|
14439
14930
|
self.flattened = options.visualObj.setUpAudio(params);
|
|
14440
14931
|
var meter = options.visualObj.getMeterFraction();
|
|
14441
|
-
if (meter.den) self.meterSize =
|
|
14932
|
+
if (meter.den) self.meterSize = meter.num / meter.den;
|
|
14442
14933
|
self.pickupLength = options.visualObj.getPickupLength();
|
|
14443
14934
|
} else if (options.sequence) self.flattened = options.sequence;else return Promise.reject(new Error("Must pass in either a visualObj or a sequence"));
|
|
14444
14935
|
self.millisecondsPerMeasure = options.millisecondsPerMeasure ? options.millisecondsPerMeasure : options.visualObj ? options.visualObj.millisecondsPerMeasure(self.flattened.tempo) : 1000;
|
|
@@ -15587,6 +16078,221 @@ module.exports = registerAudioContext;
|
|
|
15587
16078
|
|
|
15588
16079
|
/***/ }),
|
|
15589
16080
|
|
|
16081
|
+
/***/ "./src/synth/repeats.js":
|
|
16082
|
+
/*!******************************!*\
|
|
16083
|
+
!*** ./src/synth/repeats.js ***!
|
|
16084
|
+
\******************************/
|
|
16085
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
16086
|
+
|
|
16087
|
+
var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/abc_common.js");
|
|
16088
|
+
function Repeats(voice) {
|
|
16089
|
+
this.sections = [{
|
|
16090
|
+
type: 'startRepeat',
|
|
16091
|
+
index: -1
|
|
16092
|
+
}];
|
|
16093
|
+
this.addBar = function (elem) {
|
|
16094
|
+
// Record the "interesting" parts for analysis at the end.
|
|
16095
|
+
var thisIndex = voice.length - 1;
|
|
16096
|
+
var isStartRepeat = elem.type === "bar_left_repeat" || elem.type === "bar_dbl_repeat";
|
|
16097
|
+
var isEndRepeat = elem.type === "bar_right_repeat" || elem.type === "bar_dbl_repeat";
|
|
16098
|
+
var startEnding = elem.startEnding ? startEndingNumbers(elem.startEnding) : undefined;
|
|
16099
|
+
if (isEndRepeat) this.sections.push({
|
|
16100
|
+
type: "endRepeat",
|
|
16101
|
+
index: thisIndex
|
|
16102
|
+
});
|
|
16103
|
+
if (isStartRepeat) this.sections.push({
|
|
16104
|
+
type: "startRepeat",
|
|
16105
|
+
index: thisIndex
|
|
16106
|
+
});
|
|
16107
|
+
if (startEnding) this.sections.push({
|
|
16108
|
+
type: "startEnding",
|
|
16109
|
+
index: thisIndex,
|
|
16110
|
+
endings: startEnding
|
|
16111
|
+
});
|
|
16112
|
+
};
|
|
16113
|
+
this.resolveRepeats = function () {
|
|
16114
|
+
// this.sections contain all the interesting bars - start and end repeats.
|
|
16115
|
+
var e;
|
|
16116
|
+
|
|
16117
|
+
// There may be one last set of events after the last interesting bar, so capture that now.
|
|
16118
|
+
var lastSection = this.sections[this.sections.length - 1];
|
|
16119
|
+
var lastElement = voice.length - 1;
|
|
16120
|
+
if (lastSection.type === 'startRepeat') lastSection.end = lastElement;else if (lastSection.index + 1 < lastElement) this.sections.push({
|
|
16121
|
+
type: "startRepeat",
|
|
16122
|
+
index: lastSection.index + 1
|
|
16123
|
+
});
|
|
16124
|
+
|
|
16125
|
+
// console.log(voice.map((el,index) => {
|
|
16126
|
+
// return JSON.stringify({i: index, t: el.el_type, p: el.pitches ? el.pitches[0].name: undefined})
|
|
16127
|
+
// }).join("\n"))
|
|
16128
|
+
|
|
16129
|
+
// console.log(this.sections.map(s => JSON.stringify(s)).join("\n"))
|
|
16130
|
+
if (this.sections.length < 2) return voice; // If there are no repeats then don't bother copying anything
|
|
16131
|
+
|
|
16132
|
+
// Go through all the markers and turn that into an array of sets of sections in order.
|
|
16133
|
+
// The output is repeatInstructions. If "endings" is not present, then the common section should just
|
|
16134
|
+
// be copied once. If "endings" is present but is empty, that means it is a plain repeat without
|
|
16135
|
+
// endings so the common section is copied twice. If "endings" contains items, then copy the
|
|
16136
|
+
// common section followed by each ending in turn. If the last item in "endings" is -1, then
|
|
16137
|
+
// the common section should be copied one more time but there isn't a corresponding ending for it.
|
|
16138
|
+
var repeatInstructions = []; // { common: { start: number, end: number }, endings: Array<{start:number, end:number> }
|
|
16139
|
+
var currentRepeat = null;
|
|
16140
|
+
for (var i = 0; i < this.sections.length; i++) {
|
|
16141
|
+
var section = this.sections[i];
|
|
16142
|
+
//var end = i < this.sections.length-1 ? this.sections[i+1].index : lastElement
|
|
16143
|
+
switch (section.type) {
|
|
16144
|
+
case "startRepeat":
|
|
16145
|
+
if (currentRepeat) {
|
|
16146
|
+
if (!currentRepeat.common.end) currentRepeat.common.end = section.index;
|
|
16147
|
+
if (currentRepeat.endings) {
|
|
16148
|
+
for (e = 0; e < currentRepeat.endings.length; e++) {
|
|
16149
|
+
if (currentRepeat.endings[e] && !currentRepeat.endings[e].end && currentRepeat.endings[e].start !== section.index) currentRepeat.endings[e].end = section.index;
|
|
16150
|
+
}
|
|
16151
|
+
}
|
|
16152
|
+
// If the last event was an end repeat, then there is one more repeat of just the common area. (Only when there are ending markers - otherwise it is already taken care of.)
|
|
16153
|
+
if (this.sections[i - 1].type === 'endRepeat' && currentRepeat.endings && currentRepeat.endings.length) currentRepeat.endings[currentRepeat.endings.length] = {
|
|
16154
|
+
start: -1,
|
|
16155
|
+
end: -1
|
|
16156
|
+
};
|
|
16157
|
+
repeatInstructions.push(currentRepeat);
|
|
16158
|
+
}
|
|
16159
|
+
|
|
16160
|
+
// if there is a gap between the last event and this start, then
|
|
16161
|
+
// insert those items.
|
|
16162
|
+
if (currentRepeat) {
|
|
16163
|
+
var lastUsed = currentRepeat.common.end;
|
|
16164
|
+
if (currentRepeat.endings) {
|
|
16165
|
+
for (e = 0; e < currentRepeat.endings.length; e++) {
|
|
16166
|
+
if (currentRepeat.endings[e]) lastUsed = Math.max(lastUsed, currentRepeat.endings[e].end);
|
|
16167
|
+
}
|
|
16168
|
+
}
|
|
16169
|
+
if (lastUsed < section.index - 1) {
|
|
16170
|
+
console.log("gap", voice.slice(lastUsed + 1, section.index));
|
|
16171
|
+
repeatInstructions.push({
|
|
16172
|
+
common: {
|
|
16173
|
+
start: lastUsed + 1,
|
|
16174
|
+
end: section.index
|
|
16175
|
+
}
|
|
16176
|
+
});
|
|
16177
|
+
}
|
|
16178
|
+
}
|
|
16179
|
+
currentRepeat = {
|
|
16180
|
+
common: {
|
|
16181
|
+
start: section.index
|
|
16182
|
+
}
|
|
16183
|
+
};
|
|
16184
|
+
break;
|
|
16185
|
+
case "startEnding":
|
|
16186
|
+
{
|
|
16187
|
+
if (currentRepeat) {
|
|
16188
|
+
if (!currentRepeat.common.end) currentRepeat.common.end = section.index;
|
|
16189
|
+
if (!currentRepeat.endings) currentRepeat.endings = [];
|
|
16190
|
+
for (e = 0; e < section.endings.length; e++) {
|
|
16191
|
+
currentRepeat.endings[section.endings[e]] = {
|
|
16192
|
+
start: section.index + 1
|
|
16193
|
+
};
|
|
16194
|
+
}
|
|
16195
|
+
}
|
|
16196
|
+
break;
|
|
16197
|
+
}
|
|
16198
|
+
case "endRepeat":
|
|
16199
|
+
if (currentRepeat) {
|
|
16200
|
+
if (!currentRepeat.endings) currentRepeat.endings = [];
|
|
16201
|
+
if (currentRepeat.endings.length > 0) {
|
|
16202
|
+
for (e = 0; e < currentRepeat.endings.length; e++) {
|
|
16203
|
+
if (currentRepeat.endings[e] && !currentRepeat.endings[e].end) currentRepeat.endings[e].end = section.index;
|
|
16204
|
+
}
|
|
16205
|
+
}
|
|
16206
|
+
if (!currentRepeat.common.end)
|
|
16207
|
+
// This is a repeat that doesn't have first and second endings
|
|
16208
|
+
currentRepeat.common.end = section.index;
|
|
16209
|
+
}
|
|
16210
|
+
break;
|
|
16211
|
+
}
|
|
16212
|
+
}
|
|
16213
|
+
if (currentRepeat) {
|
|
16214
|
+
if (!currentRepeat.common.end) currentRepeat.common.end = lastElement;
|
|
16215
|
+
if (currentRepeat.endings) {
|
|
16216
|
+
for (e = 0; e < currentRepeat.endings.length; e++) {
|
|
16217
|
+
if (currentRepeat.endings[e] && !currentRepeat.endings[e].end) currentRepeat.endings[e].end = lastElement;
|
|
16218
|
+
}
|
|
16219
|
+
}
|
|
16220
|
+
repeatInstructions.push(currentRepeat);
|
|
16221
|
+
}
|
|
16222
|
+
// for (var x = 0; x < repeatInstructions.length; x++) {
|
|
16223
|
+
// console.log(JSON.stringify(repeatInstructions[x]))
|
|
16224
|
+
// }
|
|
16225
|
+
|
|
16226
|
+
var output = [];
|
|
16227
|
+
var lastEnd = -1;
|
|
16228
|
+
for (var r = 0; r < repeatInstructions.length; r++) {
|
|
16229
|
+
var instructions = repeatInstructions[r];
|
|
16230
|
+
if (!instructions.endings) {
|
|
16231
|
+
duplicateSpan(voice, output, instructions.common.start, instructions.common.end);
|
|
16232
|
+
} else if (instructions.endings.length === 0) {
|
|
16233
|
+
// this is when there is no endings specified - it is just a repeat
|
|
16234
|
+
duplicateSpan(voice, output, instructions.common.start, instructions.common.end);
|
|
16235
|
+
duplicateSpan(voice, output, instructions.common.start, instructions.common.end);
|
|
16236
|
+
} else {
|
|
16237
|
+
for (e = 0; e < instructions.endings.length; e++) {
|
|
16238
|
+
var ending = instructions.endings[e];
|
|
16239
|
+
if (ending) {
|
|
16240
|
+
// this is a sparse array so skip the empty ones
|
|
16241
|
+
duplicateSpan(voice, output, instructions.common.start, instructions.common.end);
|
|
16242
|
+
if (ending.start > 0) {
|
|
16243
|
+
duplicateSpan(voice, output, ending.start, ending.end);
|
|
16244
|
+
}
|
|
16245
|
+
lastEnd = Math.max(lastEnd, ending.end);
|
|
16246
|
+
}
|
|
16247
|
+
}
|
|
16248
|
+
}
|
|
16249
|
+
}
|
|
16250
|
+
return output;
|
|
16251
|
+
};
|
|
16252
|
+
}
|
|
16253
|
+
function duplicateSpan(input, output, start, end) {
|
|
16254
|
+
//console.log("dup", {start, end})
|
|
16255
|
+
for (var i = start; i <= end; i++) {
|
|
16256
|
+
output.push(duplicateItem(input[i]));
|
|
16257
|
+
}
|
|
16258
|
+
}
|
|
16259
|
+
function duplicateItem(src) {
|
|
16260
|
+
var item = Object.assign({}, src);
|
|
16261
|
+
if (item.pitches) item.pitches = parseCommon.cloneArray(item.pitches);
|
|
16262
|
+
return item;
|
|
16263
|
+
}
|
|
16264
|
+
function startEndingNumbers(startEnding) {
|
|
16265
|
+
// The ending can be in four different types: "random-string", "number", "number-number", "number,number"
|
|
16266
|
+
// If we don't get a number out of it then we will just skip the ending - we don't know what to do with it.
|
|
16267
|
+
var nums = [];
|
|
16268
|
+
var ending, endings, i;
|
|
16269
|
+
if (startEnding.indexOf(',') > 0) {
|
|
16270
|
+
endings = startEnding.split(',');
|
|
16271
|
+
for (i = 0; i < endings.length; i++) {
|
|
16272
|
+
ending = parseInt(endings[i], 10);
|
|
16273
|
+
if (ending > 0) {
|
|
16274
|
+
nums.push(ending);
|
|
16275
|
+
}
|
|
16276
|
+
}
|
|
16277
|
+
} else if (startEnding.indexOf('-') > 0) {
|
|
16278
|
+
endings = startEnding.split('-');
|
|
16279
|
+
var se = parseInt(endings[0], 10);
|
|
16280
|
+
var ee = parseInt(endings[1], 10);
|
|
16281
|
+
for (i = se; i <= ee; i++) {
|
|
16282
|
+
nums.push(i);
|
|
16283
|
+
}
|
|
16284
|
+
} else {
|
|
16285
|
+
ending = parseInt(startEnding, 10);
|
|
16286
|
+
if (ending > 0) {
|
|
16287
|
+
nums.push(ending);
|
|
16288
|
+
}
|
|
16289
|
+
}
|
|
16290
|
+
return nums;
|
|
16291
|
+
}
|
|
16292
|
+
module.exports = Repeats;
|
|
16293
|
+
|
|
16294
|
+
/***/ }),
|
|
16295
|
+
|
|
15590
16296
|
/***/ "./src/synth/sounds-cache.js":
|
|
15591
16297
|
/*!***********************************!*\
|
|
15592
16298
|
!*** ./src/synth/sounds-cache.js ***!
|
|
@@ -17823,8 +18529,11 @@ AbstractEngraver.prototype.createABCElement = function (isFirstStaff, isSingleLi
|
|
|
17823
18529
|
elemset[0] = abselem;
|
|
17824
18530
|
break;
|
|
17825
18531
|
case "tempo":
|
|
18532
|
+
// MAE 20 Nov 2025 For %%printtempo after initial header
|
|
17826
18533
|
var abselem3 = new AbsoluteElement(elem, 0, 0, 'tempo', this.tuneNumber);
|
|
17827
|
-
|
|
18534
|
+
if (!elem.suppress) {
|
|
18535
|
+
abselem3.addFixedX(new TempoElement(elem, this.tuneNumber, createNoteHead));
|
|
18536
|
+
}
|
|
17828
18537
|
elemset[0] = abselem3;
|
|
17829
18538
|
break;
|
|
17830
18539
|
case "style":
|
|
@@ -22055,6 +22764,268 @@ module.exports = drawBrace;
|
|
|
22055
22764
|
|
|
22056
22765
|
/***/ }),
|
|
22057
22766
|
|
|
22767
|
+
/***/ "./src/write/draw/chord-grid.js":
|
|
22768
|
+
/*!**************************************!*\
|
|
22769
|
+
!*** ./src/write/draw/chord-grid.js ***!
|
|
22770
|
+
\**************************************/
|
|
22771
|
+
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
|
22772
|
+
|
|
22773
|
+
var printSymbol = __webpack_require__(/*! ./print-symbol */ "./src/write/draw/print-symbol.js");
|
|
22774
|
+
var printStem = __webpack_require__(/*! ./print-stem */ "./src/write/draw/print-stem.js");
|
|
22775
|
+
function drawChordGrid(renderer, parts, leftMargin, pageWidth, fonts) {
|
|
22776
|
+
var chordFont = fonts.gchordfont;
|
|
22777
|
+
var partFont = fonts.partsfont;
|
|
22778
|
+
var annotationFont = fonts.annotationfont;
|
|
22779
|
+
var endingFont = fonts.repeatfont;
|
|
22780
|
+
var textFont = fonts.textfont;
|
|
22781
|
+
var subtitleFont = fonts.subtitlefont;
|
|
22782
|
+
var ROW_HEIGHT = 50;
|
|
22783
|
+
var ENDING_HEIGHT = 10;
|
|
22784
|
+
var ANNOTATION_HEIGHT = 14;
|
|
22785
|
+
var PART_MARGIN_TOP = 10;
|
|
22786
|
+
var PART_MARGIN_BOTTOM = 20;
|
|
22787
|
+
var TEXT_MARGIN = 16;
|
|
22788
|
+
parts.forEach(function (part) {
|
|
22789
|
+
switch (part.type) {
|
|
22790
|
+
case "text":
|
|
22791
|
+
{
|
|
22792
|
+
text(renderer, part.text, leftMargin, renderer.y, 16, textFont, null, null, false);
|
|
22793
|
+
renderer.moveY(TEXT_MARGIN);
|
|
22794
|
+
}
|
|
22795
|
+
break;
|
|
22796
|
+
case "subtitle":
|
|
22797
|
+
{
|
|
22798
|
+
text(renderer, part.subtitle, leftMargin, renderer.y + PART_MARGIN_TOP, 20, subtitleFont, null, "abcjs-subtitle", false);
|
|
22799
|
+
renderer.moveY(PART_MARGIN_BOTTOM);
|
|
22800
|
+
}
|
|
22801
|
+
break;
|
|
22802
|
+
case "part":
|
|
22803
|
+
if (part.lines.length > 0) {
|
|
22804
|
+
text(renderer, part.name, leftMargin, renderer.y + PART_MARGIN_TOP, 20, subtitleFont, part.name, "abcjs-part", false);
|
|
22805
|
+
renderer.moveY(PART_MARGIN_BOTTOM);
|
|
22806
|
+
var numCols = part.lines[0].length;
|
|
22807
|
+
var colWidth = pageWidth / numCols;
|
|
22808
|
+
part.lines.forEach(function (line, lineNum) {
|
|
22809
|
+
var hasEnding = false;
|
|
22810
|
+
var hasAnnotation = false;
|
|
22811
|
+
line.forEach(function (measure) {
|
|
22812
|
+
if (measure.ending) hasEnding = true;
|
|
22813
|
+
if (measure.annotations && measure.annotations.length > 0) hasAnnotation = true;
|
|
22814
|
+
});
|
|
22815
|
+
var extraTop = hasAnnotation ? ANNOTATION_HEIGHT : hasEnding ? ENDING_HEIGHT : 0;
|
|
22816
|
+
line.forEach(function (measure, barNum) {
|
|
22817
|
+
var RECT_WIDTH = 1;
|
|
22818
|
+
if (!measure.noBorder) {
|
|
22819
|
+
renderer.paper.rect({
|
|
22820
|
+
x: leftMargin + barNum * colWidth,
|
|
22821
|
+
y: renderer.y,
|
|
22822
|
+
width: colWidth,
|
|
22823
|
+
height: extraTop + ROW_HEIGHT
|
|
22824
|
+
});
|
|
22825
|
+
renderer.paper.rect({
|
|
22826
|
+
x: leftMargin + barNum * colWidth + RECT_WIDTH,
|
|
22827
|
+
y: renderer.y + RECT_WIDTH,
|
|
22828
|
+
width: colWidth - RECT_WIDTH * 2,
|
|
22829
|
+
height: extraTop + ROW_HEIGHT - RECT_WIDTH * 2
|
|
22830
|
+
});
|
|
22831
|
+
var repeatLeft = 0;
|
|
22832
|
+
var repeatRight = 0;
|
|
22833
|
+
var top = renderer.y;
|
|
22834
|
+
var left = leftMargin + colWidth * barNum;
|
|
22835
|
+
if (measure.hasStartRepeat) {
|
|
22836
|
+
drawRepeat(renderer, left, top, top + ROW_HEIGHT + extraTop, true, extraTop);
|
|
22837
|
+
repeatLeft = 12;
|
|
22838
|
+
}
|
|
22839
|
+
if (measure.hasEndRepeat) {
|
|
22840
|
+
drawRepeat(renderer, left + colWidth, top, top + ROW_HEIGHT + extraTop, false, extraTop);
|
|
22841
|
+
repeatRight = 12;
|
|
22842
|
+
}
|
|
22843
|
+
var endingWidth = 0;
|
|
22844
|
+
if (measure.ending) {
|
|
22845
|
+
var endingEl = text(renderer, measure.ending, leftMargin + barNum * colWidth + 4, top + 10, 12, endingFont, null, null, false);
|
|
22846
|
+
endingWidth = endingEl.getBBox().width + 4;
|
|
22847
|
+
}
|
|
22848
|
+
drawMeasure(renderer, top, leftMargin + repeatLeft, colWidth, lineNum, barNum, measure.chord, chordFont, repeatLeft + repeatRight, ROW_HEIGHT, extraTop);
|
|
22849
|
+
if (measure.annotations && measure.annotations.length > 0) {
|
|
22850
|
+
drawAnnotations(renderer, top, leftMargin + barNum * colWidth + endingWidth, measure.annotations, annotationFont);
|
|
22851
|
+
}
|
|
22852
|
+
if (extraTop) {
|
|
22853
|
+
renderer.paper.rectBeneath({
|
|
22854
|
+
x: leftMargin + barNum * colWidth,
|
|
22855
|
+
y: renderer.y,
|
|
22856
|
+
width: colWidth,
|
|
22857
|
+
height: extraTop,
|
|
22858
|
+
fill: '#e8e8e8',
|
|
22859
|
+
stroke: 'none'
|
|
22860
|
+
});
|
|
22861
|
+
}
|
|
22862
|
+
}
|
|
22863
|
+
});
|
|
22864
|
+
renderer.moveY(extraTop + ROW_HEIGHT);
|
|
22865
|
+
});
|
|
22866
|
+
renderer.moveY(PART_MARGIN_BOTTOM);
|
|
22867
|
+
}
|
|
22868
|
+
break;
|
|
22869
|
+
}
|
|
22870
|
+
});
|
|
22871
|
+
}
|
|
22872
|
+
function drawPercent(renderer, x, y, offset) {
|
|
22873
|
+
var lineX1 = x - 10;
|
|
22874
|
+
var lineX2 = x + 10;
|
|
22875
|
+
var lineY1 = y + 10;
|
|
22876
|
+
var lineY2 = y - 10;
|
|
22877
|
+
var leftDotX = x - 10;
|
|
22878
|
+
var leftDotY = -renderer.yToPitch(offset) + 2;
|
|
22879
|
+
var rightDotX = x + 6.5;
|
|
22880
|
+
var rightDotY = -renderer.yToPitch(offset) - 2.3;
|
|
22881
|
+
renderer.paper.lineToBack({
|
|
22882
|
+
x1: lineX1,
|
|
22883
|
+
x2: lineX2,
|
|
22884
|
+
y1: lineY1,
|
|
22885
|
+
y2: lineY2,
|
|
22886
|
+
'stroke-width': '3px',
|
|
22887
|
+
'stroke-linecap': "round"
|
|
22888
|
+
});
|
|
22889
|
+
printSymbol(renderer, leftDotX, leftDotY, "dots.dot", {
|
|
22890
|
+
scalex: 1,
|
|
22891
|
+
scaley: 1,
|
|
22892
|
+
klass: "",
|
|
22893
|
+
name: "dot"
|
|
22894
|
+
});
|
|
22895
|
+
printSymbol(renderer, rightDotX, rightDotY, "dots.dot", {
|
|
22896
|
+
scalex: 1,
|
|
22897
|
+
scaley: 1,
|
|
22898
|
+
klass: "",
|
|
22899
|
+
name: "dot"
|
|
22900
|
+
});
|
|
22901
|
+
}
|
|
22902
|
+
function drawRepeat(renderer, x, y1, y2, isStart, offset) {
|
|
22903
|
+
var lineX = isStart ? x + 2 : x - 4;
|
|
22904
|
+
var circleX = isStart ? x + 9 : x - 11;
|
|
22905
|
+
renderer.paper.openGroup({
|
|
22906
|
+
klass: 'abcjs-repeat'
|
|
22907
|
+
});
|
|
22908
|
+
printStem(renderer, lineX, 3 + renderer.lineThickness, y1, y2, null, "bar");
|
|
22909
|
+
printSymbol(renderer, circleX, -renderer.yToPitch(offset) - 4, "dots.dot", {
|
|
22910
|
+
scalex: 1,
|
|
22911
|
+
scaley: 1,
|
|
22912
|
+
klass: "",
|
|
22913
|
+
name: "dot"
|
|
22914
|
+
});
|
|
22915
|
+
printSymbol(renderer, circleX, -renderer.yToPitch(offset) - 8, "dots.dot", {
|
|
22916
|
+
scalex: 1,
|
|
22917
|
+
scaley: 1,
|
|
22918
|
+
klass: "",
|
|
22919
|
+
name: "dot"
|
|
22920
|
+
});
|
|
22921
|
+
renderer.paper.closeGroup();
|
|
22922
|
+
}
|
|
22923
|
+
var symbols = {
|
|
22924
|
+
'segno': "scripts.segno",
|
|
22925
|
+
'coda': "scripts.coda",
|
|
22926
|
+
"fermata": "scripts.ufermata"
|
|
22927
|
+
};
|
|
22928
|
+
function drawAnnotations(renderer, offset, left, annotations, annotationFont) {
|
|
22929
|
+
left += 3;
|
|
22930
|
+
var el;
|
|
22931
|
+
for (var a = 0; a < annotations.length; a++) {
|
|
22932
|
+
switch (annotations[a]) {
|
|
22933
|
+
case 'segno':
|
|
22934
|
+
case 'coda':
|
|
22935
|
+
case "fermata":
|
|
22936
|
+
{
|
|
22937
|
+
left += 12;
|
|
22938
|
+
el = printSymbol(renderer, left, -3, symbols[annotations[a]], {
|
|
22939
|
+
scalex: 1,
|
|
22940
|
+
scaley: 1,
|
|
22941
|
+
//klass: renderer.controller.classes.generate(klass),
|
|
22942
|
+
name: symbols[annotations[a]]
|
|
22943
|
+
});
|
|
22944
|
+
var box = el.getBBox();
|
|
22945
|
+
left += box.width;
|
|
22946
|
+
}
|
|
22947
|
+
break;
|
|
22948
|
+
default:
|
|
22949
|
+
text(renderer, annotations[a], left, offset + 12, 12, annotationFont, null, null, false);
|
|
22950
|
+
}
|
|
22951
|
+
}
|
|
22952
|
+
}
|
|
22953
|
+
function drawMeasure(renderer, offset, leftMargin, colWidth, lineNum, barNum, chords, chordFont, margin, height, extraTop) {
|
|
22954
|
+
var left = leftMargin + colWidth * barNum;
|
|
22955
|
+
if (!chords[1] && !chords[2] && !chords[3]) drawSingleChord(renderer, left, offset + extraTop, colWidth - margin, height, chords[0], chordFont, extraTop);else if (!chords[1] && !chords[3]) drawTwoChords(renderer, left, offset, colWidth - margin, height, chords[0], chords[2], chordFont, extraTop);else drawFourChords(renderer, left, offset, colWidth - margin, height, chords, chordFont, extraTop);
|
|
22956
|
+
}
|
|
22957
|
+
function renderChord(renderer, x, y, size, chord, font, maxWidth) {
|
|
22958
|
+
var el = text(renderer, chord, x, y, size, font, null, "abcjs-chord", true);
|
|
22959
|
+
var bb = el.getBBox();
|
|
22960
|
+
var fontSize = size;
|
|
22961
|
+
while (bb.width > maxWidth && fontSize >= 14) {
|
|
22962
|
+
fontSize -= 2;
|
|
22963
|
+
el.setAttribute('font-size', fontSize);
|
|
22964
|
+
bb = el.getBBox();
|
|
22965
|
+
}
|
|
22966
|
+
}
|
|
22967
|
+
var MAX_ONE_CHORD = 34;
|
|
22968
|
+
var MAX_TWO_CHORDS = 26;
|
|
22969
|
+
var MAX_FOUR_CHORDS = 20;
|
|
22970
|
+
var TOP_MARGIN = -3;
|
|
22971
|
+
function drawSingleChord(renderer, left, top, width, height, chord, font, extraTop) {
|
|
22972
|
+
if (chord === '%') drawPercent(renderer, left + width / 2, top + height / 2, extraTop + height / 2);else renderChord(renderer, left + width / 2, top + height / 2 + TOP_MARGIN, MAX_ONE_CHORD, chord, font, width);
|
|
22973
|
+
}
|
|
22974
|
+
function drawTwoChords(renderer, left, top, width, height, chord1, chord2, font, extraTop) {
|
|
22975
|
+
renderer.paper.lineToBack({
|
|
22976
|
+
x1: left,
|
|
22977
|
+
x2: left + width,
|
|
22978
|
+
y1: top + height + extraTop,
|
|
22979
|
+
y2: top + 2
|
|
22980
|
+
});
|
|
22981
|
+
renderChord(renderer, left + width / 4, top + height / 4 + 5 + extraTop + TOP_MARGIN, MAX_TWO_CHORDS, chord1, font, width / 2);
|
|
22982
|
+
renderChord(renderer, left + 3 * width / 4, top + 3 * height / 4 + extraTop + TOP_MARGIN, MAX_TWO_CHORDS, chord2, font, width / 2);
|
|
22983
|
+
}
|
|
22984
|
+
function drawFourChords(renderer, left, top, width, height, chords, font, extraTop) {
|
|
22985
|
+
var MARGIN = 3;
|
|
22986
|
+
renderer.paper.lineToBack({
|
|
22987
|
+
x1: left + MARGIN,
|
|
22988
|
+
x2: left + width - MARGIN,
|
|
22989
|
+
y1: top + height / 2 + extraTop,
|
|
22990
|
+
y2: top + height / 2 + extraTop
|
|
22991
|
+
});
|
|
22992
|
+
renderer.paper.lineToBack({
|
|
22993
|
+
x1: left + width / 2,
|
|
22994
|
+
x2: left + width / 2,
|
|
22995
|
+
y1: top + MARGIN + extraTop,
|
|
22996
|
+
y2: top + height - MARGIN + extraTop
|
|
22997
|
+
});
|
|
22998
|
+
if (chords[0]) renderChord(renderer, left + width / 4, top + height / 4 + 2 + extraTop + TOP_MARGIN, MAX_FOUR_CHORDS, shortenChord(chords[0]), font, width / 2);
|
|
22999
|
+
if (chords[1]) renderChord(renderer, left + 3 * width / 4, top + height / 4 + 2 + extraTop + TOP_MARGIN, MAX_FOUR_CHORDS, shortenChord(chords[1]), font, width / 2);
|
|
23000
|
+
if (chords[2]) renderChord(renderer, left + width / 4, top + 3 * height / 4 + extraTop + TOP_MARGIN, MAX_FOUR_CHORDS, shortenChord(chords[2]), font, width / 2);
|
|
23001
|
+
if (chords[3]) renderChord(renderer, left + 3 * width / 4, top + 3 * height / 4 + extraTop + TOP_MARGIN, MAX_FOUR_CHORDS, shortenChord(chords[3]), font, width / 2);
|
|
23002
|
+
}
|
|
23003
|
+
function shortenChord(chord) {
|
|
23004
|
+
if (chord === "No Chord") return "N.C.";
|
|
23005
|
+
return chord;
|
|
23006
|
+
}
|
|
23007
|
+
function text(renderer, str, x, y, size, font, dataName, klass, alignCenter) {
|
|
23008
|
+
var attr = {
|
|
23009
|
+
x: x,
|
|
23010
|
+
y: y,
|
|
23011
|
+
stroke: "none",
|
|
23012
|
+
'font-size': size,
|
|
23013
|
+
'font-style': font.style,
|
|
23014
|
+
'font-family': font.face,
|
|
23015
|
+
'font-weight': font.weight,
|
|
23016
|
+
'text-decoration': font.decoration
|
|
23017
|
+
};
|
|
23018
|
+
if (dataName) attr['data-name'] = dataName;
|
|
23019
|
+
if (klass) attr['class'] = klass;
|
|
23020
|
+
attr["text-anchor"] = alignCenter ? "middle" : "start";
|
|
23021
|
+
return renderer.paper.text(str, attr, null, {
|
|
23022
|
+
"alignment-baseline": "middle"
|
|
23023
|
+
});
|
|
23024
|
+
}
|
|
23025
|
+
module.exports = drawChordGrid;
|
|
23026
|
+
|
|
23027
|
+
/***/ }),
|
|
23028
|
+
|
|
22058
23029
|
/***/ "./src/write/draw/crescendo.js":
|
|
22059
23030
|
/*!*************************************!*\
|
|
22060
23031
|
!*** ./src/write/draw/crescendo.js ***!
|
|
@@ -22138,7 +23109,8 @@ var setPaperSize = __webpack_require__(/*! ./set-paper-size */ "./src/write/draw
|
|
|
22138
23109
|
var nonMusic = __webpack_require__(/*! ./non-music */ "./src/write/draw/non-music.js");
|
|
22139
23110
|
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
|
|
22140
23111
|
var Selectables = __webpack_require__(/*! ./selectables */ "./src/write/draw/selectables.js");
|
|
22141
|
-
|
|
23112
|
+
var drawChordGrid = __webpack_require__(/*! ./chord-grid */ "./src/write/draw/chord-grid.js");
|
|
23113
|
+
function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, selectTypes, tuneNumber, lineOffset, chordGrid) {
|
|
22142
23114
|
var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber);
|
|
22143
23115
|
var groupClasses = {};
|
|
22144
23116
|
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-meta-top";
|
|
@@ -22147,43 +23119,52 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
|
|
|
22147
23119
|
nonMusic(renderer, abcTune.topText, selectables);
|
|
22148
23120
|
renderer.paper.closeGroup();
|
|
22149
23121
|
renderer.moveY(renderer.spacing.music);
|
|
23122
|
+
var suppressMusic = false;
|
|
23123
|
+
if (chordGrid && abcTune.chordGrid) {
|
|
23124
|
+
drawChordGrid(renderer, abcTune.chordGrid, renderer.padding.left, width, abcTune.formatting);
|
|
23125
|
+
if (chordGrid === 'noMusic') suppressMusic = true;
|
|
23126
|
+
}
|
|
22150
23127
|
var staffgroups = [];
|
|
22151
23128
|
var nStaves = 0;
|
|
22152
|
-
|
|
22153
|
-
|
|
22154
|
-
|
|
22155
|
-
|
|
22156
|
-
|
|
22157
|
-
|
|
22158
|
-
|
|
22159
|
-
if (
|
|
22160
|
-
|
|
23129
|
+
if (!suppressMusic) {
|
|
23130
|
+
for (var line = 0; line < abcTune.lines.length; line++) {
|
|
23131
|
+
classes.incrLine();
|
|
23132
|
+
var abcLine = abcTune.lines[line];
|
|
23133
|
+
if (abcLine.staff) {
|
|
23134
|
+
// MAE 26 May 2025 - for incipits staff count limiting
|
|
23135
|
+
nStaves++;
|
|
23136
|
+
if (abcTune.formatting.maxStaves) {
|
|
23137
|
+
if (nStaves > abcTune.formatting.maxStaves) {
|
|
23138
|
+
break;
|
|
23139
|
+
}
|
|
22161
23140
|
}
|
|
23141
|
+
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber;
|
|
23142
|
+
renderer.paper.openGroup(groupClasses);
|
|
23143
|
+
if (abcLine.vskip) {
|
|
23144
|
+
renderer.moveY(abcLine.vskip);
|
|
23145
|
+
}
|
|
23146
|
+
if (staffgroups.length >= 1) addStaffPadding(renderer, renderer.spacing.staffSeparation, staffgroups[staffgroups.length - 1], abcLine.staffGroup);
|
|
23147
|
+
var staffgroup = engraveStaffLine(renderer, abcLine.staffGroup, selectables, line);
|
|
23148
|
+
staffgroup.line = lineOffset + line; // If there are non-music lines then the staffgroup array won't line up with the line array, so this keeps track.
|
|
23149
|
+
staffgroups.push(staffgroup);
|
|
23150
|
+
renderer.paper.closeGroup();
|
|
23151
|
+
} else if (abcLine.nonMusic) {
|
|
23152
|
+
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-non-music";
|
|
23153
|
+
renderer.paper.openGroup(groupClasses);
|
|
23154
|
+
nonMusic(renderer, abcLine.nonMusic, selectables);
|
|
23155
|
+
renderer.paper.closeGroup();
|
|
22162
23156
|
}
|
|
22163
|
-
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber;
|
|
22164
|
-
renderer.paper.openGroup(groupClasses);
|
|
22165
|
-
if (abcLine.vskip) {
|
|
22166
|
-
renderer.moveY(abcLine.vskip);
|
|
22167
|
-
}
|
|
22168
|
-
if (staffgroups.length >= 1) addStaffPadding(renderer, renderer.spacing.staffSeparation, staffgroups[staffgroups.length - 1], abcLine.staffGroup);
|
|
22169
|
-
var staffgroup = engraveStaffLine(renderer, abcLine.staffGroup, selectables, line);
|
|
22170
|
-
staffgroup.line = lineOffset + line; // If there are non-music lines then the staffgroup array won't line up with the line array, so this keeps track.
|
|
22171
|
-
staffgroups.push(staffgroup);
|
|
22172
|
-
renderer.paper.closeGroup();
|
|
22173
|
-
} else if (abcLine.nonMusic) {
|
|
22174
|
-
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-non-music";
|
|
22175
|
-
renderer.paper.openGroup(groupClasses);
|
|
22176
|
-
nonMusic(renderer, abcLine.nonMusic, selectables);
|
|
22177
|
-
renderer.paper.closeGroup();
|
|
22178
23157
|
}
|
|
22179
23158
|
}
|
|
22180
23159
|
classes.reset();
|
|
22181
|
-
if (
|
|
22182
|
-
if (
|
|
22183
|
-
|
|
22184
|
-
|
|
22185
|
-
|
|
22186
|
-
|
|
23160
|
+
if (!suppressMusic) {
|
|
23161
|
+
if (abcTune.bottomText && abcTune.bottomText.rows && abcTune.bottomText.rows.length > 0) {
|
|
23162
|
+
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-meta-bottom";
|
|
23163
|
+
renderer.paper.openGroup(groupClasses);
|
|
23164
|
+
renderer.moveY(24); // TODO-PER: Empirically discovered. What variable should this be?
|
|
23165
|
+
nonMusic(renderer, abcTune.bottomText, selectables);
|
|
23166
|
+
renderer.paper.closeGroup();
|
|
23167
|
+
}
|
|
22187
23168
|
}
|
|
22188
23169
|
setPaperSize(renderer, maxWidth, scale, responsive);
|
|
22189
23170
|
return {
|
|
@@ -23552,7 +24533,7 @@ function renderText(renderer, params, alreadyInGroup) {
|
|
|
23552
24533
|
|
|
23553
24534
|
// MAE 9 May 2025 for free text blocks
|
|
23554
24535
|
var text;
|
|
23555
|
-
if (params.name
|
|
24536
|
+
if (params.name === "free-text") {
|
|
23556
24537
|
text = params.text.replace(/^[ \t]*\n/gm, ' \n');
|
|
23557
24538
|
} else {
|
|
23558
24539
|
text = params.text.replace(/\n\n/g, "\n \n");
|
|
@@ -23970,6 +24951,7 @@ var EngraverController = function EngraverController(paper, params) {
|
|
|
23970
24951
|
if (params.accentAbove) this.accentAbove = params.accentAbove;
|
|
23971
24952
|
if (params.germanAlphabet) this.germanAlphabet = params.germanAlphabet;
|
|
23972
24953
|
if (params.lineThickness) this.lineThickness = params.lineThickness;
|
|
24954
|
+
if (params.chordGrid) this.chordGrid = params.chordGrid;
|
|
23973
24955
|
this.renderer.controller = this; // TODO-GD needed for highlighting
|
|
23974
24956
|
this.renderer.foregroundColor = params.foregroundColor ? params.foregroundColor : "currentColor";
|
|
23975
24957
|
if (params.ariaLabel !== undefined) this.renderer.ariaLabel = params.ariaLabel;
|
|
@@ -24177,7 +25159,7 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
24177
25159
|
}
|
|
24178
25160
|
|
|
24179
25161
|
// Do all the writing to the SVG
|
|
24180
|
-
var ret = draw(this.renderer, this.classes, abcTune, this.width, maxWidth, this.responsive, scale, this.selectTypes, tuneNumber, lineOffset);
|
|
25162
|
+
var ret = draw(this.renderer, this.classes, abcTune, this.width, maxWidth, this.responsive, scale, this.selectTypes, tuneNumber, lineOffset, this.chordGrid);
|
|
24181
25163
|
this.staffgroups = ret.staffgroups;
|
|
24182
25164
|
this.selectables = ret.selectables;
|
|
24183
25165
|
if (this.oneSvgPerLine) {
|
|
@@ -25229,7 +26211,22 @@ function createAdditionalBeams(elems, asc, beam, isGrace, dy) {
|
|
|
25229
26211
|
var auxBeamEndX = x;
|
|
25230
26212
|
var auxBeamEndY = bary + sy * (j + 1);
|
|
25231
26213
|
if (auxBeams[j].single) {
|
|
25232
|
-
|
|
26214
|
+
if (i === 0) {
|
|
26215
|
+
// This is the first note in the group, always draw the beam to the right
|
|
26216
|
+
auxBeamEndX = x + 5;
|
|
26217
|
+
} else if (i === elems.length - 1) {
|
|
26218
|
+
// This is the last note in the group, always draw the beam to the left
|
|
26219
|
+
auxBeamEndX = x - 5;
|
|
26220
|
+
} else {
|
|
26221
|
+
// This is a middle note, check the note durations of the notes to the left and right
|
|
26222
|
+
if (elems[i - 1].duration === elems[i + 1].duration) {
|
|
26223
|
+
// The notes on either side are the same duration, alternate which side the beam goes to
|
|
26224
|
+
auxBeamEndX = i % 2 === 0 ? x + 5 : x - 5;
|
|
26225
|
+
} else {
|
|
26226
|
+
// The notes on either side are different durations, draw the beam to the shorter note
|
|
26227
|
+
auxBeamEndX = elems[i - 1].duration > elems[i + 1].duration ? x + 5 : x - 5;
|
|
26228
|
+
}
|
|
26229
|
+
}
|
|
25233
26230
|
auxBeamEndY = getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, auxBeamEndX) + sy * (j + 1);
|
|
25234
26231
|
}
|
|
25235
26232
|
var b = {
|
|
@@ -26421,6 +27418,9 @@ Renderer.prototype.setVerticalSpace = function (formatting) {
|
|
|
26421
27418
|
Renderer.prototype.calcY = function (ofs) {
|
|
26422
27419
|
return this.y - ofs * spacing.STEP;
|
|
26423
27420
|
};
|
|
27421
|
+
Renderer.prototype.yToPitch = function (ofs) {
|
|
27422
|
+
return ofs / spacing.STEP;
|
|
27423
|
+
};
|
|
26424
27424
|
Renderer.prototype.moveY = function (em, numLines) {
|
|
26425
27425
|
if (numLines === undefined) numLines = 1;
|
|
26426
27426
|
this.y += em * numLines;
|
|
@@ -26581,7 +27581,7 @@ Svg.prototype.rectBeneath = function (attr) {
|
|
|
26581
27581
|
if (attr['fill-opacity']) el.setAttribute("fill-opacity", attr['fill-opacity']);
|
|
26582
27582
|
this.svg.insertBefore(el, this.svg.firstChild);
|
|
26583
27583
|
};
|
|
26584
|
-
Svg.prototype.text = function (text, attr, target) {
|
|
27584
|
+
Svg.prototype.text = function (text, attr, target, spanAttr) {
|
|
26585
27585
|
var el = document.createElementNS(svgNS, 'text');
|
|
26586
27586
|
el.setAttribute("stroke", "none");
|
|
26587
27587
|
for (var key in attr) {
|
|
@@ -26597,6 +27597,13 @@ Svg.prototype.text = function (text, attr, target) {
|
|
|
26597
27597
|
continue;
|
|
26598
27598
|
}
|
|
26599
27599
|
var line = document.createElementNS(svgNS, 'tspan');
|
|
27600
|
+
if (spanAttr) {
|
|
27601
|
+
for (var skey in spanAttr) {
|
|
27602
|
+
if (spanAttr.hasOwnProperty(skey)) {
|
|
27603
|
+
line.setAttribute(skey, spanAttr[skey]);
|
|
27604
|
+
}
|
|
27605
|
+
}
|
|
27606
|
+
}
|
|
26600
27607
|
line.setAttribute("x", attr.x ? attr.x : 0);
|
|
26601
27608
|
if (i !== 0) line.setAttribute("dy", "1.2em");
|
|
26602
27609
|
if (lines[i].indexOf("\x03") !== -1) {
|
|
@@ -26808,7 +27815,7 @@ module.exports = Svg;
|
|
|
26808
27815
|
\********************/
|
|
26809
27816
|
/***/ (function(module) {
|
|
26810
27817
|
|
|
26811
|
-
var version = '6.
|
|
27818
|
+
var version = '6.6.0';
|
|
26812
27819
|
module.exports = version;
|
|
26813
27820
|
|
|
26814
27821
|
/***/ })
|