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