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.
- package/README.md +13 -7
- package/RELEASE.md +130 -0
- package/dist/abcjs-basic-min.js +2 -2
- package/dist/abcjs-basic.js +3763 -825
- package/dist/abcjs-basic.js.map +1 -1
- package/dist/abcjs-plugin-min.js +2 -2
- package/dist/report-basic.html +37 -0
- package/dist/report-before-glyph-compress.html +37 -0
- package/dist/report-brown-ts-target-es5.html +37 -0
- package/dist/report-dev-orig-no-babel.html +37 -0
- package/dist/report-synth.html +37 -0
- package/docker-build.sh +1 -0
- package/glyphs.json +1 -0
- package/package.json +9 -9
- package/src/api/abc_tablatures.js +144 -0
- package/src/api/abc_timing_callbacks.js +49 -26
- package/src/api/abc_tunebook.js +10 -1
- package/src/api/abc_tunebook_svg.js +16 -22
- package/src/data/abc_tune.js +90 -25
- package/src/data/deline-tune.js +199 -0
- package/src/edit/abc_editor.js +33 -11
- package/src/midi/abc_midi_create.js +6 -2
- package/src/parse/abc_parse.js +10 -6
- package/src/parse/abc_parse_directive.js +19 -12
- package/src/parse/abc_parse_header.js +12 -12
- package/src/parse/abc_parse_music.js +15 -5
- package/src/parse/tune-builder.js +23 -30
- package/src/parse/wrap_lines.js +13 -36
- package/src/synth/abc_midi_flattener.js +44 -29
- package/src/synth/abc_midi_sequencer.js +52 -13
- package/src/synth/create-synth.js +22 -7
- package/src/synth/load-note.js +31 -65
- package/src/synth/place-note.js +59 -60
- package/src/synth/register-audio-context.js +4 -1
- package/src/synth/supports-audio.js +9 -8
- package/src/synth/synth-controller.js +5 -3
- package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
- package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
- package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
- package/src/tablatures/instruments/string-patterns.js +277 -0
- package/src/tablatures/instruments/string-tablature.js +56 -0
- package/src/tablatures/instruments/tab-note.js +282 -0
- package/src/tablatures/instruments/tab-notes.js +41 -0
- package/src/tablatures/instruments/violin/tab-violin.js +47 -0
- package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
- package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
- package/src/tablatures/tab-absolute-elements.js +310 -0
- package/src/tablatures/tab-common.js +29 -0
- package/src/tablatures/tab-renderer.js +243 -0
- package/src/tablatures/transposer.js +110 -0
- package/src/test/abc_parser_lint.js +62 -6
- package/src/write/abc_absolute_element.js +2 -2
- package/src/write/abc_abstract_engraver.js +9 -7
- package/src/write/abc_create_key_signature.js +1 -0
- package/src/write/abc_create_note_head.js +1 -1
- package/src/write/abc_engraver_controller.js +22 -9
- package/src/write/abc_glyphs.js +5 -2
- package/src/write/abc_relative_element.js +11 -3
- package/src/write/abc_renderer.js +5 -1
- package/src/write/add-chord.js +5 -2
- package/src/write/add-text-if.js +33 -0
- package/src/write/bottom-text.js +8 -29
- package/src/write/draw/absolute.js +12 -14
- package/src/write/draw/brace.js +3 -3
- package/src/write/draw/crescendo.js +1 -1
- package/src/write/draw/draw.js +3 -4
- package/src/write/draw/dynamics.js +8 -1
- package/src/write/draw/ending.js +4 -3
- package/src/write/draw/group-elements.js +10 -8
- package/src/write/draw/non-music.js +11 -6
- package/src/write/draw/print-line.js +24 -0
- package/src/write/draw/print-stem.js +12 -11
- package/src/write/draw/print-symbol.js +11 -10
- package/src/write/draw/relative.js +33 -13
- package/src/write/draw/selectables.js +9 -6
- package/src/write/draw/staff-group.js +45 -9
- package/src/write/draw/staff-line.js +3 -17
- package/src/write/draw/staff.js +15 -2
- package/src/write/draw/tab-line.js +40 -0
- package/src/write/draw/tempo.js +7 -7
- package/src/write/draw/text.js +11 -4
- package/src/write/draw/tie.js +2 -2
- package/src/write/draw/triplet.js +3 -3
- package/src/write/draw/voice.js +10 -2
- package/src/write/format-jazz-chord.js +15 -0
- package/src/write/free-text.js +20 -12
- package/src/write/layout/VoiceElements.js +33 -1
- package/src/write/layout/beam.js +2 -0
- package/src/write/layout/staffGroup.js +37 -2
- package/src/write/layout/voice.js +2 -1
- package/src/write/selection.js +15 -5
- package/src/write/separator.js +1 -1
- package/src/write/subtitle.js +3 -3
- package/src/write/svg.js +41 -14
- package/src/write/top-text.js +19 -25
- package/types/index.d.ts +1007 -39
- 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
|
-
|
|
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 =
|
|
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
|
-
|
|
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]
|
|
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
|
-
|
|
270
|
-
|
|
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]
|
|
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]
|
|
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] !==
|
|
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
|
-
|
|
348
|
-
self.pausedTimeSec
|
|
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(
|
|
356
|
-
var offset
|
|
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;
|
package/src/synth/load-note.js
CHANGED
|
@@ -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(
|
|
5
|
+
var soundsCache = require("./sounds-cache");
|
|
6
6
|
|
|
7
|
-
var getNote = function(url, instrument, name, audioContext) {
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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;
|
package/src/synth/place-note.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
noteBufferPromise
|
|
23
|
+
.then(function (audioBuffer) {
|
|
24
|
+
// create audio buffer
|
|
25
|
+
var source = offlineCtx.createBufferSource();
|
|
26
|
+
source.buffer = audioBuffer;
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
if (sound.cents) {
|
|
45
|
+
source.playbackRate.value = centsToFactor(sound.cents);
|
|
46
|
+
}
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
61
|
-
|
|
57
|
+
// Do the process of creating the sound and placing it in the buffer
|
|
58
|
+
source.start(0);
|
|
62
59
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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.
|
|
209
|
-
|
|
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;
|