@marmooo/midy 0.3.5 → 0.3.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/README.md +7 -9
- package/esm/midy-GM1.d.ts +29 -26
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +136 -101
- package/esm/midy-GM2.d.ts +41 -37
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +325 -300
- package/esm/midy-GMLite.d.ts +29 -26
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +129 -96
- package/esm/midy.d.ts +43 -38
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +428 -325
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +29 -26
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +136 -101
- package/script/midy-GM2.d.ts +41 -37
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +325 -300
- package/script/midy-GMLite.d.ts +29 -26
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +129 -96
- package/script/midy.d.ts +43 -38
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +428 -325
package/esm/midy.js
CHANGED
|
@@ -68,13 +68,13 @@ class Note {
|
|
|
68
68
|
writable: true,
|
|
69
69
|
value: void 0
|
|
70
70
|
});
|
|
71
|
-
Object.defineProperty(this, "
|
|
71
|
+
Object.defineProperty(this, "reverbSend", {
|
|
72
72
|
enumerable: true,
|
|
73
73
|
configurable: true,
|
|
74
74
|
writable: true,
|
|
75
75
|
value: void 0
|
|
76
76
|
});
|
|
77
|
-
Object.defineProperty(this, "
|
|
77
|
+
Object.defineProperty(this, "chorusSend", {
|
|
78
78
|
enumerable: true,
|
|
79
79
|
configurable: true,
|
|
80
80
|
writable: true,
|
|
@@ -240,13 +240,13 @@ export class Midy {
|
|
|
240
240
|
configurable: true,
|
|
241
241
|
writable: true,
|
|
242
242
|
value: 0
|
|
243
|
-
}); //
|
|
243
|
+
}); // cent
|
|
244
244
|
Object.defineProperty(this, "masterCoarseTuning", {
|
|
245
245
|
enumerable: true,
|
|
246
246
|
configurable: true,
|
|
247
247
|
writable: true,
|
|
248
248
|
value: 0
|
|
249
|
-
}); //
|
|
249
|
+
}); // cent
|
|
250
250
|
Object.defineProperty(this, "reverb", {
|
|
251
251
|
enumerable: true,
|
|
252
252
|
configurable: true,
|
|
@@ -371,13 +371,13 @@ export class Midy {
|
|
|
371
371
|
writable: true,
|
|
372
372
|
value: false
|
|
373
373
|
});
|
|
374
|
-
Object.defineProperty(this, "
|
|
374
|
+
Object.defineProperty(this, "playPromise", {
|
|
375
375
|
enumerable: true,
|
|
376
376
|
configurable: true,
|
|
377
377
|
writable: true,
|
|
378
|
-
value:
|
|
378
|
+
value: void 0
|
|
379
379
|
});
|
|
380
|
-
Object.defineProperty(this, "
|
|
380
|
+
Object.defineProperty(this, "timeline", {
|
|
381
381
|
enumerable: true,
|
|
382
382
|
configurable: true,
|
|
383
383
|
writable: true,
|
|
@@ -389,6 +389,12 @@ export class Midy {
|
|
|
389
389
|
writable: true,
|
|
390
390
|
value: []
|
|
391
391
|
});
|
|
392
|
+
Object.defineProperty(this, "instruments", {
|
|
393
|
+
enumerable: true,
|
|
394
|
+
configurable: true,
|
|
395
|
+
writable: true,
|
|
396
|
+
value: new Set()
|
|
397
|
+
});
|
|
392
398
|
Object.defineProperty(this, "exclusiveClassNotes", {
|
|
393
399
|
enumerable: true,
|
|
394
400
|
configurable: true,
|
|
@@ -519,7 +525,7 @@ export class Midy {
|
|
|
519
525
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
520
526
|
const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
|
|
521
527
|
const { instrument, sampleID } = voice.generators;
|
|
522
|
-
return
|
|
528
|
+
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
523
529
|
}
|
|
524
530
|
createChannelAudioNodes(audioContext) {
|
|
525
531
|
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
@@ -540,7 +546,7 @@ export class Midy {
|
|
|
540
546
|
channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
|
|
541
547
|
channel.channelPressureTable.fill(-1);
|
|
542
548
|
channel.polyphonicKeyPressureTable.fill(-1);
|
|
543
|
-
channel.
|
|
549
|
+
channel.keyBasedTable.fill(-1);
|
|
544
550
|
}
|
|
545
551
|
createChannels(audioContext) {
|
|
546
552
|
const channels = Array.from({ length: this.numChannels }, () => {
|
|
@@ -557,7 +563,7 @@ export class Midy {
|
|
|
557
563
|
scaleOctaveTuningTable: new Float32Array(12), // [-100, 100] cent
|
|
558
564
|
channelPressureTable: new Int8Array(6).fill(-1),
|
|
559
565
|
polyphonicKeyPressureTable: new Int8Array(6).fill(-1),
|
|
560
|
-
|
|
566
|
+
keyBasedTable: new Int8Array(128 * 128).fill(-1),
|
|
561
567
|
keyBasedGainLs: new Array(128),
|
|
562
568
|
keyBasedGainRs: new Array(128),
|
|
563
569
|
};
|
|
@@ -635,72 +641,80 @@ export class Midy {
|
|
|
635
641
|
}
|
|
636
642
|
return 0;
|
|
637
643
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
644
|
+
resetAllStates() {
|
|
645
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
646
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
647
|
+
this.voiceCache.clear();
|
|
648
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
649
|
+
this.channels[i].scheduledNotes = [];
|
|
650
|
+
this.resetChannelStates(i);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
updateStates(queueIndex, nextQueueIndex) {
|
|
654
|
+
if (nextQueueIndex < queueIndex)
|
|
655
|
+
queueIndex = 0;
|
|
656
|
+
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
657
|
+
const event = this.timeline[i];
|
|
658
|
+
switch (event.type) {
|
|
659
|
+
case "controller":
|
|
660
|
+
this.setControlChange(event.channel, event.controllerType, event.value, 0);
|
|
661
|
+
break;
|
|
662
|
+
case "programChange":
|
|
663
|
+
this.setProgramChange(event.channel, event.programNumber, 0);
|
|
664
|
+
break;
|
|
665
|
+
case "pitchBend":
|
|
666
|
+
this.setPitchBend(event.channel, event.value + 8192, 0);
|
|
667
|
+
break;
|
|
668
|
+
case "sysEx":
|
|
669
|
+
this.handleSysEx(event.data, 0);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
async playNotes() {
|
|
674
|
+
if (this.audioContext.state === "suspended") {
|
|
675
|
+
await this.audioContext.resume();
|
|
676
|
+
}
|
|
677
|
+
this.isPlaying = true;
|
|
678
|
+
this.isPaused = false;
|
|
679
|
+
this.startTime = this.audioContext.currentTime;
|
|
680
|
+
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
681
|
+
let resumeTime = this.resumeTime - this.startTime;
|
|
682
|
+
let finished = false;
|
|
683
|
+
this.notePromises = [];
|
|
684
|
+
while (queueIndex < this.timeline.length) {
|
|
685
|
+
const now = this.audioContext.currentTime;
|
|
686
|
+
const t = now + resumeTime;
|
|
687
|
+
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
688
|
+
if (this.isPausing) {
|
|
689
|
+
await this.stopNotes(0, true, now);
|
|
690
|
+
await this.audioContext.suspend();
|
|
691
|
+
this.notePromises = [];
|
|
692
|
+
break;
|
|
693
|
+
}
|
|
694
|
+
else if (this.isStopping) {
|
|
695
|
+
await this.stopNotes(0, true, now);
|
|
696
|
+
await this.audioContext.suspend();
|
|
697
|
+
finished = true;
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
else if (this.isSeeking) {
|
|
701
|
+
await this.stopNotes(0, true, now);
|
|
702
|
+
this.startTime = this.audioContext.currentTime;
|
|
703
|
+
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
704
|
+
this.updateStates(queueIndex, nextQueueIndex);
|
|
705
|
+
queueIndex = nextQueueIndex;
|
|
706
|
+
resumeTime = this.resumeTime - this.startTime;
|
|
707
|
+
this.isSeeking = false;
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
const waitTime = now + this.noteCheckInterval;
|
|
711
|
+
await this.scheduleTask(() => { }, waitTime);
|
|
712
|
+
}
|
|
713
|
+
if (finished) {
|
|
645
714
|
this.notePromises = [];
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
this.notePromises = [];
|
|
650
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
651
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
652
|
-
this.voiceCache.clear();
|
|
653
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
654
|
-
this.channels[i].scheduledNotes = [];
|
|
655
|
-
this.resetAllStates(i);
|
|
656
|
-
}
|
|
657
|
-
resolve();
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
const now = this.audioContext.currentTime;
|
|
661
|
-
const t = now + resumeTime;
|
|
662
|
-
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
663
|
-
if (this.isPausing) {
|
|
664
|
-
await this.stopNotes(0, true, now);
|
|
665
|
-
this.notePromises = [];
|
|
666
|
-
this.isPausing = false;
|
|
667
|
-
this.isPaused = true;
|
|
668
|
-
resolve();
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
else if (this.isStopping) {
|
|
672
|
-
await this.stopNotes(0, true, now);
|
|
673
|
-
this.notePromises = [];
|
|
674
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
675
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
676
|
-
this.voiceCache.clear();
|
|
677
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
678
|
-
this.channels[i].scheduledNotes = [];
|
|
679
|
-
this.resetAllStates(i);
|
|
680
|
-
}
|
|
681
|
-
this.isStopping = false;
|
|
682
|
-
this.isPaused = false;
|
|
683
|
-
resolve();
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
else if (this.isSeeking) {
|
|
687
|
-
this.stopNotes(0, true, now);
|
|
688
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
689
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
690
|
-
this.startTime = this.audioContext.currentTime;
|
|
691
|
-
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
692
|
-
resumeTime = this.resumeTime - this.startTime;
|
|
693
|
-
this.isSeeking = false;
|
|
694
|
-
await schedulePlayback();
|
|
695
|
-
}
|
|
696
|
-
else {
|
|
697
|
-
const waitTime = now + this.noteCheckInterval;
|
|
698
|
-
await this.scheduleTask(() => { }, waitTime);
|
|
699
|
-
await schedulePlayback();
|
|
700
|
-
}
|
|
701
|
-
};
|
|
702
|
-
schedulePlayback();
|
|
703
|
-
});
|
|
715
|
+
this.resetAllStates();
|
|
716
|
+
}
|
|
717
|
+
this.isPlaying = false;
|
|
704
718
|
}
|
|
705
719
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
706
720
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -837,26 +851,32 @@ export class Midy {
|
|
|
837
851
|
this.resumeTime = 0;
|
|
838
852
|
if (this.voiceCounter.size === 0)
|
|
839
853
|
this.cacheVoiceIds();
|
|
840
|
-
|
|
841
|
-
this.
|
|
854
|
+
this.playPromise = this.playNotes();
|
|
855
|
+
await this.playPromise;
|
|
842
856
|
}
|
|
843
|
-
stop() {
|
|
857
|
+
async stop() {
|
|
844
858
|
if (!this.isPlaying)
|
|
845
859
|
return;
|
|
846
860
|
this.isStopping = true;
|
|
861
|
+
await this.playPromise;
|
|
862
|
+
this.isStopping = false;
|
|
847
863
|
}
|
|
848
|
-
pause() {
|
|
864
|
+
async pause() {
|
|
849
865
|
if (!this.isPlaying || this.isPaused)
|
|
850
866
|
return;
|
|
851
867
|
const now = this.audioContext.currentTime;
|
|
852
868
|
this.resumeTime += now - this.startTime - this.startDelay;
|
|
853
869
|
this.isPausing = true;
|
|
870
|
+
await this.playPromise;
|
|
871
|
+
this.isPausing = false;
|
|
872
|
+
this.isPaused = true;
|
|
854
873
|
}
|
|
855
874
|
async resume() {
|
|
856
875
|
if (!this.isPaused)
|
|
857
876
|
return;
|
|
858
|
-
|
|
859
|
-
this.
|
|
877
|
+
this.playPromise = this.playNotes();
|
|
878
|
+
await this.playPromise;
|
|
879
|
+
this.isPaused = false;
|
|
860
880
|
}
|
|
861
881
|
seekTo(second) {
|
|
862
882
|
this.resumeTime = second;
|
|
@@ -924,13 +944,11 @@ export class Midy {
|
|
|
924
944
|
return impulse;
|
|
925
945
|
}
|
|
926
946
|
createConvolutionReverb(audioContext, impulse) {
|
|
927
|
-
const input = new GainNode(audioContext);
|
|
928
947
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
929
948
|
buffer: impulse,
|
|
930
949
|
});
|
|
931
|
-
input.connect(convolverNode);
|
|
932
950
|
return {
|
|
933
|
-
input,
|
|
951
|
+
input: convolverNode,
|
|
934
952
|
output: convolverNode,
|
|
935
953
|
convolverNode,
|
|
936
954
|
};
|
|
@@ -1166,28 +1184,29 @@ export class Midy {
|
|
|
1166
1184
|
return Math.exp(y);
|
|
1167
1185
|
}
|
|
1168
1186
|
setPortamentoVolumeEnvelope(channel, note, scheduleTime) {
|
|
1169
|
-
const state = channel.state;
|
|
1170
1187
|
const { voiceParams, startTime } = note;
|
|
1171
1188
|
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
|
|
1172
1189
|
(1 + this.getAmplitudeControl(channel, note));
|
|
1173
1190
|
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
1174
1191
|
const volDelay = startTime + voiceParams.volDelay;
|
|
1175
|
-
const
|
|
1192
|
+
const attackTime = this.getRelativeKeyBasedValue(channel, note, 73) * 2;
|
|
1193
|
+
const volAttack = volDelay + voiceParams.volAttack * attackTime;
|
|
1176
1194
|
const volHold = volAttack + voiceParams.volHold;
|
|
1177
1195
|
note.volumeEnvelopeNode.gain
|
|
1178
1196
|
.cancelScheduledValues(scheduleTime)
|
|
1179
1197
|
.setValueAtTime(sustainVolume, volHold);
|
|
1180
1198
|
}
|
|
1181
1199
|
setVolumeEnvelope(channel, note, scheduleTime) {
|
|
1182
|
-
const state = channel.state;
|
|
1183
1200
|
const { voiceParams, startTime } = note;
|
|
1184
1201
|
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
|
|
1185
1202
|
(1 + this.getAmplitudeControl(channel, note));
|
|
1186
1203
|
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
1187
1204
|
const volDelay = startTime + voiceParams.volDelay;
|
|
1188
|
-
const
|
|
1205
|
+
const attackTime = this.getRelativeKeyBasedValue(channel, note, 73) * 2;
|
|
1206
|
+
const volAttack = volDelay + voiceParams.volAttack * attackTime;
|
|
1189
1207
|
const volHold = volAttack + voiceParams.volHold;
|
|
1190
|
-
const
|
|
1208
|
+
const decayTime = this.getRelativeKeyBasedValue(channel, note, 75) * 2;
|
|
1209
|
+
const volDecay = volHold + voiceParams.volDecay * decayTime;
|
|
1191
1210
|
note.volumeEnvelopeNode.gain
|
|
1192
1211
|
.cancelScheduledValues(scheduleTime)
|
|
1193
1212
|
.setValueAtTime(0, startTime)
|
|
@@ -1230,14 +1249,13 @@ export class Midy {
|
|
|
1230
1249
|
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
1231
1250
|
}
|
|
1232
1251
|
setPortamentoFilterEnvelope(channel, note, scheduleTime) {
|
|
1233
|
-
const state = channel.state;
|
|
1234
1252
|
const { voiceParams, startTime } = note;
|
|
1235
1253
|
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1236
1254
|
const baseCent = voiceParams.initialFilterFc +
|
|
1237
1255
|
this.getFilterCutoffControl(channel, note);
|
|
1238
|
-
const
|
|
1239
|
-
|
|
1240
|
-
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) *
|
|
1256
|
+
const brightness = this.getRelativeKeyBasedValue(channel, note, 74) * 2;
|
|
1257
|
+
const baseFreq = this.centToHz(baseCent) * softPedalFactor * brightness;
|
|
1258
|
+
const peekFreq = this.centToHz(voiceParams.initialFilterFc + voiceParams.modEnvToFilterFc) * brightness;
|
|
1241
1259
|
const sustainFreq = baseFreq +
|
|
1242
1260
|
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
1243
1261
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
@@ -1251,15 +1269,14 @@ export class Midy {
|
|
|
1251
1269
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1252
1270
|
}
|
|
1253
1271
|
setFilterEnvelope(channel, note, scheduleTime) {
|
|
1254
|
-
const state = channel.state;
|
|
1255
1272
|
const { voiceParams, startTime } = note;
|
|
1256
1273
|
const softPedalFactor = this.getSoftPedalFactor(channel, note);
|
|
1257
1274
|
const baseCent = voiceParams.initialFilterFc +
|
|
1258
1275
|
this.getFilterCutoffControl(channel, note);
|
|
1259
|
-
const
|
|
1260
|
-
|
|
1276
|
+
const brightness = this.getRelativeKeyBasedValue(channel, note, 74) * 2;
|
|
1277
|
+
const baseFreq = this.centToHz(baseCent) * softPedalFactor * brightness;
|
|
1261
1278
|
const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
|
|
1262
|
-
softPedalFactor *
|
|
1279
|
+
softPedalFactor * brightness;
|
|
1263
1280
|
const sustainFreq = baseFreq +
|
|
1264
1281
|
(peekFreq - baseFreq) * (1 - voiceParams.modSustain);
|
|
1265
1282
|
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
@@ -1299,11 +1316,12 @@ export class Midy {
|
|
|
1299
1316
|
}
|
|
1300
1317
|
startVibrato(channel, note, scheduleTime) {
|
|
1301
1318
|
const { voiceParams } = note;
|
|
1302
|
-
const
|
|
1319
|
+
const vibratoRate = this.getRelativeKeyBasedValue(channel, note, 76) * 2;
|
|
1320
|
+
const vibratoDelay = this.getRelativeKeyBasedValue(channel, note, 78) * 2;
|
|
1303
1321
|
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
1304
|
-
frequency: this.centToHz(voiceParams.freqVibLFO) *
|
|
1322
|
+
frequency: this.centToHz(voiceParams.freqVibLFO) * vibratoRate,
|
|
1305
1323
|
});
|
|
1306
|
-
note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO *
|
|
1324
|
+
note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO * vibratoDelay);
|
|
1307
1325
|
note.vibratoDepth = new GainNode(this.audioContext);
|
|
1308
1326
|
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
1309
1327
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
@@ -1336,9 +1354,10 @@ export class Midy {
|
|
|
1336
1354
|
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams);
|
|
1337
1355
|
note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
|
|
1338
1356
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
1357
|
+
const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
|
|
1339
1358
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
1340
1359
|
type: "lowpass",
|
|
1341
|
-
Q: voiceParams.initialFilterQ / 5 *
|
|
1360
|
+
Q: voiceParams.initialFilterQ / 5 * filterResonance, // dB
|
|
1342
1361
|
});
|
|
1343
1362
|
const prevNote = channel.scheduledNotes.at(-1);
|
|
1344
1363
|
if (prevNote && prevNote.noteNumber !== noteNumber) {
|
|
@@ -1367,12 +1386,8 @@ export class Midy {
|
|
|
1367
1386
|
}
|
|
1368
1387
|
note.bufferSource.connect(note.filterNode);
|
|
1369
1388
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
}
|
|
1373
|
-
if (0 < state.reverbSendLevel) {
|
|
1374
|
-
this.setReverbEffectsSend(channel, note, 0, now);
|
|
1375
|
-
}
|
|
1389
|
+
this.setChorusSend(channel, note, now);
|
|
1390
|
+
this.setReverbSend(channel, note, now);
|
|
1376
1391
|
note.bufferSource.start(startTime);
|
|
1377
1392
|
return note;
|
|
1378
1393
|
}
|
|
@@ -1438,10 +1453,14 @@ export class Midy {
|
|
|
1438
1453
|
return;
|
|
1439
1454
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
|
|
1440
1455
|
if (channel.isDrum) {
|
|
1441
|
-
const
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1456
|
+
const { keyBasedGainLs, keyBasedGainRs } = channel;
|
|
1457
|
+
let gainL = keyBasedGainLs[noteNumber];
|
|
1458
|
+
let gainR = keyBasedGainRs[noteNumber];
|
|
1459
|
+
if (!gainL) {
|
|
1460
|
+
const audioNodes = this.createChannelAudioNodes(this.audioContext);
|
|
1461
|
+
gainL = keyBasedGainLs[noteNumber] = audioNodes.gainL;
|
|
1462
|
+
gainR = keyBasedGainRs[noteNumber] = audioNodes.gainR;
|
|
1463
|
+
}
|
|
1445
1464
|
note.volumeEnvelopeNode.connect(gainL);
|
|
1446
1465
|
note.volumeEnvelopeNode.connect(gainR);
|
|
1447
1466
|
}
|
|
@@ -1475,16 +1494,16 @@ export class Midy {
|
|
|
1475
1494
|
note.vibratoDepth.disconnect();
|
|
1476
1495
|
note.vibratoLFO.stop();
|
|
1477
1496
|
}
|
|
1478
|
-
if (note.
|
|
1479
|
-
note.
|
|
1497
|
+
if (note.reverbSend) {
|
|
1498
|
+
note.reverbSend.disconnect();
|
|
1480
1499
|
}
|
|
1481
|
-
if (note.
|
|
1482
|
-
note.
|
|
1500
|
+
if (note.chorusSend) {
|
|
1501
|
+
note.chorusSend.disconnect();
|
|
1483
1502
|
}
|
|
1484
1503
|
}
|
|
1485
1504
|
releaseNote(channel, note, endTime) {
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1505
|
+
const releaseTime = this.getRelativeKeyBasedValue(channel, note, 72) * 2;
|
|
1506
|
+
const volRelease = endTime + note.voiceParams.volRelease * releaseTime;
|
|
1488
1507
|
const modRelease = endTime + note.voiceParams.modRelease;
|
|
1489
1508
|
const stopTime = Math.min(volRelease, modRelease);
|
|
1490
1509
|
note.filterNode.frequency
|
|
@@ -1610,7 +1629,7 @@ export class Midy {
|
|
|
1610
1629
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1611
1630
|
if (note.noteNumber === noteNumber) {
|
|
1612
1631
|
note.pressure = pressure;
|
|
1613
|
-
this.
|
|
1632
|
+
this.setEffects(channel, note, table, scheduleTime);
|
|
1614
1633
|
}
|
|
1615
1634
|
});
|
|
1616
1635
|
this.applyVoiceParams(channel, 10);
|
|
@@ -1629,6 +1648,7 @@ export class Midy {
|
|
|
1629
1648
|
break;
|
|
1630
1649
|
}
|
|
1631
1650
|
}
|
|
1651
|
+
channel.keyBasedTable.fill(-1);
|
|
1632
1652
|
}
|
|
1633
1653
|
setChannelPressure(channelNumber, value, scheduleTime) {
|
|
1634
1654
|
const channel = this.channels[channelNumber];
|
|
@@ -1644,7 +1664,7 @@ export class Midy {
|
|
|
1644
1664
|
}
|
|
1645
1665
|
const table = channel.channelPressureTable;
|
|
1646
1666
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1647
|
-
this.
|
|
1667
|
+
this.setEffects(channel, note, table, scheduleTime);
|
|
1648
1668
|
});
|
|
1649
1669
|
this.applyVoiceParams(channel, 13);
|
|
1650
1670
|
}
|
|
@@ -1666,22 +1686,33 @@ export class Midy {
|
|
|
1666
1686
|
this.applyVoiceParams(channel, 14, scheduleTime);
|
|
1667
1687
|
}
|
|
1668
1688
|
setModLfoToPitch(channel, note, scheduleTime) {
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
.
|
|
1675
|
-
|
|
1689
|
+
if (note.modulationDepth) {
|
|
1690
|
+
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1691
|
+
this.getLFOPitchDepth(channel, note);
|
|
1692
|
+
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1693
|
+
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1694
|
+
note.modulationDepth.gain
|
|
1695
|
+
.cancelScheduledValues(scheduleTime)
|
|
1696
|
+
.setValueAtTime(depth, scheduleTime);
|
|
1697
|
+
}
|
|
1698
|
+
else {
|
|
1699
|
+
this.startModulation(channel, note, scheduleTime);
|
|
1700
|
+
}
|
|
1676
1701
|
}
|
|
1677
1702
|
setVibLfoToPitch(channel, note, scheduleTime) {
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
.
|
|
1684
|
-
.
|
|
1703
|
+
if (note.vibratoDepth) {
|
|
1704
|
+
const vibratoDepth = this.getKeyBasedValue(channel, note.noteNumber, 77) *
|
|
1705
|
+
2;
|
|
1706
|
+
const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
|
|
1707
|
+
const baseDepth = Math.abs(vibLfoToPitch) * vibratoDepth;
|
|
1708
|
+
const depth = baseDepth * Math.sign(vibLfoToPitch);
|
|
1709
|
+
note.vibratoDepth.gain
|
|
1710
|
+
.cancelScheduledValues(scheduleTime)
|
|
1711
|
+
.setValueAtTime(depth, scheduleTime);
|
|
1712
|
+
}
|
|
1713
|
+
else {
|
|
1714
|
+
this.startVibrato(channel, note, scheduleTime);
|
|
1715
|
+
}
|
|
1685
1716
|
}
|
|
1686
1717
|
setModLfoToFilterFc(channel, note, scheduleTime) {
|
|
1687
1718
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc +
|
|
@@ -1699,73 +1730,72 @@ export class Midy {
|
|
|
1699
1730
|
.cancelScheduledValues(scheduleTime)
|
|
1700
1731
|
.setValueAtTime(volumeDepth, scheduleTime);
|
|
1701
1732
|
}
|
|
1702
|
-
|
|
1703
|
-
let value = note.voiceParams.reverbEffectsSend
|
|
1733
|
+
setReverbSend(channel, note, scheduleTime) {
|
|
1734
|
+
let value = note.voiceParams.reverbEffectsSend *
|
|
1735
|
+
channel.state.reverbSendLevel;
|
|
1704
1736
|
if (channel.isDrum) {
|
|
1705
1737
|
const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 91);
|
|
1706
|
-
if (0 <= keyBasedValue)
|
|
1707
|
-
value
|
|
1708
|
-
}
|
|
1738
|
+
if (0 <= keyBasedValue)
|
|
1739
|
+
value = keyBasedValue / 127;
|
|
1709
1740
|
}
|
|
1710
|
-
if (
|
|
1741
|
+
if (!note.reverbSend) {
|
|
1711
1742
|
if (0 < value) {
|
|
1712
|
-
note.
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
}
|
|
1716
|
-
else {
|
|
1717
|
-
note.reverbEffectsSend.disconnect();
|
|
1743
|
+
note.reverbSend = new GainNode(this.audioContext, { gain: value });
|
|
1744
|
+
note.volumeEnvelopeNode.connect(note.reverbSend);
|
|
1745
|
+
note.reverbSend.connect(this.reverbEffect.input);
|
|
1718
1746
|
}
|
|
1719
1747
|
}
|
|
1720
1748
|
else {
|
|
1749
|
+
note.reverbSend.gain
|
|
1750
|
+
.cancelScheduledValues(scheduleTime)
|
|
1751
|
+
.setValueAtTime(value, scheduleTime);
|
|
1721
1752
|
if (0 < value) {
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
note.volumeEnvelopeNode.
|
|
1753
|
+
note.volumeEnvelopeNode.connect(note.reverbSend);
|
|
1754
|
+
}
|
|
1755
|
+
else {
|
|
1756
|
+
try {
|
|
1757
|
+
note.volumeEnvelopeNode.disconnect(note.reverbSend);
|
|
1727
1758
|
}
|
|
1728
|
-
|
|
1759
|
+
catch { /* empty */ }
|
|
1729
1760
|
}
|
|
1730
1761
|
}
|
|
1731
1762
|
}
|
|
1732
|
-
|
|
1733
|
-
let value = note.voiceParams.chorusEffectsSend
|
|
1763
|
+
setChorusSend(channel, note, scheduleTime) {
|
|
1764
|
+
let value = note.voiceParams.chorusEffectsSend *
|
|
1765
|
+
channel.state.chorusSendLevel;
|
|
1734
1766
|
if (channel.isDrum) {
|
|
1735
1767
|
const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 93);
|
|
1736
|
-
if (0 <= keyBasedValue)
|
|
1737
|
-
value
|
|
1738
|
-
}
|
|
1768
|
+
if (0 <= keyBasedValue)
|
|
1769
|
+
value = keyBasedValue / 127;
|
|
1739
1770
|
}
|
|
1740
|
-
if (
|
|
1771
|
+
if (!note.chorusSend) {
|
|
1741
1772
|
if (0 < value) {
|
|
1742
|
-
note.
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
}
|
|
1746
|
-
else {
|
|
1747
|
-
note.chorusEffectsSend.disconnect();
|
|
1773
|
+
note.chorusSend = new GainNode(this.audioContext, { gain: value });
|
|
1774
|
+
note.volumeEnvelopeNode.connect(note.chorusSend);
|
|
1775
|
+
note.chorusSend.connect(this.chorusEffect.input);
|
|
1748
1776
|
}
|
|
1749
1777
|
}
|
|
1750
1778
|
else {
|
|
1779
|
+
note.chorusSend.gain
|
|
1780
|
+
.cancelScheduledValues(scheduleTime)
|
|
1781
|
+
.setValueAtTime(value, scheduleTime);
|
|
1751
1782
|
if (0 < value) {
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
note.volumeEnvelopeNode.
|
|
1783
|
+
note.volumeEnvelopeNode.connect(note.chorusSend);
|
|
1784
|
+
}
|
|
1785
|
+
else {
|
|
1786
|
+
try {
|
|
1787
|
+
note.volumeEnvelopeNode.disconnect(note.chorusSend);
|
|
1757
1788
|
}
|
|
1758
|
-
|
|
1789
|
+
catch { /* empty */ }
|
|
1759
1790
|
}
|
|
1760
1791
|
}
|
|
1761
1792
|
}
|
|
1762
|
-
setDelayModLFO(note
|
|
1763
|
-
const startTime = note.startTime;
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
note.modulationLFO.connect(note.filterDepth);
|
|
1793
|
+
setDelayModLFO(note) {
|
|
1794
|
+
const startTime = note.startTime + note.voiceParams.delayModLFO;
|
|
1795
|
+
try {
|
|
1796
|
+
note.modulationLFO.start(startTime);
|
|
1797
|
+
}
|
|
1798
|
+
catch { /* empty */ }
|
|
1769
1799
|
}
|
|
1770
1800
|
setFreqModLFO(note, scheduleTime) {
|
|
1771
1801
|
const freqModLFO = note.voiceParams.freqModLFO;
|
|
@@ -1774,54 +1804,65 @@ export class Midy {
|
|
|
1774
1804
|
.setValueAtTime(freqModLFO, scheduleTime);
|
|
1775
1805
|
}
|
|
1776
1806
|
setFreqVibLFO(channel, note, scheduleTime) {
|
|
1807
|
+
const vibratoRate = this.getRelativeKeyBasedValue(channel, note, 76) * 2;
|
|
1777
1808
|
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1778
1809
|
note.vibratoLFO.frequency
|
|
1779
1810
|
.cancelScheduledValues(scheduleTime)
|
|
1780
|
-
.setValueAtTime(freqVibLFO *
|
|
1811
|
+
.setValueAtTime(freqVibLFO * vibratoRate, scheduleTime);
|
|
1812
|
+
}
|
|
1813
|
+
setDelayVibLFO(channel, note) {
|
|
1814
|
+
const vibratoDelay = this.getRelativeKeyBasedValue(channel, note, 78) * 2;
|
|
1815
|
+
const value = note.voiceParams.delayVibLFO;
|
|
1816
|
+
const startTime = note.startTime + value * vibratoDelay;
|
|
1817
|
+
try {
|
|
1818
|
+
note.vibratoLFO.start(startTime);
|
|
1819
|
+
}
|
|
1820
|
+
catch { /* empty */ }
|
|
1781
1821
|
}
|
|
1782
1822
|
createVoiceParamsHandlers() {
|
|
1783
1823
|
return {
|
|
1784
|
-
modLfoToPitch: (channel, note,
|
|
1824
|
+
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1785
1825
|
if (0 < channel.state.modulationDepth) {
|
|
1786
1826
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1787
1827
|
}
|
|
1788
1828
|
},
|
|
1789
|
-
vibLfoToPitch: (channel, note,
|
|
1829
|
+
vibLfoToPitch: (channel, note, scheduleTime) => {
|
|
1790
1830
|
if (0 < channel.state.vibratoDepth) {
|
|
1791
1831
|
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
1792
1832
|
}
|
|
1793
1833
|
},
|
|
1794
|
-
modLfoToFilterFc: (channel, note,
|
|
1834
|
+
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1795
1835
|
if (0 < channel.state.modulationDepth) {
|
|
1796
1836
|
this.setModLfoToFilterFc(channel, note, scheduleTime);
|
|
1797
1837
|
}
|
|
1798
1838
|
},
|
|
1799
|
-
modLfoToVolume: (channel, note,
|
|
1839
|
+
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1800
1840
|
if (0 < channel.state.modulationDepth) {
|
|
1801
1841
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
1802
1842
|
}
|
|
1803
1843
|
},
|
|
1804
|
-
chorusEffectsSend: (channel, note,
|
|
1805
|
-
this.
|
|
1844
|
+
chorusEffectsSend: (channel, note, scheduleTime) => {
|
|
1845
|
+
this.setChorusSend(channel, note, scheduleTime);
|
|
1806
1846
|
},
|
|
1807
|
-
reverbEffectsSend: (channel, note,
|
|
1808
|
-
this.
|
|
1847
|
+
reverbEffectsSend: (channel, note, scheduleTime) => {
|
|
1848
|
+
this.setReverbSend(channel, note, scheduleTime);
|
|
1809
1849
|
},
|
|
1810
|
-
delayModLFO: (_channel, note,
|
|
1811
|
-
|
|
1812
|
-
|
|
1850
|
+
delayModLFO: (_channel, note, _scheduleTime) => {
|
|
1851
|
+
if (0 < channel.state.modulationDepth) {
|
|
1852
|
+
this.setDelayModLFO(note);
|
|
1853
|
+
}
|
|
1854
|
+
},
|
|
1855
|
+
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1856
|
+
if (0 < channel.state.modulationDepth) {
|
|
1857
|
+
this.setFreqModLFO(note, scheduleTime);
|
|
1858
|
+
}
|
|
1859
|
+
},
|
|
1860
|
+
delayVibLFO: (channel, note, _scheduleTime) => {
|
|
1813
1861
|
if (0 < channel.state.vibratoDepth) {
|
|
1814
|
-
|
|
1815
|
-
const prevStartTime = note.startTime + prevValue * vibratoDelay;
|
|
1816
|
-
if (scheduleTime < prevStartTime)
|
|
1817
|
-
return;
|
|
1818
|
-
const value = note.voiceParams.delayVibLFO;
|
|
1819
|
-
const startTime = note.startTime + value * vibratoDelay;
|
|
1820
|
-
note.vibratoLFO.stop(scheduleTime);
|
|
1821
|
-
note.vibratoLFO.start(startTime);
|
|
1862
|
+
setDelayVibLFO(channel, note);
|
|
1822
1863
|
}
|
|
1823
1864
|
},
|
|
1824
|
-
freqVibLFO: (channel, note,
|
|
1865
|
+
freqVibLFO: (channel, note, scheduleTime) => {
|
|
1825
1866
|
if (0 < channel.state.vibratoDepth) {
|
|
1826
1867
|
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
1827
1868
|
}
|
|
@@ -1850,7 +1891,7 @@ export class Midy {
|
|
|
1850
1891
|
continue;
|
|
1851
1892
|
note.voiceParams[key] = value;
|
|
1852
1893
|
if (key in this.voiceParamsHandlers) {
|
|
1853
|
-
this.voiceParamsHandlers[key](channel, note,
|
|
1894
|
+
this.voiceParamsHandlers[key](channel, note, scheduleTime);
|
|
1854
1895
|
}
|
|
1855
1896
|
else {
|
|
1856
1897
|
if (volumeEnvelopeKeySet.has(key))
|
|
@@ -1915,7 +1956,7 @@ export class Midy {
|
|
|
1915
1956
|
handler.call(this, channelNumber, value, scheduleTime);
|
|
1916
1957
|
const channel = this.channels[channelNumber];
|
|
1917
1958
|
this.applyVoiceParams(channel, controllerType + 128, scheduleTime);
|
|
1918
|
-
this.
|
|
1959
|
+
this.setControlChangeEffects(channel, controllerType, scheduleTime);
|
|
1919
1960
|
}
|
|
1920
1961
|
else {
|
|
1921
1962
|
console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
|
|
@@ -1931,7 +1972,6 @@ export class Midy {
|
|
|
1931
1972
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1932
1973
|
}
|
|
1933
1974
|
else {
|
|
1934
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1935
1975
|
this.startModulation(channel, note, scheduleTime);
|
|
1936
1976
|
}
|
|
1937
1977
|
});
|
|
@@ -1976,8 +2016,14 @@ export class Midy {
|
|
|
1976
2016
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1977
2017
|
const channel = this.channels[channelNumber];
|
|
1978
2018
|
channel.state.volume = volume / 127;
|
|
1979
|
-
|
|
1980
|
-
|
|
2019
|
+
if (channel.isDrum) {
|
|
2020
|
+
for (let i = 0; i < 128; i++) {
|
|
2021
|
+
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
else {
|
|
2025
|
+
this.updateChannelVolume(channel, scheduleTime);
|
|
2026
|
+
}
|
|
1981
2027
|
}
|
|
1982
2028
|
panToGain(pan) {
|
|
1983
2029
|
const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
|
|
@@ -1990,8 +2036,14 @@ export class Midy {
|
|
|
1990
2036
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1991
2037
|
const channel = this.channels[channelNumber];
|
|
1992
2038
|
channel.state.pan = pan / 127;
|
|
1993
|
-
|
|
1994
|
-
|
|
2039
|
+
if (channel.isDrum) {
|
|
2040
|
+
for (let i = 0; i < 128; i++) {
|
|
2041
|
+
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
else {
|
|
2045
|
+
this.updateChannelVolume(channel, scheduleTime);
|
|
2046
|
+
}
|
|
1995
2047
|
}
|
|
1996
2048
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1997
2049
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -2017,33 +2069,27 @@ export class Midy {
|
|
|
2017
2069
|
.cancelScheduledValues(scheduleTime)
|
|
2018
2070
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
2019
2071
|
}
|
|
2020
|
-
updateKeyBasedVolume(channel, scheduleTime) {
|
|
2021
|
-
|
|
2072
|
+
updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
|
|
2073
|
+
const gainL = channel.keyBasedGainLs[keyNumber];
|
|
2074
|
+
if (!gainL)
|
|
2022
2075
|
return;
|
|
2076
|
+
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
2023
2077
|
const state = channel.state;
|
|
2024
2078
|
const defaultVolume = state.volume * state.expression;
|
|
2025
2079
|
const defaultPan = state.pan;
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2040
|
-
gainL.gain
|
|
2041
|
-
.cancelScheduledValues(scheduleTime)
|
|
2042
|
-
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
2043
|
-
gainR.gain
|
|
2044
|
-
.cancelScheduledValues(scheduleTime)
|
|
2045
|
-
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
2046
|
-
}
|
|
2080
|
+
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
2081
|
+
const volume = (0 <= keyBasedVolume)
|
|
2082
|
+
? defaultVolume * keyBasedVolume / 64
|
|
2083
|
+
: defaultVolume;
|
|
2084
|
+
const keyBasedPan = this.getKeyBasedValue(channel, keyNumber, 10);
|
|
2085
|
+
const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
|
|
2086
|
+
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2087
|
+
gainL.gain
|
|
2088
|
+
.cancelScheduledValues(scheduleTime)
|
|
2089
|
+
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
2090
|
+
gainR.gain
|
|
2091
|
+
.cancelScheduledValues(scheduleTime)
|
|
2092
|
+
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
2047
2093
|
}
|
|
2048
2094
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
2049
2095
|
const channel = this.channels[channelNumber];
|
|
@@ -2106,18 +2152,27 @@ export class Midy {
|
|
|
2106
2152
|
}
|
|
2107
2153
|
});
|
|
2108
2154
|
}
|
|
2109
|
-
setFilterResonance(channelNumber,
|
|
2155
|
+
setFilterResonance(channelNumber, ccValue, scheduleTime) {
|
|
2110
2156
|
const channel = this.channels[channelNumber];
|
|
2111
2157
|
if (channel.isDrum)
|
|
2112
2158
|
return;
|
|
2113
2159
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2114
2160
|
const state = channel.state;
|
|
2115
|
-
state.filterResonance =
|
|
2161
|
+
state.filterResonance = ccValue / 127;
|
|
2162
|
+
const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
|
|
2116
2163
|
this.processScheduledNotes(channel, (note) => {
|
|
2117
|
-
const Q = note.voiceParams.initialFilterQ / 5 *
|
|
2164
|
+
const Q = note.voiceParams.initialFilterQ / 5 * filterResonance;
|
|
2118
2165
|
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
2119
2166
|
});
|
|
2120
2167
|
}
|
|
2168
|
+
getRelativeKeyBasedValue(channel, note, controllerType) {
|
|
2169
|
+
const ccState = channel.state.array[128 + controllerType];
|
|
2170
|
+
const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, controllerType);
|
|
2171
|
+
if (keyBasedValue < 0)
|
|
2172
|
+
return ccState;
|
|
2173
|
+
const keyValue = ccState + keyBasedValue / 127 - 0.5;
|
|
2174
|
+
return keyValue < 0 ? keyValue : 0;
|
|
2175
|
+
}
|
|
2121
2176
|
setReleaseTime(channelNumber, releaseTime, scheduleTime) {
|
|
2122
2177
|
const channel = this.channels[channelNumber];
|
|
2123
2178
|
if (channel.isDrum)
|
|
@@ -2132,9 +2187,9 @@ export class Midy {
|
|
|
2132
2187
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2133
2188
|
channel.state.attackTime = attackTime / 127;
|
|
2134
2189
|
this.processScheduledNotes(channel, (note) => {
|
|
2135
|
-
if (note.startTime
|
|
2136
|
-
|
|
2137
|
-
|
|
2190
|
+
if (scheduleTime < note.startTime) {
|
|
2191
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
2192
|
+
}
|
|
2138
2193
|
});
|
|
2139
2194
|
}
|
|
2140
2195
|
setBrightness(channelNumber, brightness, scheduleTime) {
|
|
@@ -2209,67 +2264,19 @@ export class Midy {
|
|
|
2209
2264
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2210
2265
|
const channel = this.channels[channelNumber];
|
|
2211
2266
|
const state = channel.state;
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
reverbEffect.input.gain
|
|
2217
|
-
.cancelScheduledValues(scheduleTime)
|
|
2218
|
-
.setValueAtTime(state.reverbSendLevel, scheduleTime);
|
|
2219
|
-
}
|
|
2220
|
-
else {
|
|
2221
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2222
|
-
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
2223
|
-
return false;
|
|
2224
|
-
if (note.reverbEffectsSend)
|
|
2225
|
-
note.reverbEffectsSend.disconnect();
|
|
2226
|
-
});
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
else {
|
|
2230
|
-
if (0 < reverbSendLevel) {
|
|
2231
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2232
|
-
this.setReverbEffectsSend(channel, note, 0, scheduleTime);
|
|
2233
|
-
});
|
|
2234
|
-
state.reverbSendLevel = reverbSendLevel / 127;
|
|
2235
|
-
reverbEffect.input.gain
|
|
2236
|
-
.cancelScheduledValues(scheduleTime)
|
|
2237
|
-
.setValueAtTime(state.reverbSendLevel, scheduleTime);
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2267
|
+
state.reverbSendLevel = reverbSendLevel / 127;
|
|
2268
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2269
|
+
this.setReverbSend(channel, note, scheduleTime);
|
|
2270
|
+
});
|
|
2240
2271
|
}
|
|
2241
2272
|
setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
|
|
2242
2273
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2243
2274
|
const channel = this.channels[channelNumber];
|
|
2244
2275
|
const state = channel.state;
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
chorusEffect.input.gain
|
|
2250
|
-
.cancelScheduledValues(scheduleTime)
|
|
2251
|
-
.setValueAtTime(state.chorusSendLevel, scheduleTime);
|
|
2252
|
-
}
|
|
2253
|
-
else {
|
|
2254
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2255
|
-
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
2256
|
-
return false;
|
|
2257
|
-
if (note.chorusEffectsSend)
|
|
2258
|
-
note.chorusEffectsSend.disconnect();
|
|
2259
|
-
});
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2262
|
-
else {
|
|
2263
|
-
if (0 < chorusSendLevel) {
|
|
2264
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2265
|
-
this.setChorusEffectsSend(channel, note, 0, scheduleTime);
|
|
2266
|
-
});
|
|
2267
|
-
state.chorusSendLevel = chorusSendLevel / 127;
|
|
2268
|
-
chorusEffect.input.gain
|
|
2269
|
-
.cancelScheduledValues(scheduleTime)
|
|
2270
|
-
.setValueAtTime(state.chorusSendLevel, scheduleTime);
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2276
|
+
state.chorusSendLevel = chorusSendLevel / 127;
|
|
2277
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2278
|
+
this.setChorusSend(channel, note, scheduleTime);
|
|
2279
|
+
});
|
|
2273
2280
|
}
|
|
2274
2281
|
limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
|
|
2275
2282
|
if (maxLSB < channel.dataLSB) {
|
|
@@ -2343,8 +2350,8 @@ export class Midy {
|
|
|
2343
2350
|
}
|
|
2344
2351
|
handlePitchBendRangeRPN(channelNumber, scheduleTime) {
|
|
2345
2352
|
const channel = this.channels[channelNumber];
|
|
2346
|
-
this.limitData(channel, 0, 127, 0,
|
|
2347
|
-
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
2353
|
+
this.limitData(channel, 0, 127, 0, 127);
|
|
2354
|
+
const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
2348
2355
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
2349
2356
|
}
|
|
2350
2357
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
@@ -2354,7 +2361,7 @@ export class Midy {
|
|
|
2354
2361
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2355
2362
|
const state = channel.state;
|
|
2356
2363
|
const prev = state.pitchWheelSensitivity;
|
|
2357
|
-
const next = value /
|
|
2364
|
+
const next = value / 12800;
|
|
2358
2365
|
state.pitchWheelSensitivity = next;
|
|
2359
2366
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
2360
2367
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -2363,7 +2370,8 @@ export class Midy {
|
|
|
2363
2370
|
handleFineTuningRPN(channelNumber, scheduleTime) {
|
|
2364
2371
|
const channel = this.channels[channelNumber];
|
|
2365
2372
|
this.limitData(channel, 0, 127, 0, 127);
|
|
2366
|
-
const
|
|
2373
|
+
const value = channel.dataMSB * 128 + channel.dataLSB;
|
|
2374
|
+
const fineTuning = (value - 8192) / 8192 * 100;
|
|
2367
2375
|
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
2368
2376
|
}
|
|
2369
2377
|
setFineTuning(channelNumber, value, scheduleTime) {
|
|
@@ -2372,7 +2380,7 @@ export class Midy {
|
|
|
2372
2380
|
return;
|
|
2373
2381
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2374
2382
|
const prev = channel.fineTuning;
|
|
2375
|
-
const next =
|
|
2383
|
+
const next = value;
|
|
2376
2384
|
channel.fineTuning = next;
|
|
2377
2385
|
channel.detune += next - prev;
|
|
2378
2386
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -2380,7 +2388,7 @@ export class Midy {
|
|
|
2380
2388
|
handleCoarseTuningRPN(channelNumber, scheduleTime) {
|
|
2381
2389
|
const channel = this.channels[channelNumber];
|
|
2382
2390
|
this.limitDataMSB(channel, 0, 127);
|
|
2383
|
-
const coarseTuning = channel.dataMSB;
|
|
2391
|
+
const coarseTuning = (channel.dataMSB - 64) * 100;
|
|
2384
2392
|
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
2385
2393
|
}
|
|
2386
2394
|
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
@@ -2389,7 +2397,7 @@ export class Midy {
|
|
|
2389
2397
|
return;
|
|
2390
2398
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2391
2399
|
const prev = channel.coarseTuning;
|
|
2392
|
-
const next =
|
|
2400
|
+
const next = value;
|
|
2393
2401
|
channel.coarseTuning = next;
|
|
2394
2402
|
channel.detune += next - prev;
|
|
2395
2403
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -2397,22 +2405,22 @@ export class Midy {
|
|
|
2397
2405
|
handleModulationDepthRangeRPN(channelNumber, scheduleTime) {
|
|
2398
2406
|
const channel = this.channels[channelNumber];
|
|
2399
2407
|
this.limitData(channel, 0, 127, 0, 127);
|
|
2400
|
-
const
|
|
2401
|
-
this.setModulationDepthRange(channelNumber,
|
|
2408
|
+
const value = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
2409
|
+
this.setModulationDepthRange(channelNumber, value, scheduleTime);
|
|
2402
2410
|
}
|
|
2403
|
-
setModulationDepthRange(channelNumber,
|
|
2411
|
+
setModulationDepthRange(channelNumber, value, scheduleTime) {
|
|
2404
2412
|
const channel = this.channels[channelNumber];
|
|
2405
2413
|
if (channel.isDrum)
|
|
2406
2414
|
return;
|
|
2407
2415
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2408
|
-
channel.modulationDepthRange =
|
|
2416
|
+
channel.modulationDepthRange = value;
|
|
2409
2417
|
this.updateModulation(channel, scheduleTime);
|
|
2410
2418
|
}
|
|
2411
2419
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
2412
2420
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2413
2421
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
2414
2422
|
}
|
|
2415
|
-
|
|
2423
|
+
resetChannelStates(channelNumber) {
|
|
2416
2424
|
const scheduleTime = this.audioContext.currentTime;
|
|
2417
2425
|
const channel = this.channels[channelNumber];
|
|
2418
2426
|
const state = channel.state;
|
|
@@ -2430,8 +2438,8 @@ export class Midy {
|
|
|
2430
2438
|
}
|
|
2431
2439
|
this.resetChannelTable(channel);
|
|
2432
2440
|
this.mode = "GM2";
|
|
2433
|
-
this.masterFineTuning = 0; //
|
|
2434
|
-
this.masterCoarseTuning = 0; //
|
|
2441
|
+
this.masterFineTuning = 0; // cent
|
|
2442
|
+
this.masterCoarseTuning = 0; // cent
|
|
2435
2443
|
}
|
|
2436
2444
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
|
|
2437
2445
|
resetAllControllers(channelNumber, _value, scheduleTime) {
|
|
@@ -2580,11 +2588,11 @@ export class Midy {
|
|
|
2580
2588
|
case 9:
|
|
2581
2589
|
switch (data[3]) {
|
|
2582
2590
|
case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2583
|
-
return this.handlePressureSysEx(data, "channelPressureTable");
|
|
2591
|
+
return this.handlePressureSysEx(data, "channelPressureTable", scheduleTime);
|
|
2584
2592
|
case 2: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2585
|
-
return this.handlePressureSysEx(data, "polyphonicKeyPressureTable");
|
|
2593
|
+
return this.handlePressureSysEx(data, "polyphonicKeyPressureTable", scheduleTime);
|
|
2586
2594
|
case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2587
|
-
return this.handleControlChangeSysEx(data);
|
|
2595
|
+
return this.handleControlChangeSysEx(data, scheduleTime);
|
|
2588
2596
|
default:
|
|
2589
2597
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2590
2598
|
}
|
|
@@ -2617,12 +2625,13 @@ export class Midy {
|
|
|
2617
2625
|
}
|
|
2618
2626
|
}
|
|
2619
2627
|
handleMasterFineTuningSysEx(data, scheduleTime) {
|
|
2620
|
-
const
|
|
2628
|
+
const value = (data[5] * 128 + data[4]) / 16383;
|
|
2629
|
+
const fineTuning = (value - 8192) / 8192 * 100;
|
|
2621
2630
|
this.setMasterFineTuning(fineTuning, scheduleTime);
|
|
2622
2631
|
}
|
|
2623
2632
|
setMasterFineTuning(value, scheduleTime) {
|
|
2624
2633
|
const prev = this.masterFineTuning;
|
|
2625
|
-
const next =
|
|
2634
|
+
const next = value;
|
|
2626
2635
|
this.masterFineTuning = next;
|
|
2627
2636
|
const detuneChange = next - prev;
|
|
2628
2637
|
for (let i = 0; i < this.channels.length; i++) {
|
|
@@ -2634,12 +2643,12 @@ export class Midy {
|
|
|
2634
2643
|
}
|
|
2635
2644
|
}
|
|
2636
2645
|
handleMasterCoarseTuningSysEx(data, scheduleTime) {
|
|
2637
|
-
const coarseTuning = data[4];
|
|
2646
|
+
const coarseTuning = (data[4] - 64) * 100;
|
|
2638
2647
|
this.setMasterCoarseTuning(coarseTuning, scheduleTime);
|
|
2639
2648
|
}
|
|
2640
2649
|
setMasterCoarseTuning(value, scheduleTime) {
|
|
2641
2650
|
const prev = this.masterCoarseTuning;
|
|
2642
|
-
const next =
|
|
2651
|
+
const next = value;
|
|
2643
2652
|
this.masterCoarseTuning = next;
|
|
2644
2653
|
const detuneChange = next - prev;
|
|
2645
2654
|
for (let i = 0; i < this.channels.length; i++) {
|
|
@@ -2955,9 +2964,9 @@ export class Midy {
|
|
|
2955
2964
|
: 0;
|
|
2956
2965
|
return (channelPressure + polyphonicKeyPressure) / 254;
|
|
2957
2966
|
}
|
|
2958
|
-
|
|
2967
|
+
setEffects(channel, note, table, scheduleTime) {
|
|
2959
2968
|
if (0 <= table[0])
|
|
2960
|
-
this.updateDetune(channel, note,
|
|
2969
|
+
this.updateDetune(channel, note, scheduleTime);
|
|
2961
2970
|
if (0.5 <= channel.state.portamemento && 0 <= note.portamentoNoteNumber) {
|
|
2962
2971
|
if (0 <= table[1]) {
|
|
2963
2972
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
@@ -2979,7 +2988,7 @@ export class Midy {
|
|
|
2979
2988
|
if (0 <= table[5])
|
|
2980
2989
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
2981
2990
|
}
|
|
2982
|
-
handlePressureSysEx(data, tableName) {
|
|
2991
|
+
handlePressureSysEx(data, tableName, scheduleTime) {
|
|
2983
2992
|
const channelNumber = data[4];
|
|
2984
2993
|
const channel = this.channels[channelNumber];
|
|
2985
2994
|
if (channel.isDrum)
|
|
@@ -2990,36 +2999,42 @@ export class Midy {
|
|
|
2990
2999
|
const rr = data[i + 1];
|
|
2991
3000
|
table[pp] = rr;
|
|
2992
3001
|
}
|
|
3002
|
+
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
3003
|
+
this.setEffects(channel, note, table, scheduleTime);
|
|
3004
|
+
});
|
|
2993
3005
|
}
|
|
2994
3006
|
initControlTable() {
|
|
2995
3007
|
const ccCount = 128;
|
|
2996
3008
|
const slotSize = 6;
|
|
2997
3009
|
return new Int8Array(ccCount * slotSize).fill(-1);
|
|
2998
3010
|
}
|
|
2999
|
-
|
|
3011
|
+
setControlChangeEffects(channel, controllerType, scheduleTime) {
|
|
3000
3012
|
const slotSize = 6;
|
|
3001
3013
|
const offset = controllerType * slotSize;
|
|
3002
3014
|
const table = channel.controlTable.subarray(offset, offset + slotSize);
|
|
3003
3015
|
this.processScheduledNotes(channel, (note) => {
|
|
3004
|
-
this.
|
|
3016
|
+
this.setEffects(channel, note, table, scheduleTime);
|
|
3005
3017
|
});
|
|
3006
3018
|
}
|
|
3007
|
-
handleControlChangeSysEx(data) {
|
|
3019
|
+
handleControlChangeSysEx(data, scheduleTime) {
|
|
3008
3020
|
const channelNumber = data[4];
|
|
3009
3021
|
const channel = this.channels[channelNumber];
|
|
3010
3022
|
if (channel.isDrum)
|
|
3011
3023
|
return;
|
|
3024
|
+
const slotSize = 6;
|
|
3012
3025
|
const controllerType = data[5];
|
|
3013
|
-
const
|
|
3014
|
-
|
|
3026
|
+
const offset = controllerType * slotSize;
|
|
3027
|
+
const table = channel.controlTable;
|
|
3028
|
+
for (let i = 6; i < data.length; i += 2) {
|
|
3015
3029
|
const pp = data[i];
|
|
3016
3030
|
const rr = data[i + 1];
|
|
3017
|
-
table[pp] = rr;
|
|
3031
|
+
table[offset + pp] = rr;
|
|
3018
3032
|
}
|
|
3033
|
+
this.setControlChangeEffects(channel, controllerType, scheduleTime);
|
|
3019
3034
|
}
|
|
3020
3035
|
getKeyBasedValue(channel, keyNumber, controllerType) {
|
|
3021
3036
|
const index = keyNumber * 128 + controllerType;
|
|
3022
|
-
const controlValue = channel.
|
|
3037
|
+
const controlValue = channel.keyBasedTable[index];
|
|
3023
3038
|
return controlValue;
|
|
3024
3039
|
}
|
|
3025
3040
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
@@ -3028,14 +3043,102 @@ export class Midy {
|
|
|
3028
3043
|
if (!channel.isDrum)
|
|
3029
3044
|
return;
|
|
3030
3045
|
const keyNumber = data[5];
|
|
3031
|
-
const table = channel.
|
|
3032
|
-
for (let i = 6; i < data.length
|
|
3046
|
+
const table = channel.keyBasedTable;
|
|
3047
|
+
for (let i = 6; i < data.length; i += 2) {
|
|
3033
3048
|
const controllerType = data[i];
|
|
3034
3049
|
const value = data[i + 1];
|
|
3035
3050
|
const index = keyNumber * 128 + controllerType;
|
|
3036
3051
|
table[index] = value;
|
|
3052
|
+
switch (controllerType) {
|
|
3053
|
+
case 7:
|
|
3054
|
+
case 10:
|
|
3055
|
+
this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
|
|
3056
|
+
break;
|
|
3057
|
+
case 71:
|
|
3058
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3059
|
+
const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
|
|
3060
|
+
if (note.noteNumber === keyNumber) {
|
|
3061
|
+
const Q = note.voiceParams.initialFilterQ / 5 * filterResonance;
|
|
3062
|
+
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
3063
|
+
}
|
|
3064
|
+
});
|
|
3065
|
+
break;
|
|
3066
|
+
case 73:
|
|
3067
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3068
|
+
if (note.noteNumber === keyNumber) {
|
|
3069
|
+
if (0.5 <= channel.state.portamento &&
|
|
3070
|
+
0 <= note.portamentoNoteNumber) {
|
|
3071
|
+
this.setPortamentoVolumeEnvelope(channel, note, scheduleTime);
|
|
3072
|
+
}
|
|
3073
|
+
else {
|
|
3074
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
});
|
|
3078
|
+
break;
|
|
3079
|
+
case 74:
|
|
3080
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3081
|
+
if (note.noteNumber === keyNumber) {
|
|
3082
|
+
if (0.5 <= channel.state.portamento &&
|
|
3083
|
+
0 <= note.portamentoNoteNumber) {
|
|
3084
|
+
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
3085
|
+
}
|
|
3086
|
+
else {
|
|
3087
|
+
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
});
|
|
3091
|
+
break;
|
|
3092
|
+
case 75:
|
|
3093
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3094
|
+
if (note.noteNumber === keyNumber) {
|
|
3095
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
3096
|
+
}
|
|
3097
|
+
});
|
|
3098
|
+
break;
|
|
3099
|
+
case 76:
|
|
3100
|
+
if (channel.state.vibratoDepth <= 0)
|
|
3101
|
+
break;
|
|
3102
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3103
|
+
if (note.noteNumber === keyNumber) {
|
|
3104
|
+
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
3105
|
+
}
|
|
3106
|
+
});
|
|
3107
|
+
break;
|
|
3108
|
+
case 77:
|
|
3109
|
+
if (channel.state.vibratoDepth <= 0)
|
|
3110
|
+
break;
|
|
3111
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3112
|
+
if (note.noteNumber === keyNumber) {
|
|
3113
|
+
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
3114
|
+
}
|
|
3115
|
+
});
|
|
3116
|
+
break;
|
|
3117
|
+
case 78:
|
|
3118
|
+
if (channel.state.vibratoDepth <= 0)
|
|
3119
|
+
break;
|
|
3120
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3121
|
+
if (note.noteNumber === keyNumber) {
|
|
3122
|
+
this.setDelayVibLFO(channel, note);
|
|
3123
|
+
}
|
|
3124
|
+
});
|
|
3125
|
+
break;
|
|
3126
|
+
case 91:
|
|
3127
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3128
|
+
if (note.noteNumber === keyNumber) {
|
|
3129
|
+
this.setReverbSend(channel, note, scheduleTime);
|
|
3130
|
+
}
|
|
3131
|
+
});
|
|
3132
|
+
break;
|
|
3133
|
+
case 93:
|
|
3134
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3135
|
+
if (note.noteNumber === keyNumber) {
|
|
3136
|
+
this.setChorusSend(channel, note, scheduleTime);
|
|
3137
|
+
}
|
|
3138
|
+
});
|
|
3139
|
+
break;
|
|
3140
|
+
}
|
|
3037
3141
|
}
|
|
3038
|
-
this.setChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
|
|
3039
3142
|
}
|
|
3040
3143
|
handleSysEx(data, scheduleTime) {
|
|
3041
3144
|
switch (data[0]) {
|
|
@@ -3084,7 +3187,7 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
3084
3187
|
rpnLSB: 127,
|
|
3085
3188
|
mono: false, // CC#124, CC#125
|
|
3086
3189
|
modulationDepthRange: 50, // cent
|
|
3087
|
-
fineTuning: 0, //
|
|
3088
|
-
coarseTuning: 0, //
|
|
3190
|
+
fineTuning: 0, // cent
|
|
3191
|
+
coarseTuning: 0, // cent
|
|
3089
3192
|
}
|
|
3090
3193
|
});
|