@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/esm/midy-GM2.js
CHANGED
|
@@ -26,12 +26,6 @@ class Note {
|
|
|
26
26
|
writable: true,
|
|
27
27
|
value: false
|
|
28
28
|
});
|
|
29
|
-
Object.defineProperty(this, "pending", {
|
|
30
|
-
enumerable: true,
|
|
31
|
-
configurable: true,
|
|
32
|
-
writable: true,
|
|
33
|
-
value: true
|
|
34
|
-
});
|
|
35
29
|
Object.defineProperty(this, "bufferSource", {
|
|
36
30
|
enumerable: true,
|
|
37
31
|
configurable: true,
|
|
@@ -107,6 +101,9 @@ class Note {
|
|
|
107
101
|
this.noteNumber = noteNumber;
|
|
108
102
|
this.velocity = velocity;
|
|
109
103
|
this.startTime = startTime;
|
|
104
|
+
this.ready = new Promise((resolve) => {
|
|
105
|
+
this.resolveReady = resolve;
|
|
106
|
+
});
|
|
110
107
|
}
|
|
111
108
|
}
|
|
112
109
|
const drumExclusiveClassesByKit = new Array(57);
|
|
@@ -151,12 +148,12 @@ const defaultControllerState = {
|
|
|
151
148
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
152
149
|
link: { type: 127, defaultValue: 0 },
|
|
153
150
|
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
|
|
152
|
+
portamentoTimeMSB: { type: 128 + 5, defaultValue: 0 },
|
|
156
153
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
154
|
+
volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
155
|
+
panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
156
|
+
expressionMSB: { type: 128 + 11, defaultValue: 1 },
|
|
160
157
|
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
161
158
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
162
159
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
@@ -228,8 +225,9 @@ const pitchEnvelopeKeys = [
|
|
|
228
225
|
"playbackRate",
|
|
229
226
|
];
|
|
230
227
|
const pitchEnvelopeKeySet = new Set(pitchEnvelopeKeys);
|
|
231
|
-
export class MidyGM2 {
|
|
228
|
+
export class MidyGM2 extends EventTarget {
|
|
232
229
|
constructor(audioContext) {
|
|
230
|
+
super();
|
|
233
231
|
Object.defineProperty(this, "mode", {
|
|
234
232
|
enumerable: true,
|
|
235
233
|
configurable: true,
|
|
@@ -390,6 +388,12 @@ export class MidyGM2 {
|
|
|
390
388
|
writable: true,
|
|
391
389
|
value: false
|
|
392
390
|
});
|
|
391
|
+
Object.defineProperty(this, "loop", {
|
|
392
|
+
enumerable: true,
|
|
393
|
+
configurable: true,
|
|
394
|
+
writable: true,
|
|
395
|
+
value: false
|
|
396
|
+
});
|
|
393
397
|
Object.defineProperty(this, "playPromise", {
|
|
394
398
|
enumerable: true,
|
|
395
399
|
configurable: true,
|
|
@@ -546,7 +550,7 @@ export class MidyGM2 {
|
|
|
546
550
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
547
551
|
}
|
|
548
552
|
createChannelAudioNodes(audioContext) {
|
|
549
|
-
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.
|
|
553
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
|
|
550
554
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
551
555
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
552
556
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -587,10 +591,9 @@ export class MidyGM2 {
|
|
|
587
591
|
return channels;
|
|
588
592
|
}
|
|
589
593
|
async createAudioBuffer(voiceParams) {
|
|
590
|
-
const sample = voiceParams
|
|
591
|
-
const
|
|
592
|
-
const
|
|
593
|
-
const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
|
|
594
|
+
const { sample, start, end } = voiceParams;
|
|
595
|
+
const sampleEnd = sample.data.length + end;
|
|
596
|
+
const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
|
|
594
597
|
return audioBuffer;
|
|
595
598
|
}
|
|
596
599
|
isLoopDrum(channel, noteNumber) {
|
|
@@ -610,7 +613,7 @@ export class MidyGM2 {
|
|
|
610
613
|
}
|
|
611
614
|
return bufferSource;
|
|
612
615
|
}
|
|
613
|
-
|
|
616
|
+
scheduleTimelineEvents(scheduleTime, queueIndex) {
|
|
614
617
|
const timeOffset = this.resumeTime - this.startTime;
|
|
615
618
|
const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
|
|
616
619
|
const schedulingOffset = this.startDelay - timeOffset;
|
|
@@ -622,12 +625,10 @@ export class MidyGM2 {
|
|
|
622
625
|
const startTime = event.startTime + schedulingOffset;
|
|
623
626
|
switch (event.type) {
|
|
624
627
|
case "noteOn":
|
|
625
|
-
|
|
628
|
+
this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
626
629
|
break;
|
|
627
630
|
case "noteOff": {
|
|
628
|
-
|
|
629
|
-
if (notePromise)
|
|
630
|
-
this.notePromises.push(notePromise);
|
|
631
|
+
this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
631
632
|
break;
|
|
632
633
|
}
|
|
633
634
|
case "controller":
|
|
@@ -668,22 +669,23 @@ export class MidyGM2 {
|
|
|
668
669
|
}
|
|
669
670
|
}
|
|
670
671
|
updateStates(queueIndex, nextQueueIndex) {
|
|
672
|
+
const now = this.audioContext.currentTime;
|
|
671
673
|
if (nextQueueIndex < queueIndex)
|
|
672
674
|
queueIndex = 0;
|
|
673
675
|
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
674
676
|
const event = this.timeline[i];
|
|
675
677
|
switch (event.type) {
|
|
676
678
|
case "controller":
|
|
677
|
-
this.setControlChange(event.channel, event.controllerType, event.value,
|
|
679
|
+
this.setControlChange(event.channel, event.controllerType, event.value, now - this.resumeTime + event.startTime);
|
|
678
680
|
break;
|
|
679
681
|
case "programChange":
|
|
680
|
-
this.setProgramChange(event.channel, event.programNumber,
|
|
682
|
+
this.setProgramChange(event.channel, event.programNumber, now - this.resumeTime + event.startTime);
|
|
681
683
|
break;
|
|
682
684
|
case "pitchBend":
|
|
683
|
-
this.setPitchBend(event.channel, event.value + 8192,
|
|
685
|
+
this.setPitchBend(event.channel, event.value + 8192, now - this.resumeTime + event.startTime);
|
|
684
686
|
break;
|
|
685
687
|
case "sysEx":
|
|
686
|
-
this.handleSysEx(event.data,
|
|
688
|
+
this.handleSysEx(event.data, now - this.resumeTime + event.startTime);
|
|
687
689
|
}
|
|
688
690
|
}
|
|
689
691
|
}
|
|
@@ -691,52 +693,88 @@ export class MidyGM2 {
|
|
|
691
693
|
if (this.audioContext.state === "suspended") {
|
|
692
694
|
await this.audioContext.resume();
|
|
693
695
|
}
|
|
696
|
+
const paused = this.isPaused;
|
|
694
697
|
this.isPlaying = true;
|
|
695
698
|
this.isPaused = false;
|
|
696
699
|
this.startTime = this.audioContext.currentTime;
|
|
700
|
+
if (paused) {
|
|
701
|
+
this.dispatchEvent(new Event("resumed"));
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
this.dispatchEvent(new Event("started"));
|
|
705
|
+
}
|
|
697
706
|
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
698
|
-
let
|
|
707
|
+
let exitReason;
|
|
699
708
|
this.notePromises = [];
|
|
700
|
-
while (
|
|
709
|
+
while (true) {
|
|
701
710
|
const now = this.audioContext.currentTime;
|
|
702
711
|
if (0 < this.lastActiveSensing &&
|
|
703
712
|
this.activeSensingThreshold < performance.now() - this.lastActiveSensing) {
|
|
704
713
|
await this.stopNotes(0, true, now);
|
|
705
714
|
await this.audioContext.suspend();
|
|
706
|
-
|
|
715
|
+
exitReason = "aborted";
|
|
707
716
|
break;
|
|
708
717
|
}
|
|
718
|
+
if (this.timeline.length <= queueIndex) {
|
|
719
|
+
await this.stopNotes(0, true, now);
|
|
720
|
+
if (this.loop) {
|
|
721
|
+
this.notePromises = [];
|
|
722
|
+
this.resetAllStates();
|
|
723
|
+
this.startTime = this.audioContext.currentTime;
|
|
724
|
+
this.resumeTime = 0;
|
|
725
|
+
queueIndex = 0;
|
|
726
|
+
this.dispatchEvent(new Event("looped"));
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
await this.audioContext.suspend();
|
|
731
|
+
exitReason = "ended";
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
709
735
|
if (this.isPausing) {
|
|
710
736
|
await this.stopNotes(0, true, now);
|
|
711
737
|
await this.audioContext.suspend();
|
|
712
738
|
this.notePromises = [];
|
|
739
|
+
this.isPausing = false;
|
|
740
|
+
exitReason = "paused";
|
|
713
741
|
break;
|
|
714
742
|
}
|
|
715
743
|
else if (this.isStopping) {
|
|
716
744
|
await this.stopNotes(0, true, now);
|
|
717
745
|
await this.audioContext.suspend();
|
|
718
|
-
|
|
746
|
+
this.isStopping = false;
|
|
747
|
+
exitReason = "stopped";
|
|
719
748
|
break;
|
|
720
749
|
}
|
|
721
750
|
else if (this.isSeeking) {
|
|
722
|
-
|
|
751
|
+
this.stopNotes(0, true, now);
|
|
723
752
|
this.startTime = this.audioContext.currentTime;
|
|
724
753
|
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
725
754
|
this.updateStates(queueIndex, nextQueueIndex);
|
|
726
755
|
queueIndex = nextQueueIndex;
|
|
727
756
|
this.isSeeking = false;
|
|
757
|
+
this.dispatchEvent(new Event("seeked"));
|
|
728
758
|
continue;
|
|
729
759
|
}
|
|
730
|
-
queueIndex =
|
|
760
|
+
queueIndex = this.scheduleTimelineEvents(now, queueIndex);
|
|
731
761
|
const waitTime = now + this.noteCheckInterval;
|
|
732
762
|
await this.scheduleTask(() => { }, waitTime);
|
|
733
763
|
}
|
|
734
|
-
if (
|
|
764
|
+
if (exitReason !== "paused") {
|
|
735
765
|
this.notePromises = [];
|
|
736
766
|
this.resetAllStates();
|
|
737
767
|
this.lastActiveSensing = 0;
|
|
738
768
|
}
|
|
739
769
|
this.isPlaying = false;
|
|
770
|
+
if (exitReason === "paused") {
|
|
771
|
+
this.isPaused = true;
|
|
772
|
+
this.dispatchEvent(new Event("paused"));
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
this.isPaused = false;
|
|
776
|
+
this.dispatchEvent(new Event(exitReason));
|
|
777
|
+
}
|
|
740
778
|
}
|
|
741
779
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
742
780
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -873,24 +911,20 @@ export class MidyGM2 {
|
|
|
873
911
|
return;
|
|
874
912
|
this.isStopping = true;
|
|
875
913
|
await this.playPromise;
|
|
876
|
-
this.isStopping = false;
|
|
877
914
|
}
|
|
878
915
|
async pause() {
|
|
879
916
|
if (!this.isPlaying || this.isPaused)
|
|
880
917
|
return;
|
|
881
918
|
const now = this.audioContext.currentTime;
|
|
882
|
-
this.resumeTime = now
|
|
919
|
+
this.resumeTime = now + this.resumeTime - this.startTime;
|
|
883
920
|
this.isPausing = true;
|
|
884
921
|
await this.playPromise;
|
|
885
|
-
this.isPausing = false;
|
|
886
|
-
this.isPaused = true;
|
|
887
922
|
}
|
|
888
923
|
async resume() {
|
|
889
924
|
if (!this.isPaused)
|
|
890
925
|
return;
|
|
891
926
|
this.playPromise = this.playNotes();
|
|
892
927
|
await this.playPromise;
|
|
893
|
-
this.isPaused = false;
|
|
894
928
|
}
|
|
895
929
|
seekTo(second) {
|
|
896
930
|
this.resumeTime = second;
|
|
@@ -913,19 +947,23 @@ export class MidyGM2 {
|
|
|
913
947
|
const now = this.audioContext.currentTime;
|
|
914
948
|
return now + this.resumeTime - this.startTime;
|
|
915
949
|
}
|
|
916
|
-
processScheduledNotes(channel, callback) {
|
|
950
|
+
async processScheduledNotes(channel, callback) {
|
|
917
951
|
const scheduledNotes = channel.scheduledNotes;
|
|
952
|
+
const tasks = [];
|
|
918
953
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
919
954
|
const note = scheduledNotes[i];
|
|
920
955
|
if (!note)
|
|
921
956
|
continue;
|
|
922
957
|
if (note.ending)
|
|
923
958
|
continue;
|
|
924
|
-
callback(note);
|
|
959
|
+
const task = note.ready.then(() => callback(note));
|
|
960
|
+
tasks.push(task);
|
|
925
961
|
}
|
|
962
|
+
await Promise.all(tasks);
|
|
926
963
|
}
|
|
927
|
-
processActiveNotes(channel, scheduleTime, callback) {
|
|
964
|
+
async processActiveNotes(channel, scheduleTime, callback) {
|
|
928
965
|
const scheduledNotes = channel.scheduledNotes;
|
|
966
|
+
const tasks = [];
|
|
929
967
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
930
968
|
const note = scheduledNotes[i];
|
|
931
969
|
if (!note)
|
|
@@ -934,8 +972,10 @@ export class MidyGM2 {
|
|
|
934
972
|
continue;
|
|
935
973
|
if (scheduleTime < note.startTime)
|
|
936
974
|
break;
|
|
937
|
-
callback(note);
|
|
975
|
+
const task = note.ready.then(() => callback(note));
|
|
976
|
+
tasks.push(task);
|
|
938
977
|
}
|
|
978
|
+
await Promise.all(tasks);
|
|
939
979
|
}
|
|
940
980
|
createConvolutionReverbImpulse(audioContext, decay, preDecay) {
|
|
941
981
|
const sampleRate = audioContext.sampleRate;
|
|
@@ -1139,7 +1179,7 @@ export class MidyGM2 {
|
|
|
1139
1179
|
}
|
|
1140
1180
|
getPortamentoTime(channel, note) {
|
|
1141
1181
|
const deltaSemitone = Math.abs(note.noteNumber - note.portamentoNoteNumber);
|
|
1142
|
-
const value = Math.ceil(channel.state.
|
|
1182
|
+
const value = Math.ceil(channel.state.portamentoTimeMSB * 127);
|
|
1143
1183
|
return deltaSemitone / this.getPitchIncrementSpeed(value) / 10;
|
|
1144
1184
|
}
|
|
1145
1185
|
getPitchIncrementSpeed(value) {
|
|
@@ -1398,7 +1438,7 @@ export class MidyGM2 {
|
|
|
1398
1438
|
if (0 < state.vibratoDepth) {
|
|
1399
1439
|
this.startVibrato(channel, note, now);
|
|
1400
1440
|
}
|
|
1401
|
-
if (0 < state.
|
|
1441
|
+
if (0 < state.modulationDepthMSB) {
|
|
1402
1442
|
this.startModulation(channel, note, now);
|
|
1403
1443
|
}
|
|
1404
1444
|
if (channel.mono && channel.currentBufferSource) {
|
|
@@ -1409,7 +1449,13 @@ export class MidyGM2 {
|
|
|
1409
1449
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1410
1450
|
this.setChorusSend(channel, note, now);
|
|
1411
1451
|
this.setReverbSend(channel, note, now);
|
|
1412
|
-
|
|
1452
|
+
if (voiceParams.sample.type === "compressed") {
|
|
1453
|
+
const offset = voiceParams.start / audioBuffer.sampleRate;
|
|
1454
|
+
note.bufferSource.start(startTime, offset);
|
|
1455
|
+
}
|
|
1456
|
+
else {
|
|
1457
|
+
note.bufferSource.start(startTime);
|
|
1458
|
+
}
|
|
1413
1459
|
return note;
|
|
1414
1460
|
}
|
|
1415
1461
|
handleExclusiveClass(note, channelNumber, startTime) {
|
|
@@ -1494,11 +1540,7 @@ export class MidyGM2 {
|
|
|
1494
1540
|
return;
|
|
1495
1541
|
await this.setNoteAudioNode(channel, note, realtime);
|
|
1496
1542
|
this.setNoteRouting(channelNumber, note, startTime);
|
|
1497
|
-
note.
|
|
1498
|
-
const off = note.offEvent;
|
|
1499
|
-
if (off) {
|
|
1500
|
-
this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
|
|
1501
|
-
}
|
|
1543
|
+
note.resolveReady();
|
|
1502
1544
|
}
|
|
1503
1545
|
disconnectNote(note) {
|
|
1504
1546
|
note.bufferSource.disconnect();
|
|
@@ -1542,7 +1584,7 @@ export class MidyGM2 {
|
|
|
1542
1584
|
}, stopTime);
|
|
1543
1585
|
});
|
|
1544
1586
|
}
|
|
1545
|
-
noteOff(channelNumber, noteNumber,
|
|
1587
|
+
noteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
1546
1588
|
const channel = this.channels[channelNumber];
|
|
1547
1589
|
const state = channel.state;
|
|
1548
1590
|
if (!force) {
|
|
@@ -1561,13 +1603,13 @@ export class MidyGM2 {
|
|
|
1561
1603
|
if (index < 0)
|
|
1562
1604
|
return;
|
|
1563
1605
|
const note = channel.scheduledNotes[index];
|
|
1564
|
-
if (note.pending) {
|
|
1565
|
-
note.offEvent = { velocity, startTime: endTime };
|
|
1566
|
-
return;
|
|
1567
|
-
}
|
|
1568
1606
|
note.ending = true;
|
|
1569
1607
|
this.setNoteIndex(channel, index);
|
|
1570
|
-
|
|
1608
|
+
const promise = note.ready.then(() => {
|
|
1609
|
+
return this.releaseNote(channel, note, endTime);
|
|
1610
|
+
});
|
|
1611
|
+
this.notePromises.push(promise);
|
|
1612
|
+
return promise;
|
|
1571
1613
|
}
|
|
1572
1614
|
setNoteIndex(channel, index) {
|
|
1573
1615
|
let allEnds = true;
|
|
@@ -1676,7 +1718,7 @@ export class MidyGM2 {
|
|
|
1676
1718
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1677
1719
|
this.setEffects(channel, note, table, scheduleTime);
|
|
1678
1720
|
});
|
|
1679
|
-
this.applyVoiceParams(channel, 13);
|
|
1721
|
+
this.applyVoiceParams(channel, 13, scheduleTime);
|
|
1680
1722
|
}
|
|
1681
1723
|
handlePitchBendMessage(channelNumber, lsb, msb, scheduleTime) {
|
|
1682
1724
|
const pitchBend = msb * 128 + lsb;
|
|
@@ -1686,7 +1728,8 @@ export class MidyGM2 {
|
|
|
1686
1728
|
const channel = this.channels[channelNumber];
|
|
1687
1729
|
if (channel.isDrum)
|
|
1688
1730
|
return;
|
|
1689
|
-
|
|
1731
|
+
if (!(0 <= scheduleTime))
|
|
1732
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1690
1733
|
const state = channel.state;
|
|
1691
1734
|
const prev = state.pitchWheel * 2 - 1;
|
|
1692
1735
|
const next = (value - 8192) / 8192;
|
|
@@ -1699,7 +1742,8 @@ export class MidyGM2 {
|
|
|
1699
1742
|
if (note.modulationDepth) {
|
|
1700
1743
|
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1701
1744
|
this.getLFOPitchDepth(channel, note);
|
|
1702
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1745
|
+
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1746
|
+
channel.state.modulationDepthMSB;
|
|
1703
1747
|
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1704
1748
|
note.modulationDepth.gain
|
|
1705
1749
|
.cancelScheduledValues(scheduleTime)
|
|
@@ -1831,7 +1875,7 @@ export class MidyGM2 {
|
|
|
1831
1875
|
createVoiceParamsHandlers() {
|
|
1832
1876
|
return {
|
|
1833
1877
|
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1834
|
-
if (0 < channel.state.
|
|
1878
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1835
1879
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1836
1880
|
}
|
|
1837
1881
|
},
|
|
@@ -1841,12 +1885,12 @@ export class MidyGM2 {
|
|
|
1841
1885
|
}
|
|
1842
1886
|
},
|
|
1843
1887
|
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1844
|
-
if (0 < channel.state.
|
|
1888
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1845
1889
|
this.setModLfoToFilterFc(channel, note, scheduleTime);
|
|
1846
1890
|
}
|
|
1847
1891
|
},
|
|
1848
1892
|
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1849
|
-
if (0 < channel.state.
|
|
1893
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1850
1894
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
1851
1895
|
}
|
|
1852
1896
|
},
|
|
@@ -1857,12 +1901,12 @@ export class MidyGM2 {
|
|
|
1857
1901
|
this.setReverbSend(channel, note, scheduleTime);
|
|
1858
1902
|
},
|
|
1859
1903
|
delayModLFO: (_channel, note, _scheduleTime) => {
|
|
1860
|
-
if (0 < channel.state.
|
|
1904
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1861
1905
|
this.setDelayModLFO(note);
|
|
1862
1906
|
}
|
|
1863
1907
|
},
|
|
1864
1908
|
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1865
|
-
if (0 < channel.state.
|
|
1909
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1866
1910
|
this.setFreqModLFO(note, scheduleTime);
|
|
1867
1911
|
}
|
|
1868
1912
|
},
|
|
@@ -1964,7 +2008,8 @@ export class MidyGM2 {
|
|
|
1964
2008
|
this.channels[channelNumber].bankMSB = msb;
|
|
1965
2009
|
}
|
|
1966
2010
|
updateModulation(channel, scheduleTime) {
|
|
1967
|
-
const depth = channel.state.
|
|
2011
|
+
const depth = channel.state.modulationDepthMSB *
|
|
2012
|
+
channel.modulationDepthRange;
|
|
1968
2013
|
this.processScheduledNotes(channel, (note) => {
|
|
1969
2014
|
if (note.modulationDepth) {
|
|
1970
2015
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
@@ -1978,8 +2023,9 @@ export class MidyGM2 {
|
|
|
1978
2023
|
const channel = this.channels[channelNumber];
|
|
1979
2024
|
if (channel.isDrum)
|
|
1980
2025
|
return;
|
|
1981
|
-
|
|
1982
|
-
|
|
2026
|
+
if (!(0 <= scheduleTime))
|
|
2027
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2028
|
+
channel.state.modulationDepthMSB = modulation / 127;
|
|
1983
2029
|
this.updateModulation(channel, scheduleTime);
|
|
1984
2030
|
}
|
|
1985
2031
|
updatePortamento(channel, scheduleTime) {
|
|
@@ -2001,17 +2047,19 @@ export class MidyGM2 {
|
|
|
2001
2047
|
});
|
|
2002
2048
|
}
|
|
2003
2049
|
setPortamentoTime(channelNumber, portamentoTime, scheduleTime) {
|
|
2050
|
+
if (!(0 <= scheduleTime))
|
|
2051
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2004
2052
|
const channel = this.channels[channelNumber];
|
|
2005
|
-
|
|
2006
|
-
channel.state.portamentoTime = portamentoTime / 127;
|
|
2053
|
+
channel.state.portamentoTimeMSB = portamentoTime / 127;
|
|
2007
2054
|
if (channel.isDrum)
|
|
2008
2055
|
return;
|
|
2009
2056
|
this.updatePortamento(channel, scheduleTime);
|
|
2010
2057
|
}
|
|
2011
2058
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
2012
|
-
|
|
2059
|
+
if (!(0 <= scheduleTime))
|
|
2060
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2013
2061
|
const channel = this.channels[channelNumber];
|
|
2014
|
-
channel.state.
|
|
2062
|
+
channel.state.volumeMSB = volume / 127;
|
|
2015
2063
|
if (channel.isDrum) {
|
|
2016
2064
|
for (let i = 0; i < 128; i++) {
|
|
2017
2065
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2029,9 +2077,10 @@ export class MidyGM2 {
|
|
|
2029
2077
|
};
|
|
2030
2078
|
}
|
|
2031
2079
|
setPan(channelNumber, pan, scheduleTime) {
|
|
2032
|
-
|
|
2080
|
+
if (!(0 <= scheduleTime))
|
|
2081
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2033
2082
|
const channel = this.channels[channelNumber];
|
|
2034
|
-
channel.state.
|
|
2083
|
+
channel.state.panMSB = pan / 127;
|
|
2035
2084
|
if (channel.isDrum) {
|
|
2036
2085
|
for (let i = 0; i < 128; i++) {
|
|
2037
2086
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2042,9 +2091,10 @@ export class MidyGM2 {
|
|
|
2042
2091
|
}
|
|
2043
2092
|
}
|
|
2044
2093
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
2045
|
-
|
|
2094
|
+
if (!(0 <= scheduleTime))
|
|
2095
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2046
2096
|
const channel = this.channels[channelNumber];
|
|
2047
|
-
channel.state.
|
|
2097
|
+
channel.state.expressionMSB = expression / 127;
|
|
2048
2098
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2049
2099
|
}
|
|
2050
2100
|
setBankLSB(channelNumber, lsb) {
|
|
@@ -2056,8 +2106,8 @@ export class MidyGM2 {
|
|
|
2056
2106
|
}
|
|
2057
2107
|
updateChannelVolume(channel, scheduleTime) {
|
|
2058
2108
|
const state = channel.state;
|
|
2059
|
-
const volume = state.
|
|
2060
|
-
const { gainLeft, gainRight } = this.panToGain(state.
|
|
2109
|
+
const volume = state.volumeMSB * state.expressionMSB;
|
|
2110
|
+
const { gainLeft, gainRight } = this.panToGain(state.panMSB);
|
|
2061
2111
|
channel.gainL.gain
|
|
2062
2112
|
.cancelScheduledValues(scheduleTime)
|
|
2063
2113
|
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
@@ -2071,8 +2121,8 @@ export class MidyGM2 {
|
|
|
2071
2121
|
return;
|
|
2072
2122
|
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
2073
2123
|
const state = channel.state;
|
|
2074
|
-
const defaultVolume = state.
|
|
2075
|
-
const defaultPan = state.
|
|
2124
|
+
const defaultVolume = state.volumeMSB * state.expressionMSB;
|
|
2125
|
+
const defaultPan = state.panMSB;
|
|
2076
2126
|
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
2077
2127
|
const volume = (0 <= keyBasedVolume)
|
|
2078
2128
|
? defaultVolume * keyBasedVolume / 64
|
|
@@ -2091,7 +2141,8 @@ export class MidyGM2 {
|
|
|
2091
2141
|
const channel = this.channels[channelNumber];
|
|
2092
2142
|
if (channel.isDrum)
|
|
2093
2143
|
return;
|
|
2094
|
-
|
|
2144
|
+
if (!(0 <= scheduleTime))
|
|
2145
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2095
2146
|
channel.state.sustainPedal = value / 127;
|
|
2096
2147
|
if (64 <= value) {
|
|
2097
2148
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -2109,7 +2160,8 @@ export class MidyGM2 {
|
|
|
2109
2160
|
const channel = this.channels[channelNumber];
|
|
2110
2161
|
if (channel.isDrum)
|
|
2111
2162
|
return;
|
|
2112
|
-
|
|
2163
|
+
if (!(0 <= scheduleTime))
|
|
2164
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2113
2165
|
channel.state.portamento = value / 127;
|
|
2114
2166
|
this.updatePortamento(channel, scheduleTime);
|
|
2115
2167
|
}
|
|
@@ -2117,7 +2169,8 @@ export class MidyGM2 {
|
|
|
2117
2169
|
const channel = this.channels[channelNumber];
|
|
2118
2170
|
if (channel.isDrum)
|
|
2119
2171
|
return;
|
|
2120
|
-
|
|
2172
|
+
if (!(0 <= scheduleTime))
|
|
2173
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2121
2174
|
channel.state.sostenutoPedal = value / 127;
|
|
2122
2175
|
if (64 <= value) {
|
|
2123
2176
|
const sostenutoNotes = [];
|
|
@@ -2138,7 +2191,8 @@ export class MidyGM2 {
|
|
|
2138
2191
|
if (channel.isDrum)
|
|
2139
2192
|
return;
|
|
2140
2193
|
const state = channel.state;
|
|
2141
|
-
|
|
2194
|
+
if (!(0 <= scheduleTime))
|
|
2195
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2142
2196
|
state.softPedal = softPedal / 127;
|
|
2143
2197
|
this.processScheduledNotes(channel, (note) => {
|
|
2144
2198
|
if (this.isPortamento(channel, note)) {
|
|
@@ -2152,7 +2206,8 @@ export class MidyGM2 {
|
|
|
2152
2206
|
});
|
|
2153
2207
|
}
|
|
2154
2208
|
setReverbSendLevel(channelNumber, reverbSendLevel, scheduleTime) {
|
|
2155
|
-
|
|
2209
|
+
if (!(0 <= scheduleTime))
|
|
2210
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2156
2211
|
const channel = this.channels[channelNumber];
|
|
2157
2212
|
const state = channel.state;
|
|
2158
2213
|
state.reverbSendLevel = reverbSendLevel / 127;
|
|
@@ -2161,7 +2216,8 @@ export class MidyGM2 {
|
|
|
2161
2216
|
});
|
|
2162
2217
|
}
|
|
2163
2218
|
setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
|
|
2164
|
-
|
|
2219
|
+
if (!(0 <= scheduleTime))
|
|
2220
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2165
2221
|
const channel = this.channels[channelNumber];
|
|
2166
2222
|
const state = channel.state;
|
|
2167
2223
|
state.chorusSendLevel = chorusSendLevel / 127;
|
|
@@ -2235,7 +2291,8 @@ export class MidyGM2 {
|
|
|
2235
2291
|
const channel = this.channels[channelNumber];
|
|
2236
2292
|
if (channel.isDrum)
|
|
2237
2293
|
return;
|
|
2238
|
-
|
|
2294
|
+
if (!(0 <= scheduleTime))
|
|
2295
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2239
2296
|
const state = channel.state;
|
|
2240
2297
|
const prev = state.pitchWheelSensitivity;
|
|
2241
2298
|
const next = value / 12800;
|
|
@@ -2255,7 +2312,8 @@ export class MidyGM2 {
|
|
|
2255
2312
|
const channel = this.channels[channelNumber];
|
|
2256
2313
|
if (channel.isDrum)
|
|
2257
2314
|
return;
|
|
2258
|
-
|
|
2315
|
+
if (!(0 <= scheduleTime))
|
|
2316
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2259
2317
|
const prev = channel.fineTuning;
|
|
2260
2318
|
const next = value;
|
|
2261
2319
|
channel.fineTuning = next;
|
|
@@ -2272,7 +2330,8 @@ export class MidyGM2 {
|
|
|
2272
2330
|
const channel = this.channels[channelNumber];
|
|
2273
2331
|
if (channel.isDrum)
|
|
2274
2332
|
return;
|
|
2275
|
-
|
|
2333
|
+
if (!(0 <= scheduleTime))
|
|
2334
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2276
2335
|
const prev = channel.coarseTuning;
|
|
2277
2336
|
const next = value;
|
|
2278
2337
|
channel.coarseTuning = next;
|
|
@@ -2289,12 +2348,14 @@ export class MidyGM2 {
|
|
|
2289
2348
|
const channel = this.channels[channelNumber];
|
|
2290
2349
|
if (channel.isDrum)
|
|
2291
2350
|
return;
|
|
2292
|
-
|
|
2351
|
+
if (!(0 <= scheduleTime))
|
|
2352
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2293
2353
|
channel.modulationDepthRange = value;
|
|
2294
2354
|
this.updateModulation(channel, scheduleTime);
|
|
2295
2355
|
}
|
|
2296
2356
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
2297
|
-
|
|
2357
|
+
if (!(0 <= scheduleTime))
|
|
2358
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2298
2359
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
2299
2360
|
}
|
|
2300
2361
|
resetChannelStates(channelNumber) {
|
|
@@ -2323,8 +2384,8 @@ export class MidyGM2 {
|
|
|
2323
2384
|
const keys = [
|
|
2324
2385
|
"channelPressure",
|
|
2325
2386
|
"pitchWheel",
|
|
2326
|
-
"
|
|
2327
|
-
"
|
|
2387
|
+
"expressionMSB",
|
|
2388
|
+
"modulationDepthMSB",
|
|
2328
2389
|
"sustainPedal",
|
|
2329
2390
|
"portamento",
|
|
2330
2391
|
"sostenutoPedal",
|
|
@@ -2353,7 +2414,8 @@ export class MidyGM2 {
|
|
|
2353
2414
|
}
|
|
2354
2415
|
}
|
|
2355
2416
|
allNotesOff(channelNumber, _value, scheduleTime) {
|
|
2356
|
-
|
|
2417
|
+
if (!(0 <= scheduleTime))
|
|
2418
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2357
2419
|
return this.stopActiveNotes(channelNumber, 0, false, scheduleTime);
|
|
2358
2420
|
}
|
|
2359
2421
|
omniOff(channelNumber, value, scheduleTime) {
|
|
@@ -2402,7 +2464,8 @@ export class MidyGM2 {
|
|
|
2402
2464
|
}
|
|
2403
2465
|
}
|
|
2404
2466
|
GM1SystemOn(scheduleTime) {
|
|
2405
|
-
|
|
2467
|
+
if (!(0 <= scheduleTime))
|
|
2468
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2406
2469
|
this.mode = "GM1";
|
|
2407
2470
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2408
2471
|
this.allSoundOff(i, 0, scheduleTime);
|
|
@@ -2415,7 +2478,8 @@ export class MidyGM2 {
|
|
|
2415
2478
|
this.channels[9].isDrum = true;
|
|
2416
2479
|
}
|
|
2417
2480
|
GM2SystemOn(scheduleTime) {
|
|
2418
|
-
|
|
2481
|
+
if (!(0 <= scheduleTime))
|
|
2482
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2419
2483
|
this.mode = "GM2";
|
|
2420
2484
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2421
2485
|
this.allSoundOff(i, 0, scheduleTime);
|
|
@@ -2470,7 +2534,8 @@ export class MidyGM2 {
|
|
|
2470
2534
|
this.setMasterVolume(volume, scheduleTime);
|
|
2471
2535
|
}
|
|
2472
2536
|
setMasterVolume(value, scheduleTime) {
|
|
2473
|
-
|
|
2537
|
+
if (!(0 <= scheduleTime))
|
|
2538
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2474
2539
|
this.masterVolume.gain
|
|
2475
2540
|
.cancelScheduledValues(scheduleTime)
|
|
2476
2541
|
.setValueAtTime(value * value, scheduleTime);
|