abcjs 6.0.0-beta.32 → 6.0.0-beta.36

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.
Files changed (97) hide show
  1. package/README.md +13 -7
  2. package/RELEASE.md +130 -0
  3. package/dist/abcjs-basic-min.js +2 -2
  4. package/dist/abcjs-basic.js +3763 -825
  5. package/dist/abcjs-basic.js.map +1 -1
  6. package/dist/abcjs-plugin-min.js +2 -2
  7. package/dist/report-basic.html +37 -0
  8. package/dist/report-before-glyph-compress.html +37 -0
  9. package/dist/report-brown-ts-target-es5.html +37 -0
  10. package/dist/report-dev-orig-no-babel.html +37 -0
  11. package/dist/report-synth.html +37 -0
  12. package/docker-build.sh +1 -0
  13. package/glyphs.json +1 -0
  14. package/package.json +9 -9
  15. package/src/api/abc_tablatures.js +144 -0
  16. package/src/api/abc_timing_callbacks.js +49 -26
  17. package/src/api/abc_tunebook.js +10 -1
  18. package/src/api/abc_tunebook_svg.js +16 -22
  19. package/src/data/abc_tune.js +90 -25
  20. package/src/data/deline-tune.js +199 -0
  21. package/src/edit/abc_editor.js +33 -11
  22. package/src/midi/abc_midi_create.js +6 -2
  23. package/src/parse/abc_parse.js +10 -6
  24. package/src/parse/abc_parse_directive.js +19 -12
  25. package/src/parse/abc_parse_header.js +12 -12
  26. package/src/parse/abc_parse_music.js +15 -5
  27. package/src/parse/tune-builder.js +23 -30
  28. package/src/parse/wrap_lines.js +13 -36
  29. package/src/synth/abc_midi_flattener.js +44 -29
  30. package/src/synth/abc_midi_sequencer.js +52 -13
  31. package/src/synth/create-synth.js +22 -7
  32. package/src/synth/load-note.js +31 -65
  33. package/src/synth/place-note.js +59 -60
  34. package/src/synth/register-audio-context.js +4 -1
  35. package/src/synth/supports-audio.js +9 -8
  36. package/src/synth/synth-controller.js +5 -3
  37. package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
  38. package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
  39. package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
  40. package/src/tablatures/instruments/string-patterns.js +277 -0
  41. package/src/tablatures/instruments/string-tablature.js +56 -0
  42. package/src/tablatures/instruments/tab-note.js +282 -0
  43. package/src/tablatures/instruments/tab-notes.js +41 -0
  44. package/src/tablatures/instruments/violin/tab-violin.js +47 -0
  45. package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
  46. package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
  47. package/src/tablatures/tab-absolute-elements.js +310 -0
  48. package/src/tablatures/tab-common.js +29 -0
  49. package/src/tablatures/tab-renderer.js +243 -0
  50. package/src/tablatures/transposer.js +110 -0
  51. package/src/test/abc_parser_lint.js +62 -6
  52. package/src/write/abc_absolute_element.js +2 -2
  53. package/src/write/abc_abstract_engraver.js +9 -7
  54. package/src/write/abc_create_key_signature.js +1 -0
  55. package/src/write/abc_create_note_head.js +1 -1
  56. package/src/write/abc_engraver_controller.js +22 -9
  57. package/src/write/abc_glyphs.js +5 -2
  58. package/src/write/abc_relative_element.js +11 -3
  59. package/src/write/abc_renderer.js +5 -1
  60. package/src/write/add-chord.js +5 -2
  61. package/src/write/add-text-if.js +33 -0
  62. package/src/write/bottom-text.js +8 -29
  63. package/src/write/draw/absolute.js +12 -14
  64. package/src/write/draw/brace.js +3 -3
  65. package/src/write/draw/crescendo.js +1 -1
  66. package/src/write/draw/draw.js +3 -4
  67. package/src/write/draw/dynamics.js +8 -1
  68. package/src/write/draw/ending.js +4 -3
  69. package/src/write/draw/group-elements.js +10 -8
  70. package/src/write/draw/non-music.js +11 -6
  71. package/src/write/draw/print-line.js +24 -0
  72. package/src/write/draw/print-stem.js +12 -11
  73. package/src/write/draw/print-symbol.js +11 -10
  74. package/src/write/draw/relative.js +33 -13
  75. package/src/write/draw/selectables.js +9 -6
  76. package/src/write/draw/staff-group.js +45 -9
  77. package/src/write/draw/staff-line.js +3 -17
  78. package/src/write/draw/staff.js +15 -2
  79. package/src/write/draw/tab-line.js +40 -0
  80. package/src/write/draw/tempo.js +7 -7
  81. package/src/write/draw/text.js +11 -4
  82. package/src/write/draw/tie.js +2 -2
  83. package/src/write/draw/triplet.js +3 -3
  84. package/src/write/draw/voice.js +10 -2
  85. package/src/write/format-jazz-chord.js +15 -0
  86. package/src/write/free-text.js +20 -12
  87. package/src/write/layout/VoiceElements.js +33 -1
  88. package/src/write/layout/beam.js +2 -0
  89. package/src/write/layout/staffGroup.js +37 -2
  90. package/src/write/layout/voice.js +2 -1
  91. package/src/write/selection.js +15 -5
  92. package/src/write/separator.js +1 -1
  93. package/src/write/subtitle.js +3 -3
  94. package/src/write/svg.js +41 -14
  95. package/src/write/top-text.js +19 -25
  96. package/types/index.d.ts +1007 -39
  97. package/version.js +1 -1
@@ -143,9 +143,10 @@ var parseCommon = require("../parse/abc_common");
143
143
  var startRepeatPlaceholder = []; // There is a place holder for each voice.
144
144
  var skipEndingPlaceholder = []; // This is the place where the first ending starts.
145
145
  var startingDrumSet = false;
146
- for (var i = 0; i < abctune.lines.length; i++) {
146
+ var lines = abctune.lines; //abctune.deline(); TODO-PER: can switch to this, then simplify the loops below.
147
+ for (var i = 0; i < lines.length; i++) {
147
148
  // For each group of staff lines in the tune.
148
- var line = abctune.lines[i];
149
+ var line = lines[i];
149
150
  if (line.staff) {
150
151
  var staves = line.staff;
151
152
  var voiceNumber = 0;
@@ -171,13 +172,10 @@ var parseCommon = require("../parse/abc_common");
171
172
  voices[voiceNumber][cl].program = PERCUSSION_PROGRAM;
172
173
  }
173
174
  } else if (staff.key) {
174
- if (staff.key.root === 'HP')
175
- voices[voiceNumber].push({el_type: 'key', accidentals: [{acc: 'natural', note: 'g'}, {acc: 'sharp', note: 'f'}, {acc: 'sharp', note: 'c'}]});
176
- else
177
- voices[voiceNumber].push({el_type: 'key', accidentals: staff.key.accidentals });
175
+ addKey(voices[voiceNumber], staff.key);
178
176
  }
179
177
  if (staff.meter) {
180
- voices[voiceNumber].push(interpretMeter(staff.meter));
178
+ addMeter(voices[voiceNumber], staff.meter);
181
179
  }
182
180
  if (!startingDrumSet && drumOn) { // drum information is only needed once, so use the first line and track 0.
183
181
  voices[voiceNumber].push({el_type: 'drum', params: {pattern: drumPattern, bars: drumBars, on: drumOn, intro: drumIntro}});
@@ -237,6 +235,15 @@ var parseCommon = require("../parse/abc_common");
237
235
  if (elem.startTriplet) {
238
236
  tripletMultiplier = elem.tripletMultiplier;
239
237
  tripletDurationTotal = elem.startTriplet * tripletMultiplier * elem.duration;
238
+ if (elem.startTriplet != elem.tripletR) { // most commonly (3:2:2
239
+ if (v + elem.tripletR <= voice.length) {
240
+ var durationTotal = 0;
241
+ for (var w = v; w < v + elem.tripletR; w++) {
242
+ durationTotal += voice[w].duration;
243
+ }
244
+ tripletDurationTotal = tripletMultiplier * durationTotal;
245
+ }
246
+ }
240
247
  noteElem.duration = noteElem.duration * tripletMultiplier;
241
248
  noteElem.duration = Math.round(noteElem.duration*1000000)/1000000;
242
249
  tripletDurationCount = noteElem.duration;
@@ -266,13 +273,11 @@ var parseCommon = require("../parse/abc_common");
266
273
  }
267
274
  break;
268
275
  case "key":
269
- if (elem.root === 'HP')
270
- voices[voiceNumber].push({el_type: 'key', accidentals: [{acc: 'natural', note: 'g'}, {acc: 'sharp', note: 'f'}, {acc: 'sharp', note: 'c'}]});
271
- else
272
- voices[voiceNumber].push({el_type: 'key', accidentals: elem.accidentals });
276
+ case "keySignature":
277
+ addKey(voices[voiceNumber], elem);
273
278
  break;
274
279
  case "meter":
275
- voices[voiceNumber].push(interpretMeter(elem));
280
+ addMeter(voices[voiceNumber], elem);
276
281
  break;
277
282
  case "clef": // need to keep this to catch the "transpose" element.
278
283
  if (elem.transpose)
@@ -332,6 +337,8 @@ var parseCommon = require("../parse/abc_common");
332
337
  break;
333
338
  case 'stem':
334
339
  case 'scale':
340
+ case 'break':
341
+ case 'font':
335
342
  // These elements don't affect sound
336
343
  break;
337
344
  case 'midi':
@@ -351,7 +358,7 @@ var parseCommon = require("../parse/abc_common");
351
358
  voices[voiceNumber].push({ el_type: 'instrument', program: PERCUSSION_PROGRAM });
352
359
  break;
353
360
  case "program":
354
- voices[voiceNumber].push({ el_type: 'instrument', program: elem.params[0] });
361
+ addIfDifferent(voices[voiceNumber], { el_type: 'instrument', program: elem.params[0] });
355
362
  channelExplicitlySet = true;
356
363
  break;
357
364
  case "transpose":
@@ -601,6 +608,38 @@ var parseCommon = require("../parse/abc_common");
601
608
  measureLength = meter.num/meter.den;
602
609
  return meter;
603
610
  }
611
+
612
+ function removeNaturals(accidentals) {
613
+ var acc = [];
614
+ for (var i = 0; i < accidentals.length; i++) {
615
+ if (accidentals[i].acc !== "natural")
616
+ acc.push(accidentals[i])
617
+ }
618
+ return acc;
619
+ }
620
+ function addKey(arr, key) {
621
+ var newKey;
622
+ if (key.root === 'HP')
623
+ newKey = {el_type: 'key', accidentals: [{acc: 'natural', note: 'g'}, {acc: 'sharp', note: 'f'}, {acc: 'sharp', note: 'c'}]};
624
+ else
625
+ newKey = {el_type: 'key', accidentals: removeNaturals(key.accidentals) };
626
+ addIfDifferent(arr, newKey);
627
+ }
628
+ function addMeter(arr, meter) {
629
+ var newMeter = interpretMeter(meter);
630
+ addIfDifferent(arr, newMeter);
631
+ }
632
+ function addIfDifferent(arr, item) {
633
+ for (var i = arr.length-1; i >= 0; i--) {
634
+ if (arr[i].el_type === item.el_type) {
635
+ if (JSON.stringify(arr[i]) !== JSON.stringify(item))
636
+ arr.push(item);
637
+ return;
638
+ }
639
+ }
640
+ arr.push(item);
641
+ }
642
+
604
643
  })();
605
644
 
606
645
  module.exports = sequence;
@@ -43,7 +43,7 @@ function CreateSynth() {
43
43
  self.soundFontUrl = params.soundFontUrl ? params.soundFontUrl : defaultSoundFontUrl;
44
44
  if (self.soundFontUrl[self.soundFontUrl.length-1] !== '/')
45
45
  self.soundFontUrl += '/';
46
- if (params.soundFontVolumeMultiplier)
46
+ if (params.soundFontVolumeMultiplier || params.soundFontVolumeMultiplier === 0)
47
47
  self.soundFontVolumeMultiplier = params.soundFontVolumeMultiplier;
48
48
  else if (self.soundFontUrl === alternateSoundFontUrl || self.soundFontUrl === alternateSoundFontUrl2)
49
49
  self.soundFontVolumeMultiplier = 5.0;
@@ -65,7 +65,6 @@ function CreateSynth() {
65
65
  p = params.noteEnd !== undefined ? parseInt(params.noteEnd,10) : NaN;
66
66
  self.noteEnd = isNaN(p) ? 0 : p;
67
67
 
68
- self.millisecondsPerMeasure = options.millisecondsPerMeasure ? options.millisecondsPerMeasure : (options.visualObj ? options.visualObj.millisecondsPerMeasure(options.bpm) : 1000);
69
68
  self.pan = params.pan;
70
69
  self.meterSize = 1;
71
70
  if (options.visualObj) {
@@ -77,6 +76,8 @@ function CreateSynth() {
77
76
  self.flattened = options.sequence;
78
77
  else
79
78
  return Promise.reject(new Error("Must pass in either a visualObj or a sequence"));
79
+ self.millisecondsPerMeasure = options.millisecondsPerMeasure ? options.millisecondsPerMeasure : (options.visualObj ? options.visualObj.millisecondsPerMeasure(self.flattened.tempo) : 1000);
80
+ self.beatsPerMeasure = options.visualObj ? options.visualObj.getBeatsPerMeasure() : 4;
80
81
  self.sequenceCallback = params.sequenceCallback;
81
82
  self.callbackContext = params.callbackContext;
82
83
  self.onEnded = params.onEnded;
@@ -257,7 +258,7 @@ function CreateSynth() {
257
258
  for (var key2 = 0; key2 < Object.keys(uniqueSounds).length; key2++) {
258
259
  var k = Object.keys(uniqueSounds)[key2];
259
260
  var parts = k.split(":");
260
- var cents = parts[6] !== "undefined" ? parseFloat(parts[6]) : undefined;
261
+ var cents = parts[6] !== undefined ? parseFloat(parts[6]) : 0;
261
262
  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};
262
263
  allPromises.push(placeNote(audioBuffer, activeAudioContext().sampleRate, parts, uniqueSounds[k], self.soundFontVolumeMultiplier, self.programOffsets[parts.instrument], fadeTimeSec, self.noteEnd/1000));
263
264
  }
@@ -344,16 +345,28 @@ function CreateSynth() {
344
345
  if (self.debugCallback)
345
346
  self.debugCallback("pause called");
346
347
 
347
- self.stop();
348
- self.pausedTimeSec = activeAudioContext().currentTime - self.startTimeSec;
348
+ self.pausedTimeSec = self.stop();
349
+ return self.pausedTimeSec;
349
350
  };
350
351
 
351
352
  self.resume = function() {
352
353
  self.start();
353
354
  };
354
355
 
355
- self.seek = function(percent) {
356
- var offset = (self.duration-self.fadeLength/1000) * percent;
356
+ self.seek = function(position, units) {
357
+ var offset;
358
+ switch (units) {
359
+ case "seconds":
360
+ offset = position;
361
+ break;
362
+ case "beats":
363
+ offset = position * self.millisecondsPerMeasure / self.beatsPerMeasure / 1000;
364
+ break;
365
+ default:
366
+ // this is "percent" or any illegal value
367
+ offset = (self.duration-self.fadeLength/1000) * position;
368
+ break;
369
+ }
357
370
 
358
371
  // TODO-PER: can seek when paused or when playing
359
372
  if (!self.audioBufferPossible)
@@ -382,6 +395,8 @@ function CreateSynth() {
382
395
  }
383
396
  });
384
397
  self.directSource = [];
398
+ var elapsed = activeAudioContext().currentTime - self.startTimeSec;
399
+ return elapsed;
385
400
  };
386
401
  self.finished = function() {
387
402
  self.startTimeSec = undefined;
@@ -2,74 +2,40 @@
2
2
  // url = the base url for the soundfont
3
3
  // instrument = the instrument name (e.g. "acoustic_grand_piano")
4
4
  // name = the pitch name (e.g. "A3")
5
- var soundsCache = require('./sounds-cache');
5
+ var soundsCache = require("./sounds-cache");
6
6
 
7
- var getNote = function(url, instrument, name, audioContext) {
8
- return new Promise(function (resolve, reject) {
9
- if (!soundsCache[instrument])
10
- soundsCache[instrument] = {};
11
- var instrumentCache = soundsCache[instrument];
7
+ var getNote = function (url, instrument, name, audioContext) {
8
+ if (!soundsCache[instrument]) soundsCache[instrument] = {};
9
+ var instrumentCache = soundsCache[instrument];
12
10
 
13
- if (instrumentCache[name] === 'error') {
14
- return resolve({instrument: instrument, name: name, status: "error", message: "Unable to load sound font" + ' ' + url + ' ' + instrument + ' ' + name });
15
- }
16
- if (instrumentCache[name] === 'pending') {
17
- return resolve({instrument: instrument, name: name, status: "pending"});
18
- }
19
- if (instrumentCache[name]) {
20
- return resolve({instrument: instrument, name: name, status: "cached"});
21
- }
22
-
23
- // if (this.debugCallback)
24
- // this.debugCallback(`Loading sound: ${instrument} ${name}`);
25
- instrumentCache[name] = "pending"; // This can be called in parallel, so don't call it a second time before the first one has loaded.
26
- var xhr = new XMLHttpRequest();
27
- xhr.open('GET', url+instrument+'-mp3/'+name+'.mp3', true);
28
- xhr.responseType = 'arraybuffer';
29
-
30
- var self = this;
31
- function onSuccess(audioBuffer) {
32
- instrumentCache[name] = audioBuffer;
33
- // if (self.debugCallback)
34
- // self.debugCallback(`Sound loaded: ${instrument} ${name} ${url}`);
35
- resolve({instrument: instrument, name: name, status: "loaded"});
36
- }
37
-
38
- function onFailure(error) {
39
- error = "Can't decode sound. " + url + ' ' + instrument + ' ' + name + ' ' + error;
40
- if (self.debugCallback)
41
- self.debugCallback(error);
42
- return resolve({instrument: instrument, name: name, status: "error", message: error });
43
- }
44
-
45
- xhr.onload = function (e) {
46
- if (this.status === 200) {
47
- try {
48
- var promise = audioContext.decodeAudioData(this.response, onSuccess, onFailure);
49
- // older browsers only have the callback. Newer ones will report an unhandled
50
- // rejection if catch isn't handled so we need both. We don't need to report it twice, though.
51
- if (promise && promise.catch)
52
- promise.catch(function () {});
53
- } catch(error) {
54
- reject(error);
11
+ if (!instrumentCache[name])
12
+ instrumentCache[name] = new Promise(function (resolve, reject) {
13
+ var xhr = new XMLHttpRequest();
14
+ let noteUrl = url + instrument + "-mp3/" + name + ".mp3";
15
+ xhr.open("GET", noteUrl, true);
16
+ xhr.responseType = "arraybuffer";
17
+ xhr.onload = function () {
18
+ if (xhr.status !== 200) {
19
+ reject(Error("Can't load sound at " + noteUrl));
20
+ return
55
21
  }
56
- } else {
57
- instrumentCache[name] = "error"; // To keep this from trying to load repeatedly.
58
- var cantLoadMp3 = "Onload error loading sound: " + name + " " + url + " " + e.currentTarget.status + " " + e.currentTarget.statusText;
59
- if (self.debugCallback)
60
- self.debugCallback(cantLoadMp3);
61
- return resolve({instrument: instrument, name: name, status: "error", message: cantLoadMp3 });
62
- }
63
- };
64
- xhr.addEventListener("error", function () {
65
- instrumentCache[name] = "error"; // To keep this from trying to load repeatedly.
66
- var cantLoadMp3 = "Error in loading sound: " + " " + url;
67
- if (self.debugCallback)
68
- self.debugCallback(cantLoadMp3);
69
- return resolve({instrument: instrument, name: name, status: "error", message: cantLoadMp3 });
70
- }, false);
71
- xhr.send();
72
- });
22
+ var maybePromise = audioContext.decodeAudioData(xhr.response, resolve, function () {
23
+ reject(Error("Can't decode sound at " + noteUrl));
24
+ });
25
+ // In older browsers `BaseAudioContext.decodeAudio()` did not return a promise
26
+ if (maybePromise && typeof maybePromise.catch === "function") maybePromise.catch(reject);
27
+ };
28
+ xhr.onerror = function () {
29
+ reject(Error("Can't load sound at " + noteUrl));
30
+ };
31
+ xhr.send();
32
+ })
33
+ .catch(err => {
34
+ console.error("Didn't load note", instrument, name, ":", err.message);
35
+ throw err;
36
+ });
37
+
38
+ return instrumentCache[name];
73
39
  };
74
40
 
75
41
  module.exports = getNote;
@@ -17,74 +17,73 @@ function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMulti
17
17
  len = 0.005; // Have some small audible length no matter how short the note is.
18
18
  var offlineCtx = new OfflineAC(2,Math.floor((len+fadeTimeSec)*sampleRate),sampleRate);
19
19
  var noteName = pitchToNoteName[sound.pitch];
20
- var noteBuffer = soundsCache[sound.instrument][noteName];
21
- if (noteBuffer === "error" || noteBuffer === "pending") { // If the note isn't available, just leave a blank spot
22
- // If the note is still pending by now that means an error happened when loading. There was probably a timeout.
23
- console.log("Didn't load note", sound.instrument, noteName, noteBuffer);
24
- return;
25
- }
20
+ var noteBufferPromise = soundsCache[sound.instrument][noteName];
26
21
 
27
- // create audio buffer
28
- var source = offlineCtx.createBufferSource();
29
- source.buffer = noteBuffer;
22
+ noteBufferPromise
23
+ .then(function (audioBuffer) {
24
+ // create audio buffer
25
+ var source = offlineCtx.createBufferSource();
26
+ source.buffer = audioBuffer;
30
27
 
31
- // add gain
32
- // volume can be between 1 to 127. This translation to gain is just trial and error.
33
- // The smaller the first number, the more dynamic range between the quietest to loudest.
34
- // The larger the second number, the louder it will be in general.
35
- var volume = (sound.volume / 96) * volumeMultiplier;
36
- source.gainNode = offlineCtx.createGain();
28
+ // add gain
29
+ // volume can be between 1 to 127. This translation to gain is just trial and error.
30
+ // The smaller the first number, the more dynamic range between the quietest to loudest.
31
+ // The larger the second number, the louder it will be in general.
32
+ var volume = (sound.volume / 96) * volumeMultiplier;
33
+ source.gainNode = offlineCtx.createGain();
37
34
 
38
- // add pan if supported and present
39
- if (sound.pan && offlineCtx.createStereoPanner) {
40
- source.panNode = offlineCtx.createStereoPanner();
41
- source.panNode.pan.setValueAtTime(sound.pan, 0);
42
- }
43
- source.gainNode.gain.value = volume; // Math.min(2, Math.max(0, volume));
44
- source.gainNode.gain.linearRampToValueAtTime(source.gainNode.gain.value, len);
45
- source.gainNode.gain.linearRampToValueAtTime(0.0, len + fadeTimeSec);
35
+ // add pan if supported and present
36
+ if (sound.pan && offlineCtx.createStereoPanner) {
37
+ source.panNode = offlineCtx.createStereoPanner();
38
+ source.panNode.pan.setValueAtTime(sound.pan, 0);
39
+ }
40
+ source.gainNode.gain.value = volume; // Math.min(2, Math.max(0, volume));
41
+ source.gainNode.gain.linearRampToValueAtTime(source.gainNode.gain.value, len);
42
+ source.gainNode.gain.linearRampToValueAtTime(0.0, len + fadeTimeSec);
46
43
 
47
- if (sound.cents) {
48
- source.playbackRate.value = centsToFactor(sound.cents);
49
- }
44
+ if (sound.cents) {
45
+ source.playbackRate.value = centsToFactor(sound.cents);
46
+ }
50
47
 
51
- // connect all the nodes
52
- if (source.panNode) {
53
- source.panNode.connect(offlineCtx.destination);
54
- source.gainNode.connect(source.panNode);
55
- } else {
56
- source.gainNode.connect(offlineCtx.destination);
57
- }
58
- source.connect(source.gainNode);
48
+ // connect all the nodes
49
+ if (source.panNode) {
50
+ source.panNode.connect(offlineCtx.destination);
51
+ source.gainNode.connect(source.panNode);
52
+ } else {
53
+ source.gainNode.connect(offlineCtx.destination);
54
+ }
55
+ source.connect(source.gainNode);
59
56
 
60
- // Do the process of creating the sound and placing it in the buffer
61
- source.start(0);
57
+ // Do the process of creating the sound and placing it in the buffer
58
+ source.start(0);
62
59
 
63
- if (source.noteOff) {
64
- source.noteOff(len + fadeTimeSec);
65
- } else {
66
- source.stop(len + fadeTimeSec);
67
- }
68
- var fnResolve;
69
- offlineCtx.oncomplete = function(e) {
70
- if (e.renderedBuffer) { // If the system gets overloaded then this can start failing. Just drop the note if so.
71
- for (var i = 0; i < startArray.length; i++) {
72
- //Math.floor(startArray[i] * sound.tempoMultiplier * sampleRate)
73
- var start = startArray[i] * sound.tempoMultiplier;
74
- if (ofsMs)
75
- start -=ofsMs/1000;
76
- if (start < 0)
77
- start = 0; // If the item that is moved back is at the very beginning of the buffer then don't move it back. To do that would be to push everything else forward. TODO-PER: this should probably be done at some point but then it would change timing in existing apps.
78
- start = Math.floor(start*sampleRate);
79
- copyToChannel(outputAudioBuffer, e.renderedBuffer, start);
60
+ if (source.noteOff) {
61
+ source.noteOff(len + fadeTimeSec);
62
+ } else {
63
+ source.stop(len + fadeTimeSec);
80
64
  }
81
- }
82
- fnResolve();
83
- };
84
- offlineCtx.startRendering();
85
- return new Promise(function(resolve, reject) {
86
- fnResolve = resolve;
87
- });
65
+ var fnResolve;
66
+ offlineCtx.oncomplete = function(e) {
67
+ if (e.renderedBuffer) { // If the system gets overloaded then this can start failing. Just drop the note if so.
68
+ for (var i = 0; i < startArray.length; i++) {
69
+ //Math.floor(startArray[i] * sound.tempoMultiplier * sampleRate)
70
+ var start = startArray[i] * sound.tempoMultiplier;
71
+ if (ofsMs)
72
+ start -=ofsMs/1000;
73
+ if (start < 0)
74
+ start = 0; // If the item that is moved back is at the very beginning of the buffer then don't move it back. To do that would be to push everything else forward. TODO-PER: this should probably be done at some point but then it would change timing in existing apps.
75
+ start = Math.floor(start*sampleRate);
76
+ copyToChannel(outputAudioBuffer, e.renderedBuffer, start);
77
+ }
78
+ }
79
+ fnResolve();
80
+ };
81
+ offlineCtx.startRendering();
82
+ return new Promise(function(resolve) {
83
+ fnResolve = resolve;
84
+ });
85
+ })
86
+ .catch(function () {});
88
87
  }
89
88
 
90
89
  var copyToChannel = function(toBuffer, fromBuffer, start) {
@@ -10,7 +10,10 @@ function registerAudioContext(ac) {
10
10
  // no audio context passed in, so create it unless there is already one from before.
11
11
  if (!window.abcjsAudioContext) {
12
12
  var AudioContext = window.AudioContext || window.webkitAudioContext;
13
- window.abcjsAudioContext = new AudioContext();
13
+ if (AudioContext)
14
+ window.abcjsAudioContext = new AudioContext();
15
+ else
16
+ return false;
14
17
  }
15
18
  }
16
19
  return window.abcjsAudioContext.state !== "suspended";
@@ -11,17 +11,18 @@ var activeAudioContext = require('./active-audio-context');
11
11
  // But then, call it again after a user interaction to test for resume.
12
12
 
13
13
  function supportsAudio() {
14
- var aac = activeAudioContext();
15
- if (aac)
16
- return aac.resume !== undefined;
17
-
18
14
  if (!window.Promise)
19
15
  return false;
20
16
 
21
- return !!window.AudioContext ||
22
- !!window.webkitAudioContext ||
23
- !!navigator.mozAudioContext ||
24
- !!navigator.msAudioContext;
17
+ if (!window.AudioContext &&
18
+ !window.webkitAudioContext &&
19
+ !navigator.mozAudioContext &&
20
+ !navigator.msAudioContext)
21
+ return false;
22
+
23
+ var aac = activeAudioContext();
24
+ if (aac)
25
+ return aac.resume !== undefined;
25
26
  }
26
27
 
27
28
  module.exports = supportsAudio;
@@ -204,9 +204,11 @@ function SynthController() {
204
204
  return Promise.resolve({status: "ok"});
205
205
  };
206
206
 
207
- self.seek = function (percent) {
208
- self.timer.setProgress(percent);
209
- self.midiBuffer.seek(percent);
207
+ self.seek = function (percent, units) {
208
+ if (self.timer && self.midiBuffer) {
209
+ self.timer.setProgress(percent, units);
210
+ self.midiBuffer.seek(percent, units);
211
+ }
210
212
  };
211
213
 
212
214
  self.setWarp = function (newWarp) {
@@ -0,0 +1,19 @@
1
+
2
+ /**
3
+ * Dedicated fonts for violin tabs
4
+ */
5
+
6
+ /**
7
+ * Set here the fonts used by renderer/drawer
8
+ * for the violin plugin
9
+ * @param {} tune
10
+ */
11
+ // eslint-disable-next-line no-unused-vars
12
+ function setGuitarFonts(tune) {
13
+ /* enhance or change instrument fonts here */
14
+ // tune.formatting.tabnumberfont = { face: "\"Times New Roman\"", size: 9, weight: "normal", style: "normal", decoration: "none" };
15
+ // tune.formatting.tabgracefont = { face: "\"Times New Roman\"", size: 7, weight: "normal", style: "normal", decoration: "none" };
16
+ }
17
+
18
+ module.exports = setGuitarFonts;
19
+
@@ -0,0 +1,23 @@
1
+ var StringPatterns = require('../string-patterns');
2
+
3
+ function GuitarPatterns(plugin) {
4
+ this.tuning = plugin._super.params.tuning;
5
+ if (!this.tuning) {
6
+ this.tuning = ['E,', 'A', 'D', 'G' , 'B' , 'e'];
7
+ }
8
+ plugin.tuning = this.tuning;
9
+ this.strings = new StringPatterns(plugin);
10
+ }
11
+
12
+ GuitarPatterns.prototype.notesToNumber = function (notes, graces) {
13
+ var converter = this.strings;
14
+ return converter.notesToNumber(notes, graces);
15
+ };
16
+
17
+ GuitarPatterns.prototype.stringToPitch = function (stringNumber) {
18
+ var converter = this.strings;
19
+ return converter.stringToPitch(stringNumber);
20
+ };
21
+
22
+
23
+ module.exports = GuitarPatterns;
@@ -0,0 +1,50 @@
1
+ /*
2
+ Emit tab for Guitar staff
3
+ */
4
+ var StringTablature = require('../string-tablature');
5
+ var TabCommon = require('../../tab-common');
6
+ var TabRenderer = require('../../tab-renderer');
7
+ var GuitarPatterns = require('./guitar-patterns');
8
+ var setGuitarFonts = require('./guitar-fonts');
9
+
10
+ /**
11
+ * upon init mainly store provided instances for later usage
12
+ * @param {*} abcTune the parsed tune AST tree
13
+ * @param {*} tuneNumber the parsed tune AST tree
14
+ * @param {*} params complementary args provided to Tablature Plugin
15
+ */
16
+ Plugin.prototype.init = function (abcTune, tuneNumber, params) {
17
+ var _super = new TabCommon(abcTune, tuneNumber, params);
18
+ this._super = _super;
19
+ this.abcTune = abcTune;
20
+ this.linePitch = 3;
21
+ this.nbLines = 6;
22
+ this.isTabBig = true;
23
+ this.capo = params.capo;
24
+ this.transpose = params.visualTranspose;
25
+ this.tablature = new StringTablature(this.nbLines,
26
+ this.linePitch);
27
+
28
+ var semantics = new GuitarPatterns(this);
29
+ this.semantics = semantics;
30
+ };
31
+
32
+ Plugin.prototype.render = function (renderer, line, staffIndex) {
33
+ if (this._super.inError) return;
34
+ if (this.tablature.bypass(line)) return;
35
+ setGuitarFonts(this.abcTune);
36
+ var rndrer = new TabRenderer(this, renderer, line, staffIndex);
37
+ rndrer.doLayout();
38
+ };
39
+
40
+ function Plugin() {}
41
+
42
+ //
43
+ // Tablature plugin definition
44
+ //
45
+ var AbcGuitarTab = function () {
46
+ return { name: 'GuitarTab', tablature: Plugin };
47
+ };
48
+
49
+
50
+ module.exports = AbcGuitarTab;