abcjs 6.4.3 → 6.5.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/RELEASE.md +58 -1
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +445 -149
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/package.json +1 -1
- package/src/parse/abc_parse_directive.js +36 -4
- package/src/parse/abc_parse_header.js +3 -3
- package/src/parse/abc_parse_key_voice.js +41 -6
- package/src/parse/abc_parse_music.js +10 -4
- package/src/parse/abc_parse_settings.js +1 -0
- package/src/parse/abc_tokenizer.js +11 -3
- package/src/parse/abc_transpose.js +1 -1
- package/src/parse/transpose-chord.js +9 -1
- package/src/parse/tune-builder.js +14 -2
- package/src/str/output.js +4 -4
- package/src/synth/abc_midi_flattener.js +61 -21
- package/src/synth/abc_midi_sequencer.js +26 -6
- package/src/synth/chord-track.js +1 -1
- package/src/synth/create-synth.js +74 -65
- package/src/synth/place-note.js +1 -1
- package/src/test/abc_midi_sequencer_lint.js +1 -1
- package/src/test/abc_parser_lint.js +1 -1
- package/src/write/creation/abstract-engraver.js +11 -7
- package/src/write/creation/decoration.js +2 -0
- package/src/write/creation/elements/absolute-element.js +27 -16
- package/src/write/creation/elements/free-text.js +6 -1
- package/src/write/draw/draw.js +8 -0
- package/src/write/draw/ending.js +12 -3
- package/src/write/draw/text.js +8 -1
- package/src/write/interactive/selection.js +7 -1
- package/src/write/svg.js +23 -2
- package/types/index.d.ts +1 -1
- package/version.js +1 -1
package/dist/abcjs-basic.js
CHANGED
|
@@ -617,6 +617,7 @@ var tunebook = {};
|
|
|
617
617
|
}
|
|
618
618
|
}
|
|
619
619
|
}
|
|
620
|
+
|
|
620
621
|
return staves;
|
|
621
622
|
}
|
|
622
623
|
function measuresParser(staff, tune) {
|
|
@@ -1340,6 +1341,7 @@ var Tune = function Tune() {
|
|
|
1340
1341
|
};
|
|
1341
1342
|
return this.meter; // TODO-PER: is this saved value used anywhere? A get function shouldn't change state.
|
|
1342
1343
|
};
|
|
1344
|
+
|
|
1343
1345
|
this.getKeySignature = function () {
|
|
1344
1346
|
for (var i = 0; i < this.lines.length; i++) {
|
|
1345
1347
|
var line = this.lines[i];
|
|
@@ -1494,6 +1496,7 @@ var Tune = function Tune() {
|
|
|
1494
1496
|
// isTiedState = voiceTimeMilliseconds;
|
|
1495
1497
|
}
|
|
1496
1498
|
}
|
|
1499
|
+
|
|
1497
1500
|
return {
|
|
1498
1501
|
isTiedState: isTiedState,
|
|
1499
1502
|
duration: realDuration / timeDivider,
|
|
@@ -3119,6 +3122,7 @@ var Parse = function Parse() {
|
|
|
3119
3122
|
multilineVars.lineBreaks = switches.lineBreaks;
|
|
3120
3123
|
//multilineVars.continueall = true;
|
|
3121
3124
|
}
|
|
3125
|
+
|
|
3122
3126
|
header.reset(tokenizer, warn, multilineVars, tune);
|
|
3123
3127
|
try {
|
|
3124
3128
|
if (switches.format) {
|
|
@@ -3208,6 +3212,7 @@ var bookParser = function bookParser(book) {
|
|
|
3208
3212
|
});
|
|
3209
3213
|
pos += tune.length + 1; // We also lost a newline when splitting, so count that.
|
|
3210
3214
|
});
|
|
3215
|
+
|
|
3211
3216
|
if (tunes.length > 1 && !parseCommon.startsWith(tunes[0].abc, 'X:')) {
|
|
3212
3217
|
// If there is only one tune, the X: might be missing, otherwise assume the top of the file is "intertune"
|
|
3213
3218
|
// There could be file-wide directives in this, if so, we need to insert it into each tune. We can probably get away with
|
|
@@ -4314,11 +4319,24 @@ var parseDirective = {};
|
|
|
4314
4319
|
}
|
|
4315
4320
|
multilineVars.currBarNumber = tuneBuilder.setBarNumberImmediate(tokens[0].intt);
|
|
4316
4321
|
break;
|
|
4322
|
+
case "keywarn":
|
|
4323
|
+
if (tokens.length !== 1 || tokens[0].type !== 'number' || tokens[0].intt !== 1 && tokens[0].intt !== 0) {
|
|
4324
|
+
return 'Directive ' + cmd + ' requires 0 or 1 as a parameter.';
|
|
4325
|
+
}
|
|
4326
|
+
multilineVars[cmd] = tokens[0].intt === 1;
|
|
4327
|
+
break;
|
|
4317
4328
|
case "begintext":
|
|
4318
4329
|
var textBlock = '';
|
|
4319
4330
|
line = tokenizer.nextLine();
|
|
4320
4331
|
while (line && line.indexOf('%%endtext') !== 0) {
|
|
4321
|
-
|
|
4332
|
+
// MAE 9 May 2025 - for text blocks with just white space
|
|
4333
|
+
if (parseCommon.startsWith(line, "%%")) {
|
|
4334
|
+
var theLine = line.substring(2);
|
|
4335
|
+
theLine = theLine.trim() + "\n";
|
|
4336
|
+
textBlock += theLine;
|
|
4337
|
+
} else {
|
|
4338
|
+
textBlock += line.trim() + "\n";
|
|
4339
|
+
}
|
|
4322
4340
|
line = tokenizer.nextLine();
|
|
4323
4341
|
}
|
|
4324
4342
|
tuneBuilder.addText(textBlock, {
|
|
@@ -4503,6 +4521,14 @@ var parseDirective = {};
|
|
|
4503
4521
|
}
|
|
4504
4522
|
}
|
|
4505
4523
|
break;
|
|
4524
|
+
case "maxstaves":
|
|
4525
|
+
var nStaves = tokenizer.getInt(restOfString);
|
|
4526
|
+
if (nStaves.digits === 0) warn("Expected number of staves in maxstaves");else {
|
|
4527
|
+
if (nStaves.value > 0) {
|
|
4528
|
+
tune.formatting.maxStaves = nStaves.value;
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
break;
|
|
4506
4532
|
case "newpage":
|
|
4507
4533
|
var pgNum = tokenizer.getInt(restOfString);
|
|
4508
4534
|
tuneBuilder.addNewPage(pgNum.digits === 0 ? -1 : pgNum.value);
|
|
@@ -4563,6 +4589,10 @@ var parseDirective = {};
|
|
|
4563
4589
|
tune.formatting.percmap[percmap.key] = percmap.value;
|
|
4564
4590
|
}
|
|
4565
4591
|
break;
|
|
4592
|
+
case "visualtranspose":
|
|
4593
|
+
var halfSteps = tokenizer.getInt(restOfString);
|
|
4594
|
+
if (halfSteps.digits === 0) warn("Expected number of half steps in visualTranspose");else multilineVars.globalTranspose = halfSteps.value;
|
|
4595
|
+
break;
|
|
4566
4596
|
case "map":
|
|
4567
4597
|
case "playtempo":
|
|
4568
4598
|
case "auquality":
|
|
@@ -4780,6 +4810,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
4780
4810
|
}
|
|
4781
4811
|
return ret; // just to suppress warning
|
|
4782
4812
|
};
|
|
4813
|
+
|
|
4783
4814
|
var parseFraction = function parseFraction() {
|
|
4784
4815
|
// handles this much: parseNum slash decimal
|
|
4785
4816
|
var ret = parseNum();
|
|
@@ -4810,6 +4841,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
4810
4841
|
//var tok = tokens.shift();
|
|
4811
4842
|
//if (tok.token !== '+') throw "Extra characters in M: line";
|
|
4812
4843
|
}
|
|
4844
|
+
|
|
4813
4845
|
if (multilineVars.havent_set_length === true) {
|
|
4814
4846
|
multilineVars.default_length = totalLength < 0.75 ? 0.0625 : 0.125;
|
|
4815
4847
|
multilineVars.havent_set_length = false;
|
|
@@ -5085,6 +5117,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5085
5117
|
// TODO: complain about unhandled header
|
|
5086
5118
|
}
|
|
5087
5119
|
}
|
|
5120
|
+
|
|
5088
5121
|
return [0];
|
|
5089
5122
|
};
|
|
5090
5123
|
this.letter_to_body_header = function (line, i) {
|
|
@@ -5101,8 +5134,8 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5101
5134
|
return [line.length];
|
|
5102
5135
|
case "K:":
|
|
5103
5136
|
var result = parseKeyVoice.parseKey(line.substring(i + 2), tuneBuilder.hasBeginMusic());
|
|
5104
|
-
if (result.foundClef && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('clef', multilineVars.iChar + i, multilineVars.iChar + line.length, multilineVars.clef);
|
|
5105
|
-
if (result.foundKey && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('key', multilineVars.iChar + i, multilineVars.iChar + line.length, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
|
|
5137
|
+
if (result.foundClef && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false) tuneBuilder.appendStartingElement('clef', multilineVars.iChar + i, multilineVars.iChar + line.length, multilineVars.clef);
|
|
5138
|
+
if (result.foundKey && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false) tuneBuilder.appendStartingElement('key', multilineVars.iChar + i, multilineVars.iChar + line.length, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
|
|
5106
5139
|
return [line.length];
|
|
5107
5140
|
case "P:":
|
|
5108
5141
|
if (tuneBuilder.hasBeginMusic()) tuneBuilder.appendElement('part', multilineVars.iChar + i, multilineVars.iChar + line.length, {
|
|
@@ -5126,6 +5159,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5126
5159
|
// TODO: complain about unhandled header
|
|
5127
5160
|
}
|
|
5128
5161
|
}
|
|
5162
|
+
|
|
5129
5163
|
return [0];
|
|
5130
5164
|
};
|
|
5131
5165
|
var metaTextHeaders = {
|
|
@@ -5183,7 +5217,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5183
5217
|
// since the key is the last thing that can happen in the header, we can resolve the tempo now
|
|
5184
5218
|
this.resolveTempo();
|
|
5185
5219
|
var result = parseKeyVoice.parseKey(line.substring(2), false);
|
|
5186
|
-
if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic()) {
|
|
5220
|
+
if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false) {
|
|
5187
5221
|
if (result.foundClef) tuneBuilder.appendStartingElement('clef', startChar, endChar, multilineVars.clef);
|
|
5188
5222
|
if (result.foundKey) tuneBuilder.appendStartingElement('key', startChar, endChar, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
|
|
5189
5223
|
}
|
|
@@ -5592,6 +5626,7 @@ var parseKeyVoice = {};
|
|
|
5592
5626
|
str: str.substring(i)
|
|
5593
5627
|
}; // We get the note in the middle of the staff. We want the note that appears as the first ledger line below the staff.
|
|
5594
5628
|
};
|
|
5629
|
+
|
|
5595
5630
|
var normalizeAccidentals = function normalizeAccidentals(accs) {
|
|
5596
5631
|
for (var i = 0; i < accs.length; i++) {
|
|
5597
5632
|
if (accs[i].note === 'b') accs[i].note = 'B';else if (accs[i].note === 'a') accs[i].note = 'A';else if (accs[i].note === 'F') accs[i].note = 'f';else if (accs[i].note === 'E') accs[i].note = 'e';else if (accs[i].note === 'D') accs[i].note = 'd';else if (accs[i].note === 'C') accs[i].note = 'c';else if (accs[i].note === 'G' && accs[i].acc === 'sharp') accs[i].note = 'g';else if (accs[i].note === 'g' && accs[i].acc === 'flat') accs[i].note = 'G';
|
|
@@ -5714,7 +5749,7 @@ var parseKeyVoice = {};
|
|
|
5714
5749
|
multilineVars.key = parseKeyVoice.deepCopyKey(parseKeyVoice.standardKey(key, retPitch.token, acc, keyCompensate));
|
|
5715
5750
|
if (isInline) multilineVars.globalTransposeOrigKeySig = savedOrigKey;
|
|
5716
5751
|
multilineVars.key.mode = mode;
|
|
5717
|
-
if (oldKey) {
|
|
5752
|
+
if (oldKey && multilineVars.keywarn !== false) {
|
|
5718
5753
|
// Add natural in all places that the old key had an accidental.
|
|
5719
5754
|
var kk;
|
|
5720
5755
|
for (var k = 0; k < multilineVars.key.accidentals.length; k++) {
|
|
@@ -6008,6 +6043,7 @@ var parseKeyVoice = {};
|
|
|
6008
6043
|
if (multilineVars.currentVoice) {
|
|
6009
6044
|
if (multilineVars.currentVoice.index === currentVoice.index && multilineVars.currentVoice.staffNum === currentVoice.staffNum) return; // there was no change so don't reset it.
|
|
6010
6045
|
}
|
|
6046
|
+
|
|
6011
6047
|
multilineVars.currentVoice = currentVoice;
|
|
6012
6048
|
return tuneBuilder.setCurrentVoice(currentVoice.staffNum, currentVoice.index, id);
|
|
6013
6049
|
};
|
|
@@ -6124,12 +6160,47 @@ var parseKeyVoice = {};
|
|
|
6124
6160
|
case 'tenor,,':
|
|
6125
6161
|
case 'alto,,':
|
|
6126
6162
|
case 'none,,':
|
|
6163
|
+
// MAE 26 May 2025 Start of additional clefs
|
|
6164
|
+
case 'treble+8':
|
|
6165
|
+
case 'treble-8':
|
|
6166
|
+
case 'treble^8':
|
|
6167
|
+
case 'treble_8':
|
|
6168
|
+
case 'treble1':
|
|
6169
|
+
case 'treble2':
|
|
6170
|
+
case 'treble3':
|
|
6171
|
+
case 'treble4':
|
|
6172
|
+
case 'treble5':
|
|
6173
|
+
case 'bass+8':
|
|
6174
|
+
case 'bass-8':
|
|
6175
|
+
case 'bass^8':
|
|
6176
|
+
case 'bass_8':
|
|
6177
|
+
case 'bass+16':
|
|
6178
|
+
case 'bass-16':
|
|
6179
|
+
case 'bass^16':
|
|
6180
|
+
case 'bass_16':
|
|
6181
|
+
case 'bass1':
|
|
6182
|
+
case 'bass2':
|
|
6183
|
+
case 'bass3':
|
|
6184
|
+
case 'bass4':
|
|
6185
|
+
case 'bass5':
|
|
6186
|
+
case 'tenor1':
|
|
6187
|
+
case 'tenor2':
|
|
6188
|
+
case 'tenor3':
|
|
6189
|
+
case 'tenor4':
|
|
6190
|
+
case 'tenor5':
|
|
6191
|
+
case 'alto1':
|
|
6192
|
+
case 'alto2':
|
|
6193
|
+
case 'alto3':
|
|
6194
|
+
case 'alto4':
|
|
6195
|
+
case 'alto5':
|
|
6196
|
+
case 'alto+8':
|
|
6197
|
+
case 'alto-8':
|
|
6198
|
+
case 'alto^8':
|
|
6199
|
+
case 'alto_8':
|
|
6200
|
+
// MAE 26 May 2025 End of additional clefs
|
|
6201
|
+
|
|
6127
6202
|
// TODO-PER: handle the octave indicators on the clef by changing the middle property
|
|
6128
6203
|
var oct2 = 0;
|
|
6129
|
-
// for (var iii = 0; iii < token.token.length; iii++) {
|
|
6130
|
-
// if (token.token[iii] === ',') oct2 -= 7;
|
|
6131
|
-
// else if (token.token[iii] === "'") oct2 += 7;
|
|
6132
|
-
// }
|
|
6133
6204
|
staffInfo.clef = token.token.replace(/[',]/g, ""); //'//comment for emacs formatting of regexp
|
|
6134
6205
|
staffInfo.verticalPos = calcMiddle(staffInfo.clef, oct2);
|
|
6135
6206
|
multilineVars.voices[id].clef = token.token;
|
|
@@ -6223,6 +6294,7 @@ var parseKeyVoice = {};
|
|
|
6223
6294
|
// console.log("parse voice", token, tune.metaText.title);
|
|
6224
6295
|
}
|
|
6225
6296
|
}
|
|
6297
|
+
|
|
6226
6298
|
start += tokenizer.eatWhiteSpace(line, start);
|
|
6227
6299
|
}
|
|
6228
6300
|
|
|
@@ -6397,6 +6469,7 @@ MusicParser.prototype.parseMusic = function (line) {
|
|
|
6397
6469
|
// delayStartNewLine = true;
|
|
6398
6470
|
// TODO-PER: Handle inline headers
|
|
6399
6471
|
}
|
|
6472
|
+
|
|
6400
6473
|
var overlayLevel = 0;
|
|
6401
6474
|
while (i < line.length) {
|
|
6402
6475
|
var startI = i;
|
|
@@ -6535,8 +6608,7 @@ MusicParser.prototype.parseMusic = function (line) {
|
|
|
6535
6608
|
if (el.chord !== undefined) bar.chord = el.chord;
|
|
6536
6609
|
if (bar.startEnding && multilineVars.barFirstEndingNum === undefined) multilineVars.barFirstEndingNum = multilineVars.currBarNumber;else if (bar.startEnding && bar.endEnding && multilineVars.barFirstEndingNum) multilineVars.currBarNumber = multilineVars.barFirstEndingNum;else if (bar.endEnding) multilineVars.barFirstEndingNum = undefined;
|
|
6537
6610
|
if (bar.type !== 'bar_invisible' && multilineVars.measureNotEmpty) {
|
|
6538
|
-
|
|
6539
|
-
if (isFirstVoice) {
|
|
6611
|
+
if (isFirstVoice()) {
|
|
6540
6612
|
multilineVars.currBarNumber++;
|
|
6541
6613
|
if (multilineVars.barNumbers && multilineVars.currBarNumber % multilineVars.barNumbers === 0) bar.barNumber = multilineVars.currBarNumber;
|
|
6542
6614
|
}
|
|
@@ -6731,6 +6803,7 @@ MusicParser.prototype.parseMusic = function (line) {
|
|
|
6731
6803
|
if (el.startTie !== undefined) el.pitches[0].startTie = el.startTie;
|
|
6732
6804
|
} else {
|
|
6733
6805
|
el.rest = core.rest;
|
|
6806
|
+
if (core.rest.type === 'multimeasure' && isFirstVoice()) multilineVars.currBarNumber += core.rest.text - 1; // The minus one is because the measure with the rest is already counted once normally.
|
|
6734
6807
|
if (core.endSlur !== undefined) el.endSlur = core.endSlur;
|
|
6735
6808
|
if (core.endTie !== undefined) el.rest.endTie = core.endTie;
|
|
6736
6809
|
if (core.startSlur !== undefined) el.startSlur = core.startSlur;
|
|
@@ -7023,6 +7096,8 @@ var letter_to_accent = function letter_to_accent(line, i) {
|
|
|
7023
7096
|
return [1, 'segno'];
|
|
7024
7097
|
case 'T':
|
|
7025
7098
|
return [1, 'trill'];
|
|
7099
|
+
case 't':
|
|
7100
|
+
return [1, 'trillh'];
|
|
7026
7101
|
}
|
|
7027
7102
|
return [0, 0];
|
|
7028
7103
|
};
|
|
@@ -7191,8 +7266,7 @@ MusicParser.prototype.startNewLine = function () {
|
|
|
7191
7266
|
if (params.currentVoice.staffNum === multilineVars.voices[voices[mv]].staffNum && params.currentVoice.index === multilineVars.voices[voices[mv]].index) params.currentVoiceName = voices[mv];
|
|
7192
7267
|
}
|
|
7193
7268
|
}
|
|
7194
|
-
|
|
7195
|
-
if (multilineVars.barNumbers === 0 && isFirstVoice && multilineVars.currBarNumber !== 1) params.barNumber = multilineVars.currBarNumber;
|
|
7269
|
+
if (multilineVars.barNumbers === 0 && isFirstVoice() && multilineVars.currBarNumber !== 1) params.barNumber = multilineVars.currBarNumber;
|
|
7196
7270
|
tuneBuilder.startNewLine(params);
|
|
7197
7271
|
if (multilineVars.key.impliedNaturals) delete multilineVars.key.impliedNaturals;
|
|
7198
7272
|
multilineVars.partForNextLine = {};
|
|
@@ -7488,6 +7562,9 @@ var getBrokenRhythm = function getBrokenRhythm(line, index) {
|
|
|
7488
7562
|
}
|
|
7489
7563
|
return null;
|
|
7490
7564
|
};
|
|
7565
|
+
function isFirstVoice() {
|
|
7566
|
+
return multilineVars.currentVoice === undefined || multilineVars.currentVoice.staffNum === 0 && multilineVars.currentVoice.index === 0;
|
|
7567
|
+
}
|
|
7491
7568
|
module.exports = MusicParser;
|
|
7492
7569
|
|
|
7493
7570
|
/***/ }),
|
|
@@ -7498,7 +7575,7 @@ module.exports = MusicParser;
|
|
|
7498
7575
|
\*****************************************/
|
|
7499
7576
|
/***/ (function(module) {
|
|
7500
7577
|
|
|
7501
|
-
module.exports.legalAccents = ['trill', 'lowermordent', 'uppermordent', 'mordent', 'pralltriller', 'accent', 'fermata', 'invertedfermata', 'tenuto', '0', '1', '2', '3', '4', '5', '+', 'wedge', 'open', 'thumb', 'snap', 'turn', 'roll', 'breath', 'shortphrase', 'mediumphrase', 'longphrase', 'segno', 'coda', 'D.S.', 'D.C.', 'fine', 'beambr1', 'beambr2', 'slide', 'marcato', 'upbow', 'downbow', '/', '//', '///', '////', 'trem1', 'trem2', 'trem3', 'trem4', 'turnx', 'invertedturn', 'invertedturnx', 'trill(', 'trill)', 'arpeggio', 'xstem', 'mark', 'umarcato', 'style=normal', 'style=harmonic', 'style=rhythm', 'style=x', 'style=triangle', 'D.C.alcoda', 'D.C.alfine', 'D.S.alcoda', 'D.S.alfine', 'editorial', 'courtesy'];
|
|
7578
|
+
module.exports.legalAccents = ['trill', 'trillh', 'lowermordent', 'uppermordent', 'mordent', 'pralltriller', 'accent', 'fermata', 'invertedfermata', 'tenuto', '0', '1', '2', '3', '4', '5', '+', 'wedge', 'open', 'thumb', 'snap', 'turn', 'roll', 'breath', 'shortphrase', 'mediumphrase', 'longphrase', 'segno', 'coda', 'D.S.', 'D.C.', 'fine', 'beambr1', 'beambr2', 'slide', 'marcato', 'upbow', 'downbow', '/', '//', '///', '////', 'trem1', 'trem2', 'trem3', 'trem4', 'turnx', 'invertedturn', 'invertedturnx', 'trill(', 'trill)', 'arpeggio', 'xstem', 'mark', 'umarcato', 'style=normal', 'style=harmonic', 'style=rhythm', 'style=x', 'style=triangle', 'D.C.alcoda', 'D.C.alfine', 'D.S.alcoda', 'D.S.alfine', 'editorial', 'courtesy'];
|
|
7502
7579
|
module.exports.volumeDecorations = ['p', 'pp', 'f', 'ff', 'mf', 'mp', 'ppp', 'pppp', 'fff', 'ffff', 'sfz'];
|
|
7503
7580
|
module.exports.dynamicDecorations = ['crescendo(', 'crescendo)', 'diminuendo(', 'diminuendo)', 'glissando(', 'glissando)', '~(', '~)'];
|
|
7504
7581
|
module.exports.accentPseudonyms = [['<', 'accent'], ['>', 'accent'], ['tr', 'trill'], ['plus', '+'], ['emphasis', 'accent'], ['^', 'umarcato'], ['marcato', 'umarcato']];
|
|
@@ -7577,6 +7654,7 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
|
|
|
7577
7654
|
}
|
|
7578
7655
|
return str.length; // It must have been all white space
|
|
7579
7656
|
};
|
|
7657
|
+
|
|
7580
7658
|
var finished = function finished(str, i) {
|
|
7581
7659
|
return i >= str.length;
|
|
7582
7660
|
};
|
|
@@ -7637,6 +7715,7 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
|
|
|
7637
7715
|
// case 'f':return {len: i+1, token: 'F'};
|
|
7638
7716
|
// case 'g':return {len: i+1, token: 'G'};
|
|
7639
7717
|
}
|
|
7718
|
+
|
|
7640
7719
|
return {
|
|
7641
7720
|
len: 0
|
|
7642
7721
|
};
|
|
@@ -8468,6 +8547,7 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
|
|
|
8468
8547
|
|
|
8469
8548
|
// More chars: IJ ij Ď ď Đ đ Ĝ ĝ Ğ ğ Ġ ġ Ģ ģ Ĥ ĥ Ħ ħ Ĵ ĵ Ķ ķ ĸ Ĺ ĺ Ļ ļ Ľ ľ Ŀ ŀ Ł ł Ń ń Ņ ņ Ň ň ʼn Ŋ ŋ Ŕ ŕ Ŗ ŗ Ř ř Ś ś Ŝ ŝ Ş ş Š Ţ ţ Ť ť Ŧ ŧ Ŵ ŵ Ź ź Ż ż Ž
|
|
8470
8549
|
};
|
|
8550
|
+
|
|
8471
8551
|
var charMap1 = {
|
|
8472
8552
|
"#": "♯",
|
|
8473
8553
|
"b": "♭",
|
|
@@ -8713,14 +8793,38 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
|
|
|
8713
8793
|
}
|
|
8714
8794
|
}
|
|
8715
8795
|
var thePatterns = [{
|
|
8716
|
-
match: /,\s*
|
|
8796
|
+
match: /,\s*The$/,
|
|
8717
8797
|
replace: "The "
|
|
8718
8798
|
}, {
|
|
8719
|
-
match: /,\s*
|
|
8799
|
+
match: /,\s*the$/,
|
|
8800
|
+
replace: "the "
|
|
8801
|
+
}, {
|
|
8802
|
+
match: /,\s*A$/,
|
|
8720
8803
|
replace: "A "
|
|
8721
8804
|
}, {
|
|
8722
|
-
match: /,\s*
|
|
8805
|
+
match: /,\s*a$/,
|
|
8806
|
+
replace: "a "
|
|
8807
|
+
}, {
|
|
8808
|
+
match: /,\s*An$/,
|
|
8723
8809
|
replace: "An "
|
|
8810
|
+
}, {
|
|
8811
|
+
match: /,\s*an$/,
|
|
8812
|
+
replace: "an "
|
|
8813
|
+
}, {
|
|
8814
|
+
match: /,\s*Da$/,
|
|
8815
|
+
replace: "Da "
|
|
8816
|
+
}, {
|
|
8817
|
+
match: /,\s*La$/,
|
|
8818
|
+
replace: "La "
|
|
8819
|
+
}, {
|
|
8820
|
+
match: /,\s*Le$/,
|
|
8821
|
+
replace: "Le "
|
|
8822
|
+
}, {
|
|
8823
|
+
match: /,\s*Les$/,
|
|
8824
|
+
replace: "Les "
|
|
8825
|
+
}, {
|
|
8826
|
+
match: /,\s*Ye$/,
|
|
8827
|
+
replace: "Ye "
|
|
8724
8828
|
}];
|
|
8725
8829
|
this.theReverser = function (str) {
|
|
8726
8830
|
for (var i = 0; i < thePatterns.length; i++) {
|
|
@@ -8970,7 +9074,9 @@ transpose.keySignature = function (multilineVars, keyName, root, acc, localTrans
|
|
|
8970
9074
|
var newKeyName = keyName[0] === 'm' ? newKeyMinor[index] : newKey[index];
|
|
8971
9075
|
var transposedKey = newKeyName + keyName;
|
|
8972
9076
|
var newKeySig = keyAccidentals(transposedKey);
|
|
8973
|
-
if (newKeySig.length
|
|
9077
|
+
if (newKeySig.length === 0 || newKeySig[0].acc === 'flat')
|
|
9078
|
+
// key of C and all keys with flats should have chords with flats
|
|
9079
|
+
multilineVars.localTransposePreferFlats = true;
|
|
8974
9080
|
var distance = transposedKey.charCodeAt(0) - baseKey.charCodeAt(0);
|
|
8975
9081
|
if (multilineVars.localTranspose > 0) {
|
|
8976
9082
|
if (distance < 0) distance += 7;else if (distance === 0) {
|
|
@@ -9142,6 +9248,13 @@ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
|
|
|
9142
9248
|
} else {
|
|
9143
9249
|
if (freeGCchord) chord = sharpChordsFree[index];else chord = sharpChords[index];
|
|
9144
9250
|
}
|
|
9251
|
+
var isDim = extra1 && (extra1.indexOf('dim') >= 0 || extra1.indexOf('°') >= 0);
|
|
9252
|
+
console.log(isDim, chord, extra1);
|
|
9253
|
+
// We never want A#dim or D#dim
|
|
9254
|
+
if (isDim && chord === 'A#') chord = 'Bb';
|
|
9255
|
+
if (isDim && chord === 'D#') chord = 'Eb';
|
|
9256
|
+
if (isDim && chord === 'A♯') chord = 'B♭';
|
|
9257
|
+
if (isDim && chord === 'D♯') chord = 'E♭';
|
|
9145
9258
|
if (extra1) chord += extra1;
|
|
9146
9259
|
if (bass) {
|
|
9147
9260
|
var index = sharpChords.indexOf(bass);
|
|
@@ -9159,6 +9272,7 @@ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
|
|
|
9159
9272
|
}
|
|
9160
9273
|
} else chord += bass; // Don't know what to do so do nothing
|
|
9161
9274
|
}
|
|
9275
|
+
|
|
9162
9276
|
if (extra2) chord += extra2;
|
|
9163
9277
|
return chord;
|
|
9164
9278
|
}
|
|
@@ -9182,7 +9296,7 @@ var TuneBuilder = function TuneBuilder(tune) {
|
|
|
9182
9296
|
var currentVoiceName = '';
|
|
9183
9297
|
tune.reset();
|
|
9184
9298
|
this.setVisualTranspose = function (visualTranspose) {
|
|
9185
|
-
if (visualTranspose) tune.visualTranspose = visualTranspose;
|
|
9299
|
+
if (visualTranspose !== undefined) tune.visualTranspose = visualTranspose;
|
|
9186
9300
|
};
|
|
9187
9301
|
this.cleanUp = function (barsperstaff, staffnonote, currSlur) {
|
|
9188
9302
|
closeLine(tune); // Close the last line.
|
|
@@ -9369,6 +9483,7 @@ var TuneBuilder = function TuneBuilder(tune) {
|
|
|
9369
9483
|
if (hashParams.rest && hashParams.rest.type === 'invisible') {
|
|
9370
9484
|
delete hashParams.decoration; // the decorations on invisible rests should be invisible, too.
|
|
9371
9485
|
}
|
|
9486
|
+
|
|
9372
9487
|
if (tune.lines.length <= tune.lineNum || tune.lines[tune.lineNum].staff.length <= tune.staffNum) {
|
|
9373
9488
|
//console.log("pushNote IGNORED", tune.lines[tune.lineNum])
|
|
9374
9489
|
// TODO-PER: This prevents a crash, but it drops the element. Need to figure out how to start a new line, or delay adding this.
|
|
@@ -9556,7 +9671,7 @@ var TuneBuilder = function TuneBuilder(tune) {
|
|
|
9556
9671
|
};
|
|
9557
9672
|
this.getCurrentVoice = function () {
|
|
9558
9673
|
//console.log("getCurrentVoice", tune.lineNum)
|
|
9559
|
-
var currLine = tune.lines
|
|
9674
|
+
var currLine = getPrevMusicLine(tune.lines, tune.lineNum);
|
|
9560
9675
|
if (!currLine) return null;
|
|
9561
9676
|
var currStaff = currLine.staff[tune.staffNum];
|
|
9562
9677
|
if (!currStaff) return null;
|
|
@@ -9836,6 +9951,7 @@ function cleanUpSlursInLine(line, staffNum, voiceNum, currSlur) {
|
|
|
9836
9951
|
obj.endSlur.push(slurNum);
|
|
9837
9952
|
// lyr.syllable += '<' + slurNum; // TODO-PER: debugging
|
|
9838
9953
|
}
|
|
9954
|
+
|
|
9839
9955
|
if (currSlur[staffNum][voiceNum][chordPos].length === 0) delete currSlur[staffNum][voiceNum][chordPos];
|
|
9840
9956
|
return slurNum;
|
|
9841
9957
|
};
|
|
@@ -9978,6 +10094,15 @@ function wrapMusicLines(lines, barsperstaff) {
|
|
|
9978
10094
|
}
|
|
9979
10095
|
return false;
|
|
9980
10096
|
}
|
|
10097
|
+
function getPrevMusicLine(lines, currentLine) {
|
|
10098
|
+
if (lines.length <= currentLine) return null;
|
|
10099
|
+
// If the current line doesn't have music, search backwards until one is found.
|
|
10100
|
+
while (currentLine >= 0) {
|
|
10101
|
+
if (lines[currentLine].staff) return lines[currentLine];
|
|
10102
|
+
currentLine--;
|
|
10103
|
+
}
|
|
10104
|
+
return null;
|
|
10105
|
+
}
|
|
9981
10106
|
function getNextMusicLine(lines, currentLine) {
|
|
9982
10107
|
currentLine++;
|
|
9983
10108
|
while (lines.length > currentLine) {
|
|
@@ -10716,16 +10841,16 @@ var strTranspose;
|
|
|
10716
10841
|
var count = arr[0].length;
|
|
10717
10842
|
for (var i = 1; i < arr.length; i++) {
|
|
10718
10843
|
var segment = arr[i];
|
|
10719
|
-
var match = segment.match(/^( *)([A-G])([#b]?)(\w*)/);
|
|
10844
|
+
var match = segment.match(/^( *)([A-G])([#b]?)( ?)(\w*)/);
|
|
10720
10845
|
if (match) {
|
|
10721
10846
|
var start = count + 2 + match[1].length; // move past the 'K:' and optional white space
|
|
10722
|
-
var key = match[2] + match[3] + match[4]; // key name, accidental, and mode
|
|
10847
|
+
var key = match[2] + match[3] + match[4] + match[5]; // key name, accidental, optional space, and mode
|
|
10723
10848
|
var destinationKey = newKey({
|
|
10724
10849
|
root: match[2],
|
|
10725
10850
|
acc: match[3],
|
|
10726
|
-
mode: match[
|
|
10851
|
+
mode: match[5]
|
|
10727
10852
|
}, steps);
|
|
10728
|
-
var dest = destinationKey.root + destinationKey.acc + destinationKey.mode;
|
|
10853
|
+
var dest = destinationKey.root + destinationKey.acc + match[4] + destinationKey.mode;
|
|
10729
10854
|
changes.push({
|
|
10730
10855
|
start: start,
|
|
10731
10856
|
end: start + key.length,
|
|
@@ -11085,6 +11210,7 @@ var strTranspose;
|
|
|
11085
11210
|
return 0;
|
|
11086
11211
|
// this should never happen
|
|
11087
11212
|
}
|
|
11213
|
+
|
|
11088
11214
|
case '_':
|
|
11089
11215
|
switch (thisAccidental) {
|
|
11090
11216
|
case '__':
|
|
@@ -11101,6 +11227,7 @@ var strTranspose;
|
|
|
11101
11227
|
return 0;
|
|
11102
11228
|
// this should never happen
|
|
11103
11229
|
}
|
|
11230
|
+
|
|
11104
11231
|
case '^':
|
|
11105
11232
|
switch (thisAccidental) {
|
|
11106
11233
|
case '__':
|
|
@@ -11118,9 +11245,11 @@ var strTranspose;
|
|
|
11118
11245
|
// this should never happen
|
|
11119
11246
|
}
|
|
11120
11247
|
}
|
|
11248
|
+
|
|
11121
11249
|
return 0; // this should never happen
|
|
11122
11250
|
}
|
|
11123
11251
|
})();
|
|
11252
|
+
|
|
11124
11253
|
module.exports = strTranspose;
|
|
11125
11254
|
|
|
11126
11255
|
/***/ }),
|
|
@@ -11169,6 +11298,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11169
11298
|
var stressBeat1 = 105;
|
|
11170
11299
|
var stressBeatDown = 95;
|
|
11171
11300
|
var stressBeatUp = 85;
|
|
11301
|
+
var volumesPerNotePitch = [[stressBeat1, stressBeatDown, stressBeatUp]];
|
|
11172
11302
|
var beatFraction = 0.25;
|
|
11173
11303
|
var nextVolume;
|
|
11174
11304
|
var nextVolumeDelta;
|
|
@@ -11210,6 +11340,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11210
11340
|
stressBeat1 = 105;
|
|
11211
11341
|
stressBeatDown = 95;
|
|
11212
11342
|
stressBeatUp = 85;
|
|
11343
|
+
volumesPerNotePitch = [];
|
|
11213
11344
|
beatFraction = 0.25;
|
|
11214
11345
|
nextVolume = undefined;
|
|
11215
11346
|
nextVolumeDelta = undefined;
|
|
@@ -11320,6 +11451,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11320
11451
|
stressBeat1 = element.beats[0];
|
|
11321
11452
|
stressBeatDown = element.beats[1];
|
|
11322
11453
|
stressBeatUp = element.beats[2];
|
|
11454
|
+
if (!element.volumesPerNotePitch) volumesPerNotePitch = [];else volumesPerNotePitch = element.volumesPerNotePitch;
|
|
11323
11455
|
// TODO-PER: also use the last parameter - which changes which beats are strong.
|
|
11324
11456
|
break;
|
|
11325
11457
|
case "vol":
|
|
@@ -11445,6 +11577,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11445
11577
|
// console.log(JSON.stringify(voices))
|
|
11446
11578
|
}
|
|
11447
11579
|
}
|
|
11580
|
+
|
|
11448
11581
|
function getBeatFraction(meter) {
|
|
11449
11582
|
switch (parseInt(meter.den, 10)) {
|
|
11450
11583
|
case 2:
|
|
@@ -11462,21 +11595,29 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11462
11595
|
var distanceFromStart = currTime - measureStart;
|
|
11463
11596
|
return distanceFromStart / beatLength;
|
|
11464
11597
|
}
|
|
11465
|
-
function processVolume(beat, voiceOff) {
|
|
11598
|
+
function processVolume(beat, voiceOff, pitchIndexOfNote) {
|
|
11466
11599
|
if (voiceOff) return 0;
|
|
11600
|
+
var pitchStressBeat1 = stressBeat1;
|
|
11601
|
+
var pitchStressBeatDown = stressBeatDown;
|
|
11602
|
+
var pitchStressBeatUp = stressBeatUp;
|
|
11603
|
+
if (pitchIndexOfNote !== undefined && volumesPerNotePitch.length >= pitchIndexOfNote + 1) {
|
|
11604
|
+
pitchStressBeat1 = volumesPerNotePitch[pitchIndexOfNote][0];
|
|
11605
|
+
pitchStressBeatDown = volumesPerNotePitch[pitchIndexOfNote][1];
|
|
11606
|
+
pitchStressBeatUp = volumesPerNotePitch[pitchIndexOfNote][2];
|
|
11607
|
+
}
|
|
11467
11608
|
var volume;
|
|
11468
11609
|
// MAE 21 Jun 2024 - This previously wasn't allowing zero volume to be applied
|
|
11469
|
-
if (nextVolume
|
|
11610
|
+
if (nextVolume !== undefined) {
|
|
11470
11611
|
volume = nextVolume;
|
|
11471
11612
|
nextVolume = undefined;
|
|
11472
11613
|
} else if (!doBeatAccents) {
|
|
11473
|
-
volume =
|
|
11614
|
+
volume = pitchStressBeatDown;
|
|
11474
11615
|
} else if (pickupLength > beat) {
|
|
11475
|
-
volume =
|
|
11616
|
+
volume = pitchStressBeatUp;
|
|
11476
11617
|
} else {
|
|
11477
11618
|
//var barLength = meter.num / meter.den;
|
|
11478
11619
|
var barBeat = calcBeat(lastBarTime, getBeatFraction(meter), beat);
|
|
11479
|
-
if (barBeat === 0) volume =
|
|
11620
|
+
if (barBeat === 0) volume = pitchStressBeat1;else if (parseInt(barBeat, 10) === barBeat) volume = pitchStressBeatDown;else volume = pitchStressBeatUp;
|
|
11480
11621
|
}
|
|
11481
11622
|
if (nextVolumeDelta) {
|
|
11482
11623
|
volume += nextVolumeDelta;
|
|
@@ -11490,7 +11631,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11490
11631
|
var ret = {};
|
|
11491
11632
|
if (elem.decoration) {
|
|
11492
11633
|
for (var d = 0; d < elem.decoration.length; d++) {
|
|
11493
|
-
if (elem.decoration[d] === 'staccato') ret.thisBreakBetweenNotes = 'staccato';else if (elem.decoration[d] === 'tenuto') ret.thisBreakBetweenNotes = 'tenuto';else if (elem.decoration[d] === 'accent') ret.velocity = Math.min(127, velocity * 1.5);else if (elem.decoration[d] === 'trill') ret.noteModification = "trill";else if (elem.decoration[d] === 'lowermordent') ret.noteModification = "lowermordent";else if (elem.decoration[d] === 'uppermordent') ret.noteModification = "
|
|
11634
|
+
if (elem.decoration[d] === 'staccato') ret.thisBreakBetweenNotes = 'staccato';else if (elem.decoration[d] === 'tenuto') ret.thisBreakBetweenNotes = 'tenuto';else if (elem.decoration[d] === 'accent') ret.velocity = Math.min(127, velocity * 1.5);else if (elem.decoration[d] === 'trill') ret.noteModification = "trill";else if (elem.decoration[d] === 'lowermordent') ret.noteModification = "lowermordent";else if (elem.decoration[d] === 'uppermordent') ret.noteModification = "pralltriller";else if (elem.decoration[d] === 'mordent') ret.noteModification = "mordent";else if (elem.decoration[d] === 'turn') ret.noteModification = "turn";else if (elem.decoration[d] === 'roll') ret.noteModification = "roll";else if (elem.decoration[d] === 'pralltriller') ret.noteModification = "pralltriller";else if (elem.decoration[d] === 'trillh') ret.noteModification = "trillh";
|
|
11494
11635
|
}
|
|
11495
11636
|
}
|
|
11496
11637
|
return ret;
|
|
@@ -11504,6 +11645,24 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11504
11645
|
var shortestNote = durationRounded(1.0 / 32);
|
|
11505
11646
|
switch (noteModification) {
|
|
11506
11647
|
case "trill":
|
|
11648
|
+
var note = 2;
|
|
11649
|
+
while (runningDuration > 0) {
|
|
11650
|
+
currentTrack.push({
|
|
11651
|
+
cmd: 'note',
|
|
11652
|
+
pitch: p.pitch + note,
|
|
11653
|
+
volume: p.volume,
|
|
11654
|
+
start: start,
|
|
11655
|
+
duration: shortestNote,
|
|
11656
|
+
gap: 0,
|
|
11657
|
+
instrument: currentInstrument,
|
|
11658
|
+
style: 'decoration'
|
|
11659
|
+
});
|
|
11660
|
+
note = note === 2 ? 0 : 2;
|
|
11661
|
+
runningDuration -= shortestNote;
|
|
11662
|
+
start += shortestNote;
|
|
11663
|
+
}
|
|
11664
|
+
break;
|
|
11665
|
+
case "trillh":
|
|
11507
11666
|
var note = 1;
|
|
11508
11667
|
while (runningDuration > 0) {
|
|
11509
11668
|
currentTrack.push({
|
|
@@ -11521,7 +11680,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11521
11680
|
start += shortestNote;
|
|
11522
11681
|
}
|
|
11523
11682
|
break;
|
|
11524
|
-
case "
|
|
11683
|
+
case "pralltriller":
|
|
11525
11684
|
currentTrack.push({
|
|
11526
11685
|
cmd: 'note',
|
|
11527
11686
|
pitch: p.pitch,
|
|
@@ -11536,7 +11695,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11536
11695
|
start += shortestNote;
|
|
11537
11696
|
currentTrack.push({
|
|
11538
11697
|
cmd: 'note',
|
|
11539
|
-
pitch: p.pitch +
|
|
11698
|
+
pitch: p.pitch + 2,
|
|
11540
11699
|
volume: p.volume,
|
|
11541
11700
|
start: start,
|
|
11542
11701
|
duration: shortestNote,
|
|
@@ -11556,6 +11715,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11556
11715
|
instrument: currentInstrument
|
|
11557
11716
|
});
|
|
11558
11717
|
break;
|
|
11718
|
+
case "mordent":
|
|
11559
11719
|
case "lowermordent":
|
|
11560
11720
|
currentTrack.push({
|
|
11561
11721
|
cmd: 'note',
|
|
@@ -11571,7 +11731,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11571
11731
|
start += shortestNote;
|
|
11572
11732
|
currentTrack.push({
|
|
11573
11733
|
cmd: 'note',
|
|
11574
|
-
pitch: p.pitch -
|
|
11734
|
+
pitch: p.pitch - 2,
|
|
11575
11735
|
volume: p.volume,
|
|
11576
11736
|
start: start,
|
|
11577
11737
|
duration: shortestNote,
|
|
@@ -11592,10 +11752,10 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11592
11752
|
});
|
|
11593
11753
|
break;
|
|
11594
11754
|
case "turn":
|
|
11595
|
-
shortestNote = p.duration /
|
|
11755
|
+
shortestNote = p.duration / 4;
|
|
11596
11756
|
currentTrack.push({
|
|
11597
11757
|
cmd: 'note',
|
|
11598
|
-
pitch: p.pitch,
|
|
11758
|
+
pitch: p.pitch + 2,
|
|
11599
11759
|
volume: p.volume,
|
|
11600
11760
|
start: start,
|
|
11601
11761
|
duration: shortestNote,
|
|
@@ -11605,7 +11765,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11605
11765
|
});
|
|
11606
11766
|
currentTrack.push({
|
|
11607
11767
|
cmd: 'note',
|
|
11608
|
-
pitch: p.pitch
|
|
11768
|
+
pitch: p.pitch,
|
|
11609
11769
|
volume: p.volume,
|
|
11610
11770
|
start: start + shortestNote,
|
|
11611
11771
|
duration: shortestNote,
|
|
@@ -11615,7 +11775,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11615
11775
|
});
|
|
11616
11776
|
currentTrack.push({
|
|
11617
11777
|
cmd: 'note',
|
|
11618
|
-
pitch: p.pitch,
|
|
11778
|
+
pitch: p.pitch - 1,
|
|
11619
11779
|
volume: p.volume,
|
|
11620
11780
|
start: start + shortestNote * 2,
|
|
11621
11781
|
duration: shortestNote,
|
|
@@ -11625,7 +11785,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11625
11785
|
});
|
|
11626
11786
|
currentTrack.push({
|
|
11627
11787
|
cmd: 'note',
|
|
11628
|
-
pitch: p.pitch
|
|
11788
|
+
pitch: p.pitch,
|
|
11629
11789
|
volume: p.volume,
|
|
11630
11790
|
start: start + shortestNote * 3,
|
|
11631
11791
|
duration: shortestNote,
|
|
@@ -11633,15 +11793,6 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11633
11793
|
instrument: currentInstrument,
|
|
11634
11794
|
style: 'decoration'
|
|
11635
11795
|
});
|
|
11636
|
-
currentTrack.push({
|
|
11637
|
-
cmd: 'note',
|
|
11638
|
-
pitch: p.pitch,
|
|
11639
|
-
volume: p.volume,
|
|
11640
|
-
start: start + shortestNote * 4,
|
|
11641
|
-
duration: shortestNote,
|
|
11642
|
-
gap: 0,
|
|
11643
|
-
instrument: currentInstrument
|
|
11644
|
-
});
|
|
11645
11796
|
break;
|
|
11646
11797
|
case "roll":
|
|
11647
11798
|
while (runningDuration > 0) {
|
|
@@ -11729,6 +11880,11 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11729
11880
|
}
|
|
11730
11881
|
if (elem.elem) elem.elem.midiPitches = [];
|
|
11731
11882
|
for (var i = 0; i < ePitches.length; i++) {
|
|
11883
|
+
//here we can set the volume for each note in a chord, if specified
|
|
11884
|
+
var pitchVelocity = velocity;
|
|
11885
|
+
if (!ret.velocity && Array.isArray(elem.decoration) && elem.decoration.length > i) {
|
|
11886
|
+
pitchVelocity = processVolume(timeToRealTime(elem.time), voiceOff, i);
|
|
11887
|
+
}
|
|
11732
11888
|
var note = ePitches[i];
|
|
11733
11889
|
if (!note) continue;
|
|
11734
11890
|
if (note.startSlur) slurCount += note.startSlur.length;
|
|
@@ -11741,7 +11897,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11741
11897
|
var p = {
|
|
11742
11898
|
cmd: 'note',
|
|
11743
11899
|
pitch: actualPitch,
|
|
11744
|
-
volume:
|
|
11900
|
+
volume: pitchVelocity,
|
|
11745
11901
|
start: timeToRealTime(elem.time),
|
|
11746
11902
|
duration: durationRounded(note.duration),
|
|
11747
11903
|
instrument: currentInstrument,
|
|
@@ -12220,6 +12376,7 @@ var rendererFactory;
|
|
|
12220
12376
|
this.track += this.noteOnAndChannel;
|
|
12221
12377
|
this.track += "%" + pitch.toString(16) + toHex(loudness, 2); //note
|
|
12222
12378
|
};
|
|
12379
|
+
|
|
12223
12380
|
Midi.prototype.endNote = function (pitch) {
|
|
12224
12381
|
this.track += toDurationHex(this.silencelength); // only need to shift by amount of silence (if there is any)
|
|
12225
12382
|
this.silencelength = 0;
|
|
@@ -12233,6 +12390,7 @@ var rendererFactory;
|
|
|
12233
12390
|
this.track += this.noteOffAndChannel;
|
|
12234
12391
|
this.track += "%" + pitch.toString(16) + "%00"; //end note
|
|
12235
12392
|
};
|
|
12393
|
+
|
|
12236
12394
|
Midi.prototype.addRest = function (length) {
|
|
12237
12395
|
this.silencelength += length;
|
|
12238
12396
|
if (this.silencelength < 0) this.silencelength = 0;
|
|
@@ -12269,6 +12427,7 @@ var rendererFactory;
|
|
|
12269
12427
|
}
|
|
12270
12428
|
return "%00%FF" + cmdType + toHex(nameArray.length / 3, 2) + nameArray; // Each byte is represented by three chars "%XX", so divide by 3 to get the length.
|
|
12271
12429
|
}
|
|
12430
|
+
|
|
12272
12431
|
function keySignature(key) {
|
|
12273
12432
|
//00 FF 5902 03 00 - key signature
|
|
12274
12433
|
if (!key || !key.accidentals) return "";
|
|
@@ -12521,6 +12680,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12521
12680
|
|
|
12522
12681
|
// visit each voice completely in turn
|
|
12523
12682
|
var voices = [];
|
|
12683
|
+
var clefTransposeActive = [];
|
|
12524
12684
|
var inCrescendo = [];
|
|
12525
12685
|
var inDiminuendo = [];
|
|
12526
12686
|
var durationCounter = [0];
|
|
@@ -12541,6 +12701,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12541
12701
|
if (line.staff) {
|
|
12542
12702
|
var setDynamics = function setDynamics(elem) {
|
|
12543
12703
|
var volumes = {
|
|
12704
|
+
//stressBeat1, stressBeatDown, stressBeatUp
|
|
12544
12705
|
'pppp': [15, 10, 5, 1],
|
|
12545
12706
|
'ppp': [30, 20, 10, 1],
|
|
12546
12707
|
'pp': [45, 35, 20, 1],
|
|
@@ -12557,9 +12718,17 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12557
12718
|
if (elem.decoration.indexOf('pppp') >= 0) dynamicType = 'pppp';else if (elem.decoration.indexOf('ppp') >= 0) dynamicType = 'ppp';else if (elem.decoration.indexOf('pp') >= 0) dynamicType = 'pp';else if (elem.decoration.indexOf('p') >= 0) dynamicType = 'p';else if (elem.decoration.indexOf('mp') >= 0) dynamicType = 'mp';else if (elem.decoration.indexOf('mf') >= 0) dynamicType = 'mf';else if (elem.decoration.indexOf('f') >= 0) dynamicType = 'f';else if (elem.decoration.indexOf('ff') >= 0) dynamicType = 'ff';else if (elem.decoration.indexOf('fff') >= 0) dynamicType = 'fff';else if (elem.decoration.indexOf('ffff') >= 0) dynamicType = 'ffff';
|
|
12558
12719
|
if (dynamicType) {
|
|
12559
12720
|
currentVolume = volumes[dynamicType].slice(0);
|
|
12721
|
+
var volumesPerNotePitch = [currentVolume];
|
|
12722
|
+
if (Array.isArray(elem.decoration)) {
|
|
12723
|
+
volumesPerNotePitch = [];
|
|
12724
|
+
elem.decoration.forEach(function (d) {
|
|
12725
|
+
volumesPerNotePitch.push(volumes[d].slice(0));
|
|
12726
|
+
});
|
|
12727
|
+
}
|
|
12560
12728
|
voices[voiceNumber].push({
|
|
12561
12729
|
el_type: 'beat',
|
|
12562
|
-
beats: currentVolume.slice(0)
|
|
12730
|
+
beats: currentVolume.slice(0),
|
|
12731
|
+
volumesPerNotePitch: volumesPerNotePitch
|
|
12563
12732
|
});
|
|
12564
12733
|
inCrescendo[k] = false;
|
|
12565
12734
|
inDiminuendo[k] = false;
|
|
@@ -12637,15 +12806,31 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12637
12806
|
el_type: 'transpose',
|
|
12638
12807
|
transpose: staff.clef.transpose
|
|
12639
12808
|
});
|
|
12809
|
+
clefTransposeActive[voiceNumber] = false;
|
|
12640
12810
|
}
|
|
12641
12811
|
if (staff.clef && staff.clef.type) {
|
|
12642
|
-
if (staff.clef.type.indexOf("-8") >= 0)
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
})
|
|
12812
|
+
if (staff.clef.type.indexOf("-8") >= 0) {
|
|
12813
|
+
voices[voiceNumber].push({
|
|
12814
|
+
el_type: 'transpose',
|
|
12815
|
+
transpose: -12
|
|
12816
|
+
});
|
|
12817
|
+
clefTransposeActive[voiceNumber] = true;
|
|
12818
|
+
} else if (staff.clef.type.indexOf("+8") >= 0) {
|
|
12819
|
+
voices[voiceNumber].push({
|
|
12820
|
+
el_type: 'transpose',
|
|
12821
|
+
transpose: 12
|
|
12822
|
+
});
|
|
12823
|
+
clefTransposeActive[voiceNumber] = true;
|
|
12824
|
+
} else {
|
|
12825
|
+
// if we had a previous treble+8 and now have a regular clef, then cancel the transposition
|
|
12826
|
+
if (clefTransposeActive[voiceNumber]) {
|
|
12827
|
+
voices[voiceNumber].push({
|
|
12828
|
+
el_type: 'transpose',
|
|
12829
|
+
transpose: 0
|
|
12830
|
+
});
|
|
12831
|
+
clefTransposeActive[voiceNumber] = false;
|
|
12832
|
+
}
|
|
12833
|
+
}
|
|
12649
12834
|
}
|
|
12650
12835
|
if (abctune.formatting.midi && abctune.formatting.midi.drumoff) {
|
|
12651
12836
|
// If there is a drum off command right at the beginning it is put in the metaText instead of the stream,
|
|
@@ -13058,6 +13243,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
13058
13243
|
}
|
|
13059
13244
|
}
|
|
13060
13245
|
}
|
|
13246
|
+
|
|
13061
13247
|
function chordVoiceOffThisBar(voices) {
|
|
13062
13248
|
for (var i = 0; i < voices.length; i++) {
|
|
13063
13249
|
var voice = voices[i];
|
|
@@ -13365,7 +13551,7 @@ ChordTrack.prototype.interpretChord = function (name) {
|
|
|
13365
13551
|
};
|
|
13366
13552
|
var root = name.substring(0, 1);
|
|
13367
13553
|
if (root === '(') {
|
|
13368
|
-
name = name.substring(1, name.length -
|
|
13554
|
+
name = name.substring(1, name.length - 1);
|
|
13369
13555
|
if (name.length === 0) return undefined;
|
|
13370
13556
|
root = name.substring(0, 1);
|
|
13371
13557
|
}
|
|
@@ -13529,6 +13715,7 @@ ChordTrack.prototype.resolveChords = function (startTime, endTime) {
|
|
|
13529
13715
|
if (newBass) newBass = false;else isBoom = false; // only the first note in a chord is a bass note. This handles the case where bass and chord are played at the same time.
|
|
13530
13716
|
}
|
|
13531
13717
|
}
|
|
13718
|
+
|
|
13532
13719
|
return;
|
|
13533
13720
|
};
|
|
13534
13721
|
ChordTrack.prototype.processChord = function (elem) {
|
|
@@ -14136,7 +14323,7 @@ function CreateSynth() {
|
|
|
14136
14323
|
self.audioBuffers = []; // cache of the buffers so starting play can be fast.
|
|
14137
14324
|
self.duration = undefined; // the duration of the tune in seconds.
|
|
14138
14325
|
self.isRunning = false; // whether there is currently a sound buffer running.
|
|
14139
|
-
self.options =
|
|
14326
|
+
self.options = {}; // Thx tomohirohiratsuka
|
|
14140
14327
|
self.pickupLength = 0;
|
|
14141
14328
|
|
|
14142
14329
|
// Load and cache all needed sounds
|
|
@@ -14364,87 +14551,93 @@ function CreateSynth() {
|
|
|
14364
14551
|
self.isRunning = false;
|
|
14365
14552
|
if (!self.audioBufferPossible) return Promise.reject(new Error(notSupportedMessage));
|
|
14366
14553
|
if (self.debugCallback) self.debugCallback("prime called");
|
|
14367
|
-
return new Promise(function (resolve) {
|
|
14368
|
-
|
|
14369
|
-
|
|
14370
|
-
|
|
14371
|
-
|
|
14372
|
-
|
|
14373
|
-
|
|
14374
|
-
|
|
14375
|
-
seconds: 0
|
|
14376
|
-
});
|
|
14377
|
-
}
|
|
14378
|
-
self.duration += fadeTimeSec;
|
|
14379
|
-
var totalSamples = Math.floor(activeAudioContext().sampleRate * self.duration);
|
|
14380
|
-
|
|
14381
|
-
// There might be a previous run that needs to be turned off.
|
|
14382
|
-
self.stop();
|
|
14383
|
-
var noteMapTracks = createNoteMap(self.flattened);
|
|
14384
|
-
if (self.options.swing) addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength);
|
|
14385
|
-
if (self.sequenceCallback) self.sequenceCallback(noteMapTracks, self.callbackContext);
|
|
14386
|
-
var panDistances = setPan(noteMapTracks.length, self.pan);
|
|
14387
|
-
|
|
14388
|
-
// Create a simple list of all the unique sounds in this music and where they should be placed.
|
|
14389
|
-
// There appears to be a limit on how many audio buffers can be created at once so this technique limits the number needed.
|
|
14390
|
-
var uniqueSounds = {};
|
|
14391
|
-
noteMapTracks.forEach(function (noteMap, trackNumber) {
|
|
14392
|
-
var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
|
|
14393
|
-
noteMap.forEach(function (note) {
|
|
14394
|
-
var key = note.instrument + ':' + note.pitch + ':' + note.volume + ':' + Math.round((note.end - note.start) * 1000) / 1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
|
|
14395
|
-
if (self.debugCallback) self.debugCallback("noteMapTrack " + key);
|
|
14396
|
-
if (!uniqueSounds[key]) uniqueSounds[key] = [];
|
|
14397
|
-
uniqueSounds[key].push(note.start);
|
|
14398
|
-
});
|
|
14399
|
-
});
|
|
14400
|
-
|
|
14401
|
-
// Now that we know what we are trying to create, construct the audio buffer by creating each sound and placing it.
|
|
14402
|
-
var allPromises = [];
|
|
14403
|
-
var audioBuffer = activeAudioContext().createBuffer(2, totalSamples, activeAudioContext().sampleRate);
|
|
14404
|
-
for (var key2 = 0; key2 < Object.keys(uniqueSounds).length; key2++) {
|
|
14405
|
-
var k = Object.keys(uniqueSounds)[key2];
|
|
14406
|
-
var parts = k.split(":");
|
|
14407
|
-
var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
|
|
14408
|
-
parts = {
|
|
14409
|
-
instrument: parts[0],
|
|
14410
|
-
pitch: parseInt(parts[1], 10),
|
|
14411
|
-
volume: parseInt(parts[2], 10),
|
|
14412
|
-
len: parseFloat(parts[3]),
|
|
14413
|
-
pan: parseFloat(parts[4]),
|
|
14414
|
-
tempoMultiplier: parseFloat(parts[5]),
|
|
14415
|
-
cents: cents
|
|
14416
|
-
};
|
|
14417
|
-
allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000, self.debugCallback));
|
|
14418
|
-
}
|
|
14419
|
-
self.audioBuffers = [audioBuffer];
|
|
14420
|
-
if (self.debugCallback) {
|
|
14421
|
-
self.debugCallback("sampleRate = " + activeAudioContext().sampleRate);
|
|
14422
|
-
self.debugCallback("totalSamples = " + totalSamples);
|
|
14423
|
-
self.debugCallback("creationTime = " + Math.floor((activeAudioContext().currentTime - startTime) * 1000) + "ms");
|
|
14424
|
-
}
|
|
14425
|
-
function resolveData(me) {
|
|
14426
|
-
var duration = me && me.audioBuffers && me.audioBuffers.length > 0 ? me.audioBuffers[0].duration : 0;
|
|
14427
|
-
return {
|
|
14428
|
-
status: activeAudioContext().state,
|
|
14429
|
-
duration: duration
|
|
14554
|
+
return new Promise(function (resolve, reject) {
|
|
14555
|
+
try {
|
|
14556
|
+
var resolveData = function resolveData(me) {
|
|
14557
|
+
var duration = me && me.audioBuffers && me.audioBuffers.length > 0 ? me.audioBuffers[0].duration : 0;
|
|
14558
|
+
return {
|
|
14559
|
+
status: activeAudioContext().state,
|
|
14560
|
+
duration: duration
|
|
14561
|
+
};
|
|
14430
14562
|
};
|
|
14431
|
-
|
|
14432
|
-
|
|
14433
|
-
|
|
14434
|
-
if (
|
|
14435
|
-
|
|
14436
|
-
|
|
14563
|
+
var startTime = activeAudioContext().currentTime;
|
|
14564
|
+
var tempoMultiplier = self.millisecondsPerMeasure / 1000 / self.meterSize;
|
|
14565
|
+
self.duration = self.flattened.totalDuration * tempoMultiplier;
|
|
14566
|
+
if (self.duration <= 0) {
|
|
14567
|
+
self.audioBuffers = [];
|
|
14568
|
+
return resolve({
|
|
14569
|
+
status: "empty",
|
|
14570
|
+
seconds: 0
|
|
14571
|
+
});
|
|
14572
|
+
}
|
|
14573
|
+
self.duration += fadeTimeSec;
|
|
14574
|
+
var totalSamples = Math.floor(activeAudioContext().sampleRate * self.duration);
|
|
14575
|
+
|
|
14576
|
+
// There might be a previous run that needs to be turned off.
|
|
14577
|
+
self.stop();
|
|
14578
|
+
var noteMapTracks = createNoteMap(self.flattened);
|
|
14579
|
+
if (self.options.swing) addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength);
|
|
14580
|
+
if (self.sequenceCallback) self.sequenceCallback(noteMapTracks, self.callbackContext);
|
|
14581
|
+
var panDistances = setPan(noteMapTracks.length, self.pan);
|
|
14582
|
+
|
|
14583
|
+
// Create a simple list of all the unique sounds in this music and where they should be placed.
|
|
14584
|
+
// There appears to be a limit on how many audio buffers can be created at once so this technique limits the number needed.
|
|
14585
|
+
var uniqueSounds = {};
|
|
14586
|
+
noteMapTracks.forEach(function (noteMap, trackNumber) {
|
|
14587
|
+
var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
|
|
14588
|
+
noteMap.forEach(function (note) {
|
|
14589
|
+
var key = note.instrument + ':' + note.pitch + ':' + note.volume + ':' + Math.round((note.end - note.start) * 1000) / 1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
|
|
14590
|
+
if (self.debugCallback) self.debugCallback("noteMapTrack " + key);
|
|
14591
|
+
if (!uniqueSounds[key]) uniqueSounds[key] = [];
|
|
14592
|
+
uniqueSounds[key].push(note.start);
|
|
14437
14593
|
});
|
|
14438
|
-
}
|
|
14439
|
-
|
|
14594
|
+
});
|
|
14595
|
+
|
|
14596
|
+
// Now that we know what we are trying to create, construct the audio buffer by creating each sound and placing it.
|
|
14597
|
+
var allPromises = [];
|
|
14598
|
+
var audioBuffer = activeAudioContext().createBuffer(2, totalSamples, activeAudioContext().sampleRate);
|
|
14599
|
+
for (var key2 = 0; key2 < Object.keys(uniqueSounds).length; key2++) {
|
|
14600
|
+
var k = Object.keys(uniqueSounds)[key2];
|
|
14601
|
+
var parts = k.split(":");
|
|
14602
|
+
var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
|
|
14603
|
+
parts = {
|
|
14604
|
+
instrument: parts[0],
|
|
14605
|
+
pitch: parseInt(parts[1], 10),
|
|
14606
|
+
volume: parseInt(parts[2], 10),
|
|
14607
|
+
len: parseFloat(parts[3]),
|
|
14608
|
+
pan: parseFloat(parts[4]),
|
|
14609
|
+
tempoMultiplier: parseFloat(parts[5]),
|
|
14610
|
+
cents: cents
|
|
14611
|
+
};
|
|
14612
|
+
allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000, self.debugCallback));
|
|
14613
|
+
}
|
|
14614
|
+
self.audioBuffers = [audioBuffer];
|
|
14615
|
+
if (self.debugCallback) {
|
|
14616
|
+
self.debugCallback("sampleRate = " + activeAudioContext().sampleRate);
|
|
14617
|
+
self.debugCallback("totalSamples = " + totalSamples);
|
|
14618
|
+
self.debugCallback("creationTime = " + Math.floor((activeAudioContext().currentTime - startTime) * 1000) + "ms");
|
|
14619
|
+
}
|
|
14620
|
+
Promise.all(allPromises).then(function () {
|
|
14621
|
+
// Safari iOS can mess with the audioContext state, so resume if needed.
|
|
14622
|
+
if (activeAudioContext().state === "suspended") {
|
|
14440
14623
|
activeAudioContext().resume().then(function () {
|
|
14441
14624
|
resolve(resolveData(self));
|
|
14442
14625
|
});
|
|
14443
|
-
})
|
|
14444
|
-
|
|
14445
|
-
|
|
14446
|
-
|
|
14447
|
-
|
|
14626
|
+
} else if (activeAudioContext().state === "interrupted") {
|
|
14627
|
+
activeAudioContext().suspend().then(function () {
|
|
14628
|
+
activeAudioContext().resume().then(function () {
|
|
14629
|
+
resolve(resolveData(self));
|
|
14630
|
+
});
|
|
14631
|
+
});
|
|
14632
|
+
} else {
|
|
14633
|
+
resolve(resolveData(self));
|
|
14634
|
+
}
|
|
14635
|
+
})["catch"](function (error) {
|
|
14636
|
+
reject(error);
|
|
14637
|
+
});
|
|
14638
|
+
} catch (error) {
|
|
14639
|
+
reject(error);
|
|
14640
|
+
}
|
|
14448
14641
|
});
|
|
14449
14642
|
};
|
|
14450
14643
|
function setPan(numTracks, panParam) {
|
|
@@ -14584,6 +14777,7 @@ function CreateSynth() {
|
|
|
14584
14777
|
self.directSource[trackNum].buffer = audioBuffer; // tell the source which sound to play
|
|
14585
14778
|
self.directSource[trackNum].connect(activeAudioContext().destination); // connect the source to the context's destination (the speakers)
|
|
14586
14779
|
});
|
|
14780
|
+
|
|
14587
14781
|
self.directSource.forEach(function (source) {
|
|
14588
14782
|
source.start(0, seconds);
|
|
14589
14783
|
});
|
|
@@ -15257,7 +15451,7 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
|
|
|
15257
15451
|
});
|
|
15258
15452
|
})["catch"](function (error) {
|
|
15259
15453
|
if (debugCallback) debugCallback('placeNote catch: ' + error.message);
|
|
15260
|
-
return Promise.
|
|
15454
|
+
return Promise.reject(error);
|
|
15261
15455
|
});
|
|
15262
15456
|
}
|
|
15263
15457
|
var copyToChannel = function copyToChannel(toBuffer, fromBuffer, start) {
|
|
@@ -16769,6 +16963,7 @@ function convertToNumber(plugin, pitches, graceNotes) {
|
|
|
16769
16963
|
plugin.setError(tabPos.error);
|
|
16770
16964
|
return tabPos; // give up on error here
|
|
16771
16965
|
}
|
|
16966
|
+
|
|
16772
16967
|
if (tabPos.graces && tabPos.notes) {
|
|
16773
16968
|
// add graces to last note in notes
|
|
16774
16969
|
var posNote = tabPos.notes.length - 1;
|
|
@@ -16966,6 +17161,7 @@ function getLyricHeight(voice) {
|
|
|
16966
17161
|
}
|
|
16967
17162
|
return maxLyricHeight; // add spacing
|
|
16968
17163
|
}
|
|
17164
|
+
|
|
16969
17165
|
function buildTabName(plugin, renderer, dest) {
|
|
16970
17166
|
var stringSemantics = plugin.semantics;
|
|
16971
17167
|
var textSize = renderer.controller.getTextSize;
|
|
@@ -17044,6 +17240,7 @@ function getNextTabPos(tabIndex, staffGroup) {
|
|
|
17044
17240
|
if (!staffGroup[startIndex].isTabStaff) {
|
|
17045
17241
|
nbVoices = staffGroup[startIndex].voices.length; // get number of staff voices
|
|
17046
17242
|
}
|
|
17243
|
+
|
|
17047
17244
|
if (staffGroup[startIndex].isTabStaff) {
|
|
17048
17245
|
handledVoices++;
|
|
17049
17246
|
if (islastTabInStaff(startIndex, staffGroup)) {
|
|
@@ -17156,6 +17353,7 @@ function tabRenderer(plugin, renderer, line, staffIndex) {
|
|
|
17156
17353
|
}
|
|
17157
17354
|
linkStaffAndTabs(staffGroup.staffs); // crossreference tabs and staff
|
|
17158
17355
|
}
|
|
17356
|
+
|
|
17159
17357
|
module.exports = tabRenderer;
|
|
17160
17358
|
|
|
17161
17359
|
/***/ }),
|
|
@@ -17375,6 +17573,7 @@ AbstractEngraver.prototype.createABCStaff = function (staffgroup, abcstaff, temp
|
|
|
17375
17573
|
} else {
|
|
17376
17574
|
voice.duplicate = true; // bar lines and other duplicate info need not be created
|
|
17377
17575
|
}
|
|
17576
|
+
|
|
17378
17577
|
if (abcstaff.title && abcstaff.title[v]) {
|
|
17379
17578
|
voice.header = abcstaff.title[v].replace(/\\n/g, "\n");
|
|
17380
17579
|
voice.headerPosition = 6 + staffgroup.getTextSize.baselineToCenter(voice.header, "voicefont", 'staff-extra voice-name', v, abcstaff.voices.length) / spacing.STEP;
|
|
@@ -17388,11 +17587,13 @@ AbstractEngraver.prototype.createABCStaff = function (staffgroup, abcstaff, temp
|
|
|
17388
17587
|
voice.addChild(clef);
|
|
17389
17588
|
this.startlimitelem = clef; // limit ties here
|
|
17390
17589
|
}
|
|
17590
|
+
|
|
17391
17591
|
var keySig = createKeySignature(abcstaff.key, this.tuneNumber);
|
|
17392
17592
|
if (keySig) {
|
|
17393
17593
|
voice.addChild(keySig);
|
|
17394
17594
|
this.startlimitelem = keySig; // limit ties here
|
|
17395
17595
|
}
|
|
17596
|
+
|
|
17396
17597
|
if (abcstaff.meter) {
|
|
17397
17598
|
if (abcstaff.meter.type === 'specified') {
|
|
17398
17599
|
this.measureLength = abcstaff.meter.value[0].num / abcstaff.meter.value[0].den;
|
|
@@ -17401,6 +17602,7 @@ AbstractEngraver.prototype.createABCStaff = function (staffgroup, abcstaff, temp
|
|
|
17401
17602
|
voice.addChild(ts);
|
|
17402
17603
|
this.startlimitelem = ts; // limit ties here
|
|
17403
17604
|
}
|
|
17605
|
+
|
|
17404
17606
|
if (voice.duplicate) voice.children = []; // we shouldn't reprint the above if we're reusing the same staff. We just created them to get the right spacing.
|
|
17405
17607
|
var staffLines = abcstaff.clef.stafflines || abcstaff.clef.stafflines === 0 ? abcstaff.clef.stafflines : 5;
|
|
17406
17608
|
staffgroup.addVoice(voice, s, staffLines);
|
|
@@ -17564,6 +17766,7 @@ AbstractEngraver.prototype.createABCElement = function (isFirstStaff, isSingleLi
|
|
|
17564
17766
|
elemset[0] = absKey;
|
|
17565
17767
|
this.startlimitelem = elemset[0]; // limit ties here
|
|
17566
17768
|
}
|
|
17769
|
+
|
|
17567
17770
|
if (voice.duplicate && elemset.length > 0) elemset[0].invisible = true;
|
|
17568
17771
|
break;
|
|
17569
17772
|
case "stem":
|
|
@@ -17697,6 +17900,7 @@ AbstractEngraver.prototype.addGraceNotes = function (elem, voice, abselem, noteh
|
|
|
17697
17900
|
if (hint) gracebeam.setHint();
|
|
17698
17901
|
gracebeam.mainNote = abselem; // this gives us a reference back to the note this is attached to so that the stems can be attached somewhere.
|
|
17699
17902
|
}
|
|
17903
|
+
|
|
17700
17904
|
var i;
|
|
17701
17905
|
var graceoffsets = [];
|
|
17702
17906
|
for (i = elem.gracenotes.length - 1; i >= 0; i--) {
|
|
@@ -17796,6 +18000,7 @@ function addRestToAbsElement(abselem, elem, duration, dot, isMultiVoice, stemdir
|
|
|
17796
18000
|
if (duration < 0.5) restpitch = 7;else if (duration < 1) restpitch = 7; // half rest
|
|
17797
18001
|
else restpitch = 5; // whole rest
|
|
17798
18002
|
}
|
|
18003
|
+
|
|
17799
18004
|
switch (elem.rest.type) {
|
|
17800
18005
|
case "whole":
|
|
17801
18006
|
c = chartable.rest[0];
|
|
@@ -17934,6 +18139,7 @@ AbstractEngraver.prototype.addNoteToAbcElement = function (abselem, elem, dot, s
|
|
|
17934
18139
|
elem.pitches[p].highestVert = elem.pitches[pp - 1].verticalPos;
|
|
17935
18140
|
if (getDuration(elem) < 1 && (stemdir === "up" || dir === "up")) elem.pitches[p].highestVert += 6; // If the stem is up, then compensate for the length of the stem
|
|
17936
18141
|
}
|
|
18142
|
+
|
|
17937
18143
|
if (elem.startSlur) {
|
|
17938
18144
|
if (!elem.pitches[p].startSlur) elem.pitches[p].startSlur = []; //TODO possibly redundant, provided array is not optional
|
|
17939
18145
|
for (i = 0; i < elem.startSlur.length; i++) {
|
|
@@ -18103,6 +18309,7 @@ AbstractEngraver.prototype.createNote = function (elem, nostem, isSingleLineStaf
|
|
|
18103
18309
|
flatBeams: this.flatBeams
|
|
18104
18310
|
}); // above is opposite from case of slurs
|
|
18105
18311
|
}
|
|
18312
|
+
|
|
18106
18313
|
if (elem.endTriplet && this.triplet) {
|
|
18107
18314
|
this.triplet.setCloseAnchor(notehead);
|
|
18108
18315
|
}
|
|
@@ -18201,7 +18408,8 @@ AbstractEngraver.prototype.addMeasureNumber = function (number, abselem) {
|
|
|
18201
18408
|
if (abselem.isClef)
|
|
18202
18409
|
// If this is a clef rather than bar line, then the number shouldn't be centered because it could overlap the left side. This is an easy way to let it be centered but move it over, too.
|
|
18203
18410
|
dx += measureNumDim.width / 2;
|
|
18204
|
-
|
|
18411
|
+
// MAE 1 Oct 2024 - Change 13 to 13.5 since previously bar numbers were very slightly overlapping the top of the clef
|
|
18412
|
+
var vert = measureNumDim.width > 10 && abselem.abcelem.type === "treble" ? 13.5 : 11;
|
|
18205
18413
|
abselem.addFixed(new RelativeElement(number, dx, measureNumDim.width, vert + measureNumDim.height / spacing.STEP, {
|
|
18206
18414
|
type: "barNumber",
|
|
18207
18415
|
dim: this.getTextSize.attr("measurefont", 'bar-number')
|
|
@@ -18236,6 +18444,7 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
|
|
|
18236
18444
|
abselem.addRight(new RelativeElement("dots.dot", dx, 1, 5));
|
|
18237
18445
|
dx += 6; //2 hardcoded, twice;
|
|
18238
18446
|
}
|
|
18447
|
+
|
|
18239
18448
|
if (firstthin) {
|
|
18240
18449
|
anchor = new RelativeElement(null, dx, 1, 2, {
|
|
18241
18450
|
"type": "bar",
|
|
@@ -18284,6 +18493,7 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
|
|
|
18284
18493
|
});
|
|
18285
18494
|
abselem.addRight(anchor); // 3 is hardcoded
|
|
18286
18495
|
}
|
|
18496
|
+
|
|
18287
18497
|
if (seconddots) {
|
|
18288
18498
|
dx += 3; //3 hardcoded;
|
|
18289
18499
|
abselem.addRight(new RelativeElement("dots.dot", dx, 1, 7));
|
|
@@ -18291,13 +18501,15 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
|
|
|
18291
18501
|
} // 2 is hardcoded
|
|
18292
18502
|
|
|
18293
18503
|
if (elem.startEnding && isFirstStaff) {
|
|
18294
|
-
//
|
|
18295
|
-
|
|
18296
|
-
|
|
18297
|
-
|
|
18298
|
-
|
|
18504
|
+
// MAE 17 May 2025 - Fixes drawing issue
|
|
18505
|
+
if (voice.voicenumber === 0) {
|
|
18506
|
+
// only put the first & second ending marks on the first staff
|
|
18507
|
+
var textWidth = this.getTextSize.calc(elem.startEnding, "repeatfont", '').width;
|
|
18508
|
+
abselem.minspacing += textWidth + 10; // Give plenty of room for the ending number.
|
|
18509
|
+
this.partstartelem = new EndingElem(elem.startEnding, anchor, null);
|
|
18510
|
+
voice.addOther(this.partstartelem);
|
|
18511
|
+
}
|
|
18299
18512
|
}
|
|
18300
|
-
|
|
18301
18513
|
// Add a little space to the left of the bar line so that nothing can crowd it.
|
|
18302
18514
|
abselem.extraw -= 5;
|
|
18303
18515
|
if (elem.chord !== undefined) {
|
|
@@ -18617,6 +18829,7 @@ var createClef = function createClef(elem, tuneNumber) {
|
|
|
18617
18829
|
//abselem.top += 2;
|
|
18618
18830
|
}
|
|
18619
18831
|
}
|
|
18832
|
+
|
|
18620
18833
|
return abselem;
|
|
18621
18834
|
};
|
|
18622
18835
|
function clefOffsets(clef) {
|
|
@@ -18808,6 +19021,7 @@ var createNoteHead = function createNoteHead(abselem, c, pitchelem, options) {
|
|
|
18808
19021
|
}));
|
|
18809
19022
|
extraLeft = glyphs.getSymbolWidth(symb) / 2; // TODO-PER: We need a little extra width if there is an accidental, but I'm not sure why it isn't the full width of the accidental.
|
|
18810
19023
|
}
|
|
19024
|
+
|
|
18811
19025
|
return {
|
|
18812
19026
|
notehead: notehead,
|
|
18813
19027
|
accidentalshiftx: accidentalshiftx,
|
|
@@ -19088,6 +19302,7 @@ var stackedDecoration = function stackedDecoration(decoration, width, abselem, y
|
|
|
19088
19302
|
"mediumphrase": "scripts.mediumphrase",
|
|
19089
19303
|
"longphrase": "scripts.longphrase",
|
|
19090
19304
|
"trill": "scripts.trill",
|
|
19305
|
+
"trillh": "scripts.trill",
|
|
19091
19306
|
"roll": "scripts.roll",
|
|
19092
19307
|
"irishroll": "scripts.roll",
|
|
19093
19308
|
"marcato": "scripts.umarcato",
|
|
@@ -19149,6 +19364,7 @@ var stackedDecoration = function stackedDecoration(decoration, width, abselem, y
|
|
|
19149
19364
|
case "mediumphrase":
|
|
19150
19365
|
case "longphrase":
|
|
19151
19366
|
case "trill":
|
|
19367
|
+
case "trillh":
|
|
19152
19368
|
case "roll":
|
|
19153
19369
|
case "irishroll":
|
|
19154
19370
|
case "marcato":
|
|
@@ -19462,10 +19678,18 @@ AbsoluteElement.prototype.setLimit = function (member, child) {
|
|
|
19462
19678
|
if (!this.specialY[member]) this.specialY[member] = child[member];else this.specialY[member] = Math.max(this.specialY[member], child[member]);
|
|
19463
19679
|
};
|
|
19464
19680
|
AbsoluteElement.prototype._addChild = function (child) {
|
|
19465
|
-
//
|
|
19681
|
+
// console.log("Relative:",child);
|
|
19682
|
+
|
|
19683
|
+
// MAE 30 Sep 2024 - To avoid extra space for chords if there is only a bar number on the clef
|
|
19684
|
+
var okToPushTop = true;
|
|
19685
|
+
if (this.abcelem.el_type == "clef" && child.type == "barNumber") {
|
|
19686
|
+
okToPushTop = false;
|
|
19687
|
+
}
|
|
19466
19688
|
child.parent = this;
|
|
19467
19689
|
this.children[this.children.length] = child;
|
|
19468
|
-
|
|
19690
|
+
if (okToPushTop) {
|
|
19691
|
+
this.pushTop(child.top);
|
|
19692
|
+
}
|
|
19469
19693
|
this.pushBottom(child.bottom);
|
|
19470
19694
|
this.setLimit('tempoHeightAbove', child);
|
|
19471
19695
|
this.setLimit('partHeightAbove', child);
|
|
@@ -19598,6 +19822,7 @@ BeamElem.prototype.setStemDirection = function () {
|
|
|
19598
19822
|
var middleLine = 6; // hardcoded 6 is B
|
|
19599
19823
|
this.stemsUp = this.average < middleLine; // true is up, false is down;
|
|
19600
19824
|
}
|
|
19825
|
+
|
|
19601
19826
|
delete this.count;
|
|
19602
19827
|
this.total = 0;
|
|
19603
19828
|
};
|
|
@@ -19611,6 +19836,7 @@ BeamElem.prototype.calcDir = function () {
|
|
|
19611
19836
|
var middleLine = 6; // hardcoded 6 is B
|
|
19612
19837
|
this.stemsUp = this.average < middleLine; // true is up, false is down;
|
|
19613
19838
|
}
|
|
19839
|
+
|
|
19614
19840
|
var dir = this.stemsUp ? 'up' : 'down';
|
|
19615
19841
|
for (var i = 0; i < this.elems.length; i++) {
|
|
19616
19842
|
for (var j = 0; j < this.elems[i].heads.length; j++) {
|
|
@@ -19800,6 +20026,7 @@ BraceElem.prototype.continuing = function (voice) {
|
|
|
19800
20026
|
BraceElem.prototype.getWidth = function () {
|
|
19801
20027
|
return 10; // TODO-PER: right now the drawing function doesn't vary the width at all. If it does in the future then this will change.
|
|
19802
20028
|
};
|
|
20029
|
+
|
|
19803
20030
|
BraceElem.prototype.isStartVoice = function (voice) {
|
|
19804
20031
|
if (this.startVoice && this.startVoice.staff && this.startVoice.staff.voices.length > 0 && this.startVoice.staff.voices[0] === voice) return true;
|
|
19805
20032
|
return false;
|
|
@@ -19824,6 +20051,7 @@ var CrescendoElem = function CrescendoElem(anchor1, anchor2, dir, positioning) {
|
|
|
19824
20051
|
if (positioning === 'above') this.dynamicHeightAbove = 6;else this.dynamicHeightBelow = 6;
|
|
19825
20052
|
this.pitch = undefined; // This will be set later
|
|
19826
20053
|
};
|
|
20054
|
+
|
|
19827
20055
|
module.exports = CrescendoElem;
|
|
19828
20056
|
|
|
19829
20057
|
/***/ }),
|
|
@@ -19843,6 +20071,7 @@ var DynamicDecoration = function DynamicDecoration(anchor, dec, position) {
|
|
|
19843
20071
|
if (position === 'below') this.volumeHeightBelow = 6;else this.volumeHeightAbove = 6;
|
|
19844
20072
|
this.pitch = undefined; // This will be set later
|
|
19845
20073
|
};
|
|
20074
|
+
|
|
19846
20075
|
module.exports = DynamicDecoration;
|
|
19847
20076
|
|
|
19848
20077
|
/***/ }),
|
|
@@ -19863,6 +20092,7 @@ var EndingElem = function EndingElem(text, anchor1, anchor2) {
|
|
|
19863
20092
|
this.endingHeightAbove = 5;
|
|
19864
20093
|
this.pitch = undefined; // This will be set later
|
|
19865
20094
|
};
|
|
20095
|
+
|
|
19866
20096
|
module.exports = EndingElem;
|
|
19867
20097
|
|
|
19868
20098
|
/***/ }),
|
|
@@ -19887,6 +20117,11 @@ function FreeText(info, vskip, getFontAndAttr, paddingLeft, width, getTextSize)
|
|
|
19887
20117
|
move: hash.attr['font-size'] * 2
|
|
19888
20118
|
}); // move the distance of the line, plus the distance of the margin, which is also one line.
|
|
19889
20119
|
} else if (typeof text === 'string') {
|
|
20120
|
+
// MAE 9 May 2025 - Force blank text lines in a text block to have height
|
|
20121
|
+
var replaceStandaloneNewlinesForTextBlocks = function replaceStandaloneNewlinesForTextBlocks(input) {
|
|
20122
|
+
return input.replace(/^[ \t]*\n/gm, 'X\n');
|
|
20123
|
+
;
|
|
20124
|
+
};
|
|
19890
20125
|
this.rows.push({
|
|
19891
20126
|
move: hash.attr['font-size'] / 2
|
|
19892
20127
|
}); // TODO-PER: move down some - the y location should be the top of the text, but we output text specifying the center line.
|
|
@@ -19901,7 +20136,8 @@ function FreeText(info, vskip, getFontAndAttr, paddingLeft, width, getTextSize)
|
|
|
19901
20136
|
absElemType: "freeText",
|
|
19902
20137
|
name: "free-text"
|
|
19903
20138
|
});
|
|
19904
|
-
|
|
20139
|
+
var textForSize = replaceStandaloneNewlinesForTextBlocks(text);
|
|
20140
|
+
size = getTextSize.calc(textForSize, 'textfont', 'defined-text'); // was text
|
|
19905
20141
|
this.rows.push({
|
|
19906
20142
|
move: size.height
|
|
19907
20143
|
});
|
|
@@ -19968,6 +20204,7 @@ var GlissandoElem = function GlissandoElem(anchor1, anchor2) {
|
|
|
19968
20204
|
this.anchor1 = anchor1; // must have a .x and a .parent property or be null (means starts at the "beginning" of the line - after keysig)
|
|
19969
20205
|
this.anchor2 = anchor2; // must have a .x property or be null (means ends at the end of the line)
|
|
19970
20206
|
};
|
|
20207
|
+
|
|
19971
20208
|
module.exports = GlissandoElem;
|
|
19972
20209
|
|
|
19973
20210
|
/***/ }),
|
|
@@ -20502,12 +20739,14 @@ TieElem.prototype.calcX = function (lineStartX, lineEndX) {
|
|
|
20502
20739
|
if (this.anchor2) this.startX = this.anchor2.x - 20; // There is no element and no repeat mark: make a small arc
|
|
20503
20740
|
else this.startX = lineStartX; // Don't have any guidance, so extend to beginning of line
|
|
20504
20741
|
}
|
|
20742
|
+
|
|
20505
20743
|
if (!this.anchor1 && this.dotted) this.startX -= 3; // The arc needs to be long enough to tell that it is dotted.
|
|
20506
20744
|
|
|
20507
20745
|
if (this.anchor2) this.endX = this.anchor2.x; // The normal case where there is a starting element to attach to.
|
|
20508
20746
|
else if (this.endLimitX) this.endX = this.endLimitX.x; // if there is no start element, but there is a repeat mark before the start of the line.
|
|
20509
20747
|
else this.endX = lineEndX; // There is no element and no repeat mark: extend to the beginning of the line.
|
|
20510
20748
|
};
|
|
20749
|
+
|
|
20511
20750
|
TieElem.prototype.calcTieY = function () {
|
|
20512
20751
|
// If the tie comes from another line, then one or both anchors will be missing.
|
|
20513
20752
|
if (this.anchor1) this.startY = this.anchor1.pitch;else if (this.anchor2) this.startY = this.anchor2.pitch;else this.startY = this.above ? 14 : 0;
|
|
@@ -20644,6 +20883,7 @@ function TopText(metaText, metaTextInfo, formatting, lines, width, isPrint, padd
|
|
|
20644
20883
|
|
|
20645
20884
|
// TopText.prototype.addTextIf = function (marginLeft, text, font, klass, marginTop, marginBottom, anchor, getTextSize, absElemType, noMove) {
|
|
20646
20885
|
}
|
|
20886
|
+
|
|
20647
20887
|
if (isPrint) this.rows.push({
|
|
20648
20888
|
move: spacing.top
|
|
20649
20889
|
});
|
|
@@ -21715,6 +21955,7 @@ function straightPath(renderer, xLeft, yTop, yBottom, type) {
|
|
|
21715
21955
|
// right point
|
|
21716
21956
|
-wCurve * 0.1, -hCurve * 0.3, -wCurve, -hCurve - spacing.STEP // left bottom
|
|
21717
21957
|
);
|
|
21958
|
+
|
|
21718
21959
|
return renderer.paper.path({
|
|
21719
21960
|
path: pathString,
|
|
21720
21961
|
stroke: renderer.foregroundColor,
|
|
@@ -21866,10 +22107,18 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
|
|
|
21866
22107
|
renderer.paper.closeGroup();
|
|
21867
22108
|
renderer.moveY(renderer.spacing.music);
|
|
21868
22109
|
var staffgroups = [];
|
|
22110
|
+
var nStaves = 0;
|
|
21869
22111
|
for (var line = 0; line < abcTune.lines.length; line++) {
|
|
21870
22112
|
classes.incrLine();
|
|
21871
22113
|
var abcLine = abcTune.lines[line];
|
|
21872
22114
|
if (abcLine.staff) {
|
|
22115
|
+
// MAE 26 May 2025 - for incipits staff count limiting
|
|
22116
|
+
nStaves++;
|
|
22117
|
+
if (abcTune.formatting.maxStaves) {
|
|
22118
|
+
if (nStaves > abcTune.formatting.maxStaves) {
|
|
22119
|
+
break;
|
|
22120
|
+
}
|
|
22121
|
+
}
|
|
21873
22122
|
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber;
|
|
21874
22123
|
renderer.paper.openGroup(groupClasses);
|
|
21875
22124
|
if (abcLine.vskip) {
|
|
@@ -21976,6 +22225,8 @@ function drawEnding(renderer, params, linestartx, lineendx, selectables) {
|
|
|
21976
22225
|
pathString += sprintf("M %f %f L %f %f ", linestartx, y, lineendx, y);
|
|
21977
22226
|
renderer.paper.openGroup({
|
|
21978
22227
|
klass: renderer.controller.classes.generate("ending"),
|
|
22228
|
+
// MAE 17 May 2025 - Ending numbers not being drawn in correct color
|
|
22229
|
+
fill: renderer.foregroundColor,
|
|
21979
22230
|
"data-name": "ending"
|
|
21980
22231
|
});
|
|
21981
22232
|
printPath(renderer, {
|
|
@@ -22993,6 +23244,7 @@ function drawStaffGroup(renderer, params, selectables, lineNumber) {
|
|
|
22993
23244
|
// renderer.moveY(spacing.STEP, -staff.bottom);
|
|
22994
23245
|
}
|
|
22995
23246
|
}
|
|
23247
|
+
|
|
22996
23248
|
renderer.controller.classes.newMeasure();
|
|
22997
23249
|
|
|
22998
23250
|
// connect all the staves together with a vertical line
|
|
@@ -23214,6 +23466,7 @@ function drawTempo(renderer, params) {
|
|
|
23214
23466
|
// });
|
|
23215
23467
|
//return [tempoGroup];
|
|
23216
23468
|
}
|
|
23469
|
+
|
|
23217
23470
|
module.exports = drawTempo;
|
|
23218
23471
|
|
|
23219
23472
|
/***/ }),
|
|
@@ -23255,7 +23508,14 @@ function renderText(renderer, params, alreadyInGroup) {
|
|
|
23255
23508
|
if (params.cursor) {
|
|
23256
23509
|
hash.attr.cursor = params.cursor;
|
|
23257
23510
|
}
|
|
23258
|
-
|
|
23511
|
+
|
|
23512
|
+
// MAE 9 May 2025 for free text blocks
|
|
23513
|
+
var text;
|
|
23514
|
+
if (params.name == "free-text") {
|
|
23515
|
+
text = params.text.replace(/^[ \t]*\n/gm, ' \n');
|
|
23516
|
+
} else {
|
|
23517
|
+
text = params.text.replace(/\n\n/g, "\n \n");
|
|
23518
|
+
}
|
|
23259
23519
|
text = text.replace(/^\n/, "\xA0\n");
|
|
23260
23520
|
if (hash.font.box) {
|
|
23261
23521
|
if (!alreadyInGroup) renderer.paper.openGroup({
|
|
@@ -23550,6 +23810,7 @@ function drawVoice(renderer, params, bartop, selectables, staffPos) {
|
|
|
23550
23810
|
renderer.controller.classes.incrMeasure();
|
|
23551
23811
|
} else drawBeam(renderer, beam, selectables); // beams must be drawn first for proper printing of triplets, slurs and ties.
|
|
23552
23812
|
}
|
|
23813
|
+
|
|
23553
23814
|
renderer.controller.classes.startMeasure();
|
|
23554
23815
|
for (i = 0; i < params.otherchildren.length; i++) {
|
|
23555
23816
|
child = params.otherchildren[i];
|
|
@@ -23658,6 +23919,7 @@ var EngraverController = function EngraverController(paper, params) {
|
|
|
23658
23919
|
this.staffwidthScreen = 740; // TODO-PER: Not sure where this number comes from, but this is how it's always been.
|
|
23659
23920
|
this.staffwidthPrint = 680; // The number of pixels in 8.5", after 1cm of margin has been removed.
|
|
23660
23921
|
}
|
|
23922
|
+
|
|
23661
23923
|
this.listeners = [];
|
|
23662
23924
|
if (params.clickListener) this.addSelectListener(params.clickListener);
|
|
23663
23925
|
this.renderer = new Renderer(paper);
|
|
@@ -24476,8 +24738,13 @@ function keyboardSelection(ev) {
|
|
|
24476
24738
|
}
|
|
24477
24739
|
function findElementInHistory(selectables, el) {
|
|
24478
24740
|
if (!el) return -1;
|
|
24741
|
+
// This should always exist, but it occasionally causes an exception, so check first.
|
|
24742
|
+
var dataset = el.dataset;
|
|
24743
|
+
if (!dataset) return -1;
|
|
24744
|
+
var index = dataset.index;
|
|
24479
24745
|
for (var i = 0; i < selectables.length; i++) {
|
|
24480
|
-
|
|
24746
|
+
var svgDataset = selectables[i].svgEl.dataset;
|
|
24747
|
+
if (svgDataset && index === svgDataset.index) return i;
|
|
24481
24748
|
}
|
|
24482
24749
|
return -1;
|
|
24483
24750
|
}
|
|
@@ -24567,6 +24834,7 @@ function getMousePosition(self, ev) {
|
|
|
24567
24834
|
clickedOn = findElementByCoord(self, x, y);
|
|
24568
24835
|
//console.log("clicked near", clickedOn, x, y, printEl(ev.target));
|
|
24569
24836
|
}
|
|
24837
|
+
|
|
24570
24838
|
return {
|
|
24571
24839
|
x: x,
|
|
24572
24840
|
y: y,
|
|
@@ -24804,6 +25072,7 @@ function minStem(element, stemsUp, referencePitch, minStemHeight) {
|
|
|
24804
25072
|
var elem = element.children[i];
|
|
24805
25073
|
if (stemsUp && elem.top !== undefined && elem.c === "flags.ugrace") minStemHeight = Math.max(minStemHeight, elem.top - referencePitch);else if (!stemsUp && elem.bottom !== undefined && elem.c === "flags.ugrace") minStemHeight = Math.max(minStemHeight, referencePitch - elem.bottom + 7); // The extra 7 is because we are measuring the slash from the top.
|
|
24806
25074
|
}
|
|
25075
|
+
|
|
24807
25076
|
return minStemHeight;
|
|
24808
25077
|
}
|
|
24809
25078
|
function calcSlant(leftAveragePitch, rightAveragePitch, numStems, isFlat) {
|
|
@@ -25208,6 +25477,7 @@ function calcHorizontalSpacing(isLastLine, stretchLast, targetWidth, lineWidth,
|
|
|
25208
25477
|
if (!stretch) return null; // don't stretch last line too much
|
|
25209
25478
|
}
|
|
25210
25479
|
}
|
|
25480
|
+
|
|
25211
25481
|
if (Math.abs(targetWidth - lineWidth) < 2) return null; // if we are already near the target width, we're done.
|
|
25212
25482
|
var relSpace = spacingUnits * spacing;
|
|
25213
25483
|
var constSpace = lineWidth - relSpace;
|
|
@@ -25272,6 +25542,7 @@ var setUpperAndLowerElements = function setUpperAndLowerElements(renderer, staff
|
|
|
25272
25542
|
staff.originalTop = staff.top; // This is just being stored for debugging purposes.
|
|
25273
25543
|
staff.originalBottom = staff.bottom; // This is just being stored for debugging purposes.
|
|
25274
25544
|
}
|
|
25545
|
+
|
|
25275
25546
|
incTop(staff, positionY, 'lyricHeightAbove');
|
|
25276
25547
|
incTop(staff, positionY, 'chordHeightAbove', staff.specialY.chordLines.above);
|
|
25277
25548
|
if (staff.specialY.endingHeightAbove) {
|
|
@@ -25334,6 +25605,7 @@ var setUpperAndLowerElements = function setUpperAndLowerElements(renderer, staff
|
|
|
25334
25605
|
}
|
|
25335
25606
|
//console.log("Staff Height: ",heightInPitches,this.height);
|
|
25336
25607
|
};
|
|
25608
|
+
|
|
25337
25609
|
var margin = 1;
|
|
25338
25610
|
function incTop(staff, positionY, item, count) {
|
|
25339
25611
|
if (staff.specialY[item]) {
|
|
@@ -25598,6 +25870,7 @@ function finished(voices) {
|
|
|
25598
25870
|
function getDurationIndex(element) {
|
|
25599
25871
|
return element.durationindex - (element.children[element.i] && element.children[element.i].duration > 0 ? 0 : 0.0000005); // if the ith element doesn't have a duration (is not a note), its duration index is fractionally before. This enables CLEF KEYSIG TIMESIG PART, etc. to be laid out before we get to the first note of other voices
|
|
25600
25872
|
}
|
|
25873
|
+
|
|
25601
25874
|
function isSameStaff(voice1, voice2) {
|
|
25602
25875
|
if (!voice1 || !voice1.staff || !voice1.staff.voices || voice1.staff.voices.length === 0) return false;
|
|
25603
25876
|
if (!voice2 || !voice2.staff || !voice2.staff.voices || voice2.staff.voices.length === 0) return false;
|
|
@@ -25695,6 +25968,7 @@ VoiceElement.beginLayout = function (startx, voice) {
|
|
|
25695
25968
|
voice.nextx = startx; // x position where the next element of this voice should be placed assuming no other voices and no fixed width constraints
|
|
25696
25969
|
voice.spacingduration = 0; // duration left to be laid out in current iteration (omitting additional spacing due to other aspects, such as bars, dots, sharps and flats)
|
|
25697
25970
|
};
|
|
25971
|
+
|
|
25698
25972
|
VoiceElement.layoutEnded = function (voice) {
|
|
25699
25973
|
return voice.i >= voice.children.length;
|
|
25700
25974
|
};
|
|
@@ -25760,6 +26034,7 @@ VoiceElement.layoutOneItem = function (x, spacing, voice, minPadding, firstVoice
|
|
|
25760
26034
|
|
|
25761
26035
|
return x; // where we end up having placed the child
|
|
25762
26036
|
};
|
|
26037
|
+
|
|
25763
26038
|
VoiceElement.shiftRight = function (dx, voice) {
|
|
25764
26039
|
var child = voice.children[voice.i];
|
|
25765
26040
|
if (!child) return;
|
|
@@ -25922,6 +26197,7 @@ function setLane(absElems, numLanesAbove, numLanesBelow) {
|
|
|
25922
26197
|
}
|
|
25923
26198
|
}
|
|
25924
26199
|
}
|
|
26200
|
+
|
|
25925
26201
|
function yAtNote(element, beam) {
|
|
25926
26202
|
beam = beam.beams[0];
|
|
25927
26203
|
return getBarYAt(beam.startX, beam.startY, beam.endX, beam.endY, element.x);
|
|
@@ -26080,6 +26356,7 @@ Renderer.prototype.initVerticalSpace = function () {
|
|
|
26080
26356
|
<float> range is 0.0 to 1.0.
|
|
26081
26357
|
*/
|
|
26082
26358
|
};
|
|
26359
|
+
|
|
26083
26360
|
Renderer.prototype.setVerticalSpace = function (formatting) {
|
|
26084
26361
|
// conversion from pts to px 4/3
|
|
26085
26362
|
if (formatting.staffsep !== undefined) this.spacing.staffSeparation = formatting.staffsep * 4 / 3;
|
|
@@ -26201,6 +26478,7 @@ Svg.prototype.insertStyles = function (styles) {
|
|
|
26201
26478
|
this.svg.insertBefore(el, this.svg.firstChild); // prepend is not available on older browsers.
|
|
26202
26479
|
// this.svg.prepend(el);
|
|
26203
26480
|
};
|
|
26481
|
+
|
|
26204
26482
|
Svg.prototype.setParentStyles = function (attr) {
|
|
26205
26483
|
// This is needed to get the size right when there is scaling involved.
|
|
26206
26484
|
for (var key in attr) {
|
|
@@ -26270,8 +26548,13 @@ Svg.prototype.text = function (text, attr, target) {
|
|
|
26270
26548
|
el.setAttribute(key, attr[key]);
|
|
26271
26549
|
}
|
|
26272
26550
|
}
|
|
26551
|
+
var isFreeText = attr["data-name"] == "free-text";
|
|
26273
26552
|
var lines = ("" + text).split("\n");
|
|
26274
26553
|
for (var i = 0; i < lines.length; i++) {
|
|
26554
|
+
if (isFreeText && lines[i] == "") {
|
|
26555
|
+
// Don't draw empty lines
|
|
26556
|
+
continue;
|
|
26557
|
+
}
|
|
26275
26558
|
var line = document.createElementNS(svgNS, 'tspan');
|
|
26276
26559
|
line.setAttribute("x", attr.x ? attr.x : 0);
|
|
26277
26560
|
if (i !== 0) line.setAttribute("dy", "1.2em");
|
|
@@ -26293,7 +26576,19 @@ Svg.prototype.text = function (text, attr, target) {
|
|
|
26293
26576
|
ts3.textContent = parts[2];
|
|
26294
26577
|
line.appendChild(ts3);
|
|
26295
26578
|
}
|
|
26296
|
-
} else
|
|
26579
|
+
} else {
|
|
26580
|
+
// MAE 9 May 2025 - For improved block text
|
|
26581
|
+
if (isFreeText) {
|
|
26582
|
+
// Fixes issue where blank lines in text blocks didn't take up any vertical
|
|
26583
|
+
if (lines[i].trim() == "") {
|
|
26584
|
+
line.innerHTML = " ";
|
|
26585
|
+
} else {
|
|
26586
|
+
line.textContent = lines[i];
|
|
26587
|
+
}
|
|
26588
|
+
} else {
|
|
26589
|
+
line.textContent = lines[i];
|
|
26590
|
+
}
|
|
26591
|
+
}
|
|
26297
26592
|
el.appendChild(line);
|
|
26298
26593
|
}
|
|
26299
26594
|
if (target) target.appendChild(el);else this.append(el);
|
|
@@ -26343,6 +26638,7 @@ Svg.prototype.guessWidth = function (text, attr) {
|
|
|
26343
26638
|
height: attr['font-size'] + 2
|
|
26344
26639
|
}; // Just a wild guess.
|
|
26345
26640
|
}
|
|
26641
|
+
|
|
26346
26642
|
svg.removeChild(el);
|
|
26347
26643
|
return size;
|
|
26348
26644
|
};
|
|
@@ -26471,7 +26767,7 @@ module.exports = Svg;
|
|
|
26471
26767
|
\********************/
|
|
26472
26768
|
/***/ (function(module) {
|
|
26473
26769
|
|
|
26474
|
-
var version = '6.
|
|
26770
|
+
var version = '6.5.0';
|
|
26475
26771
|
module.exports = version;
|
|
26476
26772
|
|
|
26477
26773
|
/***/ })
|