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.
@@ -1,5 +1,5 @@
1
1
  /**!
2
- Copyright (c) 2009-2022 Paul Rosen and Gregory Dyke
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
@@ -18,7 +18,7 @@ return /******/ (function() { // webpackBootstrap
18
18
  /***/ (function(module, __unused_webpack_exports, __webpack_require__) {
19
19
 
20
20
  /**!
21
- Copyright (c) 2009-2022 Paul Rosen and Gregory Dyke
21
+ Copyright (c) 2009-2023 Paul Rosen and Gregory Dyke
22
22
 
23
23
  Permission is hereby granted, free of charge, to any person obtaining a copy
24
24
  of this software and associated documentation files (the "Software"), to deal
@@ -211,6 +211,8 @@ var GuitarTablature = __webpack_require__(/*! ../tablatures/instruments/guitar/t
211
211
  // Existing tab classes
212
212
  var pluginTab = {
213
213
  'violin': 'ViolinTab',
214
+ 'fiddle': 'ViolinTab',
215
+ 'mandolin': 'ViolinTab',
214
216
  'guitar': 'GuitarTab'
215
217
  };
216
218
  var abcTablatures = {
@@ -352,7 +354,7 @@ var TimingCallbacks = function TimingCallbacks(target, params) {
352
354
  self.joggerTimer = null;
353
355
  self.replaceTarget = function (newTarget) {
354
356
  self.noteTimings = newTarget.setTiming(self.qpm, self.extraMeasuresAtBeginning);
355
- if (newTarget.noteTimings.length === 0) newTarget.setTiming(0, 0);
357
+ if (newTarget.noteTimings.length === 0) self.noteTimings = newTarget.setTiming(0, 0);
356
358
  if (self.lineEndCallback) {
357
359
  self.lineEndTimings = getLineEndTimings(newTarget.noteTimings, self.lineEndAnticipation);
358
360
  }
@@ -1797,7 +1799,7 @@ var Tune = function Tune() {
1797
1799
  if (!this.engraver || !this.engraver.staffgroups) {
1798
1800
  console.log("setTiming cannot be called before the tune is drawn.");
1799
1801
  this.noteTimings = [];
1800
- return;
1802
+ return this.noteTimings;
1801
1803
  }
1802
1804
  var tempo = this.metaText ? this.metaText.tempo : null;
1803
1805
  var naturalBpm = this.getBpm(tempo);
@@ -13881,6 +13883,8 @@ function CreateSynth() {
13881
13883
  });
13882
13884
  });
13883
13885
  });
13886
+ if (self.debugCallback) self.debugCallback("notes " + JSON.stringify(notes));
13887
+
13884
13888
  // If there are lots of notes, load them in batches
13885
13889
  var batches = [];
13886
13890
  var CHUNK = 256;
@@ -13895,8 +13899,10 @@ function CreateSynth() {
13895
13899
  };
13896
13900
  var index = 0;
13897
13901
  var next = function next() {
13902
+ if (self.debugCallback) self.debugCallback("loadBatch idx=" + index + " len=" + batches.length);
13898
13903
  if (index < batches.length) {
13899
13904
  self._loadBatch(batches[index], self.soundFontUrl, startTime).then(function (data) {
13905
+ if (self.debugCallback) self.debugCallback("loadBatch then");
13900
13906
  startTime = activeAudioContext().currentTime;
13901
13907
  if (data) {
13902
13908
  if (data.error) results.error = results.error.concat(data.error);
@@ -13906,6 +13912,7 @@ function CreateSynth() {
13906
13912
  next();
13907
13913
  }, reject);
13908
13914
  } else {
13915
+ if (self.debugCallback) self.debugCallback("resolve init");
13909
13916
  resolve(results);
13910
13917
  }
13911
13918
  };
@@ -13916,6 +13923,7 @@ function CreateSynth() {
13916
13923
  // This is called recursively to see if the sounds have loaded. The "delay" parameter is how long it has been since the original call.
13917
13924
  var promises = [];
13918
13925
  batch.forEach(function (item) {
13926
+ if (self.debugCallback) self.debugCallback("getNote " + item.instrument + ':' + item.note);
13919
13927
  promises.push(getNote(soundFontUrl, item.instrument, item.note, activeAudioContext()));
13920
13928
  });
13921
13929
  return Promise.all(promises).then(function (response) {
@@ -13930,6 +13938,7 @@ function CreateSynth() {
13930
13938
  if (oneResponse.status === "loaded") loaded.push(which);else if (oneResponse.status === "pending") pending.push(which);else if (oneResponse.status === "cached") cached.push(which);else error.push(which + ' ' + oneResponse.message);
13931
13939
  }
13932
13940
  if (pending.length > 0) {
13941
+ if (self.debugCallback) self.debugCallback("pending " + JSON.stringify(pending));
13933
13942
  // 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.
13934
13943
  // Retry quickly at first so that there isn't an unnecessary delay, but increase the delay each time.
13935
13944
  if (!delay) delay = 50;else delay = delay * 2;
@@ -13944,6 +13953,7 @@ function CreateSynth() {
13944
13953
  note: which[1]
13945
13954
  });
13946
13955
  }
13956
+ if (self.debugCallback) self.debugCallback("retry " + JSON.stringify(newBatch));
13947
13957
  self._loadBatch(newBatch, soundFontUrl, startTime, delay).then(function (response) {
13948
13958
  resolve(response);
13949
13959
  })["catch"](function (error) {
@@ -13956,14 +13966,20 @@ function CreateSynth() {
13956
13966
  for (var j = 0; j < batch.length; j++) {
13957
13967
  list.push(batch[j].instrument + '/' + batch[j].note);
13958
13968
  }
13969
+ if (self.debugCallback) self.debugCallback("loadBatch timeout");
13959
13970
  return Promise.reject(new Error("timeout attempting to load: " + list.join(", ")));
13960
13971
  }
13961
- } else return Promise.resolve({
13962
- loaded: loaded,
13963
- cached: cached,
13964
- error: error
13965
- });
13966
- })["catch"](function (error) {});
13972
+ } else {
13973
+ if (self.debugCallback) self.debugCallback("loadBatch resolve");
13974
+ return Promise.resolve({
13975
+ loaded: loaded,
13976
+ cached: cached,
13977
+ error: error
13978
+ });
13979
+ }
13980
+ })["catch"](function (error) {
13981
+ if (self.debugCallback) self.debugCallback("loadBatch catch " + error.message);
13982
+ });
13967
13983
  };
13968
13984
  self.prime = function () {
13969
13985
  // At this point all of the notes are loaded. This function writes them into the output buffer.
@@ -14002,6 +14018,7 @@ function CreateSynth() {
14002
14018
  var panDistance = panDistances && panDistances.length > trackNumber ? panDistances[trackNumber] : 0;
14003
14019
  noteMap.forEach(function (note) {
14004
14020
  var key = note.instrument + ':' + note.pitch + ':' + note.volume + ':' + Math.round((note.end - note.start) * 1000) / 1000 + ':' + panDistance + ':' + tempoMultiplier + ':' + (note.cents ? note.cents : 0);
14021
+ if (self.debugCallback) self.debugCallback("noteMapTrack " + key);
14005
14022
  if (!uniqueSounds[key]) uniqueSounds[key] = [];
14006
14023
  uniqueSounds[key].push(note.start);
14007
14024
  });
@@ -14023,7 +14040,7 @@ function CreateSynth() {
14023
14040
  tempoMultiplier: parseFloat(parts[5]),
14024
14041
  cents: cents
14025
14042
  };
14026
- allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000));
14043
+ allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd / 1000, self.debugCallback));
14027
14044
  }
14028
14045
  self.audioBuffers = [audioBuffer];
14029
14046
  if (self.debugCallback) {
@@ -14462,7 +14479,7 @@ var getNote = function getNote(url, instrument, name, audioContext) {
14462
14479
  xhr.responseType = "arraybuffer";
14463
14480
  xhr.onload = function () {
14464
14481
  if (xhr.status !== 200) {
14465
- reject(Error("Can't load sound at " + noteUrl));
14482
+ reject(Error("Can't load sound at " + noteUrl + ' status=' + xhr.status));
14466
14483
  return;
14467
14484
  }
14468
14485
  var noteDecoded = function noteDecoded(audioBuffer) {
@@ -14756,7 +14773,7 @@ module.exports = pitchesToPerc;
14756
14773
  var soundsCache = __webpack_require__(/*! ./sounds-cache */ "./src/synth/sounds-cache.js");
14757
14774
  var pitchToNoteName = __webpack_require__(/*! ./pitch-to-note-name */ "./src/synth/pitch-to-note-name.js");
14758
14775
  var centsToFactor = __webpack_require__(/*! ./cents-to-factor */ "./src/synth/cents-to-factor.js");
14759
- function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec) {
14776
+ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec, debugCallback) {
14760
14777
  // sound contains { instrument, pitch, volume, len, pan, tempoMultiplier
14761
14778
  // len is in whole notes. Multiply by tempoMultiplier to get seconds.
14762
14779
  // ofsMs is an offset to subtract from the note to line up programs that have different length onsets.
@@ -14770,6 +14787,7 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
14770
14787
  var noteBufferPromise = soundsCache[sound.instrument][noteName];
14771
14788
  if (!noteBufferPromise) {
14772
14789
  // if the note isn't present then just skip it - it will leave a blank spot in the audio.
14790
+ if (debugCallback) debugCallback('placeNote skipped: ' + sound.instrument + ':' + noteName);
14773
14791
  return Promise.resolve();
14774
14792
  }
14775
14793
  return noteBufferPromise.then(function (response) {
@@ -14825,13 +14843,17 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
14825
14843
  copyToChannel(outputAudioBuffer, e.renderedBuffer, start);
14826
14844
  }
14827
14845
  }
14846
+ if (debugCallback) debugCallback('placeNote: ' + sound.instrument + ':' + noteName);
14828
14847
  fnResolve();
14829
14848
  };
14830
14849
  offlineCtx.startRendering();
14831
14850
  return new Promise(function (resolve) {
14832
14851
  fnResolve = resolve;
14833
14852
  });
14834
- })["catch"](function () {});
14853
+ })["catch"](function (error) {
14854
+ if (debugCallback) debugCallback('placeNote catch: ' + error.message);
14855
+ return Promise.resolve();
14856
+ });
14835
14857
  }
14836
14858
  var copyToChannel = function copyToChannel(toBuffer, fromBuffer, start) {
14837
14859
  for (var ch = 0; ch < 2; ch++) {
@@ -15618,7 +15640,7 @@ function StringPatterns(plugin) {
15618
15640
  this.measureAccidentals = {};
15619
15641
  this.capo = 0;
15620
15642
  if (capo) {
15621
- this.capo = capo;
15643
+ this.capo = parseInt(capo, 10);
15622
15644
  }
15623
15645
  this.transpose = plugin.transpose ? plugin.transpose : 0;
15624
15646
  this.tuning = tuning;
@@ -16612,6 +16634,7 @@ TabRenderer.prototype.doLayout = function () {
16612
16634
  this.tabStaff.voices = [];
16613
16635
  for (var ii = 0; ii < nbVoices; ii++) {
16614
16636
  var tabVoice = new VoiceElement(0, 0);
16637
+ if (ii > 0) tabVoice.duplicate = true;
16615
16638
  var nameHeight = buildTabName(this, tabVoice) / spacing.STEP;
16616
16639
  nameHeight = Math.max(nameHeight, 1); // If there is no label for the tab line, then there needs to be a little padding
16617
16640
  staffGroup.staffs[this.staffIndex].top += nameHeight;
@@ -21545,6 +21568,21 @@ function printLine(renderer, x1, x2, y, klass, name, dy) {
21545
21568
  x2 = roundNumber(x2);
21546
21569
  var y1 = roundNumber(y - dy);
21547
21570
  var y2 = roundNumber(y + dy);
21571
+ // TODO-PER: This fixes a firefox bug where it isn't displayed
21572
+ if (renderer.firefox112) {
21573
+ y += dy / 2; // Because the y coordinate is the edge of where the line goes but the width widens from the middle.
21574
+ var attr = {
21575
+ x1: x1,
21576
+ x2: x2,
21577
+ y1: y,
21578
+ y2: y,
21579
+ stroke: renderer.foregroundColor,
21580
+ 'stroke-width': Math.abs(dy * 2)
21581
+ };
21582
+ if (klass) attr['class'] = klass;
21583
+ if (name) attr['data-name'] = name;
21584
+ return renderer.paper.lineToBack(attr);
21585
+ }
21548
21586
  var pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y1, x2, y1, x2, y2, x1, y2);
21549
21587
  var options = {
21550
21588
  path: pathString,
@@ -21594,6 +21632,21 @@ function printStem(renderer, x, dx, y1, y2, klass, name) {
21594
21632
  }
21595
21633
  x = roundNumber(x);
21596
21634
  var x2 = roundNumber(x + dx);
21635
+ // TODO-PER: This fixes a firefox bug where it isn't displayed
21636
+ if (renderer.firefox112) {
21637
+ x += dx / 2; // Because the x coordinate is the edge of where the line goes but the width widens from the middle.
21638
+ var attr = {
21639
+ x1: x,
21640
+ x2: x,
21641
+ y1: y1,
21642
+ y2: y2,
21643
+ stroke: renderer.foregroundColor,
21644
+ 'stroke-width': Math.abs(dx)
21645
+ };
21646
+ if (klass) attr['class'] = klass;
21647
+ if (name) attr['data-name'] = name;
21648
+ return renderer.paper.lineToBack(attr);
21649
+ }
21597
21650
  var pathArray = [["M", x, y1], ["L", x, y2], ["L", x2, y2], ["L", x2, y1], ["z"]];
21598
21651
  var attr = {
21599
21652
  path: ""
@@ -23108,13 +23161,13 @@ EngraverController.prototype.engraveTune = function (abcTune, tuneNumber, lineOf
23108
23161
  this.selectables = ret.selectables;
23109
23162
  if (this.oneSvgPerLine) {
23110
23163
  var div = this.renderer.paper.svg.parentNode;
23111
- this.svgs = splitSvgIntoLines(div, abcTune.metaText.title, this.responsive);
23164
+ this.svgs = splitSvgIntoLines(this.renderer, div, abcTune.metaText.title, this.responsive);
23112
23165
  } else {
23113
23166
  this.svgs = [this.renderer.paper.svg];
23114
23167
  }
23115
23168
  setupSelection(this, this.svgs);
23116
23169
  };
23117
- function splitSvgIntoLines(output, title, responsive) {
23170
+ function splitSvgIntoLines(renderer, output, title, responsive) {
23118
23171
  // Each line is a top level <g> in the svg. To split it into separate
23119
23172
  // svgs iterate through each of those and put them in a new svg. Since
23120
23173
  // they are placed absolutely, the viewBox needs to be manipulated to
@@ -23144,7 +23197,9 @@ function splitSvgIntoLines(output, title, responsive) {
23144
23197
  svg.setAttribute("aria-label", fullTitle);
23145
23198
  if (responsive !== 'resize') svg.setAttribute("height", height);
23146
23199
  if (responsive === 'resize') svg.style.position = '';
23147
- svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + height);
23200
+ // TODO-PER: Hack! Not sure why this is needed.
23201
+ var viewBoxHeight = renderer.firefox112 ? height + 1 : height;
23202
+ svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + viewBoxHeight);
23148
23203
  svg.appendChild(style.cloneNode(true));
23149
23204
  var titleEl = document.createElement("title");
23150
23205
  titleEl.innerText = fullTitle;
@@ -23806,10 +23861,12 @@ function notifySelect(target, dragStep, dragMax, dragIndex, ev) {
23806
23861
  while (parent && parent.dataset && !parent.dataset.index && parent.tagName.toLowerCase() !== 'svg') {
23807
23862
  parent = parent.parentNode;
23808
23863
  }
23809
- analysis.name = parent.dataset.name;
23810
- analysis.clickedName = closest.dataset.name;
23811
- analysis.parentClasses = parent.classList;
23812
- analysis.clickedClasses = closest.classList;
23864
+ if (parent && parent.dataset) {
23865
+ analysis.name = parent.dataset.name;
23866
+ analysis.clickedName = closest.dataset.name;
23867
+ analysis.parentClasses = parent.classList;
23868
+ }
23869
+ if (closest && closest.classList) analysis.clickedClasses = closest.classList;
23813
23870
  analysis.selectableElement = target.svgEl;
23814
23871
  for (var i = 0; i < this.listeners.length; i++) {
23815
23872
  this.listeners[i](target.absEl.abcelem, target.absEl.tuneNumber, classes.join(' '), analysis, {
@@ -24992,6 +25049,7 @@ var Renderer = function Renderer(paper) {
24992
25049
  this.space = 3 * spacing.SPACE;
24993
25050
  this.padding = {}; // renderer's padding is managed by the controller
24994
25051
  this.reset();
25052
+ this.firefox112 = navigator.userAgent.indexOf('Firefox/112.0') >= 0;
24995
25053
  };
24996
25054
  Renderer.prototype.reset = function () {
24997
25055
  this.paper.clear();
@@ -25444,6 +25502,15 @@ Svg.prototype.pathToBack = function (attr) {
25444
25502
  this.prepend(el);
25445
25503
  return el;
25446
25504
  };
25505
+ Svg.prototype.lineToBack = function (attr) {
25506
+ var el = document.createElementNS(svgNS, 'line');
25507
+ var keys = Object.keys(attr);
25508
+ for (var i = 0; i < keys.length; i++) {
25509
+ el.setAttribute(keys[i], attr[keys[i]]);
25510
+ }
25511
+ this.prepend(el);
25512
+ return el;
25513
+ };
25447
25514
  Svg.prototype.append = function (el) {
25448
25515
  if (this.currentGroup.length > 0) this.currentGroup[0].appendChild(el);else this.svg.appendChild(el);
25449
25516
  };
@@ -25479,7 +25546,7 @@ module.exports = Svg;
25479
25546
  \********************/
25480
25547
  /***/ (function(module) {
25481
25548
 
25482
- var version = '6.2.0';
25549
+ var version = '6.2.2';
25483
25550
  module.exports = version;
25484
25551
 
25485
25552
  /***/ })