abcjs 6.5.1 → 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 +44 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +1198 -150
- 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/edit/abc_editarea.js +53 -50
- package/src/edit/abc_editor.js +176 -157
- 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 +4 -4
- package/src/str/output.js +97 -48
- 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 +36 -3
- 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];
|
|
@@ -1988,7 +2091,11 @@ try {
|
|
|
1988
2091
|
// if we aren't in a browser, this code will crash, but it is not needed then either.
|
|
1989
2092
|
}
|
|
1990
2093
|
var EditArea = function EditArea(textareaid) {
|
|
1991
|
-
|
|
2094
|
+
this.isEditArea = true;
|
|
2095
|
+
if (typeof textareaid === "string") {
|
|
2096
|
+
this.textarea = document.getElementById(textareaid);
|
|
2097
|
+
if (!this.textarea) this.textarea = document.querySelector(textareaid);
|
|
2098
|
+
} else this.textarea = textareaid;
|
|
1992
2099
|
this.initialText = this.textarea.value;
|
|
1993
2100
|
this.isDragging = false;
|
|
1994
2101
|
};
|
|
@@ -2156,10 +2263,17 @@ var Editor = function Editor(editarea, params) {
|
|
|
2156
2263
|
// Copy all the options that will be passed through
|
|
2157
2264
|
this.abcjsParams = gatherAbcParams(params);
|
|
2158
2265
|
if (params.indicate_changed) this.indicate_changed = true;
|
|
2266
|
+
|
|
2267
|
+
// If a string is passed in then it could either be an element's ID or a selector
|
|
2268
|
+
// If an object is passed in then it could either be an EditArea or a textarea.
|
|
2159
2269
|
if (typeof editarea === "string") {
|
|
2270
|
+
// EditArea handles both the ID and the selector
|
|
2160
2271
|
this.editarea = new EditArea(editarea);
|
|
2161
2272
|
} else {
|
|
2162
|
-
|
|
2273
|
+
// If an edit area was passed in, just use it
|
|
2274
|
+
if (editarea.isEditArea) this.editarea = editarea;else
|
|
2275
|
+
// Hopefully we were passed in a textarea or equivalent.
|
|
2276
|
+
this.editarea = new EditArea(editarea);
|
|
2163
2277
|
}
|
|
2164
2278
|
this.editarea.addSelectionListener(this);
|
|
2165
2279
|
this.editarea.addChangeListener(this);
|
|
@@ -2209,6 +2323,7 @@ var Editor = function Editor(editarea, params) {
|
|
|
2209
2323
|
this.div.parentNode.insertBefore(this.warningsdiv, this.div);
|
|
2210
2324
|
}
|
|
2211
2325
|
this.onchangeCallback = params.onchange;
|
|
2326
|
+
this.redrawCallback = params.redrawCallback;
|
|
2212
2327
|
this.currentAbc = "";
|
|
2213
2328
|
this.tunes = [];
|
|
2214
2329
|
this.bReentry = false;
|
|
@@ -2265,12 +2380,14 @@ Editor.prototype.modelChanged = function () {
|
|
|
2265
2380
|
this.bReentry = true;
|
|
2266
2381
|
try {
|
|
2267
2382
|
this.timerId = null;
|
|
2383
|
+
if (this.redrawCallback) this.redrawCallback(true);
|
|
2268
2384
|
if (this.synth && this.synth.synthControl) this.synth.synthControl.disable(true);
|
|
2269
2385
|
this.tunes = renderAbc(this.div, this.currentAbc, this.abcjsParams);
|
|
2270
2386
|
if (this.tunes.length > 0) {
|
|
2271
2387
|
this.warnings = this.tunes[0].warnings;
|
|
2272
2388
|
}
|
|
2273
2389
|
this.redrawMidi();
|
|
2390
|
+
if (this.redrawCallback) this.redrawCallback(false);
|
|
2274
2391
|
} catch (error) {
|
|
2275
2392
|
console.error("ABCJS error: ", error);
|
|
2276
2393
|
if (!this.warnings) this.warnings = [];
|
|
@@ -2295,6 +2412,9 @@ Editor.prototype.paramChanged = function (engraverParams) {
|
|
|
2295
2412
|
this.currentAbc = "";
|
|
2296
2413
|
this.fireChanged();
|
|
2297
2414
|
};
|
|
2415
|
+
Editor.prototype.getTunes = function () {
|
|
2416
|
+
return this.tunes;
|
|
2417
|
+
};
|
|
2298
2418
|
Editor.prototype.synthParamChanged = function (options) {
|
|
2299
2419
|
if (!this.synth) return;
|
|
2300
2420
|
this.synth.options = {};
|
|
@@ -2434,8 +2554,8 @@ var create;
|
|
|
2434
2554
|
var tempo = commands.tempo;
|
|
2435
2555
|
var beatsPerSecond = tempo / 60;
|
|
2436
2556
|
|
|
2437
|
-
// Fix tempo for
|
|
2438
|
-
if (time.den
|
|
2557
|
+
// Fix tempo for compound meters
|
|
2558
|
+
if (time.den === 8 && time.num !== 5 && time.num !== 7) {
|
|
2439
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.
|
|
2440
2560
|
var msPerMeasure = abcTune.millisecondsPerMeasure();
|
|
2441
2561
|
tempo = 60000 / (msPerMeasure / time.num) / 2;
|
|
@@ -2585,6 +2705,7 @@ var ParseHeader = __webpack_require__(/*! ./abc_parse_header */ "./src/parse/abc
|
|
|
2585
2705
|
var ParseMusic = __webpack_require__(/*! ./abc_parse_music */ "./src/parse/abc_parse_music.js");
|
|
2586
2706
|
var Tokenizer = __webpack_require__(/*! ./abc_tokenizer */ "./src/parse/abc_tokenizer.js");
|
|
2587
2707
|
var wrap = __webpack_require__(/*! ./wrap_lines */ "./src/parse/wrap_lines.js");
|
|
2708
|
+
var chordGrid = __webpack_require__(/*! ./chord-grid */ "./src/parse/chord-grid.js");
|
|
2588
2709
|
var Tune = __webpack_require__(/*! ../data/abc_tune */ "./src/data/abc_tune.js");
|
|
2589
2710
|
var TuneBuilder = __webpack_require__(/*! ../parse/tune-builder */ "./src/parse/tune-builder.js");
|
|
2590
2711
|
var Parse = function Parse() {
|
|
@@ -2627,6 +2748,7 @@ var Parse = function Parse() {
|
|
|
2627
2748
|
};
|
|
2628
2749
|
if (tune.lineBreaks) t.lineBreaks = tune.lineBreaks;
|
|
2629
2750
|
if (tune.visualTranspose) t.visualTranspose = tune.visualTranspose;
|
|
2751
|
+
if (tune.chordGrid) t.chordGrid = tune.chordGrid;
|
|
2630
2752
|
return t;
|
|
2631
2753
|
};
|
|
2632
2754
|
function addPositioning(el, type, value) {
|
|
@@ -3176,6 +3298,22 @@ var Parse = function Parse() {
|
|
|
3176
3298
|
addHintMeasures();
|
|
3177
3299
|
}
|
|
3178
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
|
+
}
|
|
3179
3317
|
};
|
|
3180
3318
|
};
|
|
3181
3319
|
module.exports = Parse;
|
|
@@ -9202,6 +9340,344 @@ module.exports = allNotes;
|
|
|
9202
9340
|
|
|
9203
9341
|
/***/ }),
|
|
9204
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
|
+
|
|
9205
9681
|
/***/ "./src/parse/transpose-chord.js":
|
|
9206
9682
|
/*!**************************************!*\
|
|
9207
9683
|
!*** ./src/parse/transpose-chord.js ***!
|
|
@@ -9840,7 +10316,7 @@ function resolveOverlays(tune) {
|
|
|
9840
10316
|
} else if (event.el_type === "note") {
|
|
9841
10317
|
if (inOverlay) {
|
|
9842
10318
|
overlayVoice[k].voice.push(event);
|
|
9843
|
-
} else {
|
|
10319
|
+
} else if (!event.rest || event.rest.type !== 'spacer') {
|
|
9844
10320
|
durationThisBar += event.duration;
|
|
9845
10321
|
durationsPerLines[i] += event.duration;
|
|
9846
10322
|
}
|
|
@@ -10052,11 +10528,11 @@ function cleanUpSlursInLine(line, staffNum, voiceNum, currSlur) {
|
|
|
10052
10528
|
}
|
|
10053
10529
|
}
|
|
10054
10530
|
function wrapMusicLines(lines, barsperstaff) {
|
|
10055
|
-
for (i = 0; i < lines.length; i++) {
|
|
10531
|
+
for (var i = 0; i < lines.length; i++) {
|
|
10056
10532
|
if (lines[i].staff !== undefined) {
|
|
10057
|
-
for (s = 0; s < lines[i].staff.length; s++) {
|
|
10533
|
+
for (var s = 0; s < lines[i].staff.length; s++) {
|
|
10058
10534
|
var permanentItems = [];
|
|
10059
|
-
for (v = 0; v < lines[i].staff[s].voices.length; v++) {
|
|
10535
|
+
for (var v = 0; v < lines[i].staff[s].voices.length; v++) {
|
|
10060
10536
|
var voice = lines[i].staff[s].voices[v];
|
|
10061
10537
|
var barNumThisLine = 0;
|
|
10062
10538
|
for (var n = 0; n < voice.length; n++) {
|
|
@@ -10781,7 +11257,8 @@ var keyAccidentals = __webpack_require__(/*! ../const/key-accidentals */ "./src/
|
|
|
10781
11257
|
var _require = __webpack_require__(/*! ../const/relative-major */ "./src/const/relative-major.js"),
|
|
10782
11258
|
relativeMajor = _require.relativeMajor,
|
|
10783
11259
|
transposeKey = _require.transposeKey,
|
|
10784
|
-
relativeMode = _require.relativeMode
|
|
11260
|
+
relativeMode = _require.relativeMode,
|
|
11261
|
+
isLegalMode = _require.isLegalMode;
|
|
10785
11262
|
var transposeChordName = __webpack_require__(/*! ../parse/transpose-chord */ "./src/parse/transpose-chord.js");
|
|
10786
11263
|
var strTranspose;
|
|
10787
11264
|
(function () {
|
|
@@ -10844,11 +11321,12 @@ var strTranspose;
|
|
|
10844
11321
|
var match = segment.match(/^( *)([A-G])([#b]?)( ?)(\w*)/);
|
|
10845
11322
|
if (match) {
|
|
10846
11323
|
var start = count + 2 + match[1].length; // move past the 'K:' and optional white space
|
|
10847
|
-
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
|
|
10848
11326
|
var destinationKey = newKey({
|
|
10849
11327
|
root: match[2],
|
|
10850
11328
|
acc: match[3],
|
|
10851
|
-
mode:
|
|
11329
|
+
mode: mode
|
|
10852
11330
|
}, steps);
|
|
10853
11331
|
var dest = destinationKey.root + destinationKey.acc + match[4] + destinationKey.mode;
|
|
10854
11332
|
changes.push({
|
|
@@ -10912,12 +11390,18 @@ var strTranspose;
|
|
|
10912
11390
|
}
|
|
10913
11391
|
}
|
|
10914
11392
|
if (el.el_type === 'note' && el.pitches) {
|
|
10915
|
-
|
|
10916
|
-
|
|
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);
|
|
10917
11397
|
if (note.acc) measureAccidentals[note.name.toUpperCase()] = note.acc;
|
|
10918
11398
|
var newPitch = transposePitch(note, destinationKey, letterDistance, transposedMeasureAccidentals);
|
|
10919
11399
|
if (newPitch.acc) transposedMeasureAccidentals[newPitch.upper] = newPitch.acc;
|
|
10920
|
-
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
|
+
});
|
|
10921
11405
|
}
|
|
10922
11406
|
if (el.gracenotes) {
|
|
10923
11407
|
for (var g = 0; g < el.gracenotes.length; g++) {
|
|
@@ -10993,6 +11477,7 @@ var strTranspose;
|
|
|
10993
11477
|
break;
|
|
10994
11478
|
}
|
|
10995
11479
|
}
|
|
11480
|
+
var newNote;
|
|
10996
11481
|
switch (adj) {
|
|
10997
11482
|
case -2:
|
|
10998
11483
|
acc = "__";
|
|
@@ -11011,7 +11496,7 @@ var strTranspose;
|
|
|
11011
11496
|
break;
|
|
11012
11497
|
case -3:
|
|
11013
11498
|
// This requires a triple flat, so bump down the pitch and try again
|
|
11014
|
-
|
|
11499
|
+
newNote = {};
|
|
11015
11500
|
newNote.pitch = note.pitch - 1;
|
|
11016
11501
|
newNote.oct = note.oct;
|
|
11017
11502
|
newNote.name = letters[letters.indexOf(note.name) - 1];
|
|
@@ -11023,7 +11508,7 @@ var strTranspose;
|
|
|
11023
11508
|
return transposePitch(newNote, key, letterDistance + 1, measureAccidentals);
|
|
11024
11509
|
case 3:
|
|
11025
11510
|
// This requires a triple sharp, so bump up the pitch and try again
|
|
11026
|
-
|
|
11511
|
+
newNote = {};
|
|
11027
11512
|
newNote.pitch = note.pitch + 1;
|
|
11028
11513
|
newNote.oct = note.oct;
|
|
11029
11514
|
newNote.name = letters[letters.indexOf(note.name) + 1];
|
|
@@ -11072,7 +11557,8 @@ var strTranspose;
|
|
|
11072
11557
|
var regPitch = /([_^=]*)([A-Ga-g])([,']*)/;
|
|
11073
11558
|
var regNote = /([_^=]*[A-Ga-g][,']*)(\d*\/*\d*)([\>\<\-\)\.\s\\]*)/;
|
|
11074
11559
|
var regOptionalNote = /([_^=]*[A-Ga-g][,']*)?(\d*\/*\d*)?([\>\<\-\)]*)?/;
|
|
11075
|
-
var regSpace = /(\s*)
|
|
11560
|
+
//var regSpace = /(\s*)$/
|
|
11561
|
+
//var regOptionalSpace = /(\s*)/
|
|
11076
11562
|
|
|
11077
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
|
|
11078
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.
|
|
@@ -11100,48 +11586,100 @@ var strTranspose;
|
|
|
11100
11586
|
courtesy: reg[1] === currentAcc
|
|
11101
11587
|
};
|
|
11102
11588
|
}
|
|
11103
|
-
function
|
|
11104
|
-
//
|
|
11105
|
-
// This could also be a part of a chord. If so, then the particular note needs to be teased out.
|
|
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
|
|
11106
11591
|
var note = abc.substring(start, end);
|
|
11107
|
-
var match = note.match(new RegExp(regNote.source + regSpace.source), '');
|
|
11108
|
-
if (match) {
|
|
11109
|
-
// This will match a single note
|
|
11110
|
-
var noteLen = match[1].length;
|
|
11111
|
-
var trailingLen = match[2].length + match[3].length + match[4].length;
|
|
11112
|
-
var leadingLen = end - start - noteLen - trailingLen;
|
|
11113
|
-
start += leadingLen;
|
|
11114
|
-
end -= trailingLen;
|
|
11115
|
-
} else {
|
|
11116
|
-
// 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.
|
|
11117
|
-
var regPreBracket = /([^\[]*)/;
|
|
11118
|
-
var regOpenBracket = /\[/;
|
|
11119
|
-
var regCloseBracket = /\-?](\d*\/*\d*)?([\>\<\-\)]*)/;
|
|
11120
|
-
match = note.match(new RegExp(regPreBracket.source + regOpenBracket.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regOptionalNote.source + regCloseBracket.source + regSpace.source));
|
|
11121
|
-
if (match) {
|
|
11122
|
-
// This will match a chord
|
|
11123
|
-
// Get the number of chars used by the previous notes in this chord
|
|
11124
|
-
var count = 1 + match[1].length; // one character for the open bracket
|
|
11125
|
-
for (var i = 0; i < index; i++) {
|
|
11126
|
-
// index is the iteration through the chord. This function gets called for each one.
|
|
11127
|
-
if (match[i * 3 + 2]) count += match[i * 3 + 2].length;
|
|
11128
|
-
if (match[i * 3 + 3]) count += match[i * 3 + 3].length;
|
|
11129
|
-
if (match[i * 3 + 4]) count += match[i * 3 + 4].length;
|
|
11130
|
-
}
|
|
11131
|
-
start += count;
|
|
11132
|
-
var endLen = match[index * 3 + 2] ? match[index * 3 + 2].length : 0;
|
|
11133
|
-
// endLen += match[index * 3 + 3] ? match[index * 3 + 3].length : 0
|
|
11134
|
-
// endLen += match[index * 3 + 4] ? match[index * 3 + 4].length : 0
|
|
11135
11592
|
|
|
11136
|
-
|
|
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;
|
|
11137
11617
|
}
|
|
11618
|
+
if (!found) ret.push({
|
|
11619
|
+
note: array[0],
|
|
11620
|
+
index: start + regPitch.lastIndex - array[0].length
|
|
11621
|
+
});
|
|
11138
11622
|
}
|
|
11139
|
-
return
|
|
11140
|
-
start: start,
|
|
11141
|
-
end: end,
|
|
11142
|
-
note: newPitch
|
|
11143
|
-
};
|
|
11623
|
+
return ret;
|
|
11144
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
|
+
|
|
11145
11683
|
function replaceGrace(abc, start, end, newGrace, index) {
|
|
11146
11684
|
var note = abc.substring(start, end);
|
|
11147
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.
|
|
@@ -12550,11 +13088,12 @@ module.exports = rendererFactory;
|
|
|
12550
13088
|
|
|
12551
13089
|
var sequence;
|
|
12552
13090
|
var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/abc_common.js");
|
|
13091
|
+
var Repeats = __webpack_require__(/*! ./repeats */ "./src/synth/repeats.js");
|
|
12553
13092
|
(function () {
|
|
12554
13093
|
"use strict";
|
|
12555
13094
|
|
|
12556
13095
|
var measureLength = 1; // This should be set by the meter, but just in case that is missing, we'll take a guess.
|
|
12557
|
-
// 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
|
|
12558
13097
|
// be an array of voices with all the repeats embedded, and no lines. Then it is trivial to go through the events
|
|
12559
13098
|
// one at a time and turn it into midi.
|
|
12560
13099
|
|
|
@@ -12691,8 +13230,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12691
13230
|
timing: 0
|
|
12692
13231
|
};
|
|
12693
13232
|
var currentVolume;
|
|
12694
|
-
var
|
|
12695
|
-
var skipEndingPlaceholder = []; // This is the place where the first ending starts.
|
|
13233
|
+
var repeats = [];
|
|
12696
13234
|
var startingDrumSet = false;
|
|
12697
13235
|
var lines = abctune.lines; //abctune.deline(); TODO-PER: can switch to this, then simplify the loops below.
|
|
12698
13236
|
for (var i = 0; i < lines.length; i++) {
|
|
@@ -12771,6 +13309,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12771
13309
|
el_type: "name",
|
|
12772
13310
|
trackName: voiceName
|
|
12773
13311
|
});
|
|
13312
|
+
repeats[voiceNumber] = new Repeats(voices[voiceNumber]);
|
|
12774
13313
|
}
|
|
12775
13314
|
// Negate any transposition for the percussion staff.
|
|
12776
13315
|
if (transpose && staff.clef.type === "perc") voices[voiceNumber].push({
|
|
@@ -12969,28 +13508,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12969
13508
|
}); // We need the bar marking to reset the accidentals.
|
|
12970
13509
|
setDynamics(elem);
|
|
12971
13510
|
noteEventsInBar = 0;
|
|
12972
|
-
|
|
12973
|
-
// The important part is where there is a start repeat, and end repeat, or a first ending.
|
|
12974
|
-
var endRepeat = elem.type === "bar_right_repeat" || elem.type === "bar_dbl_repeat";
|
|
12975
|
-
var startEnding = elem.startEnding === '1';
|
|
12976
|
-
var startRepeat = elem.type === "bar_left_repeat" || elem.type === "bar_dbl_repeat" || elem.type === "bar_right_repeat";
|
|
12977
|
-
if (endRepeat) {
|
|
12978
|
-
var s = startRepeatPlaceholder[voiceNumber];
|
|
12979
|
-
if (!s) s = 0; // If there wasn't a left repeat, then we repeat from the beginning.
|
|
12980
|
-
var e = skipEndingPlaceholder[voiceNumber];
|
|
12981
|
-
if (!e) e = voices[voiceNumber].length; // If there wasn't a first ending marker, then we copy everything.
|
|
12982
|
-
// duplicate each of the elements - this has to be a deep copy.
|
|
12983
|
-
for (var z = s; z < e; z++) {
|
|
12984
|
-
var item = Object.assign({}, voices[voiceNumber][z]);
|
|
12985
|
-
if (item.pitches) item.pitches = parseCommon.cloneArray(item.pitches);
|
|
12986
|
-
voices[voiceNumber].push(item);
|
|
12987
|
-
}
|
|
12988
|
-
// reset these in case there is a second repeat later on.
|
|
12989
|
-
skipEndingPlaceholder[voiceNumber] = undefined;
|
|
12990
|
-
startRepeatPlaceholder[voiceNumber] = undefined;
|
|
12991
|
-
}
|
|
12992
|
-
if (startEnding) skipEndingPlaceholder[voiceNumber] = voices[voiceNumber].length;
|
|
12993
|
-
if (startRepeat) startRepeatPlaceholder[voiceNumber] = voices[voiceNumber].length;
|
|
13511
|
+
repeats[voiceNumber].addBar(elem, voiceNumber);
|
|
12994
13512
|
rhythmHeadThisBar = false;
|
|
12995
13513
|
break;
|
|
12996
13514
|
case 'style':
|
|
@@ -13140,6 +13658,10 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13140
13658
|
}
|
|
13141
13659
|
}
|
|
13142
13660
|
}
|
|
13661
|
+
for (var r = 0; r < repeats.length; r++) {
|
|
13662
|
+
voices[r] = repeats[r].resolveRepeats();
|
|
13663
|
+
}
|
|
13664
|
+
|
|
13143
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.
|
|
13144
13666
|
insertTempoChanges(voices, tempoChanges);
|
|
13145
13667
|
if (drumIntro) {
|
|
@@ -13279,6 +13801,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13279
13801
|
num: 4,
|
|
13280
13802
|
den: 4
|
|
13281
13803
|
};
|
|
13804
|
+
measureLength = 4 / 4;
|
|
13282
13805
|
break;
|
|
13283
13806
|
case "cut_time":
|
|
13284
13807
|
meter = {
|
|
@@ -13286,22 +13809,31 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13286
13809
|
num: 2,
|
|
13287
13810
|
den: 2
|
|
13288
13811
|
};
|
|
13812
|
+
measureLength = 2 / 2;
|
|
13289
13813
|
break;
|
|
13290
13814
|
case "specified":
|
|
13291
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);
|
|
13292
13823
|
meter = {
|
|
13293
13824
|
el_type: 'meter',
|
|
13294
|
-
num:
|
|
13825
|
+
num: num,
|
|
13295
13826
|
den: element.value[0].den
|
|
13296
13827
|
};
|
|
13828
|
+
measureLength = num / parseInt(element.value[0].den, 10);
|
|
13297
13829
|
break;
|
|
13298
13830
|
default:
|
|
13299
13831
|
// This should never happen.
|
|
13300
13832
|
meter = {
|
|
13301
13833
|
el_type: 'meter'
|
|
13302
13834
|
};
|
|
13835
|
+
measureLength = 1;
|
|
13303
13836
|
}
|
|
13304
|
-
measureLength = meter.num / meter.den;
|
|
13305
13837
|
return meter;
|
|
13306
13838
|
}
|
|
13307
13839
|
function removeNaturals(accidentals) {
|
|
@@ -14397,7 +14929,7 @@ function CreateSynth() {
|
|
|
14397
14929
|
if (options.visualObj) {
|
|
14398
14930
|
self.flattened = options.visualObj.setUpAudio(params);
|
|
14399
14931
|
var meter = options.visualObj.getMeterFraction();
|
|
14400
|
-
if (meter.den) self.meterSize =
|
|
14932
|
+
if (meter.den) self.meterSize = meter.num / meter.den;
|
|
14401
14933
|
self.pickupLength = options.visualObj.getPickupLength();
|
|
14402
14934
|
} else if (options.sequence) self.flattened = options.sequence;else return Promise.reject(new Error("Must pass in either a visualObj or a sequence"));
|
|
14403
14935
|
self.millisecondsPerMeasure = options.millisecondsPerMeasure ? options.millisecondsPerMeasure : options.visualObj ? options.visualObj.millisecondsPerMeasure(self.flattened.tempo) : 1000;
|
|
@@ -15546,6 +16078,221 @@ module.exports = registerAudioContext;
|
|
|
15546
16078
|
|
|
15547
16079
|
/***/ }),
|
|
15548
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
|
+
|
|
15549
16296
|
/***/ "./src/synth/sounds-cache.js":
|
|
15550
16297
|
/*!***********************************!*\
|
|
15551
16298
|
!*** ./src/synth/sounds-cache.js ***!
|
|
@@ -17782,8 +18529,11 @@ AbstractEngraver.prototype.createABCElement = function (isFirstStaff, isSingleLi
|
|
|
17782
18529
|
elemset[0] = abselem;
|
|
17783
18530
|
break;
|
|
17784
18531
|
case "tempo":
|
|
18532
|
+
// MAE 20 Nov 2025 For %%printtempo after initial header
|
|
17785
18533
|
var abselem3 = new AbsoluteElement(elem, 0, 0, 'tempo', this.tuneNumber);
|
|
17786
|
-
|
|
18534
|
+
if (!elem.suppress) {
|
|
18535
|
+
abselem3.addFixedX(new TempoElement(elem, this.tuneNumber, createNoteHead));
|
|
18536
|
+
}
|
|
17787
18537
|
elemset[0] = abselem3;
|
|
17788
18538
|
break;
|
|
17789
18539
|
case "style":
|
|
@@ -22014,6 +22764,268 @@ module.exports = drawBrace;
|
|
|
22014
22764
|
|
|
22015
22765
|
/***/ }),
|
|
22016
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
|
+
|
|
22017
23029
|
/***/ "./src/write/draw/crescendo.js":
|
|
22018
23030
|
/*!*************************************!*\
|
|
22019
23031
|
!*** ./src/write/draw/crescendo.js ***!
|
|
@@ -22097,7 +23109,8 @@ var setPaperSize = __webpack_require__(/*! ./set-paper-size */ "./src/write/draw
|
|
|
22097
23109
|
var nonMusic = __webpack_require__(/*! ./non-music */ "./src/write/draw/non-music.js");
|
|
22098
23110
|
var spacing = __webpack_require__(/*! ../helpers/spacing */ "./src/write/helpers/spacing.js");
|
|
22099
23111
|
var Selectables = __webpack_require__(/*! ./selectables */ "./src/write/draw/selectables.js");
|
|
22100
|
-
|
|
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) {
|
|
22101
23114
|
var selectables = new Selectables(renderer.paper, selectTypes, tuneNumber);
|
|
22102
23115
|
var groupClasses = {};
|
|
22103
23116
|
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-meta-top";
|
|
@@ -22106,43 +23119,52 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
|
|
|
22106
23119
|
nonMusic(renderer, abcTune.topText, selectables);
|
|
22107
23120
|
renderer.paper.closeGroup();
|
|
22108
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
|
+
}
|
|
22109
23127
|
var staffgroups = [];
|
|
22110
23128
|
var nStaves = 0;
|
|
22111
|
-
|
|
22112
|
-
|
|
22113
|
-
|
|
22114
|
-
|
|
22115
|
-
|
|
22116
|
-
|
|
22117
|
-
|
|
22118
|
-
if (
|
|
22119
|
-
|
|
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
|
+
}
|
|
22120
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();
|
|
22121
23156
|
}
|
|
22122
|
-
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber;
|
|
22123
|
-
renderer.paper.openGroup(groupClasses);
|
|
22124
|
-
if (abcLine.vskip) {
|
|
22125
|
-
renderer.moveY(abcLine.vskip);
|
|
22126
|
-
}
|
|
22127
|
-
if (staffgroups.length >= 1) addStaffPadding(renderer, renderer.spacing.staffSeparation, staffgroups[staffgroups.length - 1], abcLine.staffGroup);
|
|
22128
|
-
var staffgroup = engraveStaffLine(renderer, abcLine.staffGroup, selectables, line);
|
|
22129
|
-
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.
|
|
22130
|
-
staffgroups.push(staffgroup);
|
|
22131
|
-
renderer.paper.closeGroup();
|
|
22132
|
-
} else if (abcLine.nonMusic) {
|
|
22133
|
-
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-non-music";
|
|
22134
|
-
renderer.paper.openGroup(groupClasses);
|
|
22135
|
-
nonMusic(renderer, abcLine.nonMusic, selectables);
|
|
22136
|
-
renderer.paper.closeGroup();
|
|
22137
23157
|
}
|
|
22138
23158
|
}
|
|
22139
23159
|
classes.reset();
|
|
22140
|
-
if (
|
|
22141
|
-
if (
|
|
22142
|
-
|
|
22143
|
-
|
|
22144
|
-
|
|
22145
|
-
|
|
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
|
+
}
|
|
22146
23168
|
}
|
|
22147
23169
|
setPaperSize(renderer, maxWidth, scale, responsive);
|
|
22148
23170
|
return {
|
|
@@ -23511,7 +24533,7 @@ function renderText(renderer, params, alreadyInGroup) {
|
|
|
23511
24533
|
|
|
23512
24534
|
// MAE 9 May 2025 for free text blocks
|
|
23513
24535
|
var text;
|
|
23514
|
-
if (params.name
|
|
24536
|
+
if (params.name === "free-text") {
|
|
23515
24537
|
text = params.text.replace(/^[ \t]*\n/gm, ' \n');
|
|
23516
24538
|
} else {
|
|
23517
24539
|
text = params.text.replace(/\n\n/g, "\n \n");
|
|
@@ -23929,6 +24951,7 @@ var EngraverController = function EngraverController(paper, params) {
|
|
|
23929
24951
|
if (params.accentAbove) this.accentAbove = params.accentAbove;
|
|
23930
24952
|
if (params.germanAlphabet) this.germanAlphabet = params.germanAlphabet;
|
|
23931
24953
|
if (params.lineThickness) this.lineThickness = params.lineThickness;
|
|
24954
|
+
if (params.chordGrid) this.chordGrid = params.chordGrid;
|
|
23932
24955
|
this.renderer.controller = this; // TODO-GD needed for highlighting
|
|
23933
24956
|
this.renderer.foregroundColor = params.foregroundColor ? params.foregroundColor : "currentColor";
|
|
23934
24957
|
if (params.ariaLabel !== undefined) this.renderer.ariaLabel = params.ariaLabel;
|
|
@@ -24136,7 +25159,7 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
24136
25159
|
}
|
|
24137
25160
|
|
|
24138
25161
|
// Do all the writing to the SVG
|
|
24139
|
-
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);
|
|
24140
25163
|
this.staffgroups = ret.staffgroups;
|
|
24141
25164
|
this.selectables = ret.selectables;
|
|
24142
25165
|
if (this.oneSvgPerLine) {
|
|
@@ -25188,7 +26211,22 @@ function createAdditionalBeams(elems, asc, beam, isGrace, dy) {
|
|
|
25188
26211
|
var auxBeamEndX = x;
|
|
25189
26212
|
var auxBeamEndY = bary + sy * (j + 1);
|
|
25190
26213
|
if (auxBeams[j].single) {
|
|
25191
|
-
|
|
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
|
+
}
|
|
25192
26230
|
auxBeamEndY = getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, auxBeamEndX) + sy * (j + 1);
|
|
25193
26231
|
}
|
|
25194
26232
|
var b = {
|
|
@@ -26380,6 +27418,9 @@ Renderer.prototype.setVerticalSpace = function (formatting) {
|
|
|
26380
27418
|
Renderer.prototype.calcY = function (ofs) {
|
|
26381
27419
|
return this.y - ofs * spacing.STEP;
|
|
26382
27420
|
};
|
|
27421
|
+
Renderer.prototype.yToPitch = function (ofs) {
|
|
27422
|
+
return ofs / spacing.STEP;
|
|
27423
|
+
};
|
|
26383
27424
|
Renderer.prototype.moveY = function (em, numLines) {
|
|
26384
27425
|
if (numLines === undefined) numLines = 1;
|
|
26385
27426
|
this.y += em * numLines;
|
|
@@ -26540,7 +27581,7 @@ Svg.prototype.rectBeneath = function (attr) {
|
|
|
26540
27581
|
if (attr['fill-opacity']) el.setAttribute("fill-opacity", attr['fill-opacity']);
|
|
26541
27582
|
this.svg.insertBefore(el, this.svg.firstChild);
|
|
26542
27583
|
};
|
|
26543
|
-
Svg.prototype.text = function (text, attr, target) {
|
|
27584
|
+
Svg.prototype.text = function (text, attr, target, spanAttr) {
|
|
26544
27585
|
var el = document.createElementNS(svgNS, 'text');
|
|
26545
27586
|
el.setAttribute("stroke", "none");
|
|
26546
27587
|
for (var key in attr) {
|
|
@@ -26556,6 +27597,13 @@ Svg.prototype.text = function (text, attr, target) {
|
|
|
26556
27597
|
continue;
|
|
26557
27598
|
}
|
|
26558
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
|
+
}
|
|
26559
27607
|
line.setAttribute("x", attr.x ? attr.x : 0);
|
|
26560
27608
|
if (i !== 0) line.setAttribute("dy", "1.2em");
|
|
26561
27609
|
if (lines[i].indexOf("\x03") !== -1) {
|
|
@@ -26767,7 +27815,7 @@ module.exports = Svg;
|
|
|
26767
27815
|
\********************/
|
|
26768
27816
|
/***/ (function(module) {
|
|
26769
27817
|
|
|
26770
|
-
var version = '6.
|
|
27818
|
+
var version = '6.6.0';
|
|
26771
27819
|
module.exports = version;
|
|
26772
27820
|
|
|
26773
27821
|
/***/ })
|