@marmooo/midy 0.4.0 → 0.4.2
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 +14 -1
- package/esm/midy-GM1.d.ts +8 -7
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +138 -81
- package/esm/midy-GM2.d.ts +8 -7
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +161 -96
- package/esm/midy-GMLite.d.ts +8 -7
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +126 -72
- package/esm/midy.d.ts +17 -12
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +275 -123
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +8 -7
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +138 -81
- package/script/midy-GM2.d.ts +8 -7
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +161 -96
- package/script/midy-GMLite.d.ts +8 -7
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +126 -72
- package/script/midy.d.ts +17 -12
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +275 -123
package/script/midy-GM2.js
CHANGED
|
@@ -29,12 +29,6 @@ class Note {
|
|
|
29
29
|
writable: true,
|
|
30
30
|
value: false
|
|
31
31
|
});
|
|
32
|
-
Object.defineProperty(this, "pending", {
|
|
33
|
-
enumerable: true,
|
|
34
|
-
configurable: true,
|
|
35
|
-
writable: true,
|
|
36
|
-
value: true
|
|
37
|
-
});
|
|
38
32
|
Object.defineProperty(this, "bufferSource", {
|
|
39
33
|
enumerable: true,
|
|
40
34
|
configurable: true,
|
|
@@ -110,6 +104,9 @@ class Note {
|
|
|
110
104
|
this.noteNumber = noteNumber;
|
|
111
105
|
this.velocity = velocity;
|
|
112
106
|
this.startTime = startTime;
|
|
107
|
+
this.ready = new Promise((resolve) => {
|
|
108
|
+
this.resolveReady = resolve;
|
|
109
|
+
});
|
|
113
110
|
}
|
|
114
111
|
}
|
|
115
112
|
const drumExclusiveClassesByKit = new Array(57);
|
|
@@ -154,12 +151,12 @@ const defaultControllerState = {
|
|
|
154
151
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
155
152
|
link: { type: 127, defaultValue: 0 },
|
|
156
153
|
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
157
|
-
|
|
158
|
-
|
|
154
|
+
modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
|
|
155
|
+
portamentoTimeMSB: { type: 128 + 5, defaultValue: 0 },
|
|
159
156
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
158
|
+
panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
159
|
+
expressionMSB: { type: 128 + 11, defaultValue: 1 },
|
|
163
160
|
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
164
161
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
165
162
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
@@ -231,8 +228,9 @@ const pitchEnvelopeKeys = [
|
|
|
231
228
|
"playbackRate",
|
|
232
229
|
];
|
|
233
230
|
const pitchEnvelopeKeySet = new Set(pitchEnvelopeKeys);
|
|
234
|
-
class MidyGM2 {
|
|
231
|
+
class MidyGM2 extends EventTarget {
|
|
235
232
|
constructor(audioContext) {
|
|
233
|
+
super();
|
|
236
234
|
Object.defineProperty(this, "mode", {
|
|
237
235
|
enumerable: true,
|
|
238
236
|
configurable: true,
|
|
@@ -393,6 +391,12 @@ class MidyGM2 {
|
|
|
393
391
|
writable: true,
|
|
394
392
|
value: false
|
|
395
393
|
});
|
|
394
|
+
Object.defineProperty(this, "loop", {
|
|
395
|
+
enumerable: true,
|
|
396
|
+
configurable: true,
|
|
397
|
+
writable: true,
|
|
398
|
+
value: false
|
|
399
|
+
});
|
|
396
400
|
Object.defineProperty(this, "playPromise", {
|
|
397
401
|
enumerable: true,
|
|
398
402
|
configurable: true,
|
|
@@ -549,7 +553,7 @@ class MidyGM2 {
|
|
|
549
553
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
550
554
|
}
|
|
551
555
|
createChannelAudioNodes(audioContext) {
|
|
552
|
-
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.
|
|
556
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
|
|
553
557
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
554
558
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
555
559
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -590,10 +594,9 @@ class MidyGM2 {
|
|
|
590
594
|
return channels;
|
|
591
595
|
}
|
|
592
596
|
async createAudioBuffer(voiceParams) {
|
|
593
|
-
const sample = voiceParams
|
|
594
|
-
const
|
|
595
|
-
const
|
|
596
|
-
const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
|
|
597
|
+
const { sample, start, end } = voiceParams;
|
|
598
|
+
const sampleEnd = sample.data.length + end;
|
|
599
|
+
const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
|
|
597
600
|
return audioBuffer;
|
|
598
601
|
}
|
|
599
602
|
isLoopDrum(channel, noteNumber) {
|
|
@@ -613,7 +616,7 @@ class MidyGM2 {
|
|
|
613
616
|
}
|
|
614
617
|
return bufferSource;
|
|
615
618
|
}
|
|
616
|
-
|
|
619
|
+
scheduleTimelineEvents(scheduleTime, queueIndex) {
|
|
617
620
|
const timeOffset = this.resumeTime - this.startTime;
|
|
618
621
|
const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
|
|
619
622
|
const schedulingOffset = this.startDelay - timeOffset;
|
|
@@ -625,12 +628,10 @@ class MidyGM2 {
|
|
|
625
628
|
const startTime = event.startTime + schedulingOffset;
|
|
626
629
|
switch (event.type) {
|
|
627
630
|
case "noteOn":
|
|
628
|
-
|
|
631
|
+
this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
629
632
|
break;
|
|
630
633
|
case "noteOff": {
|
|
631
|
-
|
|
632
|
-
if (notePromise)
|
|
633
|
-
this.notePromises.push(notePromise);
|
|
634
|
+
this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
634
635
|
break;
|
|
635
636
|
}
|
|
636
637
|
case "controller":
|
|
@@ -671,22 +672,23 @@ class MidyGM2 {
|
|
|
671
672
|
}
|
|
672
673
|
}
|
|
673
674
|
updateStates(queueIndex, nextQueueIndex) {
|
|
675
|
+
const now = this.audioContext.currentTime;
|
|
674
676
|
if (nextQueueIndex < queueIndex)
|
|
675
677
|
queueIndex = 0;
|
|
676
678
|
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
677
679
|
const event = this.timeline[i];
|
|
678
680
|
switch (event.type) {
|
|
679
681
|
case "controller":
|
|
680
|
-
this.setControlChange(event.channel, event.controllerType, event.value,
|
|
682
|
+
this.setControlChange(event.channel, event.controllerType, event.value, now - this.resumeTime + event.startTime);
|
|
681
683
|
break;
|
|
682
684
|
case "programChange":
|
|
683
|
-
this.setProgramChange(event.channel, event.programNumber,
|
|
685
|
+
this.setProgramChange(event.channel, event.programNumber, now - this.resumeTime + event.startTime);
|
|
684
686
|
break;
|
|
685
687
|
case "pitchBend":
|
|
686
|
-
this.setPitchBend(event.channel, event.value + 8192,
|
|
688
|
+
this.setPitchBend(event.channel, event.value + 8192, now - this.resumeTime + event.startTime);
|
|
687
689
|
break;
|
|
688
690
|
case "sysEx":
|
|
689
|
-
this.handleSysEx(event.data,
|
|
691
|
+
this.handleSysEx(event.data, now - this.resumeTime + event.startTime);
|
|
690
692
|
}
|
|
691
693
|
}
|
|
692
694
|
}
|
|
@@ -694,52 +696,88 @@ class MidyGM2 {
|
|
|
694
696
|
if (this.audioContext.state === "suspended") {
|
|
695
697
|
await this.audioContext.resume();
|
|
696
698
|
}
|
|
699
|
+
const paused = this.isPaused;
|
|
697
700
|
this.isPlaying = true;
|
|
698
701
|
this.isPaused = false;
|
|
699
702
|
this.startTime = this.audioContext.currentTime;
|
|
703
|
+
if (paused) {
|
|
704
|
+
this.dispatchEvent(new Event("resumed"));
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
this.dispatchEvent(new Event("started"));
|
|
708
|
+
}
|
|
700
709
|
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
701
|
-
let
|
|
710
|
+
let exitReason;
|
|
702
711
|
this.notePromises = [];
|
|
703
|
-
while (
|
|
712
|
+
while (true) {
|
|
704
713
|
const now = this.audioContext.currentTime;
|
|
705
714
|
if (0 < this.lastActiveSensing &&
|
|
706
715
|
this.activeSensingThreshold < performance.now() - this.lastActiveSensing) {
|
|
707
716
|
await this.stopNotes(0, true, now);
|
|
708
717
|
await this.audioContext.suspend();
|
|
709
|
-
|
|
718
|
+
exitReason = "aborted";
|
|
710
719
|
break;
|
|
711
720
|
}
|
|
721
|
+
if (this.timeline.length <= queueIndex) {
|
|
722
|
+
await this.stopNotes(0, true, now);
|
|
723
|
+
if (this.loop) {
|
|
724
|
+
this.notePromises = [];
|
|
725
|
+
this.resetAllStates();
|
|
726
|
+
this.startTime = this.audioContext.currentTime;
|
|
727
|
+
this.resumeTime = 0;
|
|
728
|
+
queueIndex = 0;
|
|
729
|
+
this.dispatchEvent(new Event("looped"));
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
else {
|
|
733
|
+
await this.audioContext.suspend();
|
|
734
|
+
exitReason = "ended";
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
712
738
|
if (this.isPausing) {
|
|
713
739
|
await this.stopNotes(0, true, now);
|
|
714
740
|
await this.audioContext.suspend();
|
|
715
741
|
this.notePromises = [];
|
|
742
|
+
this.isPausing = false;
|
|
743
|
+
exitReason = "paused";
|
|
716
744
|
break;
|
|
717
745
|
}
|
|
718
746
|
else if (this.isStopping) {
|
|
719
747
|
await this.stopNotes(0, true, now);
|
|
720
748
|
await this.audioContext.suspend();
|
|
721
|
-
|
|
749
|
+
this.isStopping = false;
|
|
750
|
+
exitReason = "stopped";
|
|
722
751
|
break;
|
|
723
752
|
}
|
|
724
753
|
else if (this.isSeeking) {
|
|
725
|
-
|
|
754
|
+
this.stopNotes(0, true, now);
|
|
726
755
|
this.startTime = this.audioContext.currentTime;
|
|
727
756
|
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
728
757
|
this.updateStates(queueIndex, nextQueueIndex);
|
|
729
758
|
queueIndex = nextQueueIndex;
|
|
730
759
|
this.isSeeking = false;
|
|
760
|
+
this.dispatchEvent(new Event("seeked"));
|
|
731
761
|
continue;
|
|
732
762
|
}
|
|
733
|
-
queueIndex =
|
|
763
|
+
queueIndex = this.scheduleTimelineEvents(now, queueIndex);
|
|
734
764
|
const waitTime = now + this.noteCheckInterval;
|
|
735
765
|
await this.scheduleTask(() => { }, waitTime);
|
|
736
766
|
}
|
|
737
|
-
if (
|
|
767
|
+
if (exitReason !== "paused") {
|
|
738
768
|
this.notePromises = [];
|
|
739
769
|
this.resetAllStates();
|
|
740
770
|
this.lastActiveSensing = 0;
|
|
741
771
|
}
|
|
742
772
|
this.isPlaying = false;
|
|
773
|
+
if (exitReason === "paused") {
|
|
774
|
+
this.isPaused = true;
|
|
775
|
+
this.dispatchEvent(new Event("paused"));
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
this.isPaused = false;
|
|
779
|
+
this.dispatchEvent(new Event(exitReason));
|
|
780
|
+
}
|
|
743
781
|
}
|
|
744
782
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
745
783
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -876,24 +914,20 @@ class MidyGM2 {
|
|
|
876
914
|
return;
|
|
877
915
|
this.isStopping = true;
|
|
878
916
|
await this.playPromise;
|
|
879
|
-
this.isStopping = false;
|
|
880
917
|
}
|
|
881
918
|
async pause() {
|
|
882
919
|
if (!this.isPlaying || this.isPaused)
|
|
883
920
|
return;
|
|
884
921
|
const now = this.audioContext.currentTime;
|
|
885
|
-
this.resumeTime = now
|
|
922
|
+
this.resumeTime = now + this.resumeTime - this.startTime;
|
|
886
923
|
this.isPausing = true;
|
|
887
924
|
await this.playPromise;
|
|
888
|
-
this.isPausing = false;
|
|
889
|
-
this.isPaused = true;
|
|
890
925
|
}
|
|
891
926
|
async resume() {
|
|
892
927
|
if (!this.isPaused)
|
|
893
928
|
return;
|
|
894
929
|
this.playPromise = this.playNotes();
|
|
895
930
|
await this.playPromise;
|
|
896
|
-
this.isPaused = false;
|
|
897
931
|
}
|
|
898
932
|
seekTo(second) {
|
|
899
933
|
this.resumeTime = second;
|
|
@@ -916,19 +950,23 @@ class MidyGM2 {
|
|
|
916
950
|
const now = this.audioContext.currentTime;
|
|
917
951
|
return now + this.resumeTime - this.startTime;
|
|
918
952
|
}
|
|
919
|
-
processScheduledNotes(channel, callback) {
|
|
953
|
+
async processScheduledNotes(channel, callback) {
|
|
920
954
|
const scheduledNotes = channel.scheduledNotes;
|
|
955
|
+
const tasks = [];
|
|
921
956
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
922
957
|
const note = scheduledNotes[i];
|
|
923
958
|
if (!note)
|
|
924
959
|
continue;
|
|
925
960
|
if (note.ending)
|
|
926
961
|
continue;
|
|
927
|
-
callback(note);
|
|
962
|
+
const task = note.ready.then(() => callback(note));
|
|
963
|
+
tasks.push(task);
|
|
928
964
|
}
|
|
965
|
+
await Promise.all(tasks);
|
|
929
966
|
}
|
|
930
|
-
processActiveNotes(channel, scheduleTime, callback) {
|
|
967
|
+
async processActiveNotes(channel, scheduleTime, callback) {
|
|
931
968
|
const scheduledNotes = channel.scheduledNotes;
|
|
969
|
+
const tasks = [];
|
|
932
970
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
933
971
|
const note = scheduledNotes[i];
|
|
934
972
|
if (!note)
|
|
@@ -937,8 +975,10 @@ class MidyGM2 {
|
|
|
937
975
|
continue;
|
|
938
976
|
if (scheduleTime < note.startTime)
|
|
939
977
|
break;
|
|
940
|
-
callback(note);
|
|
978
|
+
const task = note.ready.then(() => callback(note));
|
|
979
|
+
tasks.push(task);
|
|
941
980
|
}
|
|
981
|
+
await Promise.all(tasks);
|
|
942
982
|
}
|
|
943
983
|
createConvolutionReverbImpulse(audioContext, decay, preDecay) {
|
|
944
984
|
const sampleRate = audioContext.sampleRate;
|
|
@@ -1142,7 +1182,7 @@ class MidyGM2 {
|
|
|
1142
1182
|
}
|
|
1143
1183
|
getPortamentoTime(channel, note) {
|
|
1144
1184
|
const deltaSemitone = Math.abs(note.noteNumber - note.portamentoNoteNumber);
|
|
1145
|
-
const value = Math.ceil(channel.state.
|
|
1185
|
+
const value = Math.ceil(channel.state.portamentoTimeMSB * 127);
|
|
1146
1186
|
return deltaSemitone / this.getPitchIncrementSpeed(value) / 10;
|
|
1147
1187
|
}
|
|
1148
1188
|
getPitchIncrementSpeed(value) {
|
|
@@ -1401,7 +1441,7 @@ class MidyGM2 {
|
|
|
1401
1441
|
if (0 < state.vibratoDepth) {
|
|
1402
1442
|
this.startVibrato(channel, note, now);
|
|
1403
1443
|
}
|
|
1404
|
-
if (0 < state.
|
|
1444
|
+
if (0 < state.modulationDepthMSB) {
|
|
1405
1445
|
this.startModulation(channel, note, now);
|
|
1406
1446
|
}
|
|
1407
1447
|
if (channel.mono && channel.currentBufferSource) {
|
|
@@ -1412,7 +1452,13 @@ class MidyGM2 {
|
|
|
1412
1452
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1413
1453
|
this.setChorusSend(channel, note, now);
|
|
1414
1454
|
this.setReverbSend(channel, note, now);
|
|
1415
|
-
|
|
1455
|
+
if (voiceParams.sample.type === "compressed") {
|
|
1456
|
+
const offset = voiceParams.start / audioBuffer.sampleRate;
|
|
1457
|
+
note.bufferSource.start(startTime, offset);
|
|
1458
|
+
}
|
|
1459
|
+
else {
|
|
1460
|
+
note.bufferSource.start(startTime);
|
|
1461
|
+
}
|
|
1416
1462
|
return note;
|
|
1417
1463
|
}
|
|
1418
1464
|
handleExclusiveClass(note, channelNumber, startTime) {
|
|
@@ -1497,11 +1543,7 @@ class MidyGM2 {
|
|
|
1497
1543
|
return;
|
|
1498
1544
|
await this.setNoteAudioNode(channel, note, realtime);
|
|
1499
1545
|
this.setNoteRouting(channelNumber, note, startTime);
|
|
1500
|
-
note.
|
|
1501
|
-
const off = note.offEvent;
|
|
1502
|
-
if (off) {
|
|
1503
|
-
this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
|
|
1504
|
-
}
|
|
1546
|
+
note.resolveReady();
|
|
1505
1547
|
}
|
|
1506
1548
|
disconnectNote(note) {
|
|
1507
1549
|
note.bufferSource.disconnect();
|
|
@@ -1545,7 +1587,7 @@ class MidyGM2 {
|
|
|
1545
1587
|
}, stopTime);
|
|
1546
1588
|
});
|
|
1547
1589
|
}
|
|
1548
|
-
noteOff(channelNumber, noteNumber,
|
|
1590
|
+
noteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
1549
1591
|
const channel = this.channels[channelNumber];
|
|
1550
1592
|
const state = channel.state;
|
|
1551
1593
|
if (!force) {
|
|
@@ -1564,13 +1606,13 @@ class MidyGM2 {
|
|
|
1564
1606
|
if (index < 0)
|
|
1565
1607
|
return;
|
|
1566
1608
|
const note = channel.scheduledNotes[index];
|
|
1567
|
-
if (note.pending) {
|
|
1568
|
-
note.offEvent = { velocity, startTime: endTime };
|
|
1569
|
-
return;
|
|
1570
|
-
}
|
|
1571
1609
|
note.ending = true;
|
|
1572
1610
|
this.setNoteIndex(channel, index);
|
|
1573
|
-
|
|
1611
|
+
const promise = note.ready.then(() => {
|
|
1612
|
+
return this.releaseNote(channel, note, endTime);
|
|
1613
|
+
});
|
|
1614
|
+
this.notePromises.push(promise);
|
|
1615
|
+
return promise;
|
|
1574
1616
|
}
|
|
1575
1617
|
setNoteIndex(channel, index) {
|
|
1576
1618
|
let allEnds = true;
|
|
@@ -1679,7 +1721,7 @@ class MidyGM2 {
|
|
|
1679
1721
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1680
1722
|
this.setEffects(channel, note, table, scheduleTime);
|
|
1681
1723
|
});
|
|
1682
|
-
this.applyVoiceParams(channel, 13);
|
|
1724
|
+
this.applyVoiceParams(channel, 13, scheduleTime);
|
|
1683
1725
|
}
|
|
1684
1726
|
handlePitchBendMessage(channelNumber, lsb, msb, scheduleTime) {
|
|
1685
1727
|
const pitchBend = msb * 128 + lsb;
|
|
@@ -1689,7 +1731,8 @@ class MidyGM2 {
|
|
|
1689
1731
|
const channel = this.channels[channelNumber];
|
|
1690
1732
|
if (channel.isDrum)
|
|
1691
1733
|
return;
|
|
1692
|
-
|
|
1734
|
+
if (!(0 <= scheduleTime))
|
|
1735
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1693
1736
|
const state = channel.state;
|
|
1694
1737
|
const prev = state.pitchWheel * 2 - 1;
|
|
1695
1738
|
const next = (value - 8192) / 8192;
|
|
@@ -1702,7 +1745,8 @@ class MidyGM2 {
|
|
|
1702
1745
|
if (note.modulationDepth) {
|
|
1703
1746
|
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1704
1747
|
this.getLFOPitchDepth(channel, note);
|
|
1705
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1748
|
+
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1749
|
+
channel.state.modulationDepthMSB;
|
|
1706
1750
|
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1707
1751
|
note.modulationDepth.gain
|
|
1708
1752
|
.cancelScheduledValues(scheduleTime)
|
|
@@ -1834,7 +1878,7 @@ class MidyGM2 {
|
|
|
1834
1878
|
createVoiceParamsHandlers() {
|
|
1835
1879
|
return {
|
|
1836
1880
|
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1837
|
-
if (0 < channel.state.
|
|
1881
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1838
1882
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1839
1883
|
}
|
|
1840
1884
|
},
|
|
@@ -1844,12 +1888,12 @@ class MidyGM2 {
|
|
|
1844
1888
|
}
|
|
1845
1889
|
},
|
|
1846
1890
|
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1847
|
-
if (0 < channel.state.
|
|
1891
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1848
1892
|
this.setModLfoToFilterFc(channel, note, scheduleTime);
|
|
1849
1893
|
}
|
|
1850
1894
|
},
|
|
1851
1895
|
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1852
|
-
if (0 < channel.state.
|
|
1896
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1853
1897
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
1854
1898
|
}
|
|
1855
1899
|
},
|
|
@@ -1860,12 +1904,12 @@ class MidyGM2 {
|
|
|
1860
1904
|
this.setReverbSend(channel, note, scheduleTime);
|
|
1861
1905
|
},
|
|
1862
1906
|
delayModLFO: (_channel, note, _scheduleTime) => {
|
|
1863
|
-
if (0 < channel.state.
|
|
1907
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1864
1908
|
this.setDelayModLFO(note);
|
|
1865
1909
|
}
|
|
1866
1910
|
},
|
|
1867
1911
|
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1868
|
-
if (0 < channel.state.
|
|
1912
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1869
1913
|
this.setFreqModLFO(note, scheduleTime);
|
|
1870
1914
|
}
|
|
1871
1915
|
},
|
|
@@ -1967,7 +2011,8 @@ class MidyGM2 {
|
|
|
1967
2011
|
this.channels[channelNumber].bankMSB = msb;
|
|
1968
2012
|
}
|
|
1969
2013
|
updateModulation(channel, scheduleTime) {
|
|
1970
|
-
const depth = channel.state.
|
|
2014
|
+
const depth = channel.state.modulationDepthMSB *
|
|
2015
|
+
channel.modulationDepthRange;
|
|
1971
2016
|
this.processScheduledNotes(channel, (note) => {
|
|
1972
2017
|
if (note.modulationDepth) {
|
|
1973
2018
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
@@ -1981,8 +2026,9 @@ class MidyGM2 {
|
|
|
1981
2026
|
const channel = this.channels[channelNumber];
|
|
1982
2027
|
if (channel.isDrum)
|
|
1983
2028
|
return;
|
|
1984
|
-
|
|
1985
|
-
|
|
2029
|
+
if (!(0 <= scheduleTime))
|
|
2030
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2031
|
+
channel.state.modulationDepthMSB = modulation / 127;
|
|
1986
2032
|
this.updateModulation(channel, scheduleTime);
|
|
1987
2033
|
}
|
|
1988
2034
|
updatePortamento(channel, scheduleTime) {
|
|
@@ -2004,17 +2050,19 @@ class MidyGM2 {
|
|
|
2004
2050
|
});
|
|
2005
2051
|
}
|
|
2006
2052
|
setPortamentoTime(channelNumber, portamentoTime, scheduleTime) {
|
|
2053
|
+
if (!(0 <= scheduleTime))
|
|
2054
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2007
2055
|
const channel = this.channels[channelNumber];
|
|
2008
|
-
|
|
2009
|
-
channel.state.portamentoTime = portamentoTime / 127;
|
|
2056
|
+
channel.state.portamentoTimeMSB = portamentoTime / 127;
|
|
2010
2057
|
if (channel.isDrum)
|
|
2011
2058
|
return;
|
|
2012
2059
|
this.updatePortamento(channel, scheduleTime);
|
|
2013
2060
|
}
|
|
2014
2061
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
2015
|
-
|
|
2062
|
+
if (!(0 <= scheduleTime))
|
|
2063
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2016
2064
|
const channel = this.channels[channelNumber];
|
|
2017
|
-
channel.state.
|
|
2065
|
+
channel.state.volumeMSB = volume / 127;
|
|
2018
2066
|
if (channel.isDrum) {
|
|
2019
2067
|
for (let i = 0; i < 128; i++) {
|
|
2020
2068
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2032,9 +2080,10 @@ class MidyGM2 {
|
|
|
2032
2080
|
};
|
|
2033
2081
|
}
|
|
2034
2082
|
setPan(channelNumber, pan, scheduleTime) {
|
|
2035
|
-
|
|
2083
|
+
if (!(0 <= scheduleTime))
|
|
2084
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2036
2085
|
const channel = this.channels[channelNumber];
|
|
2037
|
-
channel.state.
|
|
2086
|
+
channel.state.panMSB = pan / 127;
|
|
2038
2087
|
if (channel.isDrum) {
|
|
2039
2088
|
for (let i = 0; i < 128; i++) {
|
|
2040
2089
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2045,9 +2094,10 @@ class MidyGM2 {
|
|
|
2045
2094
|
}
|
|
2046
2095
|
}
|
|
2047
2096
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
2048
|
-
|
|
2097
|
+
if (!(0 <= scheduleTime))
|
|
2098
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2049
2099
|
const channel = this.channels[channelNumber];
|
|
2050
|
-
channel.state.
|
|
2100
|
+
channel.state.expressionMSB = expression / 127;
|
|
2051
2101
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2052
2102
|
}
|
|
2053
2103
|
setBankLSB(channelNumber, lsb) {
|
|
@@ -2059,8 +2109,8 @@ class MidyGM2 {
|
|
|
2059
2109
|
}
|
|
2060
2110
|
updateChannelVolume(channel, scheduleTime) {
|
|
2061
2111
|
const state = channel.state;
|
|
2062
|
-
const volume = state.
|
|
2063
|
-
const { gainLeft, gainRight } = this.panToGain(state.
|
|
2112
|
+
const volume = state.volumeMSB * state.expressionMSB;
|
|
2113
|
+
const { gainLeft, gainRight } = this.panToGain(state.panMSB);
|
|
2064
2114
|
channel.gainL.gain
|
|
2065
2115
|
.cancelScheduledValues(scheduleTime)
|
|
2066
2116
|
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
@@ -2074,8 +2124,8 @@ class MidyGM2 {
|
|
|
2074
2124
|
return;
|
|
2075
2125
|
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
2076
2126
|
const state = channel.state;
|
|
2077
|
-
const defaultVolume = state.
|
|
2078
|
-
const defaultPan = state.
|
|
2127
|
+
const defaultVolume = state.volumeMSB * state.expressionMSB;
|
|
2128
|
+
const defaultPan = state.panMSB;
|
|
2079
2129
|
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
2080
2130
|
const volume = (0 <= keyBasedVolume)
|
|
2081
2131
|
? defaultVolume * keyBasedVolume / 64
|
|
@@ -2094,7 +2144,8 @@ class MidyGM2 {
|
|
|
2094
2144
|
const channel = this.channels[channelNumber];
|
|
2095
2145
|
if (channel.isDrum)
|
|
2096
2146
|
return;
|
|
2097
|
-
|
|
2147
|
+
if (!(0 <= scheduleTime))
|
|
2148
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2098
2149
|
channel.state.sustainPedal = value / 127;
|
|
2099
2150
|
if (64 <= value) {
|
|
2100
2151
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -2112,7 +2163,8 @@ class MidyGM2 {
|
|
|
2112
2163
|
const channel = this.channels[channelNumber];
|
|
2113
2164
|
if (channel.isDrum)
|
|
2114
2165
|
return;
|
|
2115
|
-
|
|
2166
|
+
if (!(0 <= scheduleTime))
|
|
2167
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2116
2168
|
channel.state.portamento = value / 127;
|
|
2117
2169
|
this.updatePortamento(channel, scheduleTime);
|
|
2118
2170
|
}
|
|
@@ -2120,7 +2172,8 @@ class MidyGM2 {
|
|
|
2120
2172
|
const channel = this.channels[channelNumber];
|
|
2121
2173
|
if (channel.isDrum)
|
|
2122
2174
|
return;
|
|
2123
|
-
|
|
2175
|
+
if (!(0 <= scheduleTime))
|
|
2176
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2124
2177
|
channel.state.sostenutoPedal = value / 127;
|
|
2125
2178
|
if (64 <= value) {
|
|
2126
2179
|
const sostenutoNotes = [];
|
|
@@ -2141,7 +2194,8 @@ class MidyGM2 {
|
|
|
2141
2194
|
if (channel.isDrum)
|
|
2142
2195
|
return;
|
|
2143
2196
|
const state = channel.state;
|
|
2144
|
-
|
|
2197
|
+
if (!(0 <= scheduleTime))
|
|
2198
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2145
2199
|
state.softPedal = softPedal / 127;
|
|
2146
2200
|
this.processScheduledNotes(channel, (note) => {
|
|
2147
2201
|
if (this.isPortamento(channel, note)) {
|
|
@@ -2155,7 +2209,8 @@ class MidyGM2 {
|
|
|
2155
2209
|
});
|
|
2156
2210
|
}
|
|
2157
2211
|
setReverbSendLevel(channelNumber, reverbSendLevel, scheduleTime) {
|
|
2158
|
-
|
|
2212
|
+
if (!(0 <= scheduleTime))
|
|
2213
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2159
2214
|
const channel = this.channels[channelNumber];
|
|
2160
2215
|
const state = channel.state;
|
|
2161
2216
|
state.reverbSendLevel = reverbSendLevel / 127;
|
|
@@ -2164,7 +2219,8 @@ class MidyGM2 {
|
|
|
2164
2219
|
});
|
|
2165
2220
|
}
|
|
2166
2221
|
setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
|
|
2167
|
-
|
|
2222
|
+
if (!(0 <= scheduleTime))
|
|
2223
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2168
2224
|
const channel = this.channels[channelNumber];
|
|
2169
2225
|
const state = channel.state;
|
|
2170
2226
|
state.chorusSendLevel = chorusSendLevel / 127;
|
|
@@ -2238,7 +2294,8 @@ class MidyGM2 {
|
|
|
2238
2294
|
const channel = this.channels[channelNumber];
|
|
2239
2295
|
if (channel.isDrum)
|
|
2240
2296
|
return;
|
|
2241
|
-
|
|
2297
|
+
if (!(0 <= scheduleTime))
|
|
2298
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2242
2299
|
const state = channel.state;
|
|
2243
2300
|
const prev = state.pitchWheelSensitivity;
|
|
2244
2301
|
const next = value / 12800;
|
|
@@ -2258,7 +2315,8 @@ class MidyGM2 {
|
|
|
2258
2315
|
const channel = this.channels[channelNumber];
|
|
2259
2316
|
if (channel.isDrum)
|
|
2260
2317
|
return;
|
|
2261
|
-
|
|
2318
|
+
if (!(0 <= scheduleTime))
|
|
2319
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2262
2320
|
const prev = channel.fineTuning;
|
|
2263
2321
|
const next = value;
|
|
2264
2322
|
channel.fineTuning = next;
|
|
@@ -2275,7 +2333,8 @@ class MidyGM2 {
|
|
|
2275
2333
|
const channel = this.channels[channelNumber];
|
|
2276
2334
|
if (channel.isDrum)
|
|
2277
2335
|
return;
|
|
2278
|
-
|
|
2336
|
+
if (!(0 <= scheduleTime))
|
|
2337
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2279
2338
|
const prev = channel.coarseTuning;
|
|
2280
2339
|
const next = value;
|
|
2281
2340
|
channel.coarseTuning = next;
|
|
@@ -2292,12 +2351,14 @@ class MidyGM2 {
|
|
|
2292
2351
|
const channel = this.channels[channelNumber];
|
|
2293
2352
|
if (channel.isDrum)
|
|
2294
2353
|
return;
|
|
2295
|
-
|
|
2354
|
+
if (!(0 <= scheduleTime))
|
|
2355
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2296
2356
|
channel.modulationDepthRange = value;
|
|
2297
2357
|
this.updateModulation(channel, scheduleTime);
|
|
2298
2358
|
}
|
|
2299
2359
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
2300
|
-
|
|
2360
|
+
if (!(0 <= scheduleTime))
|
|
2361
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2301
2362
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
2302
2363
|
}
|
|
2303
2364
|
resetChannelStates(channelNumber) {
|
|
@@ -2326,8 +2387,8 @@ class MidyGM2 {
|
|
|
2326
2387
|
const keys = [
|
|
2327
2388
|
"channelPressure",
|
|
2328
2389
|
"pitchWheel",
|
|
2329
|
-
"
|
|
2330
|
-
"
|
|
2390
|
+
"expressionMSB",
|
|
2391
|
+
"modulationDepthMSB",
|
|
2331
2392
|
"sustainPedal",
|
|
2332
2393
|
"portamento",
|
|
2333
2394
|
"sostenutoPedal",
|
|
@@ -2356,7 +2417,8 @@ class MidyGM2 {
|
|
|
2356
2417
|
}
|
|
2357
2418
|
}
|
|
2358
2419
|
allNotesOff(channelNumber, _value, scheduleTime) {
|
|
2359
|
-
|
|
2420
|
+
if (!(0 <= scheduleTime))
|
|
2421
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2360
2422
|
return this.stopActiveNotes(channelNumber, 0, false, scheduleTime);
|
|
2361
2423
|
}
|
|
2362
2424
|
omniOff(channelNumber, value, scheduleTime) {
|
|
@@ -2405,7 +2467,8 @@ class MidyGM2 {
|
|
|
2405
2467
|
}
|
|
2406
2468
|
}
|
|
2407
2469
|
GM1SystemOn(scheduleTime) {
|
|
2408
|
-
|
|
2470
|
+
if (!(0 <= scheduleTime))
|
|
2471
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2409
2472
|
this.mode = "GM1";
|
|
2410
2473
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2411
2474
|
this.allSoundOff(i, 0, scheduleTime);
|
|
@@ -2418,7 +2481,8 @@ class MidyGM2 {
|
|
|
2418
2481
|
this.channels[9].isDrum = true;
|
|
2419
2482
|
}
|
|
2420
2483
|
GM2SystemOn(scheduleTime) {
|
|
2421
|
-
|
|
2484
|
+
if (!(0 <= scheduleTime))
|
|
2485
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2422
2486
|
this.mode = "GM2";
|
|
2423
2487
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2424
2488
|
this.allSoundOff(i, 0, scheduleTime);
|
|
@@ -2473,7 +2537,8 @@ class MidyGM2 {
|
|
|
2473
2537
|
this.setMasterVolume(volume, scheduleTime);
|
|
2474
2538
|
}
|
|
2475
2539
|
setMasterVolume(value, scheduleTime) {
|
|
2476
|
-
|
|
2540
|
+
if (!(0 <= scheduleTime))
|
|
2541
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2477
2542
|
this.masterVolume.gain
|
|
2478
2543
|
.cancelScheduledValues(scheduleTime)
|
|
2479
2544
|
.setValueAtTime(value * value, scheduleTime);
|