@marmooo/midy 0.1.5 → 0.1.7
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/esm/midy-GM1.d.ts +6 -5
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +97 -65
- package/esm/midy-GM2.d.ts +15 -9
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +316 -129
- package/esm/midy-GMLite.d.ts +6 -5
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +97 -65
- package/esm/midy.d.ts +15 -9
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +317 -134
- package/package.json +5 -1
- package/script/midy-GM1.d.ts +6 -5
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +100 -68
- package/script/midy-GM2.d.ts +15 -9
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +319 -132
- package/script/midy-GMLite.d.ts +6 -5
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +100 -68
- package/script/midy.d.ts +15 -9
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +320 -137
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts +0 -149
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +0 -1
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +0 -180
- package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts +0 -84
- package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts.map +0 -1
- package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js +0 -216
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts +0 -149
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +0 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +0 -190
- package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts +0 -84
- package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts.map +0 -1
- package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js +0 -221
package/script/midy-GM2.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MidyGM2 = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const midi_file_1 = require("midi-file");
|
|
5
|
+
const soundfont_parser_1 = require("@marmooo/soundfont-parser");
|
|
6
6
|
class Note {
|
|
7
7
|
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
8
8
|
Object.defineProperty(this, "bufferSource", {
|
|
@@ -53,6 +53,18 @@ class Note {
|
|
|
53
53
|
writable: true,
|
|
54
54
|
value: void 0
|
|
55
55
|
});
|
|
56
|
+
Object.defineProperty(this, "reverbEffectsSend", {
|
|
57
|
+
enumerable: true,
|
|
58
|
+
configurable: true,
|
|
59
|
+
writable: true,
|
|
60
|
+
value: void 0
|
|
61
|
+
});
|
|
62
|
+
Object.defineProperty(this, "chorusEffectsSend", {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
writable: true,
|
|
66
|
+
value: void 0
|
|
67
|
+
});
|
|
56
68
|
this.noteNumber = noteNumber;
|
|
57
69
|
this.velocity = velocity;
|
|
58
70
|
this.startTime = startTime;
|
|
@@ -208,6 +220,12 @@ class MidyGM2 {
|
|
|
208
220
|
writable: true,
|
|
209
221
|
value: []
|
|
210
222
|
});
|
|
223
|
+
Object.defineProperty(this, "exclusiveClassMap", {
|
|
224
|
+
enumerable: true,
|
|
225
|
+
configurable: true,
|
|
226
|
+
writable: true,
|
|
227
|
+
value: new Map()
|
|
228
|
+
});
|
|
211
229
|
Object.defineProperty(this, "defaultOptions", {
|
|
212
230
|
enumerable: true,
|
|
213
231
|
configurable: true,
|
|
@@ -252,24 +270,26 @@ class MidyGM2 {
|
|
|
252
270
|
addSoundFont(soundFont) {
|
|
253
271
|
const index = this.soundFonts.length;
|
|
254
272
|
this.soundFonts.push(soundFont);
|
|
255
|
-
soundFont.parsed.presetHeaders
|
|
273
|
+
const presetHeaders = soundFont.parsed.presetHeaders;
|
|
274
|
+
for (let i = 0; i < presetHeaders.length; i++) {
|
|
275
|
+
const presetHeader = presetHeaders[i];
|
|
256
276
|
if (!presetHeader.presetName.startsWith("\u0000")) { // TODO: Only SF3 generated by PolyPone?
|
|
257
277
|
const banks = this.soundFontTable[presetHeader.preset];
|
|
258
278
|
banks.set(presetHeader.bank, index);
|
|
259
279
|
}
|
|
260
|
-
}
|
|
280
|
+
}
|
|
261
281
|
}
|
|
262
282
|
async loadSoundFont(soundFontUrl) {
|
|
263
283
|
const response = await fetch(soundFontUrl);
|
|
264
284
|
const arrayBuffer = await response.arrayBuffer();
|
|
265
|
-
const parsed = (0,
|
|
266
|
-
const soundFont = new
|
|
285
|
+
const parsed = (0, soundfont_parser_1.parse)(new Uint8Array(arrayBuffer));
|
|
286
|
+
const soundFont = new soundfont_parser_1.SoundFont(parsed);
|
|
267
287
|
this.addSoundFont(soundFont);
|
|
268
288
|
}
|
|
269
289
|
async loadMIDI(midiUrl) {
|
|
270
290
|
const response = await fetch(midiUrl);
|
|
271
291
|
const arrayBuffer = await response.arrayBuffer();
|
|
272
|
-
const midi = (0,
|
|
292
|
+
const midi = (0, midi_file_1.parseMidi)(new Uint8Array(arrayBuffer));
|
|
273
293
|
this.ticksPerBeat = midi.header.ticksPerBeat;
|
|
274
294
|
const midiData = this.extractMidiData(midi);
|
|
275
295
|
this.instruments = midiData.instruments;
|
|
@@ -306,27 +326,31 @@ class MidyGM2 {
|
|
|
306
326
|
return channels;
|
|
307
327
|
}
|
|
308
328
|
async createNoteBuffer(instrumentKey, isSF3) {
|
|
329
|
+
const sampleStart = instrumentKey.start;
|
|
309
330
|
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
310
331
|
if (isSF3) {
|
|
311
|
-
const sample =
|
|
312
|
-
sample.
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
channelData.set(channelData.subarray(0, sampleEnd));
|
|
317
|
-
}
|
|
332
|
+
const sample = instrumentKey.sample;
|
|
333
|
+
const start = sample.byteOffset + sampleStart;
|
|
334
|
+
const end = sample.byteOffset + sampleEnd;
|
|
335
|
+
const buffer = sample.buffer.slice(start, end);
|
|
336
|
+
const audioBuffer = await this.audioContext.decodeAudioData(buffer);
|
|
318
337
|
return audioBuffer;
|
|
319
338
|
}
|
|
320
339
|
else {
|
|
321
|
-
const sample = instrumentKey.sample
|
|
322
|
-
const
|
|
340
|
+
const sample = instrumentKey.sample;
|
|
341
|
+
const start = sample.byteOffset + sampleStart;
|
|
342
|
+
const end = sample.byteOffset + sampleEnd;
|
|
343
|
+
const buffer = sample.buffer.slice(start, end);
|
|
323
344
|
const audioBuffer = new AudioBuffer({
|
|
324
345
|
numberOfChannels: 1,
|
|
325
346
|
length: sample.length,
|
|
326
347
|
sampleRate: instrumentKey.sampleRate,
|
|
327
348
|
});
|
|
328
349
|
const channelData = audioBuffer.getChannelData(0);
|
|
329
|
-
|
|
350
|
+
const int16Array = new Int16Array(buffer);
|
|
351
|
+
for (let i = 0; i < int16Array.length; i++) {
|
|
352
|
+
channelData[i] = int16Array[i] / 32768;
|
|
353
|
+
}
|
|
330
354
|
return audioBuffer;
|
|
331
355
|
}
|
|
332
356
|
}
|
|
@@ -342,13 +366,23 @@ class MidyGM2 {
|
|
|
342
366
|
}
|
|
343
367
|
return bufferSource;
|
|
344
368
|
}
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
369
|
+
findPortamentoTarget(queueIndex) {
|
|
370
|
+
const endEvent = this.timeline[queueIndex];
|
|
371
|
+
if (!this.channels[endEvent.channel].portamento)
|
|
372
|
+
return;
|
|
373
|
+
const endTime = endEvent.startTime;
|
|
374
|
+
let target;
|
|
375
|
+
while (++queueIndex < this.timeline.length) {
|
|
376
|
+
const event = this.timeline[queueIndex];
|
|
377
|
+
if (endTime !== event.startTime)
|
|
378
|
+
break;
|
|
379
|
+
if (event.type !== "noteOn")
|
|
380
|
+
continue;
|
|
381
|
+
if (!target || event.noteNumber < target.noteNumber) {
|
|
382
|
+
target = event;
|
|
383
|
+
}
|
|
350
384
|
}
|
|
351
|
-
return
|
|
385
|
+
return target;
|
|
352
386
|
}
|
|
353
387
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
354
388
|
while (queueIndex < this.timeline.length) {
|
|
@@ -358,12 +392,15 @@ class MidyGM2 {
|
|
|
358
392
|
switch (event.type) {
|
|
359
393
|
case "noteOn":
|
|
360
394
|
if (event.velocity !== 0) {
|
|
361
|
-
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
395
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, event.portamento);
|
|
362
396
|
break;
|
|
363
397
|
}
|
|
364
398
|
/* falls through */
|
|
365
399
|
case "noteOff": {
|
|
366
|
-
const
|
|
400
|
+
const portamentoTarget = this.findPortamentoTarget(queueIndex);
|
|
401
|
+
if (portamentoTarget)
|
|
402
|
+
portamentoTarget.portamento = true;
|
|
403
|
+
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, portamentoTarget?.noteNumber, false);
|
|
367
404
|
if (notePromise) {
|
|
368
405
|
this.notePromises.push(notePromise);
|
|
369
406
|
}
|
|
@@ -408,6 +445,7 @@ class MidyGM2 {
|
|
|
408
445
|
if (queueIndex >= this.timeline.length) {
|
|
409
446
|
await Promise.all(this.notePromises);
|
|
410
447
|
this.notePromises = [];
|
|
448
|
+
this.exclusiveClassMap.clear();
|
|
411
449
|
resolve();
|
|
412
450
|
return;
|
|
413
451
|
}
|
|
@@ -423,6 +461,7 @@ class MidyGM2 {
|
|
|
423
461
|
}
|
|
424
462
|
else if (this.isStopping) {
|
|
425
463
|
await this.stopNotes(0, true);
|
|
464
|
+
this.exclusiveClassMap.clear();
|
|
426
465
|
this.notePromises = [];
|
|
427
466
|
resolve();
|
|
428
467
|
this.isStopping = false;
|
|
@@ -431,6 +470,7 @@ class MidyGM2 {
|
|
|
431
470
|
}
|
|
432
471
|
else if (this.isSeeking) {
|
|
433
472
|
this.stopNotes(0, true);
|
|
473
|
+
this.exclusiveClassMap.clear();
|
|
434
474
|
this.startTime = this.audioContext.currentTime;
|
|
435
475
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
436
476
|
offset = this.resumeTime - this.startTime;
|
|
@@ -464,9 +504,11 @@ class MidyGM2 {
|
|
|
464
504
|
bankLSB: this.channels[i].bankLSB,
|
|
465
505
|
};
|
|
466
506
|
}
|
|
467
|
-
midi.tracks.
|
|
507
|
+
for (let i = 0; i < midi.tracks.length; i++) {
|
|
508
|
+
const track = midi.tracks[i];
|
|
468
509
|
let currentTicks = 0;
|
|
469
|
-
track.
|
|
510
|
+
for (let j = 0; j < track.length; j++) {
|
|
511
|
+
const event = track[j];
|
|
470
512
|
currentTicks += event.deltaTime;
|
|
471
513
|
event.ticks = currentTicks;
|
|
472
514
|
switch (event.type) {
|
|
@@ -519,16 +561,18 @@ class MidyGM2 {
|
|
|
519
561
|
}
|
|
520
562
|
delete event.deltaTime;
|
|
521
563
|
timeline.push(event);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
524
566
|
const priority = {
|
|
525
567
|
controller: 0,
|
|
526
568
|
sysEx: 1,
|
|
569
|
+
noteOff: 2, // for portamento
|
|
570
|
+
noteOn: 3,
|
|
527
571
|
};
|
|
528
572
|
timeline.sort((a, b) => {
|
|
529
573
|
if (a.ticks !== b.ticks)
|
|
530
574
|
return a.ticks - b.ticks;
|
|
531
|
-
return (priority[a.type] ||
|
|
575
|
+
return (priority[a.type] || 4) - (priority[b.type] || 4);
|
|
532
576
|
});
|
|
533
577
|
let prevTempoTime = 0;
|
|
534
578
|
let prevTempoTicks = 0;
|
|
@@ -545,7 +589,7 @@ class MidyGM2 {
|
|
|
545
589
|
}
|
|
546
590
|
return { instruments, timeline };
|
|
547
591
|
}
|
|
548
|
-
async stopChannelNotes(channelNumber, velocity,
|
|
592
|
+
async stopChannelNotes(channelNumber, velocity, force) {
|
|
549
593
|
const now = this.audioContext.currentTime;
|
|
550
594
|
const channel = this.channels[channelNumber];
|
|
551
595
|
channel.scheduledNotes.forEach((noteList) => {
|
|
@@ -553,16 +597,17 @@ class MidyGM2 {
|
|
|
553
597
|
const note = noteList[i];
|
|
554
598
|
if (!note)
|
|
555
599
|
continue;
|
|
556
|
-
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now,
|
|
600
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, undefined, // portamentoNoteNumber
|
|
601
|
+
force);
|
|
557
602
|
this.notePromises.push(promise);
|
|
558
603
|
}
|
|
559
604
|
});
|
|
560
605
|
channel.scheduledNotes.clear();
|
|
561
606
|
await Promise.all(this.notePromises);
|
|
562
607
|
}
|
|
563
|
-
stopNotes(velocity,
|
|
608
|
+
stopNotes(velocity, force) {
|
|
564
609
|
for (let i = 0; i < this.channels.length; i++) {
|
|
565
|
-
this.stopChannelNotes(i, velocity,
|
|
610
|
+
this.stopChannelNotes(i, velocity, force);
|
|
566
611
|
}
|
|
567
612
|
return Promise.all(this.notePromises);
|
|
568
613
|
}
|
|
@@ -654,14 +699,14 @@ class MidyGM2 {
|
|
|
654
699
|
return impulse;
|
|
655
700
|
}
|
|
656
701
|
createConvolutionReverb(audioContext, impulse) {
|
|
657
|
-
const
|
|
702
|
+
const input = new GainNode(audioContext);
|
|
658
703
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
659
704
|
buffer: impulse,
|
|
660
705
|
});
|
|
661
|
-
|
|
706
|
+
input.connect(convolverNode);
|
|
662
707
|
return {
|
|
663
|
-
input
|
|
664
|
-
output,
|
|
708
|
+
input,
|
|
709
|
+
output: convolverNode,
|
|
665
710
|
convolverNode,
|
|
666
711
|
};
|
|
667
712
|
}
|
|
@@ -703,7 +748,6 @@ class MidyGM2 {
|
|
|
703
748
|
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
704
749
|
createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
|
|
705
750
|
const input = new GainNode(audioContext);
|
|
706
|
-
const output = new GainNode(audioContext);
|
|
707
751
|
const mergerGain = new GainNode(audioContext);
|
|
708
752
|
for (let i = 0; i < combDelays.length; i++) {
|
|
709
753
|
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
@@ -714,7 +758,7 @@ class MidyGM2 {
|
|
|
714
758
|
const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
|
|
715
759
|
allpasses.push(allpass);
|
|
716
760
|
}
|
|
717
|
-
allpasses.at(-1)
|
|
761
|
+
const output = allpasses.at(-1);
|
|
718
762
|
return { input, output };
|
|
719
763
|
}
|
|
720
764
|
createChorusEffect(audioContext) {
|
|
@@ -776,6 +820,17 @@ class MidyGM2 {
|
|
|
776
820
|
return instrumentKey.playbackRate(noteNumber) *
|
|
777
821
|
Math.pow(2, semitoneOffset / 12);
|
|
778
822
|
}
|
|
823
|
+
setPortamentoStartVolumeEnvelope(channel, note) {
|
|
824
|
+
const { instrumentKey, startTime } = note;
|
|
825
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
826
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
827
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
828
|
+
const portamentoTime = volDelay + channel.portamentoTime;
|
|
829
|
+
note.volumeNode.gain
|
|
830
|
+
.cancelScheduledValues(startTime)
|
|
831
|
+
.setValueAtTime(0, volDelay)
|
|
832
|
+
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
833
|
+
}
|
|
779
834
|
setVolumeEnvelope(note) {
|
|
780
835
|
const { instrumentKey, startTime } = note;
|
|
781
836
|
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
@@ -815,6 +870,25 @@ class MidyGM2 {
|
|
|
815
870
|
const maxFrequency = 20000; // max Hz of initialFilterFc
|
|
816
871
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
817
872
|
}
|
|
873
|
+
setPortamentoStartFilterEnvelope(channel, note) {
|
|
874
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
875
|
+
const softPedalFactor = 1 -
|
|
876
|
+
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
877
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
878
|
+
softPedalFactor;
|
|
879
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
880
|
+
const sustainFreq = baseFreq +
|
|
881
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
882
|
+
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
883
|
+
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
884
|
+
const portamentoTime = startTime + channel.portamentoTime;
|
|
885
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
886
|
+
note.filterNode.frequency
|
|
887
|
+
.cancelScheduledValues(startTime)
|
|
888
|
+
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
889
|
+
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
890
|
+
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
891
|
+
}
|
|
818
892
|
setFilterEnvelope(channel, note) {
|
|
819
893
|
const { instrumentKey, noteNumber, startTime } = note;
|
|
820
894
|
const softPedalFactor = 1 -
|
|
@@ -882,17 +956,23 @@ class MidyGM2 {
|
|
|
882
956
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
883
957
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
884
958
|
}
|
|
885
|
-
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
959
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3) {
|
|
886
960
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
887
961
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
888
962
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
889
963
|
note.volumeNode = new GainNode(this.audioContext);
|
|
890
964
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
891
965
|
type: "lowpass",
|
|
892
|
-
Q: instrumentKey.initialFilterQ / 10
|
|
966
|
+
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
893
967
|
});
|
|
894
|
-
|
|
895
|
-
|
|
968
|
+
if (portamento) {
|
|
969
|
+
this.setPortamentoStartVolumeEnvelope(channel, note);
|
|
970
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
971
|
+
}
|
|
972
|
+
else {
|
|
973
|
+
this.setVolumeEnvelope(note);
|
|
974
|
+
this.setFilterEnvelope(channel, note);
|
|
975
|
+
}
|
|
896
976
|
if (0 < channel.vibratoDepth) {
|
|
897
977
|
this.startVibrato(channel, note, startTime);
|
|
898
978
|
}
|
|
@@ -909,7 +989,21 @@ class MidyGM2 {
|
|
|
909
989
|
}
|
|
910
990
|
note.bufferSource.connect(note.filterNode);
|
|
911
991
|
note.filterNode.connect(note.volumeNode);
|
|
912
|
-
|
|
992
|
+
if (0 < channel.reverbSendLevel && 0 < instrumentKey.reverbEffectsSend) {
|
|
993
|
+
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
994
|
+
gain: instrumentKey.reverbEffectsSend,
|
|
995
|
+
});
|
|
996
|
+
note.volumeNode.connect(note.reverbEffectsSend);
|
|
997
|
+
note.reverbEffectsSend.connect(this.reverbEffect.input);
|
|
998
|
+
}
|
|
999
|
+
if (0 < channel.chorusSendLevel && 0 < instrumentKey.chorusEffectsSend) {
|
|
1000
|
+
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1001
|
+
gain: instrumentKey.chorusEffectsSend,
|
|
1002
|
+
});
|
|
1003
|
+
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1004
|
+
note.chorusEffectsSend.connect(this.chorusEffect.input);
|
|
1005
|
+
}
|
|
1006
|
+
note.bufferSource.start(startTime);
|
|
913
1007
|
return note;
|
|
914
1008
|
}
|
|
915
1009
|
calcBank(channel, channelNumber) {
|
|
@@ -921,7 +1015,7 @@ class MidyGM2 {
|
|
|
921
1015
|
}
|
|
922
1016
|
return channel.bank;
|
|
923
1017
|
}
|
|
924
|
-
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
1018
|
+
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, portamento) {
|
|
925
1019
|
const channel = this.channels[channelNumber];
|
|
926
1020
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
927
1021
|
const soundFontIndex = this.soundFontTable[channel.program].get(bankNumber);
|
|
@@ -932,12 +1026,25 @@ class MidyGM2 {
|
|
|
932
1026
|
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber, velocity);
|
|
933
1027
|
if (!instrumentKey)
|
|
934
1028
|
return;
|
|
935
|
-
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
1029
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3);
|
|
936
1030
|
note.volumeNode.connect(channel.gainL);
|
|
937
1031
|
note.volumeNode.connect(channel.gainR);
|
|
938
1032
|
if (channel.sostenutoPedal) {
|
|
939
1033
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
940
1034
|
}
|
|
1035
|
+
const exclusiveClass = instrumentKey.exclusiveClass;
|
|
1036
|
+
if (exclusiveClass !== 0) {
|
|
1037
|
+
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
1038
|
+
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
1039
|
+
const [prevNote, prevChannelNumber] = prevEntry;
|
|
1040
|
+
if (!prevNote.ending) {
|
|
1041
|
+
this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1042
|
+
startTime, undefined, // portamentoNoteNumber
|
|
1043
|
+
true);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
1047
|
+
}
|
|
941
1048
|
const scheduledNotes = channel.scheduledNotes;
|
|
942
1049
|
if (scheduledNotes.has(noteNumber)) {
|
|
943
1050
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -946,13 +1053,48 @@ class MidyGM2 {
|
|
|
946
1053
|
scheduledNotes.set(noteNumber, [note]);
|
|
947
1054
|
}
|
|
948
1055
|
}
|
|
949
|
-
noteOn(channelNumber, noteNumber, velocity) {
|
|
1056
|
+
noteOn(channelNumber, noteNumber, velocity, portamento) {
|
|
950
1057
|
const now = this.audioContext.currentTime;
|
|
951
|
-
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
|
|
1058
|
+
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now, portamento);
|
|
1059
|
+
}
|
|
1060
|
+
stopNote(endTime, stopTime, scheduledNotes, index) {
|
|
1061
|
+
const note = scheduledNotes[index];
|
|
1062
|
+
note.volumeNode.gain
|
|
1063
|
+
.cancelScheduledValues(endTime)
|
|
1064
|
+
.linearRampToValueAtTime(0, stopTime);
|
|
1065
|
+
note.ending = true;
|
|
1066
|
+
this.scheduleTask(() => {
|
|
1067
|
+
note.bufferSource.loop = false;
|
|
1068
|
+
}, stopTime);
|
|
1069
|
+
return new Promise((resolve) => {
|
|
1070
|
+
note.bufferSource.onended = () => {
|
|
1071
|
+
scheduledNotes[index] = null;
|
|
1072
|
+
note.bufferSource.disconnect();
|
|
1073
|
+
note.volumeNode.disconnect();
|
|
1074
|
+
note.filterNode.disconnect();
|
|
1075
|
+
if (note.modulationDepth) {
|
|
1076
|
+
note.volumeDepth.disconnect();
|
|
1077
|
+
note.modulationDepth.disconnect();
|
|
1078
|
+
note.modulationLFO.stop();
|
|
1079
|
+
}
|
|
1080
|
+
if (note.vibratoDepth) {
|
|
1081
|
+
note.vibratoDepth.disconnect();
|
|
1082
|
+
note.vibratoLFO.stop();
|
|
1083
|
+
}
|
|
1084
|
+
if (note.reverbEffectsSend) {
|
|
1085
|
+
note.reverbEffectsSend.disconnect();
|
|
1086
|
+
}
|
|
1087
|
+
if (note.chorusEffectsSend) {
|
|
1088
|
+
note.chorusEffectsSend.disconnect();
|
|
1089
|
+
}
|
|
1090
|
+
resolve();
|
|
1091
|
+
};
|
|
1092
|
+
note.bufferSource.stop(stopTime);
|
|
1093
|
+
});
|
|
952
1094
|
}
|
|
953
|
-
scheduleNoteRelease(channelNumber, noteNumber, _velocity,
|
|
1095
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, portamentoNoteNumber, force) {
|
|
954
1096
|
const channel = this.channels[channelNumber];
|
|
955
|
-
if (
|
|
1097
|
+
if (!force) {
|
|
956
1098
|
if (channel.sustainPedal)
|
|
957
1099
|
return;
|
|
958
1100
|
if (channel.sostenutoNotes.has(noteNumber))
|
|
@@ -967,38 +1109,29 @@ class MidyGM2 {
|
|
|
967
1109
|
continue;
|
|
968
1110
|
if (note.ending)
|
|
969
1111
|
continue;
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
.
|
|
973
|
-
.
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
.
|
|
977
|
-
.
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
note.bufferSource.
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
if (note.modulationDepth) {
|
|
989
|
-
note.volumeDepth.disconnect();
|
|
990
|
-
note.modulationDepth.disconnect();
|
|
991
|
-
note.modulationLFO.stop();
|
|
992
|
-
}
|
|
993
|
-
resolve();
|
|
994
|
-
};
|
|
995
|
-
note.bufferSource.stop(volEndTime);
|
|
996
|
-
});
|
|
1112
|
+
if (portamentoNoteNumber === undefined) {
|
|
1113
|
+
const volRelease = endTime + note.instrumentKey.volRelease;
|
|
1114
|
+
const modRelease = endTime + note.instrumentKey.modRelease;
|
|
1115
|
+
note.filterNode.frequency
|
|
1116
|
+
.cancelScheduledValues(endTime)
|
|
1117
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
1118
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
1119
|
+
return this.stopNote(endTime, stopTime, scheduledNotes, i);
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
const portamentoTime = endTime + channel.portamentoTime;
|
|
1123
|
+
const detuneChange = (portamentoNoteNumber - noteNumber) * 100;
|
|
1124
|
+
const detune = note.bufferSource.detune.value + detuneChange;
|
|
1125
|
+
note.bufferSource.detune
|
|
1126
|
+
.cancelScheduledValues(endTime)
|
|
1127
|
+
.linearRampToValueAtTime(detune, portamentoTime);
|
|
1128
|
+
return this.stopNote(endTime, portamentoTime, scheduledNotes, i);
|
|
1129
|
+
}
|
|
997
1130
|
}
|
|
998
1131
|
}
|
|
999
|
-
releaseNote(channelNumber, noteNumber, velocity) {
|
|
1132
|
+
releaseNote(channelNumber, noteNumber, velocity, portamentoNoteNumber) {
|
|
1000
1133
|
const now = this.audioContext.currentTime;
|
|
1001
|
-
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
1134
|
+
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, portamentoNoteNumber, false);
|
|
1002
1135
|
}
|
|
1003
1136
|
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
1004
1137
|
const velocity = halfVelocity * 2;
|
|
@@ -1146,12 +1279,14 @@ class MidyGM2 {
|
|
|
1146
1279
|
this.updateModulation(channel);
|
|
1147
1280
|
}
|
|
1148
1281
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1149
|
-
this.channels[channelNumber]
|
|
1282
|
+
const channel = this.channels[channelNumber];
|
|
1283
|
+
const factor = 5 * Math.log(10) / 127;
|
|
1284
|
+
channel.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1150
1285
|
}
|
|
1151
1286
|
setVolume(channelNumber, volume) {
|
|
1152
1287
|
const channel = this.channels[channelNumber];
|
|
1153
1288
|
channel.volume = volume / 127;
|
|
1154
|
-
this.
|
|
1289
|
+
this.updateChannelVolume(channel);
|
|
1155
1290
|
}
|
|
1156
1291
|
panToGain(pan) {
|
|
1157
1292
|
const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
|
|
@@ -1163,12 +1298,12 @@ class MidyGM2 {
|
|
|
1163
1298
|
setPan(channelNumber, pan) {
|
|
1164
1299
|
const channel = this.channels[channelNumber];
|
|
1165
1300
|
channel.pan = pan;
|
|
1166
|
-
this.
|
|
1301
|
+
this.updateChannelVolume(channel);
|
|
1167
1302
|
}
|
|
1168
1303
|
setExpression(channelNumber, expression) {
|
|
1169
1304
|
const channel = this.channels[channelNumber];
|
|
1170
1305
|
channel.expression = expression / 127;
|
|
1171
|
-
this.
|
|
1306
|
+
this.updateChannelVolume(channel);
|
|
1172
1307
|
}
|
|
1173
1308
|
setBankLSB(channelNumber, lsb) {
|
|
1174
1309
|
this.channels[channelNumber].bankLSB = lsb;
|
|
@@ -1177,7 +1312,7 @@ class MidyGM2 {
|
|
|
1177
1312
|
this.channels[channelNumber].dataLSB = value;
|
|
1178
1313
|
this.handleRPN(channelNumber);
|
|
1179
1314
|
}
|
|
1180
|
-
|
|
1315
|
+
updateChannelVolume(channel) {
|
|
1181
1316
|
const now = this.audioContext.currentTime;
|
|
1182
1317
|
const volume = channel.volume * channel.expression;
|
|
1183
1318
|
const { gainLeft, gainRight } = this.panToGain(channel.pan);
|
|
@@ -1195,30 +1330,54 @@ class MidyGM2 {
|
|
|
1195
1330
|
this.releaseSustainPedal(channelNumber, value);
|
|
1196
1331
|
}
|
|
1197
1332
|
}
|
|
1198
|
-
// TODO
|
|
1199
1333
|
setPortamento(channelNumber, value) {
|
|
1200
1334
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1201
1335
|
}
|
|
1202
1336
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1203
1337
|
const channel = this.channels[channelNumber];
|
|
1338
|
+
const reverbEffect = this.reverbEffect;
|
|
1204
1339
|
if (0 < channel.reverbSendLevel) {
|
|
1205
1340
|
if (0 < reverbSendLevel) {
|
|
1206
1341
|
const now = this.audioContext.currentTime;
|
|
1207
1342
|
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1208
|
-
reverbEffect.
|
|
1209
|
-
reverbEffect.
|
|
1343
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1344
|
+
reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1210
1345
|
}
|
|
1211
1346
|
else {
|
|
1212
|
-
channel.
|
|
1347
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1348
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1349
|
+
const note = noteList[i];
|
|
1350
|
+
if (!note)
|
|
1351
|
+
continue;
|
|
1352
|
+
if (note.instrumentKey.reverbEffectsSend <= 0)
|
|
1353
|
+
continue;
|
|
1354
|
+
note.reverbEffectsSend.disconnect();
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1213
1357
|
}
|
|
1214
1358
|
}
|
|
1215
1359
|
else {
|
|
1216
1360
|
if (0 < reverbSendLevel) {
|
|
1217
|
-
channel.merger.connect(reverbEffect.input);
|
|
1218
1361
|
const now = this.audioContext.currentTime;
|
|
1362
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1363
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1364
|
+
const note = noteList[i];
|
|
1365
|
+
if (!note)
|
|
1366
|
+
continue;
|
|
1367
|
+
if (note.instrumentKey.reverbEffectsSend <= 0)
|
|
1368
|
+
continue;
|
|
1369
|
+
if (!note.reverbEffectsSend) {
|
|
1370
|
+
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1371
|
+
gain: note.instrumentKey.reverbEffectsSend,
|
|
1372
|
+
});
|
|
1373
|
+
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1374
|
+
}
|
|
1375
|
+
note.reverbEffectsSend.connect(reverbEffect.input);
|
|
1376
|
+
}
|
|
1377
|
+
});
|
|
1219
1378
|
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1220
|
-
reverbEffect.
|
|
1221
|
-
reverbEffect.
|
|
1379
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1380
|
+
reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1222
1381
|
}
|
|
1223
1382
|
}
|
|
1224
1383
|
}
|
|
@@ -1229,20 +1388,44 @@ class MidyGM2 {
|
|
|
1229
1388
|
if (0 < chorusSendLevel) {
|
|
1230
1389
|
const now = this.audioContext.currentTime;
|
|
1231
1390
|
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1232
|
-
chorusEffect.
|
|
1233
|
-
chorusEffect.
|
|
1391
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1392
|
+
chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1234
1393
|
}
|
|
1235
1394
|
else {
|
|
1236
|
-
channel.
|
|
1395
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1396
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1397
|
+
const note = noteList[i];
|
|
1398
|
+
if (!note)
|
|
1399
|
+
continue;
|
|
1400
|
+
if (note.instrumentKey.chorusEffectsSend <= 0)
|
|
1401
|
+
continue;
|
|
1402
|
+
note.chorusEffectsSend.disconnect();
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1237
1405
|
}
|
|
1238
1406
|
}
|
|
1239
1407
|
else {
|
|
1240
1408
|
if (0 < chorusSendLevel) {
|
|
1241
|
-
channel.merger.connect(chorusEffect.input);
|
|
1242
1409
|
const now = this.audioContext.currentTime;
|
|
1410
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1411
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1412
|
+
const note = noteList[i];
|
|
1413
|
+
if (!note)
|
|
1414
|
+
continue;
|
|
1415
|
+
if (note.instrumentKey.chorusEffectsSend <= 0)
|
|
1416
|
+
continue;
|
|
1417
|
+
if (!note.chorusEffectsSend) {
|
|
1418
|
+
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1419
|
+
gain: note.instrumentKey.chorusEffectsSend,
|
|
1420
|
+
});
|
|
1421
|
+
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1422
|
+
}
|
|
1423
|
+
note.chorusEffectsSend.connect(chorusEffect.input);
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1243
1426
|
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1244
|
-
chorusEffect.
|
|
1245
|
-
chorusEffect.
|
|
1427
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1428
|
+
chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1246
1429
|
}
|
|
1247
1430
|
}
|
|
1248
1431
|
}
|
|
@@ -1428,20 +1611,22 @@ class MidyGM2 {
|
|
|
1428
1611
|
}
|
|
1429
1612
|
}
|
|
1430
1613
|
GM1SystemOn() {
|
|
1431
|
-
this.channels.
|
|
1614
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1615
|
+
const channel = this.channels[i];
|
|
1432
1616
|
channel.bankMSB = 0;
|
|
1433
1617
|
channel.bankLSB = 0;
|
|
1434
1618
|
channel.bank = 0;
|
|
1435
|
-
}
|
|
1619
|
+
}
|
|
1436
1620
|
this.channels[9].bankMSB = 1;
|
|
1437
1621
|
this.channels[9].bank = 128;
|
|
1438
1622
|
}
|
|
1439
1623
|
GM2SystemOn() {
|
|
1440
|
-
this.channels.
|
|
1624
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1625
|
+
const channel = this.channels[i];
|
|
1441
1626
|
channel.bankMSB = 121;
|
|
1442
1627
|
channel.bankLSB = 0;
|
|
1443
1628
|
channel.bank = 121 * 128;
|
|
1444
|
-
}
|
|
1629
|
+
}
|
|
1445
1630
|
this.channels[9].bankMSB = 120;
|
|
1446
1631
|
this.channels[9].bank = 120 * 128;
|
|
1447
1632
|
}
|
|
@@ -1582,10 +1767,8 @@ class MidyGM2 {
|
|
|
1582
1767
|
}
|
|
1583
1768
|
setReverbTime(value) {
|
|
1584
1769
|
this.reverb.time = this.getReverbTime(value);
|
|
1585
|
-
const { audioContext,
|
|
1586
|
-
|
|
1587
|
-
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1588
|
-
}
|
|
1770
|
+
const { audioContext, options } = this;
|
|
1771
|
+
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1589
1772
|
}
|
|
1590
1773
|
getReverbTime(value) {
|
|
1591
1774
|
return Math.pow(Math.E, (value - 40) * 0.025);
|
|
@@ -1662,10 +1845,7 @@ class MidyGM2 {
|
|
|
1662
1845
|
const now = this.audioContext.currentTime;
|
|
1663
1846
|
const modRate = this.getChorusModRate(value);
|
|
1664
1847
|
this.chorus.modRate = modRate;
|
|
1665
|
-
|
|
1666
|
-
const lfo = this.channels[i].chorusEffect.lfo;
|
|
1667
|
-
lfo.frequency.setValueAtTime(modRate, now);
|
|
1668
|
-
}
|
|
1848
|
+
this.chorusEffect.lfo.frequency.setValueAtTime(modRate, now);
|
|
1669
1849
|
}
|
|
1670
1850
|
getChorusModRate(value) {
|
|
1671
1851
|
return value * 0.122; // Hz
|
|
@@ -1674,12 +1854,9 @@ class MidyGM2 {
|
|
|
1674
1854
|
const now = this.audioContext.currentTime;
|
|
1675
1855
|
const modDepth = this.getChorusModDepth(value);
|
|
1676
1856
|
this.chorus.modDepth = modDepth;
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
.cancelScheduledValues(now)
|
|
1681
|
-
.setValueAtTime(modDepth / 2, now);
|
|
1682
|
-
}
|
|
1857
|
+
this.chorusEffect.lfoGain.gain
|
|
1858
|
+
.cancelScheduledValues(now)
|
|
1859
|
+
.setValueAtTime(modDepth / 2, now);
|
|
1683
1860
|
}
|
|
1684
1861
|
getChorusModDepth(value) {
|
|
1685
1862
|
return (value + 1) / 3200; // second
|
|
@@ -1688,14 +1865,11 @@ class MidyGM2 {
|
|
|
1688
1865
|
const now = this.audioContext.currentTime;
|
|
1689
1866
|
const feedback = this.getChorusFeedback(value);
|
|
1690
1867
|
this.chorus.feedback = feedback;
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
.cancelScheduledValues(now)
|
|
1697
|
-
.setValueAtTime(feedback, now);
|
|
1698
|
-
}
|
|
1868
|
+
const chorusEffect = this.chorusEffect;
|
|
1869
|
+
for (let i = 0; i < chorusEffect.feedbackGains.length; i++) {
|
|
1870
|
+
chorusEffect.feedbackGains[i].gain
|
|
1871
|
+
.cancelScheduledValues(now)
|
|
1872
|
+
.setValueAtTime(feedback, now);
|
|
1699
1873
|
}
|
|
1700
1874
|
}
|
|
1701
1875
|
getChorusFeedback(value) {
|
|
@@ -1703,15 +1877,28 @@ class MidyGM2 {
|
|
|
1703
1877
|
}
|
|
1704
1878
|
setChorusSendToReverb(value) {
|
|
1705
1879
|
const sendToReverb = this.getChorusSendToReverb(value);
|
|
1706
|
-
|
|
1707
|
-
|
|
1880
|
+
const sendGain = this.chorusEffect.sendGain;
|
|
1881
|
+
if (0 < this.chorus.sendToReverb) {
|
|
1708
1882
|
this.chorus.sendToReverb = sendToReverb;
|
|
1709
|
-
|
|
1710
|
-
.
|
|
1711
|
-
.
|
|
1883
|
+
if (0 < sendToReverb) {
|
|
1884
|
+
const now = this.audioContext.currentTime;
|
|
1885
|
+
sendGain.gain
|
|
1886
|
+
.cancelScheduledValues(now)
|
|
1887
|
+
.setValueAtTime(sendToReverb, now);
|
|
1888
|
+
}
|
|
1889
|
+
else {
|
|
1890
|
+
sendGain.disconnect();
|
|
1891
|
+
}
|
|
1712
1892
|
}
|
|
1713
|
-
else
|
|
1714
|
-
this.
|
|
1893
|
+
else {
|
|
1894
|
+
this.chorus.sendToReverb = sendToReverb;
|
|
1895
|
+
if (0 < sendToReverb) {
|
|
1896
|
+
const now = this.audioContext.currentTime;
|
|
1897
|
+
sendGain.connect(this.reverbEffect.input);
|
|
1898
|
+
sendGain.gain
|
|
1899
|
+
.cancelScheduledValues(now)
|
|
1900
|
+
.setValueAtTime(sendToReverb, now);
|
|
1901
|
+
}
|
|
1715
1902
|
}
|
|
1716
1903
|
}
|
|
1717
1904
|
getChorusSendToReverb(value) {
|
|
@@ -1751,7 +1938,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1751
1938
|
currentBufferSource: null,
|
|
1752
1939
|
volume: 100 / 127,
|
|
1753
1940
|
pan: 64,
|
|
1754
|
-
portamentoTime:
|
|
1941
|
+
portamentoTime: 1, // sec
|
|
1755
1942
|
reverbSendLevel: 0,
|
|
1756
1943
|
chorusSendLevel: 0,
|
|
1757
1944
|
vibratoRate: 1,
|