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
package/index.js CHANGED
@@ -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
package/license.js CHANGED
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abcjs",
3
- "version": "6.2.0",
3
+ "version": "6.2.2",
4
4
  "description": "Renderer for abc music notation",
5
5
  "main": "index.js",
6
6
  "types": "types/index.d.ts",
package/plugin.js CHANGED
@@ -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
@@ -13,6 +13,8 @@ var GuitarTablature = require('../tablatures/instruments/guitar/tab-guitar');
13
13
  // Existing tab classes
14
14
  var pluginTab = {
15
15
  'violin': 'ViolinTab',
16
+ 'fiddle': 'ViolinTab',
17
+ 'mandolin': 'ViolinTab',
16
18
  'guitar': 'GuitarTab'
17
19
  };
18
20
 
@@ -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
  }
@@ -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._loadBatch(newBatch, soundFontUrl, startTime, delay).then(function (response) {
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
- return Promise.reject(new Error("timeout attempting to load: " + list.join(", ")));
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
 
@@ -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) {
@@ -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) {
@@ -285,7 +285,7 @@ function StringPatterns(plugin) {
285
285
  this.measureAccidentals = {}
286
286
  this.capo = 0;
287
287
  if (capo) {
288
- this.capo = capo;
288
+ this.capo = parseInt(capo,10);
289
289
  }
290
290
  this.transpose = plugin.transpose ? plugin.transpose : 0
291
291
  this.tuning = tuning;
@@ -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
- svg.setAttribute("viewBox", "0 " + nextTop + " " + width + " " + height)
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
- analysis.name = parent.dataset.name;
366
- analysis.clickedName = closest.dataset.name;
367
- analysis.parentClasses = parent.classList;
368
- analysis.clickedClasses = closest.classList;
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++) {
@@ -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-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
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]: any };
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<void>
1145
+ prime(): Promise<{ status: string, duration: number}>
1146
1146
  start(): void
1147
1147
  pause(): number
1148
1148
  resume(): void
package/version.js CHANGED
@@ -1,3 +1,3 @@
1
- var version = '6.2.0';
1
+ var version = '6.2.2';
2
2
 
3
3
  module.exports = version;