abcjs 6.4.4 → 6.5.1
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 +50 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +337 -125
- 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 +2 -0
- 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 +3 -1
- package/src/str/output.js +4 -4
- package/src/synth/abc_midi_flattener.js +52 -11
- package/src/synth/abc_midi_sequencer.js +27 -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_parser_lint.js +1 -1
- package/src/write/creation/abstract-engraver.js +9 -6
- package/src/write/creation/decoration.js +2 -0
- 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
|
@@ -4319,11 +4319,24 @@ var parseDirective = {};
|
|
|
4319
4319
|
}
|
|
4320
4320
|
multilineVars.currBarNumber = tuneBuilder.setBarNumberImmediate(tokens[0].intt);
|
|
4321
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;
|
|
4322
4328
|
case "begintext":
|
|
4323
4329
|
var textBlock = '';
|
|
4324
4330
|
line = tokenizer.nextLine();
|
|
4325
4331
|
while (line && line.indexOf('%%endtext') !== 0) {
|
|
4326
|
-
|
|
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
|
+
}
|
|
4327
4340
|
line = tokenizer.nextLine();
|
|
4328
4341
|
}
|
|
4329
4342
|
tuneBuilder.addText(textBlock, {
|
|
@@ -4508,6 +4521,14 @@ var parseDirective = {};
|
|
|
4508
4521
|
}
|
|
4509
4522
|
}
|
|
4510
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;
|
|
4511
4532
|
case "newpage":
|
|
4512
4533
|
var pgNum = tokenizer.getInt(restOfString);
|
|
4513
4534
|
tuneBuilder.addNewPage(pgNum.digits === 0 ? -1 : pgNum.value);
|
|
@@ -4568,6 +4589,10 @@ var parseDirective = {};
|
|
|
4568
4589
|
tune.formatting.percmap[percmap.key] = percmap.value;
|
|
4569
4590
|
}
|
|
4570
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;
|
|
4571
4596
|
case "map":
|
|
4572
4597
|
case "playtempo":
|
|
4573
4598
|
case "auquality":
|
|
@@ -5109,8 +5134,8 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5109
5134
|
return [line.length];
|
|
5110
5135
|
case "K:":
|
|
5111
5136
|
var result = parseKeyVoice.parseKey(line.substring(i + 2), tuneBuilder.hasBeginMusic());
|
|
5112
|
-
if (result.foundClef && tuneBuilder.hasBeginMusic()) tuneBuilder.appendStartingElement('clef', multilineVars.iChar + i, multilineVars.iChar + line.length, multilineVars.clef);
|
|
5113
|
-
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));
|
|
5114
5139
|
return [line.length];
|
|
5115
5140
|
case "P:":
|
|
5116
5141
|
if (tuneBuilder.hasBeginMusic()) tuneBuilder.appendElement('part', multilineVars.iChar + i, multilineVars.iChar + line.length, {
|
|
@@ -5192,7 +5217,7 @@ var ParseHeader = function ParseHeader(tokenizer, warn, multilineVars, tune, tun
|
|
|
5192
5217
|
// since the key is the last thing that can happen in the header, we can resolve the tempo now
|
|
5193
5218
|
this.resolveTempo();
|
|
5194
5219
|
var result = parseKeyVoice.parseKey(line.substring(2), false);
|
|
5195
|
-
if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic()) {
|
|
5220
|
+
if (!multilineVars.is_in_header && tuneBuilder.hasBeginMusic() && multilineVars.keywarn !== false) {
|
|
5196
5221
|
if (result.foundClef) tuneBuilder.appendStartingElement('clef', startChar, endChar, multilineVars.clef);
|
|
5197
5222
|
if (result.foundKey) tuneBuilder.appendStartingElement('key', startChar, endChar, parseKeyVoice.fixKey(multilineVars.clef, multilineVars.key));
|
|
5198
5223
|
}
|
|
@@ -5724,7 +5749,7 @@ var parseKeyVoice = {};
|
|
|
5724
5749
|
multilineVars.key = parseKeyVoice.deepCopyKey(parseKeyVoice.standardKey(key, retPitch.token, acc, keyCompensate));
|
|
5725
5750
|
if (isInline) multilineVars.globalTransposeOrigKeySig = savedOrigKey;
|
|
5726
5751
|
multilineVars.key.mode = mode;
|
|
5727
|
-
if (oldKey) {
|
|
5752
|
+
if (oldKey && multilineVars.keywarn !== false) {
|
|
5728
5753
|
// Add natural in all places that the old key had an accidental.
|
|
5729
5754
|
var kk;
|
|
5730
5755
|
for (var k = 0; k < multilineVars.key.accidentals.length; k++) {
|
|
@@ -6135,12 +6160,47 @@ var parseKeyVoice = {};
|
|
|
6135
6160
|
case 'tenor,,':
|
|
6136
6161
|
case 'alto,,':
|
|
6137
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
|
+
|
|
6138
6202
|
// TODO-PER: handle the octave indicators on the clef by changing the middle property
|
|
6139
6203
|
var oct2 = 0;
|
|
6140
|
-
// for (var iii = 0; iii < token.token.length; iii++) {
|
|
6141
|
-
// if (token.token[iii] === ',') oct2 -= 7;
|
|
6142
|
-
// else if (token.token[iii] === "'") oct2 += 7;
|
|
6143
|
-
// }
|
|
6144
6204
|
staffInfo.clef = token.token.replace(/[',]/g, ""); //'//comment for emacs formatting of regexp
|
|
6145
6205
|
staffInfo.verticalPos = calcMiddle(staffInfo.clef, oct2);
|
|
6146
6206
|
multilineVars.voices[id].clef = token.token;
|
|
@@ -7036,6 +7096,8 @@ var letter_to_accent = function letter_to_accent(line, i) {
|
|
|
7036
7096
|
return [1, 'segno'];
|
|
7037
7097
|
case 'T':
|
|
7038
7098
|
return [1, 'trill'];
|
|
7099
|
+
case 't':
|
|
7100
|
+
return [1, 'trillh'];
|
|
7039
7101
|
}
|
|
7040
7102
|
return [0, 0];
|
|
7041
7103
|
};
|
|
@@ -7513,7 +7575,7 @@ module.exports = MusicParser;
|
|
|
7513
7575
|
\*****************************************/
|
|
7514
7576
|
/***/ (function(module) {
|
|
7515
7577
|
|
|
7516
|
-
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'];
|
|
7517
7579
|
module.exports.volumeDecorations = ['p', 'pp', 'f', 'ff', 'mf', 'mp', 'ppp', 'pppp', 'fff', 'ffff', 'sfz'];
|
|
7518
7580
|
module.exports.dynamicDecorations = ['crescendo(', 'crescendo)', 'diminuendo(', 'diminuendo)', 'glissando(', 'glissando)', '~(', '~)'];
|
|
7519
7581
|
module.exports.accentPseudonyms = [['<', 'accent'], ['>', 'accent'], ['tr', 'trill'], ['plus', '+'], ['emphasis', 'accent'], ['^', 'umarcato'], ['marcato', 'umarcato']];
|
|
@@ -8731,14 +8793,38 @@ var Tokenizer = function Tokenizer(lines, multilineVars) {
|
|
|
8731
8793
|
}
|
|
8732
8794
|
}
|
|
8733
8795
|
var thePatterns = [{
|
|
8734
|
-
match: /,\s*
|
|
8796
|
+
match: /,\s*The$/,
|
|
8735
8797
|
replace: "The "
|
|
8736
8798
|
}, {
|
|
8737
|
-
match: /,\s*
|
|
8799
|
+
match: /,\s*the$/,
|
|
8800
|
+
replace: "the "
|
|
8801
|
+
}, {
|
|
8802
|
+
match: /,\s*A$/,
|
|
8738
8803
|
replace: "A "
|
|
8739
8804
|
}, {
|
|
8740
|
-
match: /,\s*
|
|
8805
|
+
match: /,\s*a$/,
|
|
8806
|
+
replace: "a "
|
|
8807
|
+
}, {
|
|
8808
|
+
match: /,\s*An$/,
|
|
8741
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 "
|
|
8742
8828
|
}];
|
|
8743
8829
|
this.theReverser = function (str) {
|
|
8744
8830
|
for (var i = 0; i < thePatterns.length; i++) {
|
|
@@ -8988,7 +9074,9 @@ transpose.keySignature = function (multilineVars, keyName, root, acc, localTrans
|
|
|
8988
9074
|
var newKeyName = keyName[0] === 'm' ? newKeyMinor[index] : newKey[index];
|
|
8989
9075
|
var transposedKey = newKeyName + keyName;
|
|
8990
9076
|
var newKeySig = keyAccidentals(transposedKey);
|
|
8991
|
-
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;
|
|
8992
9080
|
var distance = transposedKey.charCodeAt(0) - baseKey.charCodeAt(0);
|
|
8993
9081
|
if (multilineVars.localTranspose > 0) {
|
|
8994
9082
|
if (distance < 0) distance += 7;else if (distance === 0) {
|
|
@@ -9160,6 +9248,13 @@ function transposeChordName(chord, steps, preferFlats, freeGCchord) {
|
|
|
9160
9248
|
} else {
|
|
9161
9249
|
if (freeGCchord) chord = sharpChordsFree[index];else chord = sharpChords[index];
|
|
9162
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♭';
|
|
9163
9258
|
if (extra1) chord += extra1;
|
|
9164
9259
|
if (bass) {
|
|
9165
9260
|
var index = sharpChords.indexOf(bass);
|
|
@@ -9201,7 +9296,7 @@ var TuneBuilder = function TuneBuilder(tune) {
|
|
|
9201
9296
|
var currentVoiceName = '';
|
|
9202
9297
|
tune.reset();
|
|
9203
9298
|
this.setVisualTranspose = function (visualTranspose) {
|
|
9204
|
-
if (visualTranspose) tune.visualTranspose = visualTranspose;
|
|
9299
|
+
if (visualTranspose !== undefined) tune.visualTranspose = visualTranspose;
|
|
9205
9300
|
};
|
|
9206
9301
|
this.cleanUp = function (barsperstaff, staffnonote, currSlur) {
|
|
9207
9302
|
closeLine(tune); // Close the last line.
|
|
@@ -10000,6 +10095,7 @@ function wrapMusicLines(lines, barsperstaff) {
|
|
|
10000
10095
|
return false;
|
|
10001
10096
|
}
|
|
10002
10097
|
function getPrevMusicLine(lines, currentLine) {
|
|
10098
|
+
if (lines.length <= currentLine) return null;
|
|
10003
10099
|
// If the current line doesn't have music, search backwards until one is found.
|
|
10004
10100
|
while (currentLine >= 0) {
|
|
10005
10101
|
if (lines[currentLine].staff) return lines[currentLine];
|
|
@@ -10745,16 +10841,16 @@ var strTranspose;
|
|
|
10745
10841
|
var count = arr[0].length;
|
|
10746
10842
|
for (var i = 1; i < arr.length; i++) {
|
|
10747
10843
|
var segment = arr[i];
|
|
10748
|
-
var match = segment.match(/^( *)([A-G])([#b]?)(\w*)/);
|
|
10844
|
+
var match = segment.match(/^( *)([A-G])([#b]?)( ?)(\w*)/);
|
|
10749
10845
|
if (match) {
|
|
10750
10846
|
var start = count + 2 + match[1].length; // move past the 'K:' and optional white space
|
|
10751
|
-
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
|
|
10752
10848
|
var destinationKey = newKey({
|
|
10753
10849
|
root: match[2],
|
|
10754
10850
|
acc: match[3],
|
|
10755
|
-
mode: match[
|
|
10851
|
+
mode: match[5]
|
|
10756
10852
|
}, steps);
|
|
10757
|
-
var dest = destinationKey.root + destinationKey.acc + destinationKey.mode;
|
|
10853
|
+
var dest = destinationKey.root + destinationKey.acc + match[4] + destinationKey.mode;
|
|
10758
10854
|
changes.push({
|
|
10759
10855
|
start: start,
|
|
10760
10856
|
end: start + key.length,
|
|
@@ -11202,6 +11298,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11202
11298
|
var stressBeat1 = 105;
|
|
11203
11299
|
var stressBeatDown = 95;
|
|
11204
11300
|
var stressBeatUp = 85;
|
|
11301
|
+
var volumesPerNotePitch = [[stressBeat1, stressBeatDown, stressBeatUp]];
|
|
11205
11302
|
var beatFraction = 0.25;
|
|
11206
11303
|
var nextVolume;
|
|
11207
11304
|
var nextVolumeDelta;
|
|
@@ -11243,6 +11340,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11243
11340
|
stressBeat1 = 105;
|
|
11244
11341
|
stressBeatDown = 95;
|
|
11245
11342
|
stressBeatUp = 85;
|
|
11343
|
+
volumesPerNotePitch = [];
|
|
11246
11344
|
beatFraction = 0.25;
|
|
11247
11345
|
nextVolume = undefined;
|
|
11248
11346
|
nextVolumeDelta = undefined;
|
|
@@ -11353,6 +11451,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11353
11451
|
stressBeat1 = element.beats[0];
|
|
11354
11452
|
stressBeatDown = element.beats[1];
|
|
11355
11453
|
stressBeatUp = element.beats[2];
|
|
11454
|
+
if (!element.volumesPerNotePitch) volumesPerNotePitch = [];else volumesPerNotePitch = element.volumesPerNotePitch;
|
|
11356
11455
|
// TODO-PER: also use the last parameter - which changes which beats are strong.
|
|
11357
11456
|
break;
|
|
11358
11457
|
case "vol":
|
|
@@ -11496,21 +11595,29 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11496
11595
|
var distanceFromStart = currTime - measureStart;
|
|
11497
11596
|
return distanceFromStart / beatLength;
|
|
11498
11597
|
}
|
|
11499
|
-
function processVolume(beat, voiceOff) {
|
|
11598
|
+
function processVolume(beat, voiceOff, pitchIndexOfNote) {
|
|
11500
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
|
+
}
|
|
11501
11608
|
var volume;
|
|
11502
11609
|
// MAE 21 Jun 2024 - This previously wasn't allowing zero volume to be applied
|
|
11503
|
-
if (nextVolume
|
|
11610
|
+
if (nextVolume !== undefined) {
|
|
11504
11611
|
volume = nextVolume;
|
|
11505
11612
|
nextVolume = undefined;
|
|
11506
11613
|
} else if (!doBeatAccents) {
|
|
11507
|
-
volume =
|
|
11614
|
+
volume = pitchStressBeatDown;
|
|
11508
11615
|
} else if (pickupLength > beat) {
|
|
11509
|
-
volume =
|
|
11616
|
+
volume = pitchStressBeatUp;
|
|
11510
11617
|
} else {
|
|
11511
11618
|
//var barLength = meter.num / meter.den;
|
|
11512
11619
|
var barBeat = calcBeat(lastBarTime, getBeatFraction(meter), beat);
|
|
11513
|
-
if (barBeat === 0) volume =
|
|
11620
|
+
if (barBeat === 0) volume = pitchStressBeat1;else if (parseInt(barBeat, 10) === barBeat) volume = pitchStressBeatDown;else volume = pitchStressBeatUp;
|
|
11514
11621
|
}
|
|
11515
11622
|
if (nextVolumeDelta) {
|
|
11516
11623
|
volume += nextVolumeDelta;
|
|
@@ -11524,7 +11631,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11524
11631
|
var ret = {};
|
|
11525
11632
|
if (elem.decoration) {
|
|
11526
11633
|
for (var d = 0; d < elem.decoration.length; d++) {
|
|
11527
|
-
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";
|
|
11528
11635
|
}
|
|
11529
11636
|
}
|
|
11530
11637
|
return ret;
|
|
@@ -11555,7 +11662,25 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11555
11662
|
start += shortestNote;
|
|
11556
11663
|
}
|
|
11557
11664
|
break;
|
|
11558
|
-
case "
|
|
11665
|
+
case "trillh":
|
|
11666
|
+
var note = 1;
|
|
11667
|
+
while (runningDuration > 0) {
|
|
11668
|
+
currentTrack.push({
|
|
11669
|
+
cmd: 'note',
|
|
11670
|
+
pitch: p.pitch + note,
|
|
11671
|
+
volume: p.volume,
|
|
11672
|
+
start: start,
|
|
11673
|
+
duration: shortestNote,
|
|
11674
|
+
gap: 0,
|
|
11675
|
+
instrument: currentInstrument,
|
|
11676
|
+
style: 'decoration'
|
|
11677
|
+
});
|
|
11678
|
+
note = note === 1 ? 0 : 1;
|
|
11679
|
+
runningDuration -= shortestNote;
|
|
11680
|
+
start += shortestNote;
|
|
11681
|
+
}
|
|
11682
|
+
break;
|
|
11683
|
+
case "pralltriller":
|
|
11559
11684
|
currentTrack.push({
|
|
11560
11685
|
cmd: 'note',
|
|
11561
11686
|
pitch: p.pitch,
|
|
@@ -11590,6 +11715,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11590
11715
|
instrument: currentInstrument
|
|
11591
11716
|
});
|
|
11592
11717
|
break;
|
|
11718
|
+
case "mordent":
|
|
11593
11719
|
case "lowermordent":
|
|
11594
11720
|
currentTrack.push({
|
|
11595
11721
|
cmd: 'note',
|
|
@@ -11754,6 +11880,11 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11754
11880
|
}
|
|
11755
11881
|
if (elem.elem) elem.elem.midiPitches = [];
|
|
11756
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
|
+
}
|
|
11757
11888
|
var note = ePitches[i];
|
|
11758
11889
|
if (!note) continue;
|
|
11759
11890
|
if (note.startSlur) slurCount += note.startSlur.length;
|
|
@@ -11766,7 +11897,7 @@ var pitchesToPerc = __webpack_require__(/*! ./pitches-to-perc */ "./src/synth/pi
|
|
|
11766
11897
|
var p = {
|
|
11767
11898
|
cmd: 'note',
|
|
11768
11899
|
pitch: actualPitch,
|
|
11769
|
-
volume:
|
|
11900
|
+
volume: pitchVelocity,
|
|
11770
11901
|
start: timeToRealTime(elem.time),
|
|
11771
11902
|
duration: durationRounded(note.duration),
|
|
11772
11903
|
instrument: currentInstrument,
|
|
@@ -12549,6 +12680,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12549
12680
|
|
|
12550
12681
|
// visit each voice completely in turn
|
|
12551
12682
|
var voices = [];
|
|
12683
|
+
var clefTransposeActive = [];
|
|
12552
12684
|
var inCrescendo = [];
|
|
12553
12685
|
var inDiminuendo = [];
|
|
12554
12686
|
var durationCounter = [0];
|
|
@@ -12569,6 +12701,7 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12569
12701
|
if (line.staff) {
|
|
12570
12702
|
var setDynamics = function setDynamics(elem) {
|
|
12571
12703
|
var volumes = {
|
|
12704
|
+
//stressBeat1, stressBeatDown, stressBeatUp
|
|
12572
12705
|
'pppp': [15, 10, 5, 1],
|
|
12573
12706
|
'ppp': [30, 20, 10, 1],
|
|
12574
12707
|
'pp': [45, 35, 20, 1],
|
|
@@ -12585,9 +12718,17 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12585
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';
|
|
12586
12719
|
if (dynamicType) {
|
|
12587
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
|
+
if (d in volumes) volumesPerNotePitch.push(volumes[d].slice(0));
|
|
12726
|
+
});
|
|
12727
|
+
}
|
|
12588
12728
|
voices[voiceNumber].push({
|
|
12589
12729
|
el_type: 'beat',
|
|
12590
|
-
beats: currentVolume.slice(0)
|
|
12730
|
+
beats: currentVolume.slice(0),
|
|
12731
|
+
volumesPerNotePitch: volumesPerNotePitch
|
|
12591
12732
|
});
|
|
12592
12733
|
inCrescendo[k] = false;
|
|
12593
12734
|
inDiminuendo[k] = false;
|
|
@@ -12665,15 +12806,31 @@ var parseCommon = __webpack_require__(/*! ../parse/abc_common */ "./src/parse/ab
|
|
|
12665
12806
|
el_type: 'transpose',
|
|
12666
12807
|
transpose: staff.clef.transpose
|
|
12667
12808
|
});
|
|
12809
|
+
clefTransposeActive[voiceNumber] = false;
|
|
12668
12810
|
}
|
|
12669
12811
|
if (staff.clef && staff.clef.type) {
|
|
12670
|
-
if (staff.clef.type.indexOf("-8") >= 0)
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
})
|
|
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
|
+
}
|
|
12677
12834
|
}
|
|
12678
12835
|
if (abctune.formatting.midi && abctune.formatting.midi.drumoff) {
|
|
12679
12836
|
// If there is a drum off command right at the beginning it is put in the metaText instead of the stream,
|
|
@@ -13394,7 +13551,7 @@ ChordTrack.prototype.interpretChord = function (name) {
|
|
|
13394
13551
|
};
|
|
13395
13552
|
var root = name.substring(0, 1);
|
|
13396
13553
|
if (root === '(') {
|
|
13397
|
-
name = name.substring(1, name.length -
|
|
13554
|
+
name = name.substring(1, name.length - 1);
|
|
13398
13555
|
if (name.length === 0) return undefined;
|
|
13399
13556
|
root = name.substring(0, 1);
|
|
13400
13557
|
}
|
|
@@ -14166,7 +14323,7 @@ function CreateSynth() {
|
|
|
14166
14323
|
self.audioBuffers = []; // cache of the buffers so starting play can be fast.
|
|
14167
14324
|
self.duration = undefined; // the duration of the tune in seconds.
|
|
14168
14325
|
self.isRunning = false; // whether there is currently a sound buffer running.
|
|
14169
|
-
self.options =
|
|
14326
|
+
self.options = {}; // Thx tomohirohiratsuka
|
|
14170
14327
|
self.pickupLength = 0;
|
|
14171
14328
|
|
|
14172
14329
|
// Load and cache all needed sounds
|
|
@@ -14394,87 +14551,93 @@ function CreateSynth() {
|
|
|
14394
14551
|
self.isRunning = false;
|
|
14395
14552
|
if (!self.audioBufferPossible) return Promise.reject(new Error(notSupportedMessage));
|
|
14396
14553
|
if (self.debugCallback) self.debugCallback("prime called");
|
|
14397
|
-
return new Promise(function (resolve) {
|
|
14398
|
-
|
|
14399
|
-
|
|
14400
|
-
|
|
14401
|
-
|
|
14402
|
-
|
|
14403
|
-
|
|
14404
|
-
|
|
14405
|
-
seconds: 0
|
|
14406
|
-
});
|
|
14407
|
-
}
|
|
14408
|
-
self.duration += fadeTimeSec;
|
|
14409
|
-
var totalSamples = Math.floor(activeAudioContext().sampleRate * self.duration);
|
|
14410
|
-
|
|
14411
|
-
// There might be a previous run that needs to be turned off.
|
|
14412
|
-
self.stop();
|
|
14413
|
-
var noteMapTracks = createNoteMap(self.flattened);
|
|
14414
|
-
if (self.options.swing) addSwing(noteMapTracks, self.options.swing, self.meterFraction, self.pickupLength);
|
|
14415
|
-
if (self.sequenceCallback) self.sequenceCallback(noteMapTracks, self.callbackContext);
|
|
14416
|
-
var panDistances = setPan(noteMapTracks.length, self.pan);
|
|
14417
|
-
|
|
14418
|
-
// Create a simple list of all the unique sounds in this music and where they should be placed.
|
|
14419
|
-
// There appears to be a limit on how many audio buffers can be created at once so this technique limits the number needed.
|
|
14420
|
-
var uniqueSounds = {};
|
|
14421
|
-
noteMapTracks.forEach(function (noteMap, trackNumber) {
|
|
14422
|
-
var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
|
|
14423
|
-
noteMap.forEach(function (note) {
|
|
14424
|
-
var key = note.instrument + ':' + note.pitch + ':' + note.volume + ':' + Math.round((note.end - note.start) * 1000) / 1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
|
|
14425
|
-
if (self.debugCallback) self.debugCallback("noteMapTrack " + key);
|
|
14426
|
-
if (!uniqueSounds[key]) uniqueSounds[key] = [];
|
|
14427
|
-
uniqueSounds[key].push(note.start);
|
|
14428
|
-
});
|
|
14429
|
-
});
|
|
14430
|
-
|
|
14431
|
-
// Now that we know what we are trying to create, construct the audio buffer by creating each sound and placing it.
|
|
14432
|
-
var allPromises = [];
|
|
14433
|
-
var audioBuffer = activeAudioContext().createBuffer(2, totalSamples, activeAudioContext().sampleRate);
|
|
14434
|
-
for (var key2 = 0; key2 < Object.keys(uniqueSounds).length; key2++) {
|
|
14435
|
-
var k = Object.keys(uniqueSounds)[key2];
|
|
14436
|
-
var parts = k.split(":");
|
|
14437
|
-
var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
|
|
14438
|
-
parts = {
|
|
14439
|
-
instrument: parts[0],
|
|
14440
|
-
pitch: parseInt(parts[1], 10),
|
|
14441
|
-
volume: parseInt(parts[2], 10),
|
|
14442
|
-
len: parseFloat(parts[3]),
|
|
14443
|
-
pan: parseFloat(parts[4]),
|
|
14444
|
-
tempoMultiplier: parseFloat(parts[5]),
|
|
14445
|
-
cents: cents
|
|
14446
|
-
};
|
|
14447
|
-
allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000, self.debugCallback));
|
|
14448
|
-
}
|
|
14449
|
-
self.audioBuffers = [audioBuffer];
|
|
14450
|
-
if (self.debugCallback) {
|
|
14451
|
-
self.debugCallback("sampleRate = " + activeAudioContext().sampleRate);
|
|
14452
|
-
self.debugCallback("totalSamples = " + totalSamples);
|
|
14453
|
-
self.debugCallback("creationTime = " + Math.floor((activeAudioContext().currentTime - startTime) * 1000) + "ms");
|
|
14454
|
-
}
|
|
14455
|
-
function resolveData(me) {
|
|
14456
|
-
var duration = me && me.audioBuffers && me.audioBuffers.length > 0 ? me.audioBuffers[0].duration : 0;
|
|
14457
|
-
return {
|
|
14458
|
-
status: activeAudioContext().state,
|
|
14459
|
-
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
|
+
};
|
|
14460
14562
|
};
|
|
14461
|
-
|
|
14462
|
-
|
|
14463
|
-
|
|
14464
|
-
if (
|
|
14465
|
-
|
|
14466
|
-
|
|
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
|
|
14467
14571
|
});
|
|
14468
|
-
}
|
|
14469
|
-
|
|
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);
|
|
14593
|
+
});
|
|
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") {
|
|
14470
14623
|
activeAudioContext().resume().then(function () {
|
|
14471
14624
|
resolve(resolveData(self));
|
|
14472
14625
|
});
|
|
14473
|
-
})
|
|
14474
|
-
|
|
14475
|
-
|
|
14476
|
-
|
|
14477
|
-
|
|
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
|
+
}
|
|
14478
14641
|
});
|
|
14479
14642
|
};
|
|
14480
14643
|
function setPan(numTracks, panParam) {
|
|
@@ -15288,7 +15451,7 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
|
|
|
15288
15451
|
});
|
|
15289
15452
|
})["catch"](function (error) {
|
|
15290
15453
|
if (debugCallback) debugCallback('placeNote catch: ' + error.message);
|
|
15291
|
-
return Promise.
|
|
15454
|
+
return Promise.reject(error);
|
|
15292
15455
|
});
|
|
15293
15456
|
}
|
|
15294
15457
|
var copyToChannel = function copyToChannel(toBuffer, fromBuffer, start) {
|
|
@@ -18338,13 +18501,15 @@ AbstractEngraver.prototype.createBarLine = function (voice, elem, isFirstStaff)
|
|
|
18338
18501
|
} // 2 is hardcoded
|
|
18339
18502
|
|
|
18340
18503
|
if (elem.startEnding && isFirstStaff) {
|
|
18341
|
-
//
|
|
18342
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
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
|
+
}
|
|
18346
18512
|
}
|
|
18347
|
-
|
|
18348
18513
|
// Add a little space to the left of the bar line so that nothing can crowd it.
|
|
18349
18514
|
abselem.extraw -= 5;
|
|
18350
18515
|
if (elem.chord !== undefined) {
|
|
@@ -19137,6 +19302,7 @@ var stackedDecoration = function stackedDecoration(decoration, width, abselem, y
|
|
|
19137
19302
|
"mediumphrase": "scripts.mediumphrase",
|
|
19138
19303
|
"longphrase": "scripts.longphrase",
|
|
19139
19304
|
"trill": "scripts.trill",
|
|
19305
|
+
"trillh": "scripts.trill",
|
|
19140
19306
|
"roll": "scripts.roll",
|
|
19141
19307
|
"irishroll": "scripts.roll",
|
|
19142
19308
|
"marcato": "scripts.umarcato",
|
|
@@ -19198,6 +19364,7 @@ var stackedDecoration = function stackedDecoration(decoration, width, abselem, y
|
|
|
19198
19364
|
case "mediumphrase":
|
|
19199
19365
|
case "longphrase":
|
|
19200
19366
|
case "trill":
|
|
19367
|
+
case "trillh":
|
|
19201
19368
|
case "roll":
|
|
19202
19369
|
case "irishroll":
|
|
19203
19370
|
case "marcato":
|
|
@@ -19950,6 +20117,11 @@ function FreeText(info, vskip, getFontAndAttr, paddingLeft, width, getTextSize)
|
|
|
19950
20117
|
move: hash.attr['font-size'] * 2
|
|
19951
20118
|
}); // move the distance of the line, plus the distance of the margin, which is also one line.
|
|
19952
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
|
+
};
|
|
19953
20125
|
this.rows.push({
|
|
19954
20126
|
move: hash.attr['font-size'] / 2
|
|
19955
20127
|
}); // TODO-PER: move down some - the y location should be the top of the text, but we output text specifying the center line.
|
|
@@ -19964,7 +20136,8 @@ function FreeText(info, vskip, getFontAndAttr, paddingLeft, width, getTextSize)
|
|
|
19964
20136
|
absElemType: "freeText",
|
|
19965
20137
|
name: "free-text"
|
|
19966
20138
|
});
|
|
19967
|
-
|
|
20139
|
+
var textForSize = replaceStandaloneNewlinesForTextBlocks(text);
|
|
20140
|
+
size = getTextSize.calc(textForSize, 'textfont', 'defined-text'); // was text
|
|
19968
20141
|
this.rows.push({
|
|
19969
20142
|
move: size.height
|
|
19970
20143
|
});
|
|
@@ -21934,10 +22107,18 @@ function draw(renderer, classes, abcTune, width, maxWidth, responsive, scale, se
|
|
|
21934
22107
|
renderer.paper.closeGroup();
|
|
21935
22108
|
renderer.moveY(renderer.spacing.music);
|
|
21936
22109
|
var staffgroups = [];
|
|
22110
|
+
var nStaves = 0;
|
|
21937
22111
|
for (var line = 0; line < abcTune.lines.length; line++) {
|
|
21938
22112
|
classes.incrLine();
|
|
21939
22113
|
var abcLine = abcTune.lines[line];
|
|
21940
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
|
+
}
|
|
21941
22122
|
if (classes.shouldAddClasses) groupClasses.klass = "abcjs-staff-wrapper abcjs-l" + classes.lineNumber;
|
|
21942
22123
|
renderer.paper.openGroup(groupClasses);
|
|
21943
22124
|
if (abcLine.vskip) {
|
|
@@ -22044,6 +22225,8 @@ function drawEnding(renderer, params, linestartx, lineendx, selectables) {
|
|
|
22044
22225
|
pathString += sprintf("M %f %f L %f %f ", linestartx, y, lineendx, y);
|
|
22045
22226
|
renderer.paper.openGroup({
|
|
22046
22227
|
klass: renderer.controller.classes.generate("ending"),
|
|
22228
|
+
// MAE 17 May 2025 - Ending numbers not being drawn in correct color
|
|
22229
|
+
fill: renderer.foregroundColor,
|
|
22047
22230
|
"data-name": "ending"
|
|
22048
22231
|
});
|
|
22049
22232
|
printPath(renderer, {
|
|
@@ -23325,7 +23508,14 @@ function renderText(renderer, params, alreadyInGroup) {
|
|
|
23325
23508
|
if (params.cursor) {
|
|
23326
23509
|
hash.attr.cursor = params.cursor;
|
|
23327
23510
|
}
|
|
23328
|
-
|
|
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
|
+
}
|
|
23329
23519
|
text = text.replace(/^\n/, "\xA0\n");
|
|
23330
23520
|
if (hash.font.box) {
|
|
23331
23521
|
if (!alreadyInGroup) renderer.paper.openGroup({
|
|
@@ -24548,8 +24738,13 @@ function keyboardSelection(ev) {
|
|
|
24548
24738
|
}
|
|
24549
24739
|
function findElementInHistory(selectables, el) {
|
|
24550
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;
|
|
24551
24745
|
for (var i = 0; i < selectables.length; i++) {
|
|
24552
|
-
|
|
24746
|
+
var svgDataset = selectables[i].svgEl.dataset;
|
|
24747
|
+
if (svgDataset && index === svgDataset.index) return i;
|
|
24553
24748
|
}
|
|
24554
24749
|
return -1;
|
|
24555
24750
|
}
|
|
@@ -26353,8 +26548,13 @@ Svg.prototype.text = function (text, attr, target) {
|
|
|
26353
26548
|
el.setAttribute(key, attr[key]);
|
|
26354
26549
|
}
|
|
26355
26550
|
}
|
|
26551
|
+
var isFreeText = attr["data-name"] == "free-text";
|
|
26356
26552
|
var lines = ("" + text).split("\n");
|
|
26357
26553
|
for (var i = 0; i < lines.length; i++) {
|
|
26554
|
+
if (isFreeText && lines[i] == "") {
|
|
26555
|
+
// Don't draw empty lines
|
|
26556
|
+
continue;
|
|
26557
|
+
}
|
|
26358
26558
|
var line = document.createElementNS(svgNS, 'tspan');
|
|
26359
26559
|
line.setAttribute("x", attr.x ? attr.x : 0);
|
|
26360
26560
|
if (i !== 0) line.setAttribute("dy", "1.2em");
|
|
@@ -26376,7 +26576,19 @@ Svg.prototype.text = function (text, attr, target) {
|
|
|
26376
26576
|
ts3.textContent = parts[2];
|
|
26377
26577
|
line.appendChild(ts3);
|
|
26378
26578
|
}
|
|
26379
|
-
} 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
|
+
}
|
|
26380
26592
|
el.appendChild(line);
|
|
26381
26593
|
}
|
|
26382
26594
|
if (target) target.appendChild(el);else this.append(el);
|
|
@@ -26555,7 +26767,7 @@ module.exports = Svg;
|
|
|
26555
26767
|
\********************/
|
|
26556
26768
|
/***/ (function(module) {
|
|
26557
26769
|
|
|
26558
|
-
var version = '6.
|
|
26770
|
+
var version = '6.5.1';
|
|
26559
26771
|
module.exports = version;
|
|
26560
26772
|
|
|
26561
26773
|
/***/ })
|