@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/esm/midy-GM2.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { parseMidi } from "
|
|
2
|
-
import { parse, SoundFont
|
|
1
|
+
import { parseMidi } from "midi-file";
|
|
2
|
+
import { parse, SoundFont } from "@marmooo/soundfont-parser";
|
|
3
3
|
class Note {
|
|
4
4
|
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
5
5
|
Object.defineProperty(this, "bufferSource", {
|
|
@@ -50,6 +50,18 @@ class Note {
|
|
|
50
50
|
writable: true,
|
|
51
51
|
value: void 0
|
|
52
52
|
});
|
|
53
|
+
Object.defineProperty(this, "reverbEffectsSend", {
|
|
54
|
+
enumerable: true,
|
|
55
|
+
configurable: true,
|
|
56
|
+
writable: true,
|
|
57
|
+
value: void 0
|
|
58
|
+
});
|
|
59
|
+
Object.defineProperty(this, "chorusEffectsSend", {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
configurable: true,
|
|
62
|
+
writable: true,
|
|
63
|
+
value: void 0
|
|
64
|
+
});
|
|
53
65
|
this.noteNumber = noteNumber;
|
|
54
66
|
this.velocity = velocity;
|
|
55
67
|
this.startTime = startTime;
|
|
@@ -205,6 +217,12 @@ export class MidyGM2 {
|
|
|
205
217
|
writable: true,
|
|
206
218
|
value: []
|
|
207
219
|
});
|
|
220
|
+
Object.defineProperty(this, "exclusiveClassMap", {
|
|
221
|
+
enumerable: true,
|
|
222
|
+
configurable: true,
|
|
223
|
+
writable: true,
|
|
224
|
+
value: new Map()
|
|
225
|
+
});
|
|
208
226
|
Object.defineProperty(this, "defaultOptions", {
|
|
209
227
|
enumerable: true,
|
|
210
228
|
configurable: true,
|
|
@@ -249,12 +267,14 @@ export class MidyGM2 {
|
|
|
249
267
|
addSoundFont(soundFont) {
|
|
250
268
|
const index = this.soundFonts.length;
|
|
251
269
|
this.soundFonts.push(soundFont);
|
|
252
|
-
soundFont.parsed.presetHeaders
|
|
270
|
+
const presetHeaders = soundFont.parsed.presetHeaders;
|
|
271
|
+
for (let i = 0; i < presetHeaders.length; i++) {
|
|
272
|
+
const presetHeader = presetHeaders[i];
|
|
253
273
|
if (!presetHeader.presetName.startsWith("\u0000")) { // TODO: Only SF3 generated by PolyPone?
|
|
254
274
|
const banks = this.soundFontTable[presetHeader.preset];
|
|
255
275
|
banks.set(presetHeader.bank, index);
|
|
256
276
|
}
|
|
257
|
-
}
|
|
277
|
+
}
|
|
258
278
|
}
|
|
259
279
|
async loadSoundFont(soundFontUrl) {
|
|
260
280
|
const response = await fetch(soundFontUrl);
|
|
@@ -303,27 +323,31 @@ export class MidyGM2 {
|
|
|
303
323
|
return channels;
|
|
304
324
|
}
|
|
305
325
|
async createNoteBuffer(instrumentKey, isSF3) {
|
|
326
|
+
const sampleStart = instrumentKey.start;
|
|
306
327
|
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
307
328
|
if (isSF3) {
|
|
308
|
-
const sample =
|
|
309
|
-
sample.
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
channelData.set(channelData.subarray(0, sampleEnd));
|
|
314
|
-
}
|
|
329
|
+
const sample = instrumentKey.sample;
|
|
330
|
+
const start = sample.byteOffset + sampleStart;
|
|
331
|
+
const end = sample.byteOffset + sampleEnd;
|
|
332
|
+
const buffer = sample.buffer.slice(start, end);
|
|
333
|
+
const audioBuffer = await this.audioContext.decodeAudioData(buffer);
|
|
315
334
|
return audioBuffer;
|
|
316
335
|
}
|
|
317
336
|
else {
|
|
318
|
-
const sample = instrumentKey.sample
|
|
319
|
-
const
|
|
337
|
+
const sample = instrumentKey.sample;
|
|
338
|
+
const start = sample.byteOffset + sampleStart;
|
|
339
|
+
const end = sample.byteOffset + sampleEnd;
|
|
340
|
+
const buffer = sample.buffer.slice(start, end);
|
|
320
341
|
const audioBuffer = new AudioBuffer({
|
|
321
342
|
numberOfChannels: 1,
|
|
322
343
|
length: sample.length,
|
|
323
344
|
sampleRate: instrumentKey.sampleRate,
|
|
324
345
|
});
|
|
325
346
|
const channelData = audioBuffer.getChannelData(0);
|
|
326
|
-
|
|
347
|
+
const int16Array = new Int16Array(buffer);
|
|
348
|
+
for (let i = 0; i < int16Array.length; i++) {
|
|
349
|
+
channelData[i] = int16Array[i] / 32768;
|
|
350
|
+
}
|
|
327
351
|
return audioBuffer;
|
|
328
352
|
}
|
|
329
353
|
}
|
|
@@ -339,13 +363,23 @@ export class MidyGM2 {
|
|
|
339
363
|
}
|
|
340
364
|
return bufferSource;
|
|
341
365
|
}
|
|
342
|
-
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
366
|
+
findPortamentoTarget(queueIndex) {
|
|
367
|
+
const endEvent = this.timeline[queueIndex];
|
|
368
|
+
if (!this.channels[endEvent.channel].portamento)
|
|
369
|
+
return;
|
|
370
|
+
const endTime = endEvent.startTime;
|
|
371
|
+
let target;
|
|
372
|
+
while (++queueIndex < this.timeline.length) {
|
|
373
|
+
const event = this.timeline[queueIndex];
|
|
374
|
+
if (endTime !== event.startTime)
|
|
375
|
+
break;
|
|
376
|
+
if (event.type !== "noteOn")
|
|
377
|
+
continue;
|
|
378
|
+
if (!target || event.noteNumber < target.noteNumber) {
|
|
379
|
+
target = event;
|
|
380
|
+
}
|
|
347
381
|
}
|
|
348
|
-
return
|
|
382
|
+
return target;
|
|
349
383
|
}
|
|
350
384
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
351
385
|
while (queueIndex < this.timeline.length) {
|
|
@@ -355,12 +389,15 @@ export class MidyGM2 {
|
|
|
355
389
|
switch (event.type) {
|
|
356
390
|
case "noteOn":
|
|
357
391
|
if (event.velocity !== 0) {
|
|
358
|
-
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
392
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, event.portamento);
|
|
359
393
|
break;
|
|
360
394
|
}
|
|
361
395
|
/* falls through */
|
|
362
396
|
case "noteOff": {
|
|
363
|
-
const
|
|
397
|
+
const portamentoTarget = this.findPortamentoTarget(queueIndex);
|
|
398
|
+
if (portamentoTarget)
|
|
399
|
+
portamentoTarget.portamento = true;
|
|
400
|
+
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, portamentoTarget?.noteNumber, false);
|
|
364
401
|
if (notePromise) {
|
|
365
402
|
this.notePromises.push(notePromise);
|
|
366
403
|
}
|
|
@@ -405,6 +442,7 @@ export class MidyGM2 {
|
|
|
405
442
|
if (queueIndex >= this.timeline.length) {
|
|
406
443
|
await Promise.all(this.notePromises);
|
|
407
444
|
this.notePromises = [];
|
|
445
|
+
this.exclusiveClassMap.clear();
|
|
408
446
|
resolve();
|
|
409
447
|
return;
|
|
410
448
|
}
|
|
@@ -420,6 +458,7 @@ export class MidyGM2 {
|
|
|
420
458
|
}
|
|
421
459
|
else if (this.isStopping) {
|
|
422
460
|
await this.stopNotes(0, true);
|
|
461
|
+
this.exclusiveClassMap.clear();
|
|
423
462
|
this.notePromises = [];
|
|
424
463
|
resolve();
|
|
425
464
|
this.isStopping = false;
|
|
@@ -428,6 +467,7 @@ export class MidyGM2 {
|
|
|
428
467
|
}
|
|
429
468
|
else if (this.isSeeking) {
|
|
430
469
|
this.stopNotes(0, true);
|
|
470
|
+
this.exclusiveClassMap.clear();
|
|
431
471
|
this.startTime = this.audioContext.currentTime;
|
|
432
472
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
433
473
|
offset = this.resumeTime - this.startTime;
|
|
@@ -461,9 +501,11 @@ export class MidyGM2 {
|
|
|
461
501
|
bankLSB: this.channels[i].bankLSB,
|
|
462
502
|
};
|
|
463
503
|
}
|
|
464
|
-
midi.tracks.
|
|
504
|
+
for (let i = 0; i < midi.tracks.length; i++) {
|
|
505
|
+
const track = midi.tracks[i];
|
|
465
506
|
let currentTicks = 0;
|
|
466
|
-
track.
|
|
507
|
+
for (let j = 0; j < track.length; j++) {
|
|
508
|
+
const event = track[j];
|
|
467
509
|
currentTicks += event.deltaTime;
|
|
468
510
|
event.ticks = currentTicks;
|
|
469
511
|
switch (event.type) {
|
|
@@ -516,16 +558,18 @@ export class MidyGM2 {
|
|
|
516
558
|
}
|
|
517
559
|
delete event.deltaTime;
|
|
518
560
|
timeline.push(event);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
521
563
|
const priority = {
|
|
522
564
|
controller: 0,
|
|
523
565
|
sysEx: 1,
|
|
566
|
+
noteOff: 2, // for portamento
|
|
567
|
+
noteOn: 3,
|
|
524
568
|
};
|
|
525
569
|
timeline.sort((a, b) => {
|
|
526
570
|
if (a.ticks !== b.ticks)
|
|
527
571
|
return a.ticks - b.ticks;
|
|
528
|
-
return (priority[a.type] ||
|
|
572
|
+
return (priority[a.type] || 4) - (priority[b.type] || 4);
|
|
529
573
|
});
|
|
530
574
|
let prevTempoTime = 0;
|
|
531
575
|
let prevTempoTicks = 0;
|
|
@@ -542,7 +586,7 @@ export class MidyGM2 {
|
|
|
542
586
|
}
|
|
543
587
|
return { instruments, timeline };
|
|
544
588
|
}
|
|
545
|
-
async stopChannelNotes(channelNumber, velocity,
|
|
589
|
+
async stopChannelNotes(channelNumber, velocity, force) {
|
|
546
590
|
const now = this.audioContext.currentTime;
|
|
547
591
|
const channel = this.channels[channelNumber];
|
|
548
592
|
channel.scheduledNotes.forEach((noteList) => {
|
|
@@ -550,16 +594,17 @@ export class MidyGM2 {
|
|
|
550
594
|
const note = noteList[i];
|
|
551
595
|
if (!note)
|
|
552
596
|
continue;
|
|
553
|
-
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now,
|
|
597
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, undefined, // portamentoNoteNumber
|
|
598
|
+
force);
|
|
554
599
|
this.notePromises.push(promise);
|
|
555
600
|
}
|
|
556
601
|
});
|
|
557
602
|
channel.scheduledNotes.clear();
|
|
558
603
|
await Promise.all(this.notePromises);
|
|
559
604
|
}
|
|
560
|
-
stopNotes(velocity,
|
|
605
|
+
stopNotes(velocity, force) {
|
|
561
606
|
for (let i = 0; i < this.channels.length; i++) {
|
|
562
|
-
this.stopChannelNotes(i, velocity,
|
|
607
|
+
this.stopChannelNotes(i, velocity, force);
|
|
563
608
|
}
|
|
564
609
|
return Promise.all(this.notePromises);
|
|
565
610
|
}
|
|
@@ -651,14 +696,14 @@ export class MidyGM2 {
|
|
|
651
696
|
return impulse;
|
|
652
697
|
}
|
|
653
698
|
createConvolutionReverb(audioContext, impulse) {
|
|
654
|
-
const
|
|
699
|
+
const input = new GainNode(audioContext);
|
|
655
700
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
656
701
|
buffer: impulse,
|
|
657
702
|
});
|
|
658
|
-
|
|
703
|
+
input.connect(convolverNode);
|
|
659
704
|
return {
|
|
660
|
-
input
|
|
661
|
-
output,
|
|
705
|
+
input,
|
|
706
|
+
output: convolverNode,
|
|
662
707
|
convolverNode,
|
|
663
708
|
};
|
|
664
709
|
}
|
|
@@ -700,7 +745,6 @@ export class MidyGM2 {
|
|
|
700
745
|
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
701
746
|
createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
|
|
702
747
|
const input = new GainNode(audioContext);
|
|
703
|
-
const output = new GainNode(audioContext);
|
|
704
748
|
const mergerGain = new GainNode(audioContext);
|
|
705
749
|
for (let i = 0; i < combDelays.length; i++) {
|
|
706
750
|
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
@@ -711,7 +755,7 @@ export class MidyGM2 {
|
|
|
711
755
|
const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
|
|
712
756
|
allpasses.push(allpass);
|
|
713
757
|
}
|
|
714
|
-
allpasses.at(-1)
|
|
758
|
+
const output = allpasses.at(-1);
|
|
715
759
|
return { input, output };
|
|
716
760
|
}
|
|
717
761
|
createChorusEffect(audioContext) {
|
|
@@ -773,6 +817,17 @@ export class MidyGM2 {
|
|
|
773
817
|
return instrumentKey.playbackRate(noteNumber) *
|
|
774
818
|
Math.pow(2, semitoneOffset / 12);
|
|
775
819
|
}
|
|
820
|
+
setPortamentoStartVolumeEnvelope(channel, note) {
|
|
821
|
+
const { instrumentKey, startTime } = note;
|
|
822
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
823
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
824
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
825
|
+
const portamentoTime = volDelay + channel.portamentoTime;
|
|
826
|
+
note.volumeNode.gain
|
|
827
|
+
.cancelScheduledValues(startTime)
|
|
828
|
+
.setValueAtTime(0, volDelay)
|
|
829
|
+
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
830
|
+
}
|
|
776
831
|
setVolumeEnvelope(note) {
|
|
777
832
|
const { instrumentKey, startTime } = note;
|
|
778
833
|
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
@@ -812,6 +867,25 @@ export class MidyGM2 {
|
|
|
812
867
|
const maxFrequency = 20000; // max Hz of initialFilterFc
|
|
813
868
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
814
869
|
}
|
|
870
|
+
setPortamentoStartFilterEnvelope(channel, note) {
|
|
871
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
872
|
+
const softPedalFactor = 1 -
|
|
873
|
+
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
874
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
875
|
+
softPedalFactor;
|
|
876
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
877
|
+
const sustainFreq = baseFreq +
|
|
878
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
879
|
+
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
880
|
+
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
881
|
+
const portamentoTime = startTime + channel.portamentoTime;
|
|
882
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
883
|
+
note.filterNode.frequency
|
|
884
|
+
.cancelScheduledValues(startTime)
|
|
885
|
+
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
886
|
+
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
887
|
+
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
888
|
+
}
|
|
815
889
|
setFilterEnvelope(channel, note) {
|
|
816
890
|
const { instrumentKey, noteNumber, startTime } = note;
|
|
817
891
|
const softPedalFactor = 1 -
|
|
@@ -879,17 +953,23 @@ export class MidyGM2 {
|
|
|
879
953
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
880
954
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
881
955
|
}
|
|
882
|
-
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
956
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3) {
|
|
883
957
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
884
958
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
885
959
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
886
960
|
note.volumeNode = new GainNode(this.audioContext);
|
|
887
961
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
888
962
|
type: "lowpass",
|
|
889
|
-
Q: instrumentKey.initialFilterQ / 10
|
|
963
|
+
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
890
964
|
});
|
|
891
|
-
|
|
892
|
-
|
|
965
|
+
if (portamento) {
|
|
966
|
+
this.setPortamentoStartVolumeEnvelope(channel, note);
|
|
967
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
968
|
+
}
|
|
969
|
+
else {
|
|
970
|
+
this.setVolumeEnvelope(note);
|
|
971
|
+
this.setFilterEnvelope(channel, note);
|
|
972
|
+
}
|
|
893
973
|
if (0 < channel.vibratoDepth) {
|
|
894
974
|
this.startVibrato(channel, note, startTime);
|
|
895
975
|
}
|
|
@@ -906,7 +986,21 @@ export class MidyGM2 {
|
|
|
906
986
|
}
|
|
907
987
|
note.bufferSource.connect(note.filterNode);
|
|
908
988
|
note.filterNode.connect(note.volumeNode);
|
|
909
|
-
|
|
989
|
+
if (0 < channel.reverbSendLevel && 0 < instrumentKey.reverbEffectsSend) {
|
|
990
|
+
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
991
|
+
gain: instrumentKey.reverbEffectsSend,
|
|
992
|
+
});
|
|
993
|
+
note.volumeNode.connect(note.reverbEffectsSend);
|
|
994
|
+
note.reverbEffectsSend.connect(this.reverbEffect.input);
|
|
995
|
+
}
|
|
996
|
+
if (0 < channel.chorusSendLevel && 0 < instrumentKey.chorusEffectsSend) {
|
|
997
|
+
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
998
|
+
gain: instrumentKey.chorusEffectsSend,
|
|
999
|
+
});
|
|
1000
|
+
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1001
|
+
note.chorusEffectsSend.connect(this.chorusEffect.input);
|
|
1002
|
+
}
|
|
1003
|
+
note.bufferSource.start(startTime);
|
|
910
1004
|
return note;
|
|
911
1005
|
}
|
|
912
1006
|
calcBank(channel, channelNumber) {
|
|
@@ -918,7 +1012,7 @@ export class MidyGM2 {
|
|
|
918
1012
|
}
|
|
919
1013
|
return channel.bank;
|
|
920
1014
|
}
|
|
921
|
-
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
1015
|
+
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, portamento) {
|
|
922
1016
|
const channel = this.channels[channelNumber];
|
|
923
1017
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
924
1018
|
const soundFontIndex = this.soundFontTable[channel.program].get(bankNumber);
|
|
@@ -929,12 +1023,25 @@ export class MidyGM2 {
|
|
|
929
1023
|
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber, velocity);
|
|
930
1024
|
if (!instrumentKey)
|
|
931
1025
|
return;
|
|
932
|
-
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
1026
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3);
|
|
933
1027
|
note.volumeNode.connect(channel.gainL);
|
|
934
1028
|
note.volumeNode.connect(channel.gainR);
|
|
935
1029
|
if (channel.sostenutoPedal) {
|
|
936
1030
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
937
1031
|
}
|
|
1032
|
+
const exclusiveClass = instrumentKey.exclusiveClass;
|
|
1033
|
+
if (exclusiveClass !== 0) {
|
|
1034
|
+
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
1035
|
+
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
1036
|
+
const [prevNote, prevChannelNumber] = prevEntry;
|
|
1037
|
+
if (!prevNote.ending) {
|
|
1038
|
+
this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1039
|
+
startTime, undefined, // portamentoNoteNumber
|
|
1040
|
+
true);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
1044
|
+
}
|
|
938
1045
|
const scheduledNotes = channel.scheduledNotes;
|
|
939
1046
|
if (scheduledNotes.has(noteNumber)) {
|
|
940
1047
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -943,13 +1050,48 @@ export class MidyGM2 {
|
|
|
943
1050
|
scheduledNotes.set(noteNumber, [note]);
|
|
944
1051
|
}
|
|
945
1052
|
}
|
|
946
|
-
noteOn(channelNumber, noteNumber, velocity) {
|
|
1053
|
+
noteOn(channelNumber, noteNumber, velocity, portamento) {
|
|
947
1054
|
const now = this.audioContext.currentTime;
|
|
948
|
-
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
|
|
1055
|
+
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now, portamento);
|
|
1056
|
+
}
|
|
1057
|
+
stopNote(endTime, stopTime, scheduledNotes, index) {
|
|
1058
|
+
const note = scheduledNotes[index];
|
|
1059
|
+
note.volumeNode.gain
|
|
1060
|
+
.cancelScheduledValues(endTime)
|
|
1061
|
+
.linearRampToValueAtTime(0, stopTime);
|
|
1062
|
+
note.ending = true;
|
|
1063
|
+
this.scheduleTask(() => {
|
|
1064
|
+
note.bufferSource.loop = false;
|
|
1065
|
+
}, stopTime);
|
|
1066
|
+
return new Promise((resolve) => {
|
|
1067
|
+
note.bufferSource.onended = () => {
|
|
1068
|
+
scheduledNotes[index] = null;
|
|
1069
|
+
note.bufferSource.disconnect();
|
|
1070
|
+
note.volumeNode.disconnect();
|
|
1071
|
+
note.filterNode.disconnect();
|
|
1072
|
+
if (note.modulationDepth) {
|
|
1073
|
+
note.volumeDepth.disconnect();
|
|
1074
|
+
note.modulationDepth.disconnect();
|
|
1075
|
+
note.modulationLFO.stop();
|
|
1076
|
+
}
|
|
1077
|
+
if (note.vibratoDepth) {
|
|
1078
|
+
note.vibratoDepth.disconnect();
|
|
1079
|
+
note.vibratoLFO.stop();
|
|
1080
|
+
}
|
|
1081
|
+
if (note.reverbEffectsSend) {
|
|
1082
|
+
note.reverbEffectsSend.disconnect();
|
|
1083
|
+
}
|
|
1084
|
+
if (note.chorusEffectsSend) {
|
|
1085
|
+
note.chorusEffectsSend.disconnect();
|
|
1086
|
+
}
|
|
1087
|
+
resolve();
|
|
1088
|
+
};
|
|
1089
|
+
note.bufferSource.stop(stopTime);
|
|
1090
|
+
});
|
|
949
1091
|
}
|
|
950
|
-
scheduleNoteRelease(channelNumber, noteNumber, _velocity,
|
|
1092
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, portamentoNoteNumber, force) {
|
|
951
1093
|
const channel = this.channels[channelNumber];
|
|
952
|
-
if (
|
|
1094
|
+
if (!force) {
|
|
953
1095
|
if (channel.sustainPedal)
|
|
954
1096
|
return;
|
|
955
1097
|
if (channel.sostenutoNotes.has(noteNumber))
|
|
@@ -964,38 +1106,29 @@ export class MidyGM2 {
|
|
|
964
1106
|
continue;
|
|
965
1107
|
if (note.ending)
|
|
966
1108
|
continue;
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
.
|
|
970
|
-
.
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
.
|
|
974
|
-
.
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
note.bufferSource.
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
if (note.modulationDepth) {
|
|
986
|
-
note.volumeDepth.disconnect();
|
|
987
|
-
note.modulationDepth.disconnect();
|
|
988
|
-
note.modulationLFO.stop();
|
|
989
|
-
}
|
|
990
|
-
resolve();
|
|
991
|
-
};
|
|
992
|
-
note.bufferSource.stop(volEndTime);
|
|
993
|
-
});
|
|
1109
|
+
if (portamentoNoteNumber === undefined) {
|
|
1110
|
+
const volRelease = endTime + note.instrumentKey.volRelease;
|
|
1111
|
+
const modRelease = endTime + note.instrumentKey.modRelease;
|
|
1112
|
+
note.filterNode.frequency
|
|
1113
|
+
.cancelScheduledValues(endTime)
|
|
1114
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
1115
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
1116
|
+
return this.stopNote(endTime, stopTime, scheduledNotes, i);
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
const portamentoTime = endTime + channel.portamentoTime;
|
|
1120
|
+
const detuneChange = (portamentoNoteNumber - noteNumber) * 100;
|
|
1121
|
+
const detune = note.bufferSource.detune.value + detuneChange;
|
|
1122
|
+
note.bufferSource.detune
|
|
1123
|
+
.cancelScheduledValues(endTime)
|
|
1124
|
+
.linearRampToValueAtTime(detune, portamentoTime);
|
|
1125
|
+
return this.stopNote(endTime, portamentoTime, scheduledNotes, i);
|
|
1126
|
+
}
|
|
994
1127
|
}
|
|
995
1128
|
}
|
|
996
|
-
releaseNote(channelNumber, noteNumber, velocity) {
|
|
1129
|
+
releaseNote(channelNumber, noteNumber, velocity, portamentoNoteNumber) {
|
|
997
1130
|
const now = this.audioContext.currentTime;
|
|
998
|
-
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
1131
|
+
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, portamentoNoteNumber, false);
|
|
999
1132
|
}
|
|
1000
1133
|
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
1001
1134
|
const velocity = halfVelocity * 2;
|
|
@@ -1143,12 +1276,14 @@ export class MidyGM2 {
|
|
|
1143
1276
|
this.updateModulation(channel);
|
|
1144
1277
|
}
|
|
1145
1278
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1146
|
-
this.channels[channelNumber]
|
|
1279
|
+
const channel = this.channels[channelNumber];
|
|
1280
|
+
const factor = 5 * Math.log(10) / 127;
|
|
1281
|
+
channel.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1147
1282
|
}
|
|
1148
1283
|
setVolume(channelNumber, volume) {
|
|
1149
1284
|
const channel = this.channels[channelNumber];
|
|
1150
1285
|
channel.volume = volume / 127;
|
|
1151
|
-
this.
|
|
1286
|
+
this.updateChannelVolume(channel);
|
|
1152
1287
|
}
|
|
1153
1288
|
panToGain(pan) {
|
|
1154
1289
|
const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
|
|
@@ -1160,12 +1295,12 @@ export class MidyGM2 {
|
|
|
1160
1295
|
setPan(channelNumber, pan) {
|
|
1161
1296
|
const channel = this.channels[channelNumber];
|
|
1162
1297
|
channel.pan = pan;
|
|
1163
|
-
this.
|
|
1298
|
+
this.updateChannelVolume(channel);
|
|
1164
1299
|
}
|
|
1165
1300
|
setExpression(channelNumber, expression) {
|
|
1166
1301
|
const channel = this.channels[channelNumber];
|
|
1167
1302
|
channel.expression = expression / 127;
|
|
1168
|
-
this.
|
|
1303
|
+
this.updateChannelVolume(channel);
|
|
1169
1304
|
}
|
|
1170
1305
|
setBankLSB(channelNumber, lsb) {
|
|
1171
1306
|
this.channels[channelNumber].bankLSB = lsb;
|
|
@@ -1174,7 +1309,7 @@ export class MidyGM2 {
|
|
|
1174
1309
|
this.channels[channelNumber].dataLSB = value;
|
|
1175
1310
|
this.handleRPN(channelNumber);
|
|
1176
1311
|
}
|
|
1177
|
-
|
|
1312
|
+
updateChannelVolume(channel) {
|
|
1178
1313
|
const now = this.audioContext.currentTime;
|
|
1179
1314
|
const volume = channel.volume * channel.expression;
|
|
1180
1315
|
const { gainLeft, gainRight } = this.panToGain(channel.pan);
|
|
@@ -1192,30 +1327,54 @@ export class MidyGM2 {
|
|
|
1192
1327
|
this.releaseSustainPedal(channelNumber, value);
|
|
1193
1328
|
}
|
|
1194
1329
|
}
|
|
1195
|
-
// TODO
|
|
1196
1330
|
setPortamento(channelNumber, value) {
|
|
1197
1331
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1198
1332
|
}
|
|
1199
1333
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1200
1334
|
const channel = this.channels[channelNumber];
|
|
1335
|
+
const reverbEffect = this.reverbEffect;
|
|
1201
1336
|
if (0 < channel.reverbSendLevel) {
|
|
1202
1337
|
if (0 < reverbSendLevel) {
|
|
1203
1338
|
const now = this.audioContext.currentTime;
|
|
1204
1339
|
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1205
|
-
reverbEffect.
|
|
1206
|
-
reverbEffect.
|
|
1340
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1341
|
+
reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1207
1342
|
}
|
|
1208
1343
|
else {
|
|
1209
|
-
channel.
|
|
1344
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1345
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1346
|
+
const note = noteList[i];
|
|
1347
|
+
if (!note)
|
|
1348
|
+
continue;
|
|
1349
|
+
if (note.instrumentKey.reverbEffectsSend <= 0)
|
|
1350
|
+
continue;
|
|
1351
|
+
note.reverbEffectsSend.disconnect();
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1210
1354
|
}
|
|
1211
1355
|
}
|
|
1212
1356
|
else {
|
|
1213
1357
|
if (0 < reverbSendLevel) {
|
|
1214
|
-
channel.merger.connect(reverbEffect.input);
|
|
1215
1358
|
const now = this.audioContext.currentTime;
|
|
1359
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1360
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1361
|
+
const note = noteList[i];
|
|
1362
|
+
if (!note)
|
|
1363
|
+
continue;
|
|
1364
|
+
if (note.instrumentKey.reverbEffectsSend <= 0)
|
|
1365
|
+
continue;
|
|
1366
|
+
if (!note.reverbEffectsSend) {
|
|
1367
|
+
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1368
|
+
gain: note.instrumentKey.reverbEffectsSend,
|
|
1369
|
+
});
|
|
1370
|
+
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1371
|
+
}
|
|
1372
|
+
note.reverbEffectsSend.connect(reverbEffect.input);
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1216
1375
|
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1217
|
-
reverbEffect.
|
|
1218
|
-
reverbEffect.
|
|
1376
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1377
|
+
reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1219
1378
|
}
|
|
1220
1379
|
}
|
|
1221
1380
|
}
|
|
@@ -1226,20 +1385,44 @@ export class MidyGM2 {
|
|
|
1226
1385
|
if (0 < chorusSendLevel) {
|
|
1227
1386
|
const now = this.audioContext.currentTime;
|
|
1228
1387
|
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1229
|
-
chorusEffect.
|
|
1230
|
-
chorusEffect.
|
|
1388
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1389
|
+
chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1231
1390
|
}
|
|
1232
1391
|
else {
|
|
1233
|
-
channel.
|
|
1392
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1393
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1394
|
+
const note = noteList[i];
|
|
1395
|
+
if (!note)
|
|
1396
|
+
continue;
|
|
1397
|
+
if (note.instrumentKey.chorusEffectsSend <= 0)
|
|
1398
|
+
continue;
|
|
1399
|
+
note.chorusEffectsSend.disconnect();
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1234
1402
|
}
|
|
1235
1403
|
}
|
|
1236
1404
|
else {
|
|
1237
1405
|
if (0 < chorusSendLevel) {
|
|
1238
|
-
channel.merger.connect(chorusEffect.input);
|
|
1239
1406
|
const now = this.audioContext.currentTime;
|
|
1407
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1408
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1409
|
+
const note = noteList[i];
|
|
1410
|
+
if (!note)
|
|
1411
|
+
continue;
|
|
1412
|
+
if (note.instrumentKey.chorusEffectsSend <= 0)
|
|
1413
|
+
continue;
|
|
1414
|
+
if (!note.chorusEffectsSend) {
|
|
1415
|
+
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1416
|
+
gain: note.instrumentKey.chorusEffectsSend,
|
|
1417
|
+
});
|
|
1418
|
+
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1419
|
+
}
|
|
1420
|
+
note.chorusEffectsSend.connect(chorusEffect.input);
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1240
1423
|
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1241
|
-
chorusEffect.
|
|
1242
|
-
chorusEffect.
|
|
1424
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1425
|
+
chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1243
1426
|
}
|
|
1244
1427
|
}
|
|
1245
1428
|
}
|
|
@@ -1425,20 +1608,22 @@ export class MidyGM2 {
|
|
|
1425
1608
|
}
|
|
1426
1609
|
}
|
|
1427
1610
|
GM1SystemOn() {
|
|
1428
|
-
this.channels.
|
|
1611
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1612
|
+
const channel = this.channels[i];
|
|
1429
1613
|
channel.bankMSB = 0;
|
|
1430
1614
|
channel.bankLSB = 0;
|
|
1431
1615
|
channel.bank = 0;
|
|
1432
|
-
}
|
|
1616
|
+
}
|
|
1433
1617
|
this.channels[9].bankMSB = 1;
|
|
1434
1618
|
this.channels[9].bank = 128;
|
|
1435
1619
|
}
|
|
1436
1620
|
GM2SystemOn() {
|
|
1437
|
-
this.channels.
|
|
1621
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1622
|
+
const channel = this.channels[i];
|
|
1438
1623
|
channel.bankMSB = 121;
|
|
1439
1624
|
channel.bankLSB = 0;
|
|
1440
1625
|
channel.bank = 121 * 128;
|
|
1441
|
-
}
|
|
1626
|
+
}
|
|
1442
1627
|
this.channels[9].bankMSB = 120;
|
|
1443
1628
|
this.channels[9].bank = 120 * 128;
|
|
1444
1629
|
}
|
|
@@ -1579,10 +1764,8 @@ export class MidyGM2 {
|
|
|
1579
1764
|
}
|
|
1580
1765
|
setReverbTime(value) {
|
|
1581
1766
|
this.reverb.time = this.getReverbTime(value);
|
|
1582
|
-
const { audioContext,
|
|
1583
|
-
|
|
1584
|
-
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1585
|
-
}
|
|
1767
|
+
const { audioContext, options } = this;
|
|
1768
|
+
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1586
1769
|
}
|
|
1587
1770
|
getReverbTime(value) {
|
|
1588
1771
|
return Math.pow(Math.E, (value - 40) * 0.025);
|
|
@@ -1659,10 +1842,7 @@ export class MidyGM2 {
|
|
|
1659
1842
|
const now = this.audioContext.currentTime;
|
|
1660
1843
|
const modRate = this.getChorusModRate(value);
|
|
1661
1844
|
this.chorus.modRate = modRate;
|
|
1662
|
-
|
|
1663
|
-
const lfo = this.channels[i].chorusEffect.lfo;
|
|
1664
|
-
lfo.frequency.setValueAtTime(modRate, now);
|
|
1665
|
-
}
|
|
1845
|
+
this.chorusEffect.lfo.frequency.setValueAtTime(modRate, now);
|
|
1666
1846
|
}
|
|
1667
1847
|
getChorusModRate(value) {
|
|
1668
1848
|
return value * 0.122; // Hz
|
|
@@ -1671,12 +1851,9 @@ export class MidyGM2 {
|
|
|
1671
1851
|
const now = this.audioContext.currentTime;
|
|
1672
1852
|
const modDepth = this.getChorusModDepth(value);
|
|
1673
1853
|
this.chorus.modDepth = modDepth;
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
.cancelScheduledValues(now)
|
|
1678
|
-
.setValueAtTime(modDepth / 2, now);
|
|
1679
|
-
}
|
|
1854
|
+
this.chorusEffect.lfoGain.gain
|
|
1855
|
+
.cancelScheduledValues(now)
|
|
1856
|
+
.setValueAtTime(modDepth / 2, now);
|
|
1680
1857
|
}
|
|
1681
1858
|
getChorusModDepth(value) {
|
|
1682
1859
|
return (value + 1) / 3200; // second
|
|
@@ -1685,14 +1862,11 @@ export class MidyGM2 {
|
|
|
1685
1862
|
const now = this.audioContext.currentTime;
|
|
1686
1863
|
const feedback = this.getChorusFeedback(value);
|
|
1687
1864
|
this.chorus.feedback = feedback;
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
.cancelScheduledValues(now)
|
|
1694
|
-
.setValueAtTime(feedback, now);
|
|
1695
|
-
}
|
|
1865
|
+
const chorusEffect = this.chorusEffect;
|
|
1866
|
+
for (let i = 0; i < chorusEffect.feedbackGains.length; i++) {
|
|
1867
|
+
chorusEffect.feedbackGains[i].gain
|
|
1868
|
+
.cancelScheduledValues(now)
|
|
1869
|
+
.setValueAtTime(feedback, now);
|
|
1696
1870
|
}
|
|
1697
1871
|
}
|
|
1698
1872
|
getChorusFeedback(value) {
|
|
@@ -1700,15 +1874,28 @@ export class MidyGM2 {
|
|
|
1700
1874
|
}
|
|
1701
1875
|
setChorusSendToReverb(value) {
|
|
1702
1876
|
const sendToReverb = this.getChorusSendToReverb(value);
|
|
1703
|
-
|
|
1704
|
-
|
|
1877
|
+
const sendGain = this.chorusEffect.sendGain;
|
|
1878
|
+
if (0 < this.chorus.sendToReverb) {
|
|
1705
1879
|
this.chorus.sendToReverb = sendToReverb;
|
|
1706
|
-
|
|
1707
|
-
.
|
|
1708
|
-
.
|
|
1880
|
+
if (0 < sendToReverb) {
|
|
1881
|
+
const now = this.audioContext.currentTime;
|
|
1882
|
+
sendGain.gain
|
|
1883
|
+
.cancelScheduledValues(now)
|
|
1884
|
+
.setValueAtTime(sendToReverb, now);
|
|
1885
|
+
}
|
|
1886
|
+
else {
|
|
1887
|
+
sendGain.disconnect();
|
|
1888
|
+
}
|
|
1709
1889
|
}
|
|
1710
|
-
else
|
|
1711
|
-
this.
|
|
1890
|
+
else {
|
|
1891
|
+
this.chorus.sendToReverb = sendToReverb;
|
|
1892
|
+
if (0 < sendToReverb) {
|
|
1893
|
+
const now = this.audioContext.currentTime;
|
|
1894
|
+
sendGain.connect(this.reverbEffect.input);
|
|
1895
|
+
sendGain.gain
|
|
1896
|
+
.cancelScheduledValues(now)
|
|
1897
|
+
.setValueAtTime(sendToReverb, now);
|
|
1898
|
+
}
|
|
1712
1899
|
}
|
|
1713
1900
|
}
|
|
1714
1901
|
getChorusSendToReverb(value) {
|
|
@@ -1747,7 +1934,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1747
1934
|
currentBufferSource: null,
|
|
1748
1935
|
volume: 100 / 127,
|
|
1749
1936
|
pan: 64,
|
|
1750
|
-
portamentoTime:
|
|
1937
|
+
portamentoTime: 1, // sec
|
|
1751
1938
|
reverbSendLevel: 0,
|
|
1752
1939
|
chorusSendLevel: 0,
|
|
1753
1940
|
vibratoRate: 1,
|