abcjs 6.2.0 → 6.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -1
- package/README.md +3 -0
- package/RELEASE.md +31 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic-min.js.LICENSE +1 -1
- package/dist/abcjs-basic.js +89 -22
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/dist/abcjs-plugin-min.js.LICENSE +1 -1
- package/index.js +1 -1
- package/license.js +1 -1
- package/package.json +1 -1
- package/plugin.js +1 -1
- package/src/api/abc_tablatures.js +2 -0
- package/src/api/abc_timing_callbacks.js +1 -1
- package/src/data/abc_tune.js +1 -1
- package/src/synth/create-synth.js +30 -4
- package/src/synth/load-note.js +1 -1
- package/src/synth/place-note.js +10 -2
- package/src/tablatures/instruments/string-patterns.js +1 -1
- package/src/tablatures/tab-renderer.js +1 -0
- package/src/write/draw/print-line.js +19 -0
- package/src/write/draw/print-stem.js +18 -0
- package/src/write/engraver-controller.js +5 -3
- package/src/write/interactive/selection.js +7 -4
- package/src/write/renderer.js +1 -0
- package/src/write/svg.js +10 -0
- package/test.js +1 -1
- package/types/index.d.ts +2 -2
- package/version.js +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**!
|
|
2
|
-
Copyright (c) 2009-
|
|
2
|
+
Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
|
|
3
3
|
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**!
|
|
2
|
-
Copyright (c) 2009-
|
|
2
|
+
Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
|
|
3
3
|
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
package/license.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**!
|
|
2
|
-
Copyright (c) 2009-
|
|
2
|
+
Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
|
|
3
3
|
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
package/package.json
CHANGED
package/plugin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**!
|
|
2
|
-
Copyright (c) 2009-
|
|
2
|
+
Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
|
|
3
3
|
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -17,7 +17,7 @@ var TimingCallbacks = function(target, params) {
|
|
|
17
17
|
self.replaceTarget = function(newTarget) {
|
|
18
18
|
self.noteTimings = newTarget.setTiming(self.qpm, self.extraMeasuresAtBeginning);
|
|
19
19
|
if (newTarget.noteTimings.length === 0)
|
|
20
|
-
newTarget.setTiming(0,0);
|
|
20
|
+
self.noteTimings = newTarget.setTiming(0,0);
|
|
21
21
|
if (self.lineEndCallback) {
|
|
22
22
|
self.lineEndTimings = getLineEndTimings(newTarget.noteTimings, self.lineEndAnticipation);
|
|
23
23
|
}
|
package/src/data/abc_tune.js
CHANGED
|
@@ -572,7 +572,7 @@ var Tune = function() {
|
|
|
572
572
|
if (!this.engraver || !this.engraver.staffgroups) {
|
|
573
573
|
console.log("setTiming cannot be called before the tune is drawn.");
|
|
574
574
|
this.noteTimings = [];
|
|
575
|
-
return;
|
|
575
|
+
return this.noteTimings;
|
|
576
576
|
}
|
|
577
577
|
|
|
578
578
|
var tempo = this.metaText ? this.metaText.tempo : null;
|
|
@@ -165,6 +165,9 @@ function CreateSynth() {
|
|
|
165
165
|
notes.push({ instrument: instrument, note: note });
|
|
166
166
|
});
|
|
167
167
|
});
|
|
168
|
+
if (self.debugCallback)
|
|
169
|
+
self.debugCallback("notes "+JSON.stringify(notes));
|
|
170
|
+
|
|
168
171
|
// If there are lots of notes, load them in batches
|
|
169
172
|
var batches = [];
|
|
170
173
|
var CHUNK = 256;
|
|
@@ -181,8 +184,13 @@ function CreateSynth() {
|
|
|
181
184
|
|
|
182
185
|
var index = 0;
|
|
183
186
|
var next = function() {
|
|
187
|
+
if (self.debugCallback)
|
|
188
|
+
self.debugCallback("loadBatch idx="+index+ " len="+batches.length);
|
|
189
|
+
|
|
184
190
|
if (index < batches.length) {
|
|
185
191
|
self._loadBatch(batches[index], self.soundFontUrl, startTime).then(function(data) {
|
|
192
|
+
if (self.debugCallback)
|
|
193
|
+
self.debugCallback("loadBatch then");
|
|
186
194
|
startTime = activeAudioContext().currentTime;
|
|
187
195
|
if (data) {
|
|
188
196
|
if (data.error)
|
|
@@ -194,6 +202,9 @@ function CreateSynth() {
|
|
|
194
202
|
next();
|
|
195
203
|
}, reject);
|
|
196
204
|
} else {
|
|
205
|
+
if (self.debugCallback)
|
|
206
|
+
self.debugCallback("resolve init");
|
|
207
|
+
|
|
197
208
|
resolve(results);
|
|
198
209
|
}
|
|
199
210
|
};
|
|
@@ -205,6 +216,8 @@ function CreateSynth() {
|
|
|
205
216
|
// This is called recursively to see if the sounds have loaded. The "delay" parameter is how long it has been since the original call.
|
|
206
217
|
var promises = [];
|
|
207
218
|
batch.forEach(function(item) {
|
|
219
|
+
if (self.debugCallback)
|
|
220
|
+
self.debugCallback("getNote " + item.instrument+':'+item.note);
|
|
208
221
|
promises.push(getNote(soundFontUrl, item.instrument, item.note, activeAudioContext()));
|
|
209
222
|
});
|
|
210
223
|
return Promise.all(promises).then(function(response) {
|
|
@@ -227,6 +240,8 @@ function CreateSynth() {
|
|
|
227
240
|
error.push(which + ' ' + oneResponse.message);
|
|
228
241
|
}
|
|
229
242
|
if (pending.length > 0) {
|
|
243
|
+
if (self.debugCallback)
|
|
244
|
+
self.debugCallback("pending " + JSON.stringify(pending));
|
|
230
245
|
// There was probably a second call for notes before the first one finished, so just retry a few times to see if they stop being pending.
|
|
231
246
|
// Retry quickly at first so that there isn't an unnecessary delay, but increase the delay each time.
|
|
232
247
|
if (!delay)
|
|
@@ -241,7 +256,9 @@ function CreateSynth() {
|
|
|
241
256
|
which = pending[i].split(":");
|
|
242
257
|
newBatch.push({instrument: which[0], note: which[1]});
|
|
243
258
|
}
|
|
244
|
-
self.
|
|
259
|
+
if (self.debugCallback)
|
|
260
|
+
self.debugCallback("retry " + JSON.stringify(newBatch));
|
|
261
|
+
self._loadBatch(newBatch, soundFontUrl, startTime, delay).then(function (response) {
|
|
245
262
|
resolve(response);
|
|
246
263
|
}).catch(function (error) {
|
|
247
264
|
reject(error);
|
|
@@ -252,11 +269,18 @@ function CreateSynth() {
|
|
|
252
269
|
var list = [];
|
|
253
270
|
for (var j = 0; j < batch.length; j++)
|
|
254
271
|
list.push(batch[j].instrument+'/'+batch[j].note)
|
|
255
|
-
|
|
272
|
+
if (self.debugCallback)
|
|
273
|
+
self.debugCallback("loadBatch timeout")
|
|
274
|
+
return Promise.reject(new Error("timeout attempting to load: " + list.join(", ")));
|
|
256
275
|
}
|
|
257
|
-
} else
|
|
276
|
+
} else {
|
|
277
|
+
if (self.debugCallback)
|
|
278
|
+
self.debugCallback("loadBatch resolve")
|
|
258
279
|
return Promise.resolve({loaded: loaded, cached: cached, error: error});
|
|
280
|
+
}
|
|
259
281
|
}).catch(function (error) {
|
|
282
|
+
if (self.debugCallback)
|
|
283
|
+
self.debugCallback("loadBatch catch "+error.message)
|
|
260
284
|
});
|
|
261
285
|
});
|
|
262
286
|
|
|
@@ -299,6 +323,8 @@ function CreateSynth() {
|
|
|
299
323
|
var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
|
|
300
324
|
noteMap.forEach(function(note) {
|
|
301
325
|
var key = note.instrument + ':' + note.pitch + ':' +note.volume + ':' + Math.round((note.end-note.start)*1000)/1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
|
|
326
|
+
if (self.debugCallback)
|
|
327
|
+
self.debugCallback("noteMapTrack "+key)
|
|
302
328
|
if (!uniqueSounds[key])
|
|
303
329
|
uniqueSounds[key] = [];
|
|
304
330
|
uniqueSounds[key].push(note.start);
|
|
@@ -313,7 +339,7 @@ function CreateSynth() {
|
|
|
313
339
|
var parts = k.split(":");
|
|
314
340
|
var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
|
|
315
341
|
parts = {instrument: parts[0], pitch: parseInt(parts[1], 10), volume: parseInt(parts[2], 10), len: parseFloat(parts[3]), pan: parseFloat(parts[4]), tempoMultiplier: parseFloat(parts[5]), cents: cents};
|
|
316
|
-
allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd/1000));
|
|
342
|
+
allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd/1000, self.debugCallback));
|
|
317
343
|
}
|
|
318
344
|
self.audioBuffers = [audioBuffer];
|
|
319
345
|
|
package/src/synth/load-note.js
CHANGED
|
@@ -16,7 +16,7 @@ var getNote = function (url, instrument, name, audioContext) {
|
|
|
16
16
|
xhr.responseType = "arraybuffer";
|
|
17
17
|
xhr.onload = function () {
|
|
18
18
|
if (xhr.status !== 200) {
|
|
19
|
-
reject(Error("Can't load sound at " + noteUrl));
|
|
19
|
+
reject(Error("Can't load sound at " + noteUrl + ' status=' + xhr.status));
|
|
20
20
|
return
|
|
21
21
|
}
|
|
22
22
|
var noteDecoded = function(audioBuffer) {
|
package/src/synth/place-note.js
CHANGED
|
@@ -2,7 +2,7 @@ var soundsCache = require('./sounds-cache');
|
|
|
2
2
|
var pitchToNoteName = require('./pitch-to-note-name');
|
|
3
3
|
var centsToFactor = require("./cents-to-factor");
|
|
4
4
|
|
|
5
|
-
function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec) {
|
|
5
|
+
function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec, debugCallback) {
|
|
6
6
|
// sound contains { instrument, pitch, volume, len, pan, tempoMultiplier
|
|
7
7
|
// len is in whole notes. Multiply by tempoMultiplier to get seconds.
|
|
8
8
|
// ofsMs is an offset to subtract from the note to line up programs that have different length onsets.
|
|
@@ -21,6 +21,8 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
|
|
|
21
21
|
|
|
22
22
|
if (!noteBufferPromise) {
|
|
23
23
|
// if the note isn't present then just skip it - it will leave a blank spot in the audio.
|
|
24
|
+
if (debugCallback)
|
|
25
|
+
debugCallback('placeNote skipped: '+sound.instrument+':'+noteName)
|
|
24
26
|
return Promise.resolve();
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -81,6 +83,8 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
|
|
|
81
83
|
copyToChannel(outputAudioBuffer, e.renderedBuffer, start);
|
|
82
84
|
}
|
|
83
85
|
}
|
|
86
|
+
if (debugCallback)
|
|
87
|
+
debugCallback('placeNote: '+sound.instrument+':'+noteName)
|
|
84
88
|
fnResolve();
|
|
85
89
|
};
|
|
86
90
|
offlineCtx.startRendering();
|
|
@@ -88,7 +92,11 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
|
|
|
88
92
|
fnResolve = resolve;
|
|
89
93
|
});
|
|
90
94
|
})
|
|
91
|
-
.catch(function () {
|
|
95
|
+
.catch(function (error) {
|
|
96
|
+
if (debugCallback)
|
|
97
|
+
debugCallback('placeNote catch: '+error.message)
|
|
98
|
+
return Promise.resolve()
|
|
99
|
+
});
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
var copyToChannel = function(toBuffer, fromBuffer, start) {
|
|
@@ -227,6 +227,7 @@ TabRenderer.prototype.doLayout = function () {
|
|
|
227
227
|
this.tabStaff.voices = [];
|
|
228
228
|
for (var ii = 0; ii < nbVoices; ii++) {
|
|
229
229
|
var tabVoice = new VoiceElement(0, 0);
|
|
230
|
+
if (ii > 0) tabVoice.duplicate = true;
|
|
230
231
|
var nameHeight = buildTabName(this, tabVoice) / spacing.STEP;
|
|
231
232
|
nameHeight = Math.max(nameHeight, 1) // If there is no label for the tab line, then there needs to be a little padding
|
|
232
233
|
staffGroup.staffs[this.staffIndex].top += nameHeight;
|
|
@@ -7,6 +7,25 @@ function printLine(renderer, x1, x2, y, klass, name, dy) {
|
|
|
7
7
|
x2 = roundNumber(x2);
|
|
8
8
|
var y1 = roundNumber(y - dy);
|
|
9
9
|
var y2 = roundNumber(y + dy);
|
|
10
|
+
// TODO-PER: This fixes a firefox bug where it isn't displayed
|
|
11
|
+
if (renderer.firefox112) {
|
|
12
|
+
y += dy / 2; // Because the y coordinate is the edge of where the line goes but the width widens from the middle.
|
|
13
|
+
var attr = {
|
|
14
|
+
x1: x1,
|
|
15
|
+
x2: x2,
|
|
16
|
+
y1: y,
|
|
17
|
+
y2: y,
|
|
18
|
+
stroke: renderer.foregroundColor,
|
|
19
|
+
'stroke-width': Math.abs(dy*2)
|
|
20
|
+
}
|
|
21
|
+
if (klass)
|
|
22
|
+
attr['class'] = klass;
|
|
23
|
+
if (name)
|
|
24
|
+
attr['data-name'] = name;
|
|
25
|
+
|
|
26
|
+
return renderer.paper.lineToBack(attr);
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
var pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y1, x2, y1,
|
|
11
30
|
x2, y2, x1, y2);
|
|
12
31
|
var options = { path: pathString, stroke: "none", fill: fill };
|
|
@@ -12,6 +12,24 @@ function printStem(renderer, x, dx, y1, y2, klass, name) {
|
|
|
12
12
|
}
|
|
13
13
|
x = roundNumber(x);
|
|
14
14
|
var x2 = roundNumber(x + dx);
|
|
15
|
+
// TODO-PER: This fixes a firefox bug where it isn't displayed
|
|
16
|
+
if (renderer.firefox112) {
|
|
17
|
+
x += dx / 2; // Because the x coordinate is the edge of where the line goes but the width widens from the middle.
|
|
18
|
+
var attr = {
|
|
19
|
+
x1: x,
|
|
20
|
+
x2: x,
|
|
21
|
+
y1: y1,
|
|
22
|
+
y2: y2,
|
|
23
|
+
stroke: renderer.foregroundColor,
|
|
24
|
+
'stroke-width': Math.abs(dx)
|
|
25
|
+
}
|
|
26
|
+
if (klass)
|
|
27
|
+
attr['class'] = klass;
|
|
28
|
+
if (name)
|
|
29
|
+
attr['data-name'] = name;
|
|
30
|
+
|
|
31
|
+
return renderer.paper.lineToBack(attr);
|
|
32
|
+
}
|
|
15
33
|
var pathArray = [["M", x, y1], ["L", x, y2], ["L", x2, y2], ["L", x2, y1], ["z"]];
|
|
16
34
|
var attr = { path: "" };
|
|
17
35
|
for (var i = 0; i < pathArray.length; i++)
|
|
@@ -256,14 +256,14 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
|
|
|
256
256
|
|
|
257
257
|
if (this.oneSvgPerLine) {
|
|
258
258
|
var div = this.renderer.paper.svg.parentNode
|
|
259
|
-
this.svgs = splitSvgIntoLines(div, abcTune.metaText.title, this.responsive)
|
|
259
|
+
this.svgs = splitSvgIntoLines(this.renderer, div, abcTune.metaText.title, this.responsive)
|
|
260
260
|
} else {
|
|
261
261
|
this.svgs = [this.renderer.paper.svg];
|
|
262
262
|
}
|
|
263
263
|
setupSelection(this, this.svgs);
|
|
264
264
|
};
|
|
265
265
|
|
|
266
|
-
function splitSvgIntoLines(output, title, responsive) {
|
|
266
|
+
function splitSvgIntoLines(renderer, output, title, responsive) {
|
|
267
267
|
// Each line is a top level <g> in the svg. To split it into separate
|
|
268
268
|
// svgs iterate through each of those and put them in a new svg. Since
|
|
269
269
|
// they are placed absolutely, the viewBox needs to be manipulated to
|
|
@@ -297,7 +297,9 @@ function splitSvgIntoLines(output, title, responsive) {
|
|
|
297
297
|
svg.setAttribute("height", height)
|
|
298
298
|
if (responsive === 'resize')
|
|
299
299
|
svg.style.position = ''
|
|
300
|
-
|
|
300
|
+
// TODO-PER: Hack! Not sure why this is needed.
|
|
301
|
+
var viewBoxHeight = renderer.firefox112 ? height+1 : height
|
|
302
|
+
svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + viewBoxHeight)
|
|
301
303
|
svg.appendChild(style.cloneNode(true))
|
|
302
304
|
var titleEl = document.createElement("title")
|
|
303
305
|
titleEl.innerText = fullTitle
|
|
@@ -362,10 +362,13 @@ function notifySelect(target, dragStep, dragMax, dragIndex, ev) {
|
|
|
362
362
|
var parent = ev.target;
|
|
363
363
|
while (parent && parent.dataset && !parent.dataset.index && parent.tagName.toLowerCase() !== 'svg')
|
|
364
364
|
parent = parent.parentNode;
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
365
|
+
if (parent && parent.dataset) {
|
|
366
|
+
analysis.name = parent.dataset.name;
|
|
367
|
+
analysis.clickedName = closest.dataset.name;
|
|
368
|
+
analysis.parentClasses = parent.classList;
|
|
369
|
+
}
|
|
370
|
+
if (closest && closest.classList)
|
|
371
|
+
analysis.clickedClasses = closest.classList;
|
|
369
372
|
analysis.selectableElement = target.svgEl;
|
|
370
373
|
|
|
371
374
|
for (var i = 0; i < this.listeners.length; i++) {
|
package/src/write/renderer.js
CHANGED
|
@@ -16,6 +16,7 @@ var Renderer = function (paper) {
|
|
|
16
16
|
this.space = 3 * spacing.SPACE;
|
|
17
17
|
this.padding = {}; // renderer's padding is managed by the controller
|
|
18
18
|
this.reset();
|
|
19
|
+
this.firefox112 = navigator.userAgent.indexOf('Firefox/112.0') >= 0
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
Renderer.prototype.reset = function () {
|
package/src/write/svg.js
CHANGED
|
@@ -345,6 +345,16 @@ Svg.prototype.pathToBack = function (attr) {
|
|
|
345
345
|
return el;
|
|
346
346
|
};
|
|
347
347
|
|
|
348
|
+
Svg.prototype.lineToBack = function (attr) {
|
|
349
|
+
var el = document.createElementNS(svgNS, 'line');
|
|
350
|
+
var keys = Object.keys(attr)
|
|
351
|
+
for (var i = 0; i < keys.length; i++)
|
|
352
|
+
el.setAttribute(keys[i], attr[keys[i]]);
|
|
353
|
+
this.prepend(el);
|
|
354
|
+
return el;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
|
|
348
358
|
Svg.prototype.append = function (el) {
|
|
349
359
|
if (this.currentGroup.length > 0)
|
|
350
360
|
this.currentGroup[0].appendChild(el);
|
package/test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**!
|
|
2
|
-
Copyright (c) 2009-
|
|
2
|
+
Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
|
|
3
3
|
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
package/types/index.d.ts
CHANGED
|
@@ -259,7 +259,7 @@ declare module 'abcjs' {
|
|
|
259
259
|
dragColor?: string;
|
|
260
260
|
dragging?: boolean;
|
|
261
261
|
foregroundColor?: string;
|
|
262
|
-
format?: { [attr in FormatAttributes]
|
|
262
|
+
format?: { [attr in FormatAttributes]?: any };
|
|
263
263
|
header_only?: boolean;
|
|
264
264
|
initialClef?: boolean;
|
|
265
265
|
jazzchords?: boolean;
|
|
@@ -1142,7 +1142,7 @@ declare module 'abcjs' {
|
|
|
1142
1142
|
|
|
1143
1143
|
export interface MidiBuffer {
|
|
1144
1144
|
init(params?: MidiBufferOptions): Promise<MidiBufferPromise>
|
|
1145
|
-
prime(): Promise<
|
|
1145
|
+
prime(): Promise<{ status: string, duration: number}>
|
|
1146
1146
|
start(): void
|
|
1147
1147
|
pause(): number
|
|
1148
1148
|
resume(): void
|
package/version.js
CHANGED