@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-GM2.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,
|
|
@@ -228,13 +228,13 @@ class MidyGM2 {
|
|
|
228
228
|
configurable: true,
|
|
229
229
|
writable: true,
|
|
230
230
|
value: 0
|
|
231
|
-
}); //
|
|
231
|
+
}); // cent
|
|
232
232
|
Object.defineProperty(this, "masterCoarseTuning", {
|
|
233
233
|
enumerable: true,
|
|
234
234
|
configurable: true,
|
|
235
235
|
writable: true,
|
|
236
236
|
value: 0
|
|
237
|
-
}); //
|
|
237
|
+
}); // cent
|
|
238
238
|
Object.defineProperty(this, "reverb", {
|
|
239
239
|
enumerable: true,
|
|
240
240
|
configurable: true,
|
|
@@ -359,13 +359,13 @@ class MidyGM2 {
|
|
|
359
359
|
writable: true,
|
|
360
360
|
value: false
|
|
361
361
|
});
|
|
362
|
-
Object.defineProperty(this, "
|
|
362
|
+
Object.defineProperty(this, "playPromise", {
|
|
363
363
|
enumerable: true,
|
|
364
364
|
configurable: true,
|
|
365
365
|
writable: true,
|
|
366
|
-
value:
|
|
366
|
+
value: void 0
|
|
367
367
|
});
|
|
368
|
-
Object.defineProperty(this, "
|
|
368
|
+
Object.defineProperty(this, "timeline", {
|
|
369
369
|
enumerable: true,
|
|
370
370
|
configurable: true,
|
|
371
371
|
writable: true,
|
|
@@ -377,6 +377,12 @@ class MidyGM2 {
|
|
|
377
377
|
writable: true,
|
|
378
378
|
value: []
|
|
379
379
|
});
|
|
380
|
+
Object.defineProperty(this, "instruments", {
|
|
381
|
+
enumerable: true,
|
|
382
|
+
configurable: true,
|
|
383
|
+
writable: true,
|
|
384
|
+
value: new Set()
|
|
385
|
+
});
|
|
380
386
|
Object.defineProperty(this, "exclusiveClassNotes", {
|
|
381
387
|
enumerable: true,
|
|
382
388
|
configurable: true,
|
|
@@ -507,7 +513,7 @@ class MidyGM2 {
|
|
|
507
513
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
508
514
|
const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
|
|
509
515
|
const { instrument, sampleID } = voice.generators;
|
|
510
|
-
return
|
|
516
|
+
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
511
517
|
}
|
|
512
518
|
createChannelAudioNodes(audioContext) {
|
|
513
519
|
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
|
|
@@ -527,7 +533,7 @@ class MidyGM2 {
|
|
|
527
533
|
channel.controlTable.fill(-1);
|
|
528
534
|
channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
|
|
529
535
|
channel.channelPressureTable.fill(-1);
|
|
530
|
-
channel.
|
|
536
|
+
channel.keyBasedTable.fill(-1);
|
|
531
537
|
}
|
|
532
538
|
createChannels(audioContext) {
|
|
533
539
|
const channels = Array.from({ length: this.numChannels }, () => {
|
|
@@ -543,7 +549,7 @@ class MidyGM2 {
|
|
|
543
549
|
controlTable: this.initControlTable(),
|
|
544
550
|
scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
|
|
545
551
|
channelPressureTable: new Int8Array(6).fill(-1),
|
|
546
|
-
|
|
552
|
+
keyBasedTable: new Int8Array(128 * 128).fill(-1),
|
|
547
553
|
keyBasedGainLs: new Array(128),
|
|
548
554
|
keyBasedGainRs: new Array(128),
|
|
549
555
|
};
|
|
@@ -618,72 +624,80 @@ class MidyGM2 {
|
|
|
618
624
|
}
|
|
619
625
|
return 0;
|
|
620
626
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
627
|
+
resetAllStates() {
|
|
628
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
629
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
630
|
+
this.voiceCache.clear();
|
|
631
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
632
|
+
this.channels[i].scheduledNotes = [];
|
|
633
|
+
this.resetChannelStates(i);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
updateStates(queueIndex, nextQueueIndex) {
|
|
637
|
+
if (nextQueueIndex < queueIndex)
|
|
638
|
+
queueIndex = 0;
|
|
639
|
+
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
640
|
+
const event = this.timeline[i];
|
|
641
|
+
switch (event.type) {
|
|
642
|
+
case "controller":
|
|
643
|
+
this.setControlChange(event.channel, event.controllerType, event.value, 0);
|
|
644
|
+
break;
|
|
645
|
+
case "programChange":
|
|
646
|
+
this.setProgramChange(event.channel, event.programNumber, 0);
|
|
647
|
+
break;
|
|
648
|
+
case "pitchBend":
|
|
649
|
+
this.setPitchBend(event.channel, event.value + 8192, 0);
|
|
650
|
+
break;
|
|
651
|
+
case "sysEx":
|
|
652
|
+
this.handleSysEx(event.data, 0);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
async playNotes() {
|
|
657
|
+
if (this.audioContext.state === "suspended") {
|
|
658
|
+
await this.audioContext.resume();
|
|
659
|
+
}
|
|
660
|
+
this.isPlaying = true;
|
|
661
|
+
this.isPaused = false;
|
|
662
|
+
this.startTime = this.audioContext.currentTime;
|
|
663
|
+
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
664
|
+
let resumeTime = this.resumeTime - this.startTime;
|
|
665
|
+
let finished = false;
|
|
666
|
+
this.notePromises = [];
|
|
667
|
+
while (queueIndex < this.timeline.length) {
|
|
668
|
+
const now = this.audioContext.currentTime;
|
|
669
|
+
const t = now + resumeTime;
|
|
670
|
+
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
671
|
+
if (this.isPausing) {
|
|
672
|
+
await this.stopNotes(0, true, now);
|
|
673
|
+
await this.audioContext.suspend();
|
|
674
|
+
this.notePromises = [];
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
else if (this.isStopping) {
|
|
678
|
+
await this.stopNotes(0, true, now);
|
|
679
|
+
await this.audioContext.suspend();
|
|
680
|
+
finished = true;
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
else if (this.isSeeking) {
|
|
684
|
+
await this.stopNotes(0, true, now);
|
|
685
|
+
this.startTime = this.audioContext.currentTime;
|
|
686
|
+
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
687
|
+
this.updateStates(queueIndex, nextQueueIndex);
|
|
688
|
+
queueIndex = nextQueueIndex;
|
|
689
|
+
resumeTime = this.resumeTime - this.startTime;
|
|
690
|
+
this.isSeeking = false;
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
693
|
+
const waitTime = now + this.noteCheckInterval;
|
|
694
|
+
await this.scheduleTask(() => { }, waitTime);
|
|
695
|
+
}
|
|
696
|
+
if (finished) {
|
|
628
697
|
this.notePromises = [];
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
this.notePromises = [];
|
|
633
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
634
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
635
|
-
this.voiceCache.clear();
|
|
636
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
637
|
-
this.channels[i].scheduledNotes = [];
|
|
638
|
-
this.resetAllStates(i);
|
|
639
|
-
}
|
|
640
|
-
resolve();
|
|
641
|
-
return;
|
|
642
|
-
}
|
|
643
|
-
const now = this.audioContext.currentTime;
|
|
644
|
-
const t = now + resumeTime;
|
|
645
|
-
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
646
|
-
if (this.isPausing) {
|
|
647
|
-
await this.stopNotes(0, true, now);
|
|
648
|
-
this.notePromises = [];
|
|
649
|
-
this.isPausing = false;
|
|
650
|
-
this.isPaused = true;
|
|
651
|
-
resolve();
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
else if (this.isStopping) {
|
|
655
|
-
await this.stopNotes(0, true, now);
|
|
656
|
-
this.notePromises = [];
|
|
657
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
658
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
659
|
-
this.voiceCache.clear();
|
|
660
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
661
|
-
this.channels[i].scheduledNotes = [];
|
|
662
|
-
this.resetAllStates(i);
|
|
663
|
-
}
|
|
664
|
-
this.isStopping = false;
|
|
665
|
-
this.isPaused = false;
|
|
666
|
-
resolve();
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
|
-
else if (this.isSeeking) {
|
|
670
|
-
this.stopNotes(0, true, now);
|
|
671
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
672
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
673
|
-
this.startTime = this.audioContext.currentTime;
|
|
674
|
-
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
675
|
-
resumeTime = this.resumeTime - this.startTime;
|
|
676
|
-
this.isSeeking = false;
|
|
677
|
-
await schedulePlayback();
|
|
678
|
-
}
|
|
679
|
-
else {
|
|
680
|
-
const waitTime = now + this.noteCheckInterval;
|
|
681
|
-
await this.scheduleTask(() => { }, waitTime);
|
|
682
|
-
await schedulePlayback();
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
schedulePlayback();
|
|
686
|
-
});
|
|
698
|
+
this.resetAllStates();
|
|
699
|
+
}
|
|
700
|
+
this.isPlaying = false;
|
|
687
701
|
}
|
|
688
702
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
689
703
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -820,26 +834,32 @@ class MidyGM2 {
|
|
|
820
834
|
this.resumeTime = 0;
|
|
821
835
|
if (this.voiceCounter.size === 0)
|
|
822
836
|
this.cacheVoiceIds();
|
|
823
|
-
|
|
824
|
-
this.
|
|
837
|
+
this.playPromise = this.playNotes();
|
|
838
|
+
await this.playPromise;
|
|
825
839
|
}
|
|
826
|
-
stop() {
|
|
840
|
+
async stop() {
|
|
827
841
|
if (!this.isPlaying)
|
|
828
842
|
return;
|
|
829
843
|
this.isStopping = true;
|
|
844
|
+
await this.playPromise;
|
|
845
|
+
this.isStopping = false;
|
|
830
846
|
}
|
|
831
|
-
pause() {
|
|
847
|
+
async pause() {
|
|
832
848
|
if (!this.isPlaying || this.isPaused)
|
|
833
849
|
return;
|
|
834
850
|
const now = this.audioContext.currentTime;
|
|
835
851
|
this.resumeTime += now - this.startTime - this.startDelay;
|
|
836
852
|
this.isPausing = true;
|
|
853
|
+
await this.playPromise;
|
|
854
|
+
this.isPausing = false;
|
|
855
|
+
this.isPaused = true;
|
|
837
856
|
}
|
|
838
857
|
async resume() {
|
|
839
858
|
if (!this.isPaused)
|
|
840
859
|
return;
|
|
841
|
-
|
|
842
|
-
this.
|
|
860
|
+
this.playPromise = this.playNotes();
|
|
861
|
+
await this.playPromise;
|
|
862
|
+
this.isPaused = false;
|
|
843
863
|
}
|
|
844
864
|
seekTo(second) {
|
|
845
865
|
this.resumeTime = second;
|
|
@@ -907,13 +927,11 @@ class MidyGM2 {
|
|
|
907
927
|
return impulse;
|
|
908
928
|
}
|
|
909
929
|
createConvolutionReverb(audioContext, impulse) {
|
|
910
|
-
const input = new GainNode(audioContext);
|
|
911
930
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
912
931
|
buffer: impulse,
|
|
913
932
|
});
|
|
914
|
-
input.connect(convolverNode);
|
|
915
933
|
return {
|
|
916
|
-
input,
|
|
934
|
+
input: convolverNode,
|
|
917
935
|
output: convolverNode,
|
|
918
936
|
convolverNode,
|
|
919
937
|
};
|
|
@@ -1276,10 +1294,12 @@ class MidyGM2 {
|
|
|
1276
1294
|
startVibrato(channel, note, scheduleTime) {
|
|
1277
1295
|
const { voiceParams } = note;
|
|
1278
1296
|
const state = channel.state;
|
|
1297
|
+
const vibratoRate = state.vibratoRate * 2;
|
|
1298
|
+
const vibratoDelay = state.vibratoDelay * 2;
|
|
1279
1299
|
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
1280
|
-
frequency: this.centToHz(voiceParams.freqVibLFO) *
|
|
1300
|
+
frequency: this.centToHz(voiceParams.freqVibLFO) * vibratoRate,
|
|
1281
1301
|
});
|
|
1282
|
-
note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO *
|
|
1302
|
+
note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO * vibratoDelay);
|
|
1283
1303
|
note.vibratoDepth = new GainNode(this.audioContext);
|
|
1284
1304
|
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
1285
1305
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
@@ -1343,12 +1363,8 @@ class MidyGM2 {
|
|
|
1343
1363
|
}
|
|
1344
1364
|
note.bufferSource.connect(note.filterNode);
|
|
1345
1365
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
}
|
|
1349
|
-
if (0 < state.reverbSendLevel) {
|
|
1350
|
-
this.setReverbEffectsSend(channel, note, 0, now);
|
|
1351
|
-
}
|
|
1366
|
+
this.setChorusSend(channel, note, now);
|
|
1367
|
+
this.setReverbSend(channel, note, now);
|
|
1352
1368
|
note.bufferSource.start(startTime);
|
|
1353
1369
|
return note;
|
|
1354
1370
|
}
|
|
@@ -1414,10 +1430,14 @@ class MidyGM2 {
|
|
|
1414
1430
|
return;
|
|
1415
1431
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
|
|
1416
1432
|
if (channel.isDrum) {
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1433
|
+
const { keyBasedGainLs, keyBasedGainRs } = channel;
|
|
1434
|
+
let gainL = keyBasedGainLs[noteNumber];
|
|
1435
|
+
let gainR = keyBasedGainRs[noteNumber];
|
|
1436
|
+
if (!gainL) {
|
|
1437
|
+
const audioNodes = this.createChannelAudioNodes(this.audioContext);
|
|
1438
|
+
gainL = keyBasedGainLs[noteNumber] = audioNodes.gainL;
|
|
1439
|
+
gainR = keyBasedGainRs[noteNumber] = audioNodes.gainR;
|
|
1440
|
+
}
|
|
1421
1441
|
note.volumeEnvelopeNode.connect(gainL);
|
|
1422
1442
|
note.volumeEnvelopeNode.connect(gainR);
|
|
1423
1443
|
}
|
|
@@ -1451,11 +1471,11 @@ class MidyGM2 {
|
|
|
1451
1471
|
note.vibratoDepth.disconnect();
|
|
1452
1472
|
note.vibratoLFO.stop();
|
|
1453
1473
|
}
|
|
1454
|
-
if (note.
|
|
1455
|
-
note.
|
|
1474
|
+
if (note.reverbSend) {
|
|
1475
|
+
note.reverbSend.disconnect();
|
|
1456
1476
|
}
|
|
1457
|
-
if (note.
|
|
1458
|
-
note.
|
|
1477
|
+
if (note.chorusSend) {
|
|
1478
|
+
note.chorusSend.disconnect();
|
|
1459
1479
|
}
|
|
1460
1480
|
}
|
|
1461
1481
|
releaseNote(channel, note, endTime) {
|
|
@@ -1591,6 +1611,7 @@ class MidyGM2 {
|
|
|
1591
1611
|
break;
|
|
1592
1612
|
}
|
|
1593
1613
|
}
|
|
1614
|
+
channel.keyBasedTable.fill(-1);
|
|
1594
1615
|
}
|
|
1595
1616
|
setChannelPressure(channelNumber, value, scheduleTime) {
|
|
1596
1617
|
const channel = this.channels[channelNumber];
|
|
@@ -1606,7 +1627,7 @@ class MidyGM2 {
|
|
|
1606
1627
|
}
|
|
1607
1628
|
const table = channel.channelPressureTable;
|
|
1608
1629
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1609
|
-
this.
|
|
1630
|
+
this.setEffects(channel, note, table, scheduleTime);
|
|
1610
1631
|
});
|
|
1611
1632
|
this.applyVoiceParams(channel, 13);
|
|
1612
1633
|
}
|
|
@@ -1628,22 +1649,32 @@ class MidyGM2 {
|
|
|
1628
1649
|
this.applyVoiceParams(channel, 14, scheduleTime);
|
|
1629
1650
|
}
|
|
1630
1651
|
setModLfoToPitch(channel, note, scheduleTime) {
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
.
|
|
1637
|
-
|
|
1652
|
+
if (note.modulationDepth) {
|
|
1653
|
+
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1654
|
+
this.getLFOPitchDepth(channel, note);
|
|
1655
|
+
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1656
|
+
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1657
|
+
note.modulationDepth.gain
|
|
1658
|
+
.cancelScheduledValues(scheduleTime)
|
|
1659
|
+
.setValueAtTime(depth, scheduleTime);
|
|
1660
|
+
}
|
|
1661
|
+
else {
|
|
1662
|
+
this.startModulation(channel, note, scheduleTime);
|
|
1663
|
+
}
|
|
1638
1664
|
}
|
|
1639
1665
|
setVibLfoToPitch(channel, note, scheduleTime) {
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
.
|
|
1646
|
-
|
|
1666
|
+
if (note.vibratoDepth) {
|
|
1667
|
+
const vibratoDepth = channel.state.vibratoDepth * 2;
|
|
1668
|
+
const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
|
|
1669
|
+
const baseDepth = Math.abs(vibLfoToPitch) * vibratoDepth;
|
|
1670
|
+
const depth = baseDepth * Math.sign(vibLfoToPitch);
|
|
1671
|
+
note.vibratoDepth.gain
|
|
1672
|
+
.cancelScheduledValues(scheduleTime)
|
|
1673
|
+
.setValueAtTime(depth, scheduleTime);
|
|
1674
|
+
}
|
|
1675
|
+
else {
|
|
1676
|
+
this.startVibrato(channel, note, scheduleTime);
|
|
1677
|
+
}
|
|
1647
1678
|
}
|
|
1648
1679
|
setModLfoToFilterFc(channel, note, scheduleTime) {
|
|
1649
1680
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc +
|
|
@@ -1661,73 +1692,72 @@ class MidyGM2 {
|
|
|
1661
1692
|
.cancelScheduledValues(scheduleTime)
|
|
1662
1693
|
.setValueAtTime(volumeDepth, scheduleTime);
|
|
1663
1694
|
}
|
|
1664
|
-
|
|
1665
|
-
let value = note.voiceParams.reverbEffectsSend
|
|
1695
|
+
setReverbSend(channel, note, scheduleTime) {
|
|
1696
|
+
let value = note.voiceParams.reverbEffectsSend *
|
|
1697
|
+
channel.state.reverbSendLevel;
|
|
1666
1698
|
if (channel.isDrum) {
|
|
1667
1699
|
const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 91);
|
|
1668
|
-
if (0 <= keyBasedValue)
|
|
1669
|
-
value
|
|
1670
|
-
}
|
|
1700
|
+
if (0 <= keyBasedValue)
|
|
1701
|
+
value = keyBasedValue / 127;
|
|
1671
1702
|
}
|
|
1672
|
-
if (
|
|
1703
|
+
if (!note.reverbSend) {
|
|
1673
1704
|
if (0 < value) {
|
|
1674
|
-
note.
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
}
|
|
1678
|
-
else {
|
|
1679
|
-
note.reverbEffectsSend.disconnect();
|
|
1705
|
+
note.reverbSend = new GainNode(this.audioContext, { gain: value });
|
|
1706
|
+
note.volumeEnvelopeNode.connect(note.reverbSend);
|
|
1707
|
+
note.reverbSend.connect(this.reverbEffect.input);
|
|
1680
1708
|
}
|
|
1681
1709
|
}
|
|
1682
1710
|
else {
|
|
1711
|
+
note.reverbSend.gain
|
|
1712
|
+
.cancelScheduledValues(scheduleTime)
|
|
1713
|
+
.setValueAtTime(value, scheduleTime);
|
|
1683
1714
|
if (0 < value) {
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
note.
|
|
1715
|
+
note.volumeEnvelopeNode.connect(note.reverbSend);
|
|
1716
|
+
}
|
|
1717
|
+
else {
|
|
1718
|
+
try {
|
|
1719
|
+
note.volumeEnvelopeNode.disconnect(note.reverbSend);
|
|
1689
1720
|
}
|
|
1690
|
-
|
|
1721
|
+
catch { /* empty */ }
|
|
1691
1722
|
}
|
|
1692
1723
|
}
|
|
1693
1724
|
}
|
|
1694
|
-
|
|
1695
|
-
let value = note.voiceParams.chorusEffectsSend
|
|
1725
|
+
setChorusSend(channel, note, scheduleTime) {
|
|
1726
|
+
let value = note.voiceParams.chorusEffectsSend *
|
|
1727
|
+
channel.state.chorusSendLevel;
|
|
1696
1728
|
if (channel.isDrum) {
|
|
1697
1729
|
const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 93);
|
|
1698
|
-
if (0 <= keyBasedValue)
|
|
1699
|
-
value
|
|
1700
|
-
}
|
|
1730
|
+
if (0 <= keyBasedValue)
|
|
1731
|
+
value = keyBasedValue / 127;
|
|
1701
1732
|
}
|
|
1702
|
-
if (
|
|
1733
|
+
if (!note.chorusSend) {
|
|
1703
1734
|
if (0 < value) {
|
|
1704
|
-
note.
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
}
|
|
1708
|
-
else {
|
|
1709
|
-
note.chorusEffectsSend.disconnect();
|
|
1735
|
+
note.chorusSend = new GainNode(this.audioContext, { gain: value });
|
|
1736
|
+
note.volumeEnvelopeNode.connect(note.chorusSend);
|
|
1737
|
+
note.chorusSend.connect(this.chorusEffect.input);
|
|
1710
1738
|
}
|
|
1711
1739
|
}
|
|
1712
1740
|
else {
|
|
1741
|
+
note.chorusSend.gain
|
|
1742
|
+
.cancelScheduledValues(scheduleTime)
|
|
1743
|
+
.setValueAtTime(value, scheduleTime);
|
|
1713
1744
|
if (0 < value) {
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
note.volumeEnvelopeNode.
|
|
1745
|
+
note.volumeEnvelopeNode.connect(note.chorusSend);
|
|
1746
|
+
}
|
|
1747
|
+
else {
|
|
1748
|
+
try {
|
|
1749
|
+
note.volumeEnvelopeNode.disconnect(note.chorusSend);
|
|
1719
1750
|
}
|
|
1720
|
-
|
|
1751
|
+
catch { /* empty */ }
|
|
1721
1752
|
}
|
|
1722
1753
|
}
|
|
1723
1754
|
}
|
|
1724
|
-
setDelayModLFO(note
|
|
1725
|
-
const startTime = note.startTime;
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
note.modulationLFO.connect(note.filterDepth);
|
|
1755
|
+
setDelayModLFO(note) {
|
|
1756
|
+
const startTime = note.startTime + note.voiceParams.delayModLFO;
|
|
1757
|
+
try {
|
|
1758
|
+
note.modulationLFO.start(startTime);
|
|
1759
|
+
}
|
|
1760
|
+
catch { /* empty */ }
|
|
1731
1761
|
}
|
|
1732
1762
|
setFreqModLFO(note, scheduleTime) {
|
|
1733
1763
|
const freqModLFO = note.voiceParams.freqModLFO;
|
|
@@ -1736,54 +1766,65 @@ class MidyGM2 {
|
|
|
1736
1766
|
.setValueAtTime(freqModLFO, scheduleTime);
|
|
1737
1767
|
}
|
|
1738
1768
|
setFreqVibLFO(channel, note, scheduleTime) {
|
|
1769
|
+
const vibratoRate = channel.state.vibratoRate * 2;
|
|
1739
1770
|
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1740
1771
|
note.vibratoLFO.frequency
|
|
1741
1772
|
.cancelScheduledValues(scheduleTime)
|
|
1742
|
-
.setValueAtTime(freqVibLFO *
|
|
1773
|
+
.setValueAtTime(freqVibLFO * vibratoRate, scheduleTime);
|
|
1774
|
+
}
|
|
1775
|
+
setDelayVibLFO(channel, note) {
|
|
1776
|
+
const vibratoDelay = channel.state.vibratoDelay * 2;
|
|
1777
|
+
const value = note.voiceParams.delayVibLFO;
|
|
1778
|
+
const startTime = note.startTime + value * vibratoDelay;
|
|
1779
|
+
try {
|
|
1780
|
+
note.vibratoLFO.start(startTime);
|
|
1781
|
+
}
|
|
1782
|
+
catch { /* empty */ }
|
|
1743
1783
|
}
|
|
1744
1784
|
createVoiceParamsHandlers() {
|
|
1745
1785
|
return {
|
|
1746
|
-
modLfoToPitch: (channel, note,
|
|
1786
|
+
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1747
1787
|
if (0 < channel.state.modulationDepth) {
|
|
1748
1788
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1749
1789
|
}
|
|
1750
1790
|
},
|
|
1751
|
-
vibLfoToPitch: (channel, note,
|
|
1791
|
+
vibLfoToPitch: (channel, note, scheduleTime) => {
|
|
1752
1792
|
if (0 < channel.state.vibratoDepth) {
|
|
1753
1793
|
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
1754
1794
|
}
|
|
1755
1795
|
},
|
|
1756
|
-
modLfoToFilterFc: (channel, note,
|
|
1796
|
+
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1757
1797
|
if (0 < channel.state.modulationDepth) {
|
|
1758
1798
|
this.setModLfoToFilterFc(channel, note, scheduleTime);
|
|
1759
1799
|
}
|
|
1760
1800
|
},
|
|
1761
|
-
modLfoToVolume: (channel, note,
|
|
1801
|
+
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1762
1802
|
if (0 < channel.state.modulationDepth) {
|
|
1763
1803
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
1764
1804
|
}
|
|
1765
1805
|
},
|
|
1766
|
-
chorusEffectsSend: (channel, note,
|
|
1767
|
-
this.
|
|
1806
|
+
chorusEffectsSend: (channel, note, scheduleTime) => {
|
|
1807
|
+
this.setChorusSend(channel, note, scheduleTime);
|
|
1808
|
+
},
|
|
1809
|
+
reverbEffectsSend: (channel, note, scheduleTime) => {
|
|
1810
|
+
this.setReverbSend(channel, note, scheduleTime);
|
|
1811
|
+
},
|
|
1812
|
+
delayModLFO: (_channel, note, _scheduleTime) => {
|
|
1813
|
+
if (0 < channel.state.modulationDepth) {
|
|
1814
|
+
this.setDelayModLFO(note);
|
|
1815
|
+
}
|
|
1768
1816
|
},
|
|
1769
|
-
|
|
1770
|
-
|
|
1817
|
+
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1818
|
+
if (0 < channel.state.modulationDepth) {
|
|
1819
|
+
this.setFreqModLFO(note, scheduleTime);
|
|
1820
|
+
}
|
|
1771
1821
|
},
|
|
1772
|
-
|
|
1773
|
-
freqModLFO: (_channel, note, _prevValue, scheduleTime) => this.setFreqModLFO(note, scheduleTime),
|
|
1774
|
-
delayVibLFO: (channel, note, prevValue, scheduleTime) => {
|
|
1822
|
+
delayVibLFO: (channel, note, _scheduleTime) => {
|
|
1775
1823
|
if (0 < channel.state.vibratoDepth) {
|
|
1776
|
-
|
|
1777
|
-
const prevStartTime = note.startTime + prevValue * vibratoDelay;
|
|
1778
|
-
if (scheduleTime < prevStartTime)
|
|
1779
|
-
return;
|
|
1780
|
-
const value = note.voiceParams.delayVibLFO;
|
|
1781
|
-
const startTime = note.startTime + value * vibratoDelay;
|
|
1782
|
-
note.vibratoLFO.stop(scheduleTime);
|
|
1783
|
-
note.vibratoLFO.start(startTime);
|
|
1824
|
+
setDelayVibLFO(channel, note);
|
|
1784
1825
|
}
|
|
1785
1826
|
},
|
|
1786
|
-
freqVibLFO: (channel, note,
|
|
1827
|
+
freqVibLFO: (channel, note, scheduleTime) => {
|
|
1787
1828
|
if (0 < channel.state.vibratoDepth) {
|
|
1788
1829
|
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
1789
1830
|
}
|
|
@@ -1811,7 +1852,7 @@ class MidyGM2 {
|
|
|
1811
1852
|
continue;
|
|
1812
1853
|
note.voiceParams[key] = value;
|
|
1813
1854
|
if (key in this.voiceParamsHandlers) {
|
|
1814
|
-
this.voiceParamsHandlers[key](channel, note,
|
|
1855
|
+
this.voiceParamsHandlers[key](channel, note, scheduleTime);
|
|
1815
1856
|
}
|
|
1816
1857
|
else {
|
|
1817
1858
|
if (volumeEnvelopeKeySet.has(key))
|
|
@@ -1866,7 +1907,7 @@ class MidyGM2 {
|
|
|
1866
1907
|
handler.call(this, channelNumber, value, scheduleTime);
|
|
1867
1908
|
const channel = this.channels[channelNumber];
|
|
1868
1909
|
this.applyVoiceParams(channel, controllerType + 128, scheduleTime);
|
|
1869
|
-
this.
|
|
1910
|
+
this.setControlChangeEffects(channel, controllerType, scheduleTime);
|
|
1870
1911
|
}
|
|
1871
1912
|
else {
|
|
1872
1913
|
console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
|
|
@@ -1882,7 +1923,6 @@ class MidyGM2 {
|
|
|
1882
1923
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1883
1924
|
}
|
|
1884
1925
|
else {
|
|
1885
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1886
1926
|
this.startModulation(channel, note, scheduleTime);
|
|
1887
1927
|
}
|
|
1888
1928
|
});
|
|
@@ -1927,8 +1967,14 @@ class MidyGM2 {
|
|
|
1927
1967
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1928
1968
|
const channel = this.channels[channelNumber];
|
|
1929
1969
|
channel.state.volume = volume / 127;
|
|
1930
|
-
|
|
1931
|
-
|
|
1970
|
+
if (channel.isDrum) {
|
|
1971
|
+
for (let i = 0; i < 128; i++) {
|
|
1972
|
+
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
else {
|
|
1976
|
+
this.updateChannelVolume(channel, scheduleTime);
|
|
1977
|
+
}
|
|
1932
1978
|
}
|
|
1933
1979
|
panToGain(pan) {
|
|
1934
1980
|
const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
|
|
@@ -1941,8 +1987,14 @@ class MidyGM2 {
|
|
|
1941
1987
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1942
1988
|
const channel = this.channels[channelNumber];
|
|
1943
1989
|
channel.state.pan = pan / 127;
|
|
1944
|
-
|
|
1945
|
-
|
|
1990
|
+
if (channel.isDrum) {
|
|
1991
|
+
for (let i = 0; i < 128; i++) {
|
|
1992
|
+
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
else {
|
|
1996
|
+
this.updateChannelVolume(channel, scheduleTime);
|
|
1997
|
+
}
|
|
1946
1998
|
}
|
|
1947
1999
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1948
2000
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -1968,33 +2020,27 @@ class MidyGM2 {
|
|
|
1968
2020
|
.cancelScheduledValues(scheduleTime)
|
|
1969
2021
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1970
2022
|
}
|
|
1971
|
-
updateKeyBasedVolume(channel, scheduleTime) {
|
|
1972
|
-
|
|
2023
|
+
updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
|
|
2024
|
+
const gainL = channel.keyBasedGainLs[keyNumber];
|
|
2025
|
+
if (!gainL)
|
|
1973
2026
|
return;
|
|
2027
|
+
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
1974
2028
|
const state = channel.state;
|
|
1975
2029
|
const defaultVolume = state.volume * state.expression;
|
|
1976
2030
|
const defaultPan = state.pan;
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
1991
|
-
gainL.gain
|
|
1992
|
-
.cancelScheduledValues(scheduleTime)
|
|
1993
|
-
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
1994
|
-
gainR.gain
|
|
1995
|
-
.cancelScheduledValues(scheduleTime)
|
|
1996
|
-
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1997
|
-
}
|
|
2031
|
+
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
2032
|
+
const volume = (0 <= keyBasedVolume)
|
|
2033
|
+
? defaultVolume * keyBasedVolume / 64
|
|
2034
|
+
: defaultVolume;
|
|
2035
|
+
const keyBasedPan = this.getKeyBasedValue(channel, keyNumber, 10);
|
|
2036
|
+
const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
|
|
2037
|
+
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2038
|
+
gainL.gain
|
|
2039
|
+
.cancelScheduledValues(scheduleTime)
|
|
2040
|
+
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
2041
|
+
gainR.gain
|
|
2042
|
+
.cancelScheduledValues(scheduleTime)
|
|
2043
|
+
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1998
2044
|
}
|
|
1999
2045
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
2000
2046
|
const channel = this.channels[channelNumber];
|
|
@@ -2061,67 +2107,19 @@ class MidyGM2 {
|
|
|
2061
2107
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2062
2108
|
const channel = this.channels[channelNumber];
|
|
2063
2109
|
const state = channel.state;
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
reverbEffect.input.gain
|
|
2069
|
-
.cancelScheduledValues(scheduleTime)
|
|
2070
|
-
.setValueAtTime(state.reverbSendLevel, scheduleTime);
|
|
2071
|
-
}
|
|
2072
|
-
else {
|
|
2073
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2074
|
-
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
2075
|
-
return false;
|
|
2076
|
-
if (note.reverbEffectsSend)
|
|
2077
|
-
note.reverbEffectsSend.disconnect();
|
|
2078
|
-
});
|
|
2079
|
-
}
|
|
2080
|
-
}
|
|
2081
|
-
else {
|
|
2082
|
-
if (0 < reverbSendLevel) {
|
|
2083
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2084
|
-
this.setReverbEffectsSend(channel, note, 0, scheduleTime);
|
|
2085
|
-
});
|
|
2086
|
-
state.reverbSendLevel = reverbSendLevel / 127;
|
|
2087
|
-
reverbEffect.input.gain
|
|
2088
|
-
.cancelScheduledValues(scheduleTime)
|
|
2089
|
-
.setValueAtTime(state.reverbSendLevel, scheduleTime);
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2110
|
+
state.reverbSendLevel = reverbSendLevel / 127;
|
|
2111
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2112
|
+
this.setReverbSend(channel, note, scheduleTime);
|
|
2113
|
+
});
|
|
2092
2114
|
}
|
|
2093
2115
|
setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
|
|
2094
2116
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2095
2117
|
const channel = this.channels[channelNumber];
|
|
2096
2118
|
const state = channel.state;
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
chorusEffect.input.gain
|
|
2102
|
-
.cancelScheduledValues(scheduleTime)
|
|
2103
|
-
.setValueAtTime(state.chorusSendLevel, scheduleTime);
|
|
2104
|
-
}
|
|
2105
|
-
else {
|
|
2106
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2107
|
-
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
2108
|
-
return false;
|
|
2109
|
-
if (note.chorusEffectsSend)
|
|
2110
|
-
note.chorusEffectsSend.disconnect();
|
|
2111
|
-
});
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
else {
|
|
2115
|
-
if (0 < chorusSendLevel) {
|
|
2116
|
-
this.processScheduledNotes(channel, (note) => {
|
|
2117
|
-
this.setChorusEffectsSend(channel, note, 0, scheduleTime);
|
|
2118
|
-
});
|
|
2119
|
-
state.chorusSendLevel = chorusSendLevel / 127;
|
|
2120
|
-
chorusEffect.input.gain
|
|
2121
|
-
.cancelScheduledValues(scheduleTime)
|
|
2122
|
-
.setValueAtTime(state.chorusSendLevel, scheduleTime);
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2119
|
+
state.chorusSendLevel = chorusSendLevel / 127;
|
|
2120
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2121
|
+
this.setChorusSend(channel, note, scheduleTime);
|
|
2122
|
+
});
|
|
2125
2123
|
}
|
|
2126
2124
|
limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
|
|
2127
2125
|
if (maxLSB < channel.dataLSB) {
|
|
@@ -2181,8 +2179,8 @@ class MidyGM2 {
|
|
|
2181
2179
|
}
|
|
2182
2180
|
handlePitchBendRangeRPN(channelNumber, scheduleTime) {
|
|
2183
2181
|
const channel = this.channels[channelNumber];
|
|
2184
|
-
this.limitData(channel, 0, 127, 0,
|
|
2185
|
-
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
2182
|
+
this.limitData(channel, 0, 127, 0, 127);
|
|
2183
|
+
const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
2186
2184
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
2187
2185
|
}
|
|
2188
2186
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
@@ -2192,7 +2190,7 @@ class MidyGM2 {
|
|
|
2192
2190
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2193
2191
|
const state = channel.state;
|
|
2194
2192
|
const prev = state.pitchWheelSensitivity;
|
|
2195
|
-
const next = value /
|
|
2193
|
+
const next = value / 12800;
|
|
2196
2194
|
state.pitchWheelSensitivity = next;
|
|
2197
2195
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
2198
2196
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -2201,7 +2199,8 @@ class MidyGM2 {
|
|
|
2201
2199
|
handleFineTuningRPN(channelNumber, scheduleTime) {
|
|
2202
2200
|
const channel = this.channels[channelNumber];
|
|
2203
2201
|
this.limitData(channel, 0, 127, 0, 127);
|
|
2204
|
-
const
|
|
2202
|
+
const value = channel.dataMSB * 128 + channel.dataLSB;
|
|
2203
|
+
const fineTuning = (value - 8192) / 8192 * 100;
|
|
2205
2204
|
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
2206
2205
|
}
|
|
2207
2206
|
setFineTuning(channelNumber, value, scheduleTime) {
|
|
@@ -2210,7 +2209,7 @@ class MidyGM2 {
|
|
|
2210
2209
|
return;
|
|
2211
2210
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2212
2211
|
const prev = channel.fineTuning;
|
|
2213
|
-
const next =
|
|
2212
|
+
const next = value;
|
|
2214
2213
|
channel.fineTuning = next;
|
|
2215
2214
|
channel.detune += next - prev;
|
|
2216
2215
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -2218,7 +2217,7 @@ class MidyGM2 {
|
|
|
2218
2217
|
handleCoarseTuningRPN(channelNumber, scheduleTime) {
|
|
2219
2218
|
const channel = this.channels[channelNumber];
|
|
2220
2219
|
this.limitDataMSB(channel, 0, 127);
|
|
2221
|
-
const coarseTuning = channel.dataMSB;
|
|
2220
|
+
const coarseTuning = (channel.dataMSB - 64) * 100;
|
|
2222
2221
|
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
2223
2222
|
}
|
|
2224
2223
|
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
@@ -2227,7 +2226,7 @@ class MidyGM2 {
|
|
|
2227
2226
|
return;
|
|
2228
2227
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2229
2228
|
const prev = channel.coarseTuning;
|
|
2230
|
-
const next =
|
|
2229
|
+
const next = value;
|
|
2231
2230
|
channel.coarseTuning = next;
|
|
2232
2231
|
channel.detune += next - prev;
|
|
2233
2232
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -2235,22 +2234,22 @@ class MidyGM2 {
|
|
|
2235
2234
|
handleModulationDepthRangeRPN(channelNumber, scheduleTime) {
|
|
2236
2235
|
const channel = this.channels[channelNumber];
|
|
2237
2236
|
this.limitData(channel, 0, 127, 0, 127);
|
|
2238
|
-
const
|
|
2239
|
-
this.setModulationDepthRange(channelNumber,
|
|
2237
|
+
const value = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
2238
|
+
this.setModulationDepthRange(channelNumber, value, scheduleTime);
|
|
2240
2239
|
}
|
|
2241
|
-
setModulationDepthRange(channelNumber,
|
|
2240
|
+
setModulationDepthRange(channelNumber, value, scheduleTime) {
|
|
2242
2241
|
const channel = this.channels[channelNumber];
|
|
2243
2242
|
if (channel.isDrum)
|
|
2244
2243
|
return;
|
|
2245
2244
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2246
|
-
channel.modulationDepthRange =
|
|
2245
|
+
channel.modulationDepthRange = value;
|
|
2247
2246
|
this.updateModulation(channel, scheduleTime);
|
|
2248
2247
|
}
|
|
2249
2248
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
2250
2249
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2251
2250
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
2252
2251
|
}
|
|
2253
|
-
|
|
2252
|
+
resetChannelStates(channelNumber) {
|
|
2254
2253
|
const scheduleTime = this.audioContext.currentTime;
|
|
2255
2254
|
const channel = this.channels[channelNumber];
|
|
2256
2255
|
const state = channel.state;
|
|
@@ -2268,8 +2267,8 @@ class MidyGM2 {
|
|
|
2268
2267
|
}
|
|
2269
2268
|
this.resetChannelTable(channel);
|
|
2270
2269
|
this.mode = "GM2";
|
|
2271
|
-
this.masterFineTuning = 0; //
|
|
2272
|
-
this.masterCoarseTuning = 0; //
|
|
2270
|
+
this.masterFineTuning = 0; // cent
|
|
2271
|
+
this.masterCoarseTuning = 0; // cent
|
|
2273
2272
|
}
|
|
2274
2273
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
|
|
2275
2274
|
resetAllControllers(channelNumber, _value, scheduleTime) {
|
|
@@ -2403,9 +2402,9 @@ class MidyGM2 {
|
|
|
2403
2402
|
case 9:
|
|
2404
2403
|
switch (data[3]) {
|
|
2405
2404
|
case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2406
|
-
return this.handlePressureSysEx(data, "channelPressureTable");
|
|
2405
|
+
return this.handlePressureSysEx(data, "channelPressureTable", scheduleTime);
|
|
2407
2406
|
case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
|
|
2408
|
-
return this.handleControlChangeSysEx(data);
|
|
2407
|
+
return this.handleControlChangeSysEx(data, scheduleTime);
|
|
2409
2408
|
default:
|
|
2410
2409
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2411
2410
|
}
|
|
@@ -2438,12 +2437,13 @@ class MidyGM2 {
|
|
|
2438
2437
|
}
|
|
2439
2438
|
}
|
|
2440
2439
|
handleMasterFineTuningSysEx(data, scheduleTime) {
|
|
2441
|
-
const
|
|
2440
|
+
const value = (data[5] * 128 + data[4]) / 16383;
|
|
2441
|
+
const fineTuning = (value - 8192) / 8192 * 100;
|
|
2442
2442
|
this.setMasterFineTuning(fineTuning, scheduleTime);
|
|
2443
2443
|
}
|
|
2444
2444
|
setMasterFineTuning(value, scheduleTime) {
|
|
2445
2445
|
const prev = this.masterFineTuning;
|
|
2446
|
-
const next =
|
|
2446
|
+
const next = value;
|
|
2447
2447
|
this.masterFineTuning = next;
|
|
2448
2448
|
const detuneChange = next - prev;
|
|
2449
2449
|
for (let i = 0; i < this.channels.length; i++) {
|
|
@@ -2455,12 +2455,12 @@ class MidyGM2 {
|
|
|
2455
2455
|
}
|
|
2456
2456
|
}
|
|
2457
2457
|
handleMasterCoarseTuningSysEx(data, scheduleTime) {
|
|
2458
|
-
const coarseTuning = data[4];
|
|
2458
|
+
const coarseTuning = (data[4] - 64) * 100;
|
|
2459
2459
|
this.setMasterCoarseTuning(coarseTuning, scheduleTime);
|
|
2460
2460
|
}
|
|
2461
2461
|
setMasterCoarseTuning(value, scheduleTime) {
|
|
2462
2462
|
const prev = this.masterCoarseTuning;
|
|
2463
|
-
const next =
|
|
2463
|
+
const next = value;
|
|
2464
2464
|
this.masterCoarseTuning = next;
|
|
2465
2465
|
const detuneChange = next - prev;
|
|
2466
2466
|
for (let i = 0; i < this.channels.length; i++) {
|
|
@@ -2724,9 +2724,9 @@ class MidyGM2 {
|
|
|
2724
2724
|
: 0;
|
|
2725
2725
|
return channelPressure / 127;
|
|
2726
2726
|
}
|
|
2727
|
-
|
|
2727
|
+
setEffects(channel, note, table, scheduleTime) {
|
|
2728
2728
|
if (0 <= table[0])
|
|
2729
|
-
this.updateDetune(channel, note,
|
|
2729
|
+
this.updateDetune(channel, note, scheduleTime);
|
|
2730
2730
|
if (0.5 <= channel.state.portamemento && 0 <= note.portamentoNoteNumber) {
|
|
2731
2731
|
if (0 <= table[1]) {
|
|
2732
2732
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
@@ -2748,7 +2748,7 @@ class MidyGM2 {
|
|
|
2748
2748
|
if (0 <= table[5])
|
|
2749
2749
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
2750
2750
|
}
|
|
2751
|
-
handlePressureSysEx(data, tableName) {
|
|
2751
|
+
handlePressureSysEx(data, tableName, scheduleTime) {
|
|
2752
2752
|
const channelNumber = data[4];
|
|
2753
2753
|
const channel = this.channels[channelNumber];
|
|
2754
2754
|
if (channel.isDrum)
|
|
@@ -2759,36 +2759,42 @@ class MidyGM2 {
|
|
|
2759
2759
|
const rr = data[i + 1];
|
|
2760
2760
|
table[pp] = rr;
|
|
2761
2761
|
}
|
|
2762
|
+
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
2763
|
+
this.setEffects(channel, note, table, scheduleTime);
|
|
2764
|
+
});
|
|
2762
2765
|
}
|
|
2763
2766
|
initControlTable() {
|
|
2764
2767
|
const ccCount = 128;
|
|
2765
2768
|
const slotSize = 6;
|
|
2766
2769
|
return new Int8Array(ccCount * slotSize).fill(-1);
|
|
2767
2770
|
}
|
|
2768
|
-
|
|
2771
|
+
setControlChangeEffects(channel, controllerType, scheduleTime) {
|
|
2769
2772
|
const slotSize = 6;
|
|
2770
2773
|
const offset = controllerType * slotSize;
|
|
2771
2774
|
const table = channel.controlTable.subarray(offset, offset + slotSize);
|
|
2772
2775
|
this.processScheduledNotes(channel, (note) => {
|
|
2773
|
-
this.
|
|
2776
|
+
this.setEffects(channel, note, table, scheduleTime);
|
|
2774
2777
|
});
|
|
2775
2778
|
}
|
|
2776
|
-
handleControlChangeSysEx(data) {
|
|
2779
|
+
handleControlChangeSysEx(data, scheduleTime) {
|
|
2777
2780
|
const channelNumber = data[4];
|
|
2778
2781
|
const channel = this.channels[channelNumber];
|
|
2779
2782
|
if (channel.isDrum)
|
|
2780
2783
|
return;
|
|
2784
|
+
const slotSize = 6;
|
|
2781
2785
|
const controllerType = data[5];
|
|
2782
|
-
const
|
|
2783
|
-
|
|
2786
|
+
const offset = controllerType * slotSize;
|
|
2787
|
+
const table = channel.controlTable;
|
|
2788
|
+
for (let i = 6; i < data.length; i += 2) {
|
|
2784
2789
|
const pp = data[i];
|
|
2785
2790
|
const rr = data[i + 1];
|
|
2786
|
-
table[pp] = rr;
|
|
2791
|
+
table[offset + pp] = rr;
|
|
2787
2792
|
}
|
|
2793
|
+
this.setControlChangeEffects(channel, controllerType, scheduleTime);
|
|
2788
2794
|
}
|
|
2789
2795
|
getKeyBasedValue(channel, keyNumber, controllerType) {
|
|
2790
2796
|
const index = keyNumber * 128 + controllerType;
|
|
2791
|
-
const controlValue = channel.
|
|
2797
|
+
const controlValue = channel.keyBasedTable[index];
|
|
2792
2798
|
return controlValue;
|
|
2793
2799
|
}
|
|
2794
2800
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
@@ -2797,14 +2803,33 @@ class MidyGM2 {
|
|
|
2797
2803
|
if (!channel.isDrum)
|
|
2798
2804
|
return;
|
|
2799
2805
|
const keyNumber = data[5];
|
|
2800
|
-
const table = channel.
|
|
2801
|
-
for (let i = 6; i < data.length
|
|
2806
|
+
const table = channel.keyBasedTable;
|
|
2807
|
+
for (let i = 6; i < data.length; i += 2) {
|
|
2802
2808
|
const controllerType = data[i];
|
|
2803
2809
|
const value = data[i + 1];
|
|
2804
2810
|
const index = keyNumber * 128 + controllerType;
|
|
2805
2811
|
table[index] = value;
|
|
2812
|
+
switch (controllerType) {
|
|
2813
|
+
case 7:
|
|
2814
|
+
case 10:
|
|
2815
|
+
this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
|
|
2816
|
+
break;
|
|
2817
|
+
case 91:
|
|
2818
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2819
|
+
if (note.noteNumber === keyNumber) {
|
|
2820
|
+
this.setReverbSend(channel, note, scheduleTime);
|
|
2821
|
+
}
|
|
2822
|
+
});
|
|
2823
|
+
break;
|
|
2824
|
+
case 93:
|
|
2825
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2826
|
+
if (note.noteNumber === keyNumber) {
|
|
2827
|
+
this.setChorusSend(channel, note, scheduleTime);
|
|
2828
|
+
}
|
|
2829
|
+
});
|
|
2830
|
+
break;
|
|
2831
|
+
}
|
|
2806
2832
|
}
|
|
2807
|
-
this.setChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
|
|
2808
2833
|
}
|
|
2809
2834
|
handleSysEx(data, scheduleTime) {
|
|
2810
2835
|
switch (data[0]) {
|
|
@@ -2854,7 +2879,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
2854
2879
|
rpnLSB: 127,
|
|
2855
2880
|
mono: false, // CC#124, CC#125
|
|
2856
2881
|
modulationDepthRange: 50, // cent
|
|
2857
|
-
fineTuning: 0, //
|
|
2858
|
-
coarseTuning: 0, //
|
|
2882
|
+
fineTuning: 0, // cent
|
|
2883
|
+
coarseTuning: 0, // cent
|
|
2859
2884
|
}
|
|
2860
2885
|
});
|