@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.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Midy = 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 Midy {
|
|
|
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 Midy {
|
|
|
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;
|
|
@@ -309,27 +329,31 @@ class Midy {
|
|
|
309
329
|
return channels;
|
|
310
330
|
}
|
|
311
331
|
async createNoteBuffer(instrumentKey, isSF3) {
|
|
332
|
+
const sampleStart = instrumentKey.start;
|
|
312
333
|
const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
|
|
313
334
|
if (isSF3) {
|
|
314
|
-
const sample =
|
|
315
|
-
sample.
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
channelData.set(channelData.subarray(0, sampleEnd));
|
|
320
|
-
}
|
|
335
|
+
const sample = instrumentKey.sample;
|
|
336
|
+
const start = sample.byteOffset + sampleStart;
|
|
337
|
+
const end = sample.byteOffset + sampleEnd;
|
|
338
|
+
const buffer = sample.buffer.slice(start, end);
|
|
339
|
+
const audioBuffer = await this.audioContext.decodeAudioData(buffer);
|
|
321
340
|
return audioBuffer;
|
|
322
341
|
}
|
|
323
342
|
else {
|
|
324
|
-
const sample = instrumentKey.sample
|
|
325
|
-
const
|
|
343
|
+
const sample = instrumentKey.sample;
|
|
344
|
+
const start = sample.byteOffset + sampleStart;
|
|
345
|
+
const end = sample.byteOffset + sampleEnd;
|
|
346
|
+
const buffer = sample.buffer.slice(start, end);
|
|
326
347
|
const audioBuffer = new AudioBuffer({
|
|
327
348
|
numberOfChannels: 1,
|
|
328
349
|
length: sample.length,
|
|
329
350
|
sampleRate: instrumentKey.sampleRate,
|
|
330
351
|
});
|
|
331
352
|
const channelData = audioBuffer.getChannelData(0);
|
|
332
|
-
|
|
353
|
+
const int16Array = new Int16Array(buffer);
|
|
354
|
+
for (let i = 0; i < int16Array.length; i++) {
|
|
355
|
+
channelData[i] = int16Array[i] / 32768;
|
|
356
|
+
}
|
|
333
357
|
return audioBuffer;
|
|
334
358
|
}
|
|
335
359
|
}
|
|
@@ -345,13 +369,23 @@ class Midy {
|
|
|
345
369
|
}
|
|
346
370
|
return bufferSource;
|
|
347
371
|
}
|
|
348
|
-
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
372
|
+
findPortamentoTarget(queueIndex) {
|
|
373
|
+
const endEvent = this.timeline[queueIndex];
|
|
374
|
+
if (!this.channels[endEvent.channel].portamento)
|
|
375
|
+
return;
|
|
376
|
+
const endTime = endEvent.startTime;
|
|
377
|
+
let target;
|
|
378
|
+
while (++queueIndex < this.timeline.length) {
|
|
379
|
+
const event = this.timeline[queueIndex];
|
|
380
|
+
if (endTime !== event.startTime)
|
|
381
|
+
break;
|
|
382
|
+
if (event.type !== "noteOn")
|
|
383
|
+
continue;
|
|
384
|
+
if (!target || event.noteNumber < target.noteNumber) {
|
|
385
|
+
target = event;
|
|
386
|
+
}
|
|
353
387
|
}
|
|
354
|
-
return
|
|
388
|
+
return target;
|
|
355
389
|
}
|
|
356
390
|
async scheduleTimelineEvents(t, offset, queueIndex) {
|
|
357
391
|
while (queueIndex < this.timeline.length) {
|
|
@@ -361,12 +395,15 @@ class Midy {
|
|
|
361
395
|
switch (event.type) {
|
|
362
396
|
case "noteOn":
|
|
363
397
|
if (event.velocity !== 0) {
|
|
364
|
-
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
|
|
398
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, event.portamento);
|
|
365
399
|
break;
|
|
366
400
|
}
|
|
367
401
|
/* falls through */
|
|
368
402
|
case "noteOff": {
|
|
369
|
-
const
|
|
403
|
+
const portamentoTarget = this.findPortamentoTarget(queueIndex);
|
|
404
|
+
if (portamentoTarget)
|
|
405
|
+
portamentoTarget.portamento = true;
|
|
406
|
+
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, portamentoTarget?.noteNumber, false);
|
|
370
407
|
if (notePromise) {
|
|
371
408
|
this.notePromises.push(notePromise);
|
|
372
409
|
}
|
|
@@ -414,6 +451,7 @@ class Midy {
|
|
|
414
451
|
if (queueIndex >= this.timeline.length) {
|
|
415
452
|
await Promise.all(this.notePromises);
|
|
416
453
|
this.notePromises = [];
|
|
454
|
+
this.exclusiveClassMap.clear();
|
|
417
455
|
resolve();
|
|
418
456
|
return;
|
|
419
457
|
}
|
|
@@ -429,6 +467,7 @@ class Midy {
|
|
|
429
467
|
}
|
|
430
468
|
else if (this.isStopping) {
|
|
431
469
|
await this.stopNotes(0, true);
|
|
470
|
+
this.exclusiveClassMap.clear();
|
|
432
471
|
this.notePromises = [];
|
|
433
472
|
resolve();
|
|
434
473
|
this.isStopping = false;
|
|
@@ -437,6 +476,7 @@ class Midy {
|
|
|
437
476
|
}
|
|
438
477
|
else if (this.isSeeking) {
|
|
439
478
|
this.stopNotes(0, true);
|
|
479
|
+
this.exclusiveClassMap.clear();
|
|
440
480
|
this.startTime = this.audioContext.currentTime;
|
|
441
481
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
442
482
|
offset = this.resumeTime - this.startTime;
|
|
@@ -470,9 +510,11 @@ class Midy {
|
|
|
470
510
|
bankLSB: this.channels[i].bankLSB,
|
|
471
511
|
};
|
|
472
512
|
}
|
|
473
|
-
midi.tracks.
|
|
513
|
+
for (let i = 0; i < midi.tracks.length; i++) {
|
|
514
|
+
const track = midi.tracks[i];
|
|
474
515
|
let currentTicks = 0;
|
|
475
|
-
track.
|
|
516
|
+
for (let j = 0; j < track.length; j++) {
|
|
517
|
+
const event = track[j];
|
|
476
518
|
currentTicks += event.deltaTime;
|
|
477
519
|
event.ticks = currentTicks;
|
|
478
520
|
switch (event.type) {
|
|
@@ -525,16 +567,18 @@ class Midy {
|
|
|
525
567
|
}
|
|
526
568
|
delete event.deltaTime;
|
|
527
569
|
timeline.push(event);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
530
572
|
const priority = {
|
|
531
573
|
controller: 0,
|
|
532
574
|
sysEx: 1,
|
|
575
|
+
noteOff: 2, // for portamento
|
|
576
|
+
noteOn: 3,
|
|
533
577
|
};
|
|
534
578
|
timeline.sort((a, b) => {
|
|
535
579
|
if (a.ticks !== b.ticks)
|
|
536
580
|
return a.ticks - b.ticks;
|
|
537
|
-
return (priority[a.type] ||
|
|
581
|
+
return (priority[a.type] || 4) - (priority[b.type] || 4);
|
|
538
582
|
});
|
|
539
583
|
let prevTempoTime = 0;
|
|
540
584
|
let prevTempoTicks = 0;
|
|
@@ -551,7 +595,7 @@ class Midy {
|
|
|
551
595
|
}
|
|
552
596
|
return { instruments, timeline };
|
|
553
597
|
}
|
|
554
|
-
async stopChannelNotes(channelNumber, velocity,
|
|
598
|
+
async stopChannelNotes(channelNumber, velocity, force) {
|
|
555
599
|
const now = this.audioContext.currentTime;
|
|
556
600
|
const channel = this.channels[channelNumber];
|
|
557
601
|
channel.scheduledNotes.forEach((noteList) => {
|
|
@@ -559,16 +603,17 @@ class Midy {
|
|
|
559
603
|
const note = noteList[i];
|
|
560
604
|
if (!note)
|
|
561
605
|
continue;
|
|
562
|
-
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now,
|
|
606
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, undefined, // portamentoNoteNumber
|
|
607
|
+
force);
|
|
563
608
|
this.notePromises.push(promise);
|
|
564
609
|
}
|
|
565
610
|
});
|
|
566
611
|
channel.scheduledNotes.clear();
|
|
567
612
|
await Promise.all(this.notePromises);
|
|
568
613
|
}
|
|
569
|
-
stopNotes(velocity,
|
|
614
|
+
stopNotes(velocity, force) {
|
|
570
615
|
for (let i = 0; i < this.channels.length; i++) {
|
|
571
|
-
this.stopChannelNotes(i, velocity,
|
|
616
|
+
this.stopChannelNotes(i, velocity, force);
|
|
572
617
|
}
|
|
573
618
|
return Promise.all(this.notePromises);
|
|
574
619
|
}
|
|
@@ -660,14 +705,14 @@ class Midy {
|
|
|
660
705
|
return impulse;
|
|
661
706
|
}
|
|
662
707
|
createConvolutionReverb(audioContext, impulse) {
|
|
663
|
-
const
|
|
708
|
+
const input = new GainNode(audioContext);
|
|
664
709
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
665
710
|
buffer: impulse,
|
|
666
711
|
});
|
|
667
|
-
|
|
712
|
+
input.connect(convolverNode);
|
|
668
713
|
return {
|
|
669
|
-
input
|
|
670
|
-
output,
|
|
714
|
+
input,
|
|
715
|
+
output: convolverNode,
|
|
671
716
|
convolverNode,
|
|
672
717
|
};
|
|
673
718
|
}
|
|
@@ -709,7 +754,6 @@ class Midy {
|
|
|
709
754
|
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
710
755
|
createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
|
|
711
756
|
const input = new GainNode(audioContext);
|
|
712
|
-
const output = new GainNode(audioContext);
|
|
713
757
|
const mergerGain = new GainNode(audioContext);
|
|
714
758
|
for (let i = 0; i < combDelays.length; i++) {
|
|
715
759
|
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
@@ -720,7 +764,7 @@ class Midy {
|
|
|
720
764
|
const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
|
|
721
765
|
allpasses.push(allpass);
|
|
722
766
|
}
|
|
723
|
-
allpasses.at(-1)
|
|
767
|
+
const output = allpasses.at(-1);
|
|
724
768
|
return { input, output };
|
|
725
769
|
}
|
|
726
770
|
createChorusEffect(audioContext) {
|
|
@@ -782,6 +826,17 @@ class Midy {
|
|
|
782
826
|
return instrumentKey.playbackRate(noteNumber) *
|
|
783
827
|
Math.pow(2, semitoneOffset / 12);
|
|
784
828
|
}
|
|
829
|
+
setPortamentoStartVolumeEnvelope(channel, note) {
|
|
830
|
+
const { instrumentKey, startTime } = note;
|
|
831
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
832
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
833
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
834
|
+
const portamentoTime = volDelay + channel.portamentoTime;
|
|
835
|
+
note.volumeNode.gain
|
|
836
|
+
.cancelScheduledValues(startTime)
|
|
837
|
+
.setValueAtTime(0, volDelay)
|
|
838
|
+
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
839
|
+
}
|
|
785
840
|
setVolumeEnvelope(channel, note) {
|
|
786
841
|
const { instrumentKey, startTime } = note;
|
|
787
842
|
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
@@ -821,6 +876,25 @@ class Midy {
|
|
|
821
876
|
const maxFrequency = 20000; // max Hz of initialFilterFc
|
|
822
877
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
823
878
|
}
|
|
879
|
+
setPortamentoStartFilterEnvelope(channel, note) {
|
|
880
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
881
|
+
const softPedalFactor = 1 -
|
|
882
|
+
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
883
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
884
|
+
softPedalFactor * channel.brightness;
|
|
885
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor * channel.brightness;
|
|
886
|
+
const sustainFreq = baseFreq +
|
|
887
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
888
|
+
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
889
|
+
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
890
|
+
const portamentoTime = startTime + channel.portamentoTime;
|
|
891
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
892
|
+
note.filterNode.frequency
|
|
893
|
+
.cancelScheduledValues(startTime)
|
|
894
|
+
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
895
|
+
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
896
|
+
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
897
|
+
}
|
|
824
898
|
setFilterEnvelope(channel, note) {
|
|
825
899
|
const { instrumentKey, noteNumber, startTime } = note;
|
|
826
900
|
const softPedalFactor = 1 -
|
|
@@ -888,7 +962,7 @@ class Midy {
|
|
|
888
962
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
889
963
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
890
964
|
}
|
|
891
|
-
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
965
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3) {
|
|
892
966
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
893
967
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
894
968
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
@@ -897,8 +971,14 @@ class Midy {
|
|
|
897
971
|
type: "lowpass",
|
|
898
972
|
Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
|
|
899
973
|
});
|
|
900
|
-
|
|
901
|
-
|
|
974
|
+
if (portamento) {
|
|
975
|
+
this.setPortamentoStartVolumeEnvelope(channel, note);
|
|
976
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
this.setVolumeEnvelope(channel, note);
|
|
980
|
+
this.setFilterEnvelope(channel, note);
|
|
981
|
+
}
|
|
902
982
|
if (0 < channel.vibratoDepth) {
|
|
903
983
|
this.startVibrato(channel, note, startTime);
|
|
904
984
|
}
|
|
@@ -915,7 +995,21 @@ class Midy {
|
|
|
915
995
|
}
|
|
916
996
|
note.bufferSource.connect(note.filterNode);
|
|
917
997
|
note.filterNode.connect(note.volumeNode);
|
|
918
|
-
|
|
998
|
+
if (0 < channel.reverbSendLevel && 0 < instrumentKey.reverbEffectsSend) {
|
|
999
|
+
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1000
|
+
gain: instrumentKey.reverbEffectsSend,
|
|
1001
|
+
});
|
|
1002
|
+
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1003
|
+
note.reverbEffectsSend.connect(this.reverbEffect.input);
|
|
1004
|
+
}
|
|
1005
|
+
if (0 < channel.chorusSendLevel && 0 < instrumentKey.chorusEffectsSend) {
|
|
1006
|
+
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1007
|
+
gain: instrumentKey.chorusEffectsSend,
|
|
1008
|
+
});
|
|
1009
|
+
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1010
|
+
note.chorusEffectsSend.connect(this.chorusEffect.input);
|
|
1011
|
+
}
|
|
1012
|
+
note.bufferSource.start(startTime);
|
|
919
1013
|
return note;
|
|
920
1014
|
}
|
|
921
1015
|
calcBank(channel, channelNumber) {
|
|
@@ -927,7 +1021,7 @@ class Midy {
|
|
|
927
1021
|
}
|
|
928
1022
|
return channel.bank;
|
|
929
1023
|
}
|
|
930
|
-
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
1024
|
+
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, portamento) {
|
|
931
1025
|
const channel = this.channels[channelNumber];
|
|
932
1026
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
933
1027
|
const soundFontIndex = this.soundFontTable[channel.program].get(bankNumber);
|
|
@@ -938,12 +1032,25 @@ class Midy {
|
|
|
938
1032
|
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber, velocity);
|
|
939
1033
|
if (!instrumentKey)
|
|
940
1034
|
return;
|
|
941
|
-
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
1035
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3);
|
|
942
1036
|
note.volumeNode.connect(channel.gainL);
|
|
943
1037
|
note.volumeNode.connect(channel.gainR);
|
|
944
1038
|
if (channel.sostenutoPedal) {
|
|
945
1039
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
946
1040
|
}
|
|
1041
|
+
const exclusiveClass = instrumentKey.exclusiveClass;
|
|
1042
|
+
if (exclusiveClass !== 0) {
|
|
1043
|
+
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
1044
|
+
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
1045
|
+
const [prevNote, prevChannelNumber] = prevEntry;
|
|
1046
|
+
if (!prevNote.ending) {
|
|
1047
|
+
this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1048
|
+
startTime, undefined, // portamentoNoteNumber
|
|
1049
|
+
true);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
1053
|
+
}
|
|
947
1054
|
const scheduledNotes = channel.scheduledNotes;
|
|
948
1055
|
if (scheduledNotes.has(noteNumber)) {
|
|
949
1056
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -952,13 +1059,48 @@ class Midy {
|
|
|
952
1059
|
scheduledNotes.set(noteNumber, [note]);
|
|
953
1060
|
}
|
|
954
1061
|
}
|
|
955
|
-
noteOn(channelNumber, noteNumber, velocity) {
|
|
1062
|
+
noteOn(channelNumber, noteNumber, velocity, portamento) {
|
|
956
1063
|
const now = this.audioContext.currentTime;
|
|
957
|
-
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
|
|
1064
|
+
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now, portamento);
|
|
1065
|
+
}
|
|
1066
|
+
stopNote(endTime, stopTime, scheduledNotes, index) {
|
|
1067
|
+
const note = scheduledNotes[index];
|
|
1068
|
+
note.volumeNode.gain
|
|
1069
|
+
.cancelScheduledValues(endTime)
|
|
1070
|
+
.linearRampToValueAtTime(0, stopTime);
|
|
1071
|
+
note.ending = true;
|
|
1072
|
+
this.scheduleTask(() => {
|
|
1073
|
+
note.bufferSource.loop = false;
|
|
1074
|
+
}, stopTime);
|
|
1075
|
+
return new Promise((resolve) => {
|
|
1076
|
+
note.bufferSource.onended = () => {
|
|
1077
|
+
scheduledNotes[index] = null;
|
|
1078
|
+
note.bufferSource.disconnect();
|
|
1079
|
+
note.volumeNode.disconnect();
|
|
1080
|
+
note.filterNode.disconnect();
|
|
1081
|
+
if (note.modulationDepth) {
|
|
1082
|
+
note.volumeDepth.disconnect();
|
|
1083
|
+
note.modulationDepth.disconnect();
|
|
1084
|
+
note.modulationLFO.stop();
|
|
1085
|
+
}
|
|
1086
|
+
if (note.vibratoDepth) {
|
|
1087
|
+
note.vibratoDepth.disconnect();
|
|
1088
|
+
note.vibratoLFO.stop();
|
|
1089
|
+
}
|
|
1090
|
+
if (note.reverbEffectsSend) {
|
|
1091
|
+
note.reverbEffectsSend.disconnect();
|
|
1092
|
+
}
|
|
1093
|
+
if (note.chorusEffectsSend) {
|
|
1094
|
+
note.chorusEffectsSend.disconnect();
|
|
1095
|
+
}
|
|
1096
|
+
resolve();
|
|
1097
|
+
};
|
|
1098
|
+
note.bufferSource.stop(stopTime);
|
|
1099
|
+
});
|
|
958
1100
|
}
|
|
959
|
-
scheduleNoteRelease(channelNumber, noteNumber, _velocity,
|
|
1101
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, portamentoNoteNumber, force) {
|
|
960
1102
|
const channel = this.channels[channelNumber];
|
|
961
|
-
if (
|
|
1103
|
+
if (!force) {
|
|
962
1104
|
if (channel.sustainPedal)
|
|
963
1105
|
return;
|
|
964
1106
|
if (channel.sostenutoNotes.has(noteNumber))
|
|
@@ -973,43 +1115,30 @@ class Midy {
|
|
|
973
1115
|
continue;
|
|
974
1116
|
if (note.ending)
|
|
975
1117
|
continue;
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
.
|
|
980
|
-
.
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
.
|
|
984
|
-
.
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
note.bufferSource.
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
if (note.modulationDepth) {
|
|
996
|
-
note.volumeDepth.disconnect();
|
|
997
|
-
note.modulationDepth.disconnect();
|
|
998
|
-
note.modulationLFO.stop();
|
|
999
|
-
}
|
|
1000
|
-
if (note.vibratoDepth) {
|
|
1001
|
-
note.vibratoDepth.disconnect();
|
|
1002
|
-
note.vibratoLFO.stop();
|
|
1003
|
-
}
|
|
1004
|
-
resolve();
|
|
1005
|
-
};
|
|
1006
|
-
note.bufferSource.stop(volEndTime);
|
|
1007
|
-
});
|
|
1118
|
+
if (portamentoNoteNumber === undefined) {
|
|
1119
|
+
const volRelease = endTime +
|
|
1120
|
+
note.instrumentKey.volRelease * channel.releaseTime;
|
|
1121
|
+
const modRelease = endTime + note.instrumentKey.modRelease;
|
|
1122
|
+
note.filterNode.frequency
|
|
1123
|
+
.cancelScheduledValues(endTime)
|
|
1124
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
1125
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
1126
|
+
return this.stopNote(endTime, stopTime, scheduledNotes, i);
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
const portamentoTime = endTime + channel.portamentoTime;
|
|
1130
|
+
const detuneChange = (portamentoNoteNumber - noteNumber) * 100;
|
|
1131
|
+
const detune = note.bufferSource.detune.value + detuneChange;
|
|
1132
|
+
note.bufferSource.detune
|
|
1133
|
+
.cancelScheduledValues(endTime)
|
|
1134
|
+
.linearRampToValueAtTime(detune, portamentoTime);
|
|
1135
|
+
return this.stopNote(endTime, portamentoTime, scheduledNotes, i);
|
|
1136
|
+
}
|
|
1008
1137
|
}
|
|
1009
1138
|
}
|
|
1010
|
-
releaseNote(channelNumber, noteNumber, velocity) {
|
|
1139
|
+
releaseNote(channelNumber, noteNumber, velocity, portamentoNoteNumber) {
|
|
1011
1140
|
const now = this.audioContext.currentTime;
|
|
1012
|
-
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
1141
|
+
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, portamentoNoteNumber, false);
|
|
1013
1142
|
}
|
|
1014
1143
|
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
1015
1144
|
const velocity = halfVelocity * 2;
|
|
@@ -1050,7 +1179,7 @@ class Midy {
|
|
|
1050
1179
|
case 0x90:
|
|
1051
1180
|
return this.noteOn(channelNumber, data1, data2);
|
|
1052
1181
|
case 0xA0:
|
|
1053
|
-
return
|
|
1182
|
+
return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
|
|
1054
1183
|
case 0xB0:
|
|
1055
1184
|
return this.handleControlChange(channelNumber, data1, data2);
|
|
1056
1185
|
case 0xC0:
|
|
@@ -1184,12 +1313,14 @@ class Midy {
|
|
|
1184
1313
|
this.updateModulation(channel);
|
|
1185
1314
|
}
|
|
1186
1315
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1187
|
-
this.channels[channelNumber]
|
|
1316
|
+
const channel = this.channels[channelNumber];
|
|
1317
|
+
const factor = 5 * Math.log(10) / 127;
|
|
1318
|
+
channel.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1188
1319
|
}
|
|
1189
1320
|
setVolume(channelNumber, volume) {
|
|
1190
1321
|
const channel = this.channels[channelNumber];
|
|
1191
1322
|
channel.volume = volume / 127;
|
|
1192
|
-
this.
|
|
1323
|
+
this.updateChannelVolume(channel);
|
|
1193
1324
|
}
|
|
1194
1325
|
panToGain(pan) {
|
|
1195
1326
|
const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
|
|
@@ -1201,12 +1332,12 @@ class Midy {
|
|
|
1201
1332
|
setPan(channelNumber, pan) {
|
|
1202
1333
|
const channel = this.channels[channelNumber];
|
|
1203
1334
|
channel.pan = pan;
|
|
1204
|
-
this.
|
|
1335
|
+
this.updateChannelVolume(channel);
|
|
1205
1336
|
}
|
|
1206
1337
|
setExpression(channelNumber, expression) {
|
|
1207
1338
|
const channel = this.channels[channelNumber];
|
|
1208
1339
|
channel.expression = expression / 127;
|
|
1209
|
-
this.
|
|
1340
|
+
this.updateChannelVolume(channel);
|
|
1210
1341
|
}
|
|
1211
1342
|
setBankLSB(channelNumber, lsb) {
|
|
1212
1343
|
this.channels[channelNumber].bankLSB = lsb;
|
|
@@ -1215,7 +1346,7 @@ class Midy {
|
|
|
1215
1346
|
this.channels[channelNumber].dataLSB = value;
|
|
1216
1347
|
this.handleRPN(channelNumber, 0);
|
|
1217
1348
|
}
|
|
1218
|
-
|
|
1349
|
+
updateChannelVolume(channel) {
|
|
1219
1350
|
const now = this.audioContext.currentTime;
|
|
1220
1351
|
const volume = channel.volume * channel.expression;
|
|
1221
1352
|
const { gainLeft, gainRight } = this.panToGain(channel.pan);
|
|
@@ -1233,30 +1364,54 @@ class Midy {
|
|
|
1233
1364
|
this.releaseSustainPedal(channelNumber, value);
|
|
1234
1365
|
}
|
|
1235
1366
|
}
|
|
1236
|
-
// TODO
|
|
1237
1367
|
setPortamento(channelNumber, value) {
|
|
1238
1368
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1239
1369
|
}
|
|
1240
1370
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1241
1371
|
const channel = this.channels[channelNumber];
|
|
1372
|
+
const reverbEffect = this.reverbEffect;
|
|
1242
1373
|
if (0 < channel.reverbSendLevel) {
|
|
1243
1374
|
if (0 < reverbSendLevel) {
|
|
1244
1375
|
const now = this.audioContext.currentTime;
|
|
1245
1376
|
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1246
|
-
reverbEffect.
|
|
1247
|
-
reverbEffect.
|
|
1377
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1378
|
+
reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1248
1379
|
}
|
|
1249
1380
|
else {
|
|
1250
|
-
channel.
|
|
1381
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1382
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1383
|
+
const note = noteList[i];
|
|
1384
|
+
if (!note)
|
|
1385
|
+
continue;
|
|
1386
|
+
if (note.instrumentKey.reverbEffectsSend <= 0)
|
|
1387
|
+
continue;
|
|
1388
|
+
note.reverbEffectsSend.disconnect();
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1251
1391
|
}
|
|
1252
1392
|
}
|
|
1253
1393
|
else {
|
|
1254
1394
|
if (0 < reverbSendLevel) {
|
|
1255
|
-
channel.merger.connect(reverbEffect.input);
|
|
1256
1395
|
const now = this.audioContext.currentTime;
|
|
1396
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1397
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1398
|
+
const note = noteList[i];
|
|
1399
|
+
if (!note)
|
|
1400
|
+
continue;
|
|
1401
|
+
if (note.instrumentKey.reverbEffectsSend <= 0)
|
|
1402
|
+
continue;
|
|
1403
|
+
if (!note.reverbEffectsSend) {
|
|
1404
|
+
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1405
|
+
gain: note.instrumentKey.reverbEffectsSend,
|
|
1406
|
+
});
|
|
1407
|
+
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1408
|
+
}
|
|
1409
|
+
note.reverbEffectsSend.connect(reverbEffect.input);
|
|
1410
|
+
}
|
|
1411
|
+
});
|
|
1257
1412
|
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1258
|
-
reverbEffect.
|
|
1259
|
-
reverbEffect.
|
|
1413
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1414
|
+
reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1260
1415
|
}
|
|
1261
1416
|
}
|
|
1262
1417
|
}
|
|
@@ -1267,20 +1422,44 @@ class Midy {
|
|
|
1267
1422
|
if (0 < chorusSendLevel) {
|
|
1268
1423
|
const now = this.audioContext.currentTime;
|
|
1269
1424
|
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1270
|
-
chorusEffect.
|
|
1271
|
-
chorusEffect.
|
|
1425
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1426
|
+
chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1272
1427
|
}
|
|
1273
1428
|
else {
|
|
1274
|
-
channel.
|
|
1429
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1430
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1431
|
+
const note = noteList[i];
|
|
1432
|
+
if (!note)
|
|
1433
|
+
continue;
|
|
1434
|
+
if (note.instrumentKey.chorusEffectsSend <= 0)
|
|
1435
|
+
continue;
|
|
1436
|
+
note.chorusEffectsSend.disconnect();
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1275
1439
|
}
|
|
1276
1440
|
}
|
|
1277
1441
|
else {
|
|
1278
1442
|
if (0 < chorusSendLevel) {
|
|
1279
|
-
channel.merger.connect(chorusEffect.input);
|
|
1280
1443
|
const now = this.audioContext.currentTime;
|
|
1444
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1445
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1446
|
+
const note = noteList[i];
|
|
1447
|
+
if (!note)
|
|
1448
|
+
continue;
|
|
1449
|
+
if (note.instrumentKey.chorusEffectsSend <= 0)
|
|
1450
|
+
continue;
|
|
1451
|
+
if (!note.chorusEffectsSend) {
|
|
1452
|
+
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1453
|
+
gain: note.instrumentKey.chorusEffectsSend,
|
|
1454
|
+
});
|
|
1455
|
+
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1456
|
+
}
|
|
1457
|
+
note.chorusEffectsSend.connect(chorusEffect.input);
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1281
1460
|
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1282
|
-
chorusEffect.
|
|
1283
|
-
chorusEffect.
|
|
1461
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1462
|
+
chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1284
1463
|
}
|
|
1285
1464
|
}
|
|
1286
1465
|
}
|
|
@@ -1557,20 +1736,22 @@ class Midy {
|
|
|
1557
1736
|
}
|
|
1558
1737
|
}
|
|
1559
1738
|
GM1SystemOn() {
|
|
1560
|
-
this.channels.
|
|
1739
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1740
|
+
const channel = this.channels[i];
|
|
1561
1741
|
channel.bankMSB = 0;
|
|
1562
1742
|
channel.bankLSB = 0;
|
|
1563
1743
|
channel.bank = 0;
|
|
1564
|
-
}
|
|
1744
|
+
}
|
|
1565
1745
|
this.channels[9].bankMSB = 1;
|
|
1566
1746
|
this.channels[9].bank = 128;
|
|
1567
1747
|
}
|
|
1568
1748
|
GM2SystemOn() {
|
|
1569
|
-
this.channels.
|
|
1749
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1750
|
+
const channel = this.channels[i];
|
|
1570
1751
|
channel.bankMSB = 121;
|
|
1571
1752
|
channel.bankLSB = 0;
|
|
1572
1753
|
channel.bank = 121 * 128;
|
|
1573
|
-
}
|
|
1754
|
+
}
|
|
1574
1755
|
this.channels[9].bankMSB = 120;
|
|
1575
1756
|
this.channels[9].bank = 120 * 128;
|
|
1576
1757
|
}
|
|
@@ -1711,10 +1892,8 @@ class Midy {
|
|
|
1711
1892
|
}
|
|
1712
1893
|
setReverbTime(value) {
|
|
1713
1894
|
this.reverb.time = this.getReverbTime(value);
|
|
1714
|
-
const { audioContext,
|
|
1715
|
-
|
|
1716
|
-
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1717
|
-
}
|
|
1895
|
+
const { audioContext, options } = this;
|
|
1896
|
+
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1718
1897
|
}
|
|
1719
1898
|
getReverbTime(value) {
|
|
1720
1899
|
return Math.pow(Math.E, (value - 40) * 0.025);
|
|
@@ -1791,10 +1970,7 @@ class Midy {
|
|
|
1791
1970
|
const now = this.audioContext.currentTime;
|
|
1792
1971
|
const modRate = this.getChorusModRate(value);
|
|
1793
1972
|
this.chorus.modRate = modRate;
|
|
1794
|
-
|
|
1795
|
-
const lfo = this.channels[i].chorusEffect.lfo;
|
|
1796
|
-
lfo.frequency.setValueAtTime(modRate, now);
|
|
1797
|
-
}
|
|
1973
|
+
this.chorusEffect.lfo.frequency.setValueAtTime(modRate, now);
|
|
1798
1974
|
}
|
|
1799
1975
|
getChorusModRate(value) {
|
|
1800
1976
|
return value * 0.122; // Hz
|
|
@@ -1803,12 +1979,9 @@ class Midy {
|
|
|
1803
1979
|
const now = this.audioContext.currentTime;
|
|
1804
1980
|
const modDepth = this.getChorusModDepth(value);
|
|
1805
1981
|
this.chorus.modDepth = modDepth;
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
.cancelScheduledValues(now)
|
|
1810
|
-
.setValueAtTime(modDepth / 2, now);
|
|
1811
|
-
}
|
|
1982
|
+
this.chorusEffect.lfoGain.gain
|
|
1983
|
+
.cancelScheduledValues(now)
|
|
1984
|
+
.setValueAtTime(modDepth / 2, now);
|
|
1812
1985
|
}
|
|
1813
1986
|
getChorusModDepth(value) {
|
|
1814
1987
|
return (value + 1) / 3200; // second
|
|
@@ -1817,14 +1990,11 @@ class Midy {
|
|
|
1817
1990
|
const now = this.audioContext.currentTime;
|
|
1818
1991
|
const feedback = this.getChorusFeedback(value);
|
|
1819
1992
|
this.chorus.feedback = feedback;
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
.cancelScheduledValues(now)
|
|
1826
|
-
.setValueAtTime(feedback, now);
|
|
1827
|
-
}
|
|
1993
|
+
const chorusEffect = this.chorusEffect;
|
|
1994
|
+
for (let i = 0; i < chorusEffect.feedbackGains.length; i++) {
|
|
1995
|
+
chorusEffect.feedbackGains[i].gain
|
|
1996
|
+
.cancelScheduledValues(now)
|
|
1997
|
+
.setValueAtTime(feedback, now);
|
|
1828
1998
|
}
|
|
1829
1999
|
}
|
|
1830
2000
|
getChorusFeedback(value) {
|
|
@@ -1832,15 +2002,28 @@ class Midy {
|
|
|
1832
2002
|
}
|
|
1833
2003
|
setChorusSendToReverb(value) {
|
|
1834
2004
|
const sendToReverb = this.getChorusSendToReverb(value);
|
|
1835
|
-
|
|
1836
|
-
|
|
2005
|
+
const sendGain = this.chorusEffect.sendGain;
|
|
2006
|
+
if (0 < this.chorus.sendToReverb) {
|
|
1837
2007
|
this.chorus.sendToReverb = sendToReverb;
|
|
1838
|
-
|
|
1839
|
-
.
|
|
1840
|
-
.
|
|
2008
|
+
if (0 < sendToReverb) {
|
|
2009
|
+
const now = this.audioContext.currentTime;
|
|
2010
|
+
sendGain.gain
|
|
2011
|
+
.cancelScheduledValues(now)
|
|
2012
|
+
.setValueAtTime(sendToReverb, now);
|
|
2013
|
+
}
|
|
2014
|
+
else {
|
|
2015
|
+
sendGain.disconnect();
|
|
2016
|
+
}
|
|
1841
2017
|
}
|
|
1842
|
-
else
|
|
1843
|
-
this.
|
|
2018
|
+
else {
|
|
2019
|
+
this.chorus.sendToReverb = sendToReverb;
|
|
2020
|
+
if (0 < sendToReverb) {
|
|
2021
|
+
const now = this.audioContext.currentTime;
|
|
2022
|
+
sendGain.connect(this.reverbEffect.input);
|
|
2023
|
+
sendGain.gain
|
|
2024
|
+
.cancelScheduledValues(now)
|
|
2025
|
+
.setValueAtTime(sendToReverb, now);
|
|
2026
|
+
}
|
|
1844
2027
|
}
|
|
1845
2028
|
}
|
|
1846
2029
|
getChorusSendToReverb(value) {
|
|
@@ -1880,7 +2063,7 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
1880
2063
|
currentBufferSource: null,
|
|
1881
2064
|
volume: 100 / 127,
|
|
1882
2065
|
pan: 64,
|
|
1883
|
-
portamentoTime:
|
|
2066
|
+
portamentoTime: 1, // sec
|
|
1884
2067
|
filterResonance: 1,
|
|
1885
2068
|
releaseTime: 1,
|
|
1886
2069
|
attackTime: 1,
|