@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.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 Midy {
|
|
|
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 Midy {
|
|
|
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);
|
|
@@ -306,27 +326,31 @@ export class Midy {
|
|
|
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 @@ export class Midy {
|
|
|
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 @@ export class Midy {
|
|
|
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
|
}
|
|
@@ -411,6 +448,7 @@ export class Midy {
|
|
|
411
448
|
if (queueIndex >= this.timeline.length) {
|
|
412
449
|
await Promise.all(this.notePromises);
|
|
413
450
|
this.notePromises = [];
|
|
451
|
+
this.exclusiveClassMap.clear();
|
|
414
452
|
resolve();
|
|
415
453
|
return;
|
|
416
454
|
}
|
|
@@ -426,6 +464,7 @@ export class Midy {
|
|
|
426
464
|
}
|
|
427
465
|
else if (this.isStopping) {
|
|
428
466
|
await this.stopNotes(0, true);
|
|
467
|
+
this.exclusiveClassMap.clear();
|
|
429
468
|
this.notePromises = [];
|
|
430
469
|
resolve();
|
|
431
470
|
this.isStopping = false;
|
|
@@ -434,6 +473,7 @@ export class Midy {
|
|
|
434
473
|
}
|
|
435
474
|
else if (this.isSeeking) {
|
|
436
475
|
this.stopNotes(0, true);
|
|
476
|
+
this.exclusiveClassMap.clear();
|
|
437
477
|
this.startTime = this.audioContext.currentTime;
|
|
438
478
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
439
479
|
offset = this.resumeTime - this.startTime;
|
|
@@ -467,9 +507,11 @@ export class Midy {
|
|
|
467
507
|
bankLSB: this.channels[i].bankLSB,
|
|
468
508
|
};
|
|
469
509
|
}
|
|
470
|
-
midi.tracks.
|
|
510
|
+
for (let i = 0; i < midi.tracks.length; i++) {
|
|
511
|
+
const track = midi.tracks[i];
|
|
471
512
|
let currentTicks = 0;
|
|
472
|
-
track.
|
|
513
|
+
for (let j = 0; j < track.length; j++) {
|
|
514
|
+
const event = track[j];
|
|
473
515
|
currentTicks += event.deltaTime;
|
|
474
516
|
event.ticks = currentTicks;
|
|
475
517
|
switch (event.type) {
|
|
@@ -522,16 +564,18 @@ export class Midy {
|
|
|
522
564
|
}
|
|
523
565
|
delete event.deltaTime;
|
|
524
566
|
timeline.push(event);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
527
569
|
const priority = {
|
|
528
570
|
controller: 0,
|
|
529
571
|
sysEx: 1,
|
|
572
|
+
noteOff: 2, // for portamento
|
|
573
|
+
noteOn: 3,
|
|
530
574
|
};
|
|
531
575
|
timeline.sort((a, b) => {
|
|
532
576
|
if (a.ticks !== b.ticks)
|
|
533
577
|
return a.ticks - b.ticks;
|
|
534
|
-
return (priority[a.type] ||
|
|
578
|
+
return (priority[a.type] || 4) - (priority[b.type] || 4);
|
|
535
579
|
});
|
|
536
580
|
let prevTempoTime = 0;
|
|
537
581
|
let prevTempoTicks = 0;
|
|
@@ -548,7 +592,7 @@ export class Midy {
|
|
|
548
592
|
}
|
|
549
593
|
return { instruments, timeline };
|
|
550
594
|
}
|
|
551
|
-
async stopChannelNotes(channelNumber, velocity,
|
|
595
|
+
async stopChannelNotes(channelNumber, velocity, force) {
|
|
552
596
|
const now = this.audioContext.currentTime;
|
|
553
597
|
const channel = this.channels[channelNumber];
|
|
554
598
|
channel.scheduledNotes.forEach((noteList) => {
|
|
@@ -556,16 +600,17 @@ export class Midy {
|
|
|
556
600
|
const note = noteList[i];
|
|
557
601
|
if (!note)
|
|
558
602
|
continue;
|
|
559
|
-
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now,
|
|
603
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, undefined, // portamentoNoteNumber
|
|
604
|
+
force);
|
|
560
605
|
this.notePromises.push(promise);
|
|
561
606
|
}
|
|
562
607
|
});
|
|
563
608
|
channel.scheduledNotes.clear();
|
|
564
609
|
await Promise.all(this.notePromises);
|
|
565
610
|
}
|
|
566
|
-
stopNotes(velocity,
|
|
611
|
+
stopNotes(velocity, force) {
|
|
567
612
|
for (let i = 0; i < this.channels.length; i++) {
|
|
568
|
-
this.stopChannelNotes(i, velocity,
|
|
613
|
+
this.stopChannelNotes(i, velocity, force);
|
|
569
614
|
}
|
|
570
615
|
return Promise.all(this.notePromises);
|
|
571
616
|
}
|
|
@@ -657,14 +702,14 @@ export class Midy {
|
|
|
657
702
|
return impulse;
|
|
658
703
|
}
|
|
659
704
|
createConvolutionReverb(audioContext, impulse) {
|
|
660
|
-
const
|
|
705
|
+
const input = new GainNode(audioContext);
|
|
661
706
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
662
707
|
buffer: impulse,
|
|
663
708
|
});
|
|
664
|
-
|
|
709
|
+
input.connect(convolverNode);
|
|
665
710
|
return {
|
|
666
|
-
input
|
|
667
|
-
output,
|
|
711
|
+
input,
|
|
712
|
+
output: convolverNode,
|
|
668
713
|
convolverNode,
|
|
669
714
|
};
|
|
670
715
|
}
|
|
@@ -706,7 +751,6 @@ export class Midy {
|
|
|
706
751
|
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
707
752
|
createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
|
|
708
753
|
const input = new GainNode(audioContext);
|
|
709
|
-
const output = new GainNode(audioContext);
|
|
710
754
|
const mergerGain = new GainNode(audioContext);
|
|
711
755
|
for (let i = 0; i < combDelays.length; i++) {
|
|
712
756
|
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
@@ -717,7 +761,7 @@ export class Midy {
|
|
|
717
761
|
const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
|
|
718
762
|
allpasses.push(allpass);
|
|
719
763
|
}
|
|
720
|
-
allpasses.at(-1)
|
|
764
|
+
const output = allpasses.at(-1);
|
|
721
765
|
return { input, output };
|
|
722
766
|
}
|
|
723
767
|
createChorusEffect(audioContext) {
|
|
@@ -779,6 +823,17 @@ export class Midy {
|
|
|
779
823
|
return instrumentKey.playbackRate(noteNumber) *
|
|
780
824
|
Math.pow(2, semitoneOffset / 12);
|
|
781
825
|
}
|
|
826
|
+
setPortamentoStartVolumeEnvelope(channel, note) {
|
|
827
|
+
const { instrumentKey, startTime } = note;
|
|
828
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
829
|
+
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
830
|
+
const volDelay = startTime + instrumentKey.volDelay;
|
|
831
|
+
const portamentoTime = volDelay + channel.portamentoTime;
|
|
832
|
+
note.volumeNode.gain
|
|
833
|
+
.cancelScheduledValues(startTime)
|
|
834
|
+
.setValueAtTime(0, volDelay)
|
|
835
|
+
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
836
|
+
}
|
|
782
837
|
setVolumeEnvelope(channel, note) {
|
|
783
838
|
const { instrumentKey, startTime } = note;
|
|
784
839
|
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
@@ -818,6 +873,25 @@ export class Midy {
|
|
|
818
873
|
const maxFrequency = 20000; // max Hz of initialFilterFc
|
|
819
874
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
820
875
|
}
|
|
876
|
+
setPortamentoStartFilterEnvelope(channel, note) {
|
|
877
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
878
|
+
const softPedalFactor = 1 -
|
|
879
|
+
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
880
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
881
|
+
softPedalFactor * channel.brightness;
|
|
882
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor * channel.brightness;
|
|
883
|
+
const sustainFreq = baseFreq +
|
|
884
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
885
|
+
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
886
|
+
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
887
|
+
const portamentoTime = startTime + channel.portamentoTime;
|
|
888
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
889
|
+
note.filterNode.frequency
|
|
890
|
+
.cancelScheduledValues(startTime)
|
|
891
|
+
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
892
|
+
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
893
|
+
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
894
|
+
}
|
|
821
895
|
setFilterEnvelope(channel, note) {
|
|
822
896
|
const { instrumentKey, noteNumber, startTime } = note;
|
|
823
897
|
const softPedalFactor = 1 -
|
|
@@ -885,7 +959,7 @@ export class Midy {
|
|
|
885
959
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
886
960
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
887
961
|
}
|
|
888
|
-
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
962
|
+
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3) {
|
|
889
963
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
890
964
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
891
965
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
@@ -894,8 +968,14 @@ export class Midy {
|
|
|
894
968
|
type: "lowpass",
|
|
895
969
|
Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
|
|
896
970
|
});
|
|
897
|
-
|
|
898
|
-
|
|
971
|
+
if (portamento) {
|
|
972
|
+
this.setPortamentoStartVolumeEnvelope(channel, note);
|
|
973
|
+
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
this.setVolumeEnvelope(channel, note);
|
|
977
|
+
this.setFilterEnvelope(channel, note);
|
|
978
|
+
}
|
|
899
979
|
if (0 < channel.vibratoDepth) {
|
|
900
980
|
this.startVibrato(channel, note, startTime);
|
|
901
981
|
}
|
|
@@ -912,7 +992,21 @@ export class Midy {
|
|
|
912
992
|
}
|
|
913
993
|
note.bufferSource.connect(note.filterNode);
|
|
914
994
|
note.filterNode.connect(note.volumeNode);
|
|
915
|
-
|
|
995
|
+
if (0 < channel.reverbSendLevel && 0 < instrumentKey.reverbEffectsSend) {
|
|
996
|
+
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
997
|
+
gain: instrumentKey.reverbEffectsSend,
|
|
998
|
+
});
|
|
999
|
+
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1000
|
+
note.reverbEffectsSend.connect(this.reverbEffect.input);
|
|
1001
|
+
}
|
|
1002
|
+
if (0 < channel.chorusSendLevel && 0 < instrumentKey.chorusEffectsSend) {
|
|
1003
|
+
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1004
|
+
gain: instrumentKey.chorusEffectsSend,
|
|
1005
|
+
});
|
|
1006
|
+
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1007
|
+
note.chorusEffectsSend.connect(this.chorusEffect.input);
|
|
1008
|
+
}
|
|
1009
|
+
note.bufferSource.start(startTime);
|
|
916
1010
|
return note;
|
|
917
1011
|
}
|
|
918
1012
|
calcBank(channel, channelNumber) {
|
|
@@ -924,7 +1018,7 @@ export class Midy {
|
|
|
924
1018
|
}
|
|
925
1019
|
return channel.bank;
|
|
926
1020
|
}
|
|
927
|
-
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
1021
|
+
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, portamento) {
|
|
928
1022
|
const channel = this.channels[channelNumber];
|
|
929
1023
|
const bankNumber = this.calcBank(channel, channelNumber);
|
|
930
1024
|
const soundFontIndex = this.soundFontTable[channel.program].get(bankNumber);
|
|
@@ -935,12 +1029,25 @@ export class Midy {
|
|
|
935
1029
|
const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber, velocity);
|
|
936
1030
|
if (!instrumentKey)
|
|
937
1031
|
return;
|
|
938
|
-
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
1032
|
+
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, portamento, isSF3);
|
|
939
1033
|
note.volumeNode.connect(channel.gainL);
|
|
940
1034
|
note.volumeNode.connect(channel.gainR);
|
|
941
1035
|
if (channel.sostenutoPedal) {
|
|
942
1036
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
943
1037
|
}
|
|
1038
|
+
const exclusiveClass = instrumentKey.exclusiveClass;
|
|
1039
|
+
if (exclusiveClass !== 0) {
|
|
1040
|
+
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
1041
|
+
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
1042
|
+
const [prevNote, prevChannelNumber] = prevEntry;
|
|
1043
|
+
if (!prevNote.ending) {
|
|
1044
|
+
this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1045
|
+
startTime, undefined, // portamentoNoteNumber
|
|
1046
|
+
true);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
|
|
1050
|
+
}
|
|
944
1051
|
const scheduledNotes = channel.scheduledNotes;
|
|
945
1052
|
if (scheduledNotes.has(noteNumber)) {
|
|
946
1053
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -949,13 +1056,48 @@ export class Midy {
|
|
|
949
1056
|
scheduledNotes.set(noteNumber, [note]);
|
|
950
1057
|
}
|
|
951
1058
|
}
|
|
952
|
-
noteOn(channelNumber, noteNumber, velocity) {
|
|
1059
|
+
noteOn(channelNumber, noteNumber, velocity, portamento) {
|
|
953
1060
|
const now = this.audioContext.currentTime;
|
|
954
|
-
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
|
|
1061
|
+
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now, portamento);
|
|
1062
|
+
}
|
|
1063
|
+
stopNote(endTime, stopTime, scheduledNotes, index) {
|
|
1064
|
+
const note = scheduledNotes[index];
|
|
1065
|
+
note.volumeNode.gain
|
|
1066
|
+
.cancelScheduledValues(endTime)
|
|
1067
|
+
.linearRampToValueAtTime(0, stopTime);
|
|
1068
|
+
note.ending = true;
|
|
1069
|
+
this.scheduleTask(() => {
|
|
1070
|
+
note.bufferSource.loop = false;
|
|
1071
|
+
}, stopTime);
|
|
1072
|
+
return new Promise((resolve) => {
|
|
1073
|
+
note.bufferSource.onended = () => {
|
|
1074
|
+
scheduledNotes[index] = null;
|
|
1075
|
+
note.bufferSource.disconnect();
|
|
1076
|
+
note.volumeNode.disconnect();
|
|
1077
|
+
note.filterNode.disconnect();
|
|
1078
|
+
if (note.modulationDepth) {
|
|
1079
|
+
note.volumeDepth.disconnect();
|
|
1080
|
+
note.modulationDepth.disconnect();
|
|
1081
|
+
note.modulationLFO.stop();
|
|
1082
|
+
}
|
|
1083
|
+
if (note.vibratoDepth) {
|
|
1084
|
+
note.vibratoDepth.disconnect();
|
|
1085
|
+
note.vibratoLFO.stop();
|
|
1086
|
+
}
|
|
1087
|
+
if (note.reverbEffectsSend) {
|
|
1088
|
+
note.reverbEffectsSend.disconnect();
|
|
1089
|
+
}
|
|
1090
|
+
if (note.chorusEffectsSend) {
|
|
1091
|
+
note.chorusEffectsSend.disconnect();
|
|
1092
|
+
}
|
|
1093
|
+
resolve();
|
|
1094
|
+
};
|
|
1095
|
+
note.bufferSource.stop(stopTime);
|
|
1096
|
+
});
|
|
955
1097
|
}
|
|
956
|
-
scheduleNoteRelease(channelNumber, noteNumber, _velocity,
|
|
1098
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, portamentoNoteNumber, force) {
|
|
957
1099
|
const channel = this.channels[channelNumber];
|
|
958
|
-
if (
|
|
1100
|
+
if (!force) {
|
|
959
1101
|
if (channel.sustainPedal)
|
|
960
1102
|
return;
|
|
961
1103
|
if (channel.sostenutoNotes.has(noteNumber))
|
|
@@ -970,43 +1112,30 @@ export class Midy {
|
|
|
970
1112
|
continue;
|
|
971
1113
|
if (note.ending)
|
|
972
1114
|
continue;
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
.
|
|
977
|
-
.
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
.
|
|
981
|
-
.
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
note.bufferSource.
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
if (note.modulationDepth) {
|
|
993
|
-
note.volumeDepth.disconnect();
|
|
994
|
-
note.modulationDepth.disconnect();
|
|
995
|
-
note.modulationLFO.stop();
|
|
996
|
-
}
|
|
997
|
-
if (note.vibratoDepth) {
|
|
998
|
-
note.vibratoDepth.disconnect();
|
|
999
|
-
note.vibratoLFO.stop();
|
|
1000
|
-
}
|
|
1001
|
-
resolve();
|
|
1002
|
-
};
|
|
1003
|
-
note.bufferSource.stop(volEndTime);
|
|
1004
|
-
});
|
|
1115
|
+
if (portamentoNoteNumber === undefined) {
|
|
1116
|
+
const volRelease = endTime +
|
|
1117
|
+
note.instrumentKey.volRelease * channel.releaseTime;
|
|
1118
|
+
const modRelease = endTime + note.instrumentKey.modRelease;
|
|
1119
|
+
note.filterNode.frequency
|
|
1120
|
+
.cancelScheduledValues(endTime)
|
|
1121
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
1122
|
+
const stopTime = Math.min(volRelease, modRelease);
|
|
1123
|
+
return this.stopNote(endTime, stopTime, scheduledNotes, i);
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
const portamentoTime = endTime + channel.portamentoTime;
|
|
1127
|
+
const detuneChange = (portamentoNoteNumber - noteNumber) * 100;
|
|
1128
|
+
const detune = note.bufferSource.detune.value + detuneChange;
|
|
1129
|
+
note.bufferSource.detune
|
|
1130
|
+
.cancelScheduledValues(endTime)
|
|
1131
|
+
.linearRampToValueAtTime(detune, portamentoTime);
|
|
1132
|
+
return this.stopNote(endTime, portamentoTime, scheduledNotes, i);
|
|
1133
|
+
}
|
|
1005
1134
|
}
|
|
1006
1135
|
}
|
|
1007
|
-
releaseNote(channelNumber, noteNumber, velocity) {
|
|
1136
|
+
releaseNote(channelNumber, noteNumber, velocity, portamentoNoteNumber) {
|
|
1008
1137
|
const now = this.audioContext.currentTime;
|
|
1009
|
-
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now);
|
|
1138
|
+
return this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, portamentoNoteNumber, false);
|
|
1010
1139
|
}
|
|
1011
1140
|
releaseSustainPedal(channelNumber, halfVelocity) {
|
|
1012
1141
|
const velocity = halfVelocity * 2;
|
|
@@ -1047,7 +1176,7 @@ export class Midy {
|
|
|
1047
1176
|
case 0x90:
|
|
1048
1177
|
return this.noteOn(channelNumber, data1, data2);
|
|
1049
1178
|
case 0xA0:
|
|
1050
|
-
return
|
|
1179
|
+
return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
|
|
1051
1180
|
case 0xB0:
|
|
1052
1181
|
return this.handleControlChange(channelNumber, data1, data2);
|
|
1053
1182
|
case 0xC0:
|
|
@@ -1181,12 +1310,14 @@ export class Midy {
|
|
|
1181
1310
|
this.updateModulation(channel);
|
|
1182
1311
|
}
|
|
1183
1312
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1184
|
-
this.channels[channelNumber]
|
|
1313
|
+
const channel = this.channels[channelNumber];
|
|
1314
|
+
const factor = 5 * Math.log(10) / 127;
|
|
1315
|
+
channel.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1185
1316
|
}
|
|
1186
1317
|
setVolume(channelNumber, volume) {
|
|
1187
1318
|
const channel = this.channels[channelNumber];
|
|
1188
1319
|
channel.volume = volume / 127;
|
|
1189
|
-
this.
|
|
1320
|
+
this.updateChannelVolume(channel);
|
|
1190
1321
|
}
|
|
1191
1322
|
panToGain(pan) {
|
|
1192
1323
|
const theta = Math.PI / 2 * Math.max(0, pan - 1) / 126;
|
|
@@ -1198,12 +1329,12 @@ export class Midy {
|
|
|
1198
1329
|
setPan(channelNumber, pan) {
|
|
1199
1330
|
const channel = this.channels[channelNumber];
|
|
1200
1331
|
channel.pan = pan;
|
|
1201
|
-
this.
|
|
1332
|
+
this.updateChannelVolume(channel);
|
|
1202
1333
|
}
|
|
1203
1334
|
setExpression(channelNumber, expression) {
|
|
1204
1335
|
const channel = this.channels[channelNumber];
|
|
1205
1336
|
channel.expression = expression / 127;
|
|
1206
|
-
this.
|
|
1337
|
+
this.updateChannelVolume(channel);
|
|
1207
1338
|
}
|
|
1208
1339
|
setBankLSB(channelNumber, lsb) {
|
|
1209
1340
|
this.channels[channelNumber].bankLSB = lsb;
|
|
@@ -1212,7 +1343,7 @@ export class Midy {
|
|
|
1212
1343
|
this.channels[channelNumber].dataLSB = value;
|
|
1213
1344
|
this.handleRPN(channelNumber, 0);
|
|
1214
1345
|
}
|
|
1215
|
-
|
|
1346
|
+
updateChannelVolume(channel) {
|
|
1216
1347
|
const now = this.audioContext.currentTime;
|
|
1217
1348
|
const volume = channel.volume * channel.expression;
|
|
1218
1349
|
const { gainLeft, gainRight } = this.panToGain(channel.pan);
|
|
@@ -1230,30 +1361,54 @@ export class Midy {
|
|
|
1230
1361
|
this.releaseSustainPedal(channelNumber, value);
|
|
1231
1362
|
}
|
|
1232
1363
|
}
|
|
1233
|
-
// TODO
|
|
1234
1364
|
setPortamento(channelNumber, value) {
|
|
1235
1365
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1236
1366
|
}
|
|
1237
1367
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1238
1368
|
const channel = this.channels[channelNumber];
|
|
1369
|
+
const reverbEffect = this.reverbEffect;
|
|
1239
1370
|
if (0 < channel.reverbSendLevel) {
|
|
1240
1371
|
if (0 < reverbSendLevel) {
|
|
1241
1372
|
const now = this.audioContext.currentTime;
|
|
1242
1373
|
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1243
|
-
reverbEffect.
|
|
1244
|
-
reverbEffect.
|
|
1374
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1375
|
+
reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1245
1376
|
}
|
|
1246
1377
|
else {
|
|
1247
|
-
channel.
|
|
1378
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1379
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1380
|
+
const note = noteList[i];
|
|
1381
|
+
if (!note)
|
|
1382
|
+
continue;
|
|
1383
|
+
if (note.instrumentKey.reverbEffectsSend <= 0)
|
|
1384
|
+
continue;
|
|
1385
|
+
note.reverbEffectsSend.disconnect();
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1248
1388
|
}
|
|
1249
1389
|
}
|
|
1250
1390
|
else {
|
|
1251
1391
|
if (0 < reverbSendLevel) {
|
|
1252
|
-
channel.merger.connect(reverbEffect.input);
|
|
1253
1392
|
const now = this.audioContext.currentTime;
|
|
1393
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1394
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1395
|
+
const note = noteList[i];
|
|
1396
|
+
if (!note)
|
|
1397
|
+
continue;
|
|
1398
|
+
if (note.instrumentKey.reverbEffectsSend <= 0)
|
|
1399
|
+
continue;
|
|
1400
|
+
if (!note.reverbEffectsSend) {
|
|
1401
|
+
note.reverbEffectsSend = new GainNode(this.audioContext, {
|
|
1402
|
+
gain: note.instrumentKey.reverbEffectsSend,
|
|
1403
|
+
});
|
|
1404
|
+
note.volumeNode.connect(note.reverbEffectsSend);
|
|
1405
|
+
}
|
|
1406
|
+
note.reverbEffectsSend.connect(reverbEffect.input);
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1254
1409
|
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1255
|
-
reverbEffect.
|
|
1256
|
-
reverbEffect.
|
|
1410
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1411
|
+
reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1257
1412
|
}
|
|
1258
1413
|
}
|
|
1259
1414
|
}
|
|
@@ -1264,20 +1419,44 @@ export class Midy {
|
|
|
1264
1419
|
if (0 < chorusSendLevel) {
|
|
1265
1420
|
const now = this.audioContext.currentTime;
|
|
1266
1421
|
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1267
|
-
chorusEffect.
|
|
1268
|
-
chorusEffect.
|
|
1422
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1423
|
+
chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1269
1424
|
}
|
|
1270
1425
|
else {
|
|
1271
|
-
channel.
|
|
1426
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1427
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1428
|
+
const note = noteList[i];
|
|
1429
|
+
if (!note)
|
|
1430
|
+
continue;
|
|
1431
|
+
if (note.instrumentKey.chorusEffectsSend <= 0)
|
|
1432
|
+
continue;
|
|
1433
|
+
note.chorusEffectsSend.disconnect();
|
|
1434
|
+
}
|
|
1435
|
+
});
|
|
1272
1436
|
}
|
|
1273
1437
|
}
|
|
1274
1438
|
else {
|
|
1275
1439
|
if (0 < chorusSendLevel) {
|
|
1276
|
-
channel.merger.connect(chorusEffect.input);
|
|
1277
1440
|
const now = this.audioContext.currentTime;
|
|
1441
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1442
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1443
|
+
const note = noteList[i];
|
|
1444
|
+
if (!note)
|
|
1445
|
+
continue;
|
|
1446
|
+
if (note.instrumentKey.chorusEffectsSend <= 0)
|
|
1447
|
+
continue;
|
|
1448
|
+
if (!note.chorusEffectsSend) {
|
|
1449
|
+
note.chorusEffectsSend = new GainNode(this.audioContext, {
|
|
1450
|
+
gain: note.instrumentKey.chorusEffectsSend,
|
|
1451
|
+
});
|
|
1452
|
+
note.volumeNode.connect(note.chorusEffectsSend);
|
|
1453
|
+
}
|
|
1454
|
+
note.chorusEffectsSend.connect(chorusEffect.input);
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1278
1457
|
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1279
|
-
chorusEffect.
|
|
1280
|
-
chorusEffect.
|
|
1458
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1459
|
+
chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1281
1460
|
}
|
|
1282
1461
|
}
|
|
1283
1462
|
}
|
|
@@ -1554,20 +1733,22 @@ export class Midy {
|
|
|
1554
1733
|
}
|
|
1555
1734
|
}
|
|
1556
1735
|
GM1SystemOn() {
|
|
1557
|
-
this.channels.
|
|
1736
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1737
|
+
const channel = this.channels[i];
|
|
1558
1738
|
channel.bankMSB = 0;
|
|
1559
1739
|
channel.bankLSB = 0;
|
|
1560
1740
|
channel.bank = 0;
|
|
1561
|
-
}
|
|
1741
|
+
}
|
|
1562
1742
|
this.channels[9].bankMSB = 1;
|
|
1563
1743
|
this.channels[9].bank = 128;
|
|
1564
1744
|
}
|
|
1565
1745
|
GM2SystemOn() {
|
|
1566
|
-
this.channels.
|
|
1746
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
1747
|
+
const channel = this.channels[i];
|
|
1567
1748
|
channel.bankMSB = 121;
|
|
1568
1749
|
channel.bankLSB = 0;
|
|
1569
1750
|
channel.bank = 121 * 128;
|
|
1570
|
-
}
|
|
1751
|
+
}
|
|
1571
1752
|
this.channels[9].bankMSB = 120;
|
|
1572
1753
|
this.channels[9].bank = 120 * 128;
|
|
1573
1754
|
}
|
|
@@ -1708,10 +1889,8 @@ export class Midy {
|
|
|
1708
1889
|
}
|
|
1709
1890
|
setReverbTime(value) {
|
|
1710
1891
|
this.reverb.time = this.getReverbTime(value);
|
|
1711
|
-
const { audioContext,
|
|
1712
|
-
|
|
1713
|
-
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1714
|
-
}
|
|
1892
|
+
const { audioContext, options } = this;
|
|
1893
|
+
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1715
1894
|
}
|
|
1716
1895
|
getReverbTime(value) {
|
|
1717
1896
|
return Math.pow(Math.E, (value - 40) * 0.025);
|
|
@@ -1788,10 +1967,7 @@ export class Midy {
|
|
|
1788
1967
|
const now = this.audioContext.currentTime;
|
|
1789
1968
|
const modRate = this.getChorusModRate(value);
|
|
1790
1969
|
this.chorus.modRate = modRate;
|
|
1791
|
-
|
|
1792
|
-
const lfo = this.channels[i].chorusEffect.lfo;
|
|
1793
|
-
lfo.frequency.setValueAtTime(modRate, now);
|
|
1794
|
-
}
|
|
1970
|
+
this.chorusEffect.lfo.frequency.setValueAtTime(modRate, now);
|
|
1795
1971
|
}
|
|
1796
1972
|
getChorusModRate(value) {
|
|
1797
1973
|
return value * 0.122; // Hz
|
|
@@ -1800,12 +1976,9 @@ export class Midy {
|
|
|
1800
1976
|
const now = this.audioContext.currentTime;
|
|
1801
1977
|
const modDepth = this.getChorusModDepth(value);
|
|
1802
1978
|
this.chorus.modDepth = modDepth;
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
.cancelScheduledValues(now)
|
|
1807
|
-
.setValueAtTime(modDepth / 2, now);
|
|
1808
|
-
}
|
|
1979
|
+
this.chorusEffect.lfoGain.gain
|
|
1980
|
+
.cancelScheduledValues(now)
|
|
1981
|
+
.setValueAtTime(modDepth / 2, now);
|
|
1809
1982
|
}
|
|
1810
1983
|
getChorusModDepth(value) {
|
|
1811
1984
|
return (value + 1) / 3200; // second
|
|
@@ -1814,14 +1987,11 @@ export class Midy {
|
|
|
1814
1987
|
const now = this.audioContext.currentTime;
|
|
1815
1988
|
const feedback = this.getChorusFeedback(value);
|
|
1816
1989
|
this.chorus.feedback = feedback;
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
.cancelScheduledValues(now)
|
|
1823
|
-
.setValueAtTime(feedback, now);
|
|
1824
|
-
}
|
|
1990
|
+
const chorusEffect = this.chorusEffect;
|
|
1991
|
+
for (let i = 0; i < chorusEffect.feedbackGains.length; i++) {
|
|
1992
|
+
chorusEffect.feedbackGains[i].gain
|
|
1993
|
+
.cancelScheduledValues(now)
|
|
1994
|
+
.setValueAtTime(feedback, now);
|
|
1825
1995
|
}
|
|
1826
1996
|
}
|
|
1827
1997
|
getChorusFeedback(value) {
|
|
@@ -1829,15 +1999,28 @@ export class Midy {
|
|
|
1829
1999
|
}
|
|
1830
2000
|
setChorusSendToReverb(value) {
|
|
1831
2001
|
const sendToReverb = this.getChorusSendToReverb(value);
|
|
1832
|
-
|
|
1833
|
-
|
|
2002
|
+
const sendGain = this.chorusEffect.sendGain;
|
|
2003
|
+
if (0 < this.chorus.sendToReverb) {
|
|
1834
2004
|
this.chorus.sendToReverb = sendToReverb;
|
|
1835
|
-
|
|
1836
|
-
.
|
|
1837
|
-
.
|
|
2005
|
+
if (0 < sendToReverb) {
|
|
2006
|
+
const now = this.audioContext.currentTime;
|
|
2007
|
+
sendGain.gain
|
|
2008
|
+
.cancelScheduledValues(now)
|
|
2009
|
+
.setValueAtTime(sendToReverb, now);
|
|
2010
|
+
}
|
|
2011
|
+
else {
|
|
2012
|
+
sendGain.disconnect();
|
|
2013
|
+
}
|
|
1838
2014
|
}
|
|
1839
|
-
else
|
|
1840
|
-
this.
|
|
2015
|
+
else {
|
|
2016
|
+
this.chorus.sendToReverb = sendToReverb;
|
|
2017
|
+
if (0 < sendToReverb) {
|
|
2018
|
+
const now = this.audioContext.currentTime;
|
|
2019
|
+
sendGain.connect(this.reverbEffect.input);
|
|
2020
|
+
sendGain.gain
|
|
2021
|
+
.cancelScheduledValues(now)
|
|
2022
|
+
.setValueAtTime(sendToReverb, now);
|
|
2023
|
+
}
|
|
1841
2024
|
}
|
|
1842
2025
|
}
|
|
1843
2026
|
getChorusSendToReverb(value) {
|
|
@@ -1876,7 +2059,7 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
1876
2059
|
currentBufferSource: null,
|
|
1877
2060
|
volume: 100 / 127,
|
|
1878
2061
|
pan: 64,
|
|
1879
|
-
portamentoTime:
|
|
2062
|
+
portamentoTime: 1, // sec
|
|
1880
2063
|
filterResonance: 1,
|
|
1881
2064
|
releaseTime: 1,
|
|
1882
2065
|
attackTime: 1,
|