@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.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,
|
|
@@ -116,6 +110,9 @@ class Note {
|
|
|
116
110
|
this.noteNumber = noteNumber;
|
|
117
111
|
this.velocity = velocity;
|
|
118
112
|
this.startTime = startTime;
|
|
113
|
+
this.ready = new Promise((resolve) => {
|
|
114
|
+
this.resolveReady = resolve;
|
|
115
|
+
});
|
|
119
116
|
}
|
|
120
117
|
}
|
|
121
118
|
const drumExclusiveClassesByKit = new Array(57);
|
|
@@ -161,14 +158,19 @@ const defaultControllerState = {
|
|
|
161
158
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
162
159
|
link: { type: 127, defaultValue: 0 },
|
|
163
160
|
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
|
|
162
|
+
portamentoTimeMSB: { type: 128 + 5, defaultValue: 0 },
|
|
166
163
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
164
|
+
volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
165
|
+
panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
166
|
+
expressionMSB: { type: 128 + 11, defaultValue: 1 },
|
|
170
167
|
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
168
|
+
modulationDepthLSB: { type: 128 + 33, defaultValue: 0 },
|
|
169
|
+
portamentoTimeLSB: { type: 128 + 37, defaultValue: 0 },
|
|
171
170
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
171
|
+
volumeLSB: { type: 128 + 39, defaultValue: 0 },
|
|
172
|
+
panLSB: { type: 128 + 42, defaultValue: 0 },
|
|
173
|
+
expressionLSB: { type: 128 + 43, defaultValue: 0 },
|
|
172
174
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
173
175
|
portamento: { type: 128 + 65, defaultValue: 0 },
|
|
174
176
|
sostenutoPedal: { type: 128 + 66, defaultValue: 0 },
|
|
@@ -181,12 +183,14 @@ const defaultControllerState = {
|
|
|
181
183
|
vibratoRate: { type: 128 + 76, defaultValue: 64 / 127 },
|
|
182
184
|
vibratoDepth: { type: 128 + 77, defaultValue: 64 / 127 },
|
|
183
185
|
vibratoDelay: { type: 128 + 78, defaultValue: 64 / 127 },
|
|
186
|
+
portamentoNoteNumber: { type: 128 + 84, defaultValue: 0 },
|
|
184
187
|
reverbSendLevel: { type: 128 + 91, defaultValue: 0 },
|
|
185
188
|
chorusSendLevel: { type: 128 + 93, defaultValue: 0 },
|
|
186
189
|
// dataIncrement: { type: 128 + 96, defaultValue: 0 },
|
|
187
190
|
// dataDecrement: { type: 128 + 97, defaultValue: 0 },
|
|
188
191
|
// rpnLSB: { type: 128 + 100, defaultValue: 127 },
|
|
189
192
|
// rpnMSB: { type: 128 + 101, defaultValue: 127 },
|
|
193
|
+
// rpgMakerLoop: { type: 128 + 111, defaultValue: 0 },
|
|
190
194
|
// allSoundOff: { type: 128 + 120, defaultValue: 0 },
|
|
191
195
|
// resetAllControllers: { type: 128 + 121, defaultValue: 0 },
|
|
192
196
|
// allNotesOff: { type: 128 + 123, defaultValue: 0 },
|
|
@@ -246,8 +250,9 @@ const pitchEnvelopeKeys = [
|
|
|
246
250
|
"playbackRate",
|
|
247
251
|
];
|
|
248
252
|
const pitchEnvelopeKeySet = new Set(pitchEnvelopeKeys);
|
|
249
|
-
class Midy {
|
|
253
|
+
class Midy extends EventTarget {
|
|
250
254
|
constructor(audioContext) {
|
|
255
|
+
super();
|
|
251
256
|
Object.defineProperty(this, "mode", {
|
|
252
257
|
enumerable: true,
|
|
253
258
|
configurable: true,
|
|
@@ -408,6 +413,18 @@ class Midy {
|
|
|
408
413
|
writable: true,
|
|
409
414
|
value: false
|
|
410
415
|
});
|
|
416
|
+
Object.defineProperty(this, "loop", {
|
|
417
|
+
enumerable: true,
|
|
418
|
+
configurable: true,
|
|
419
|
+
writable: true,
|
|
420
|
+
value: true
|
|
421
|
+
});
|
|
422
|
+
Object.defineProperty(this, "loopStart", {
|
|
423
|
+
enumerable: true,
|
|
424
|
+
configurable: true,
|
|
425
|
+
writable: true,
|
|
426
|
+
value: 0
|
|
427
|
+
});
|
|
411
428
|
Object.defineProperty(this, "playPromise", {
|
|
412
429
|
enumerable: true,
|
|
413
430
|
configurable: true,
|
|
@@ -564,7 +581,7 @@ class Midy {
|
|
|
564
581
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
565
582
|
}
|
|
566
583
|
createChannelAudioNodes(audioContext) {
|
|
567
|
-
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.
|
|
584
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
|
|
568
585
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
569
586
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
570
587
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -607,10 +624,9 @@ class Midy {
|
|
|
607
624
|
return channels;
|
|
608
625
|
}
|
|
609
626
|
async createAudioBuffer(voiceParams) {
|
|
610
|
-
const sample = voiceParams
|
|
611
|
-
const
|
|
612
|
-
const
|
|
613
|
-
const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
|
|
627
|
+
const { sample, start, end } = voiceParams;
|
|
628
|
+
const sampleEnd = sample.data.length + end;
|
|
629
|
+
const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
|
|
614
630
|
return audioBuffer;
|
|
615
631
|
}
|
|
616
632
|
isLoopDrum(channel, noteNumber) {
|
|
@@ -630,7 +646,7 @@ class Midy {
|
|
|
630
646
|
}
|
|
631
647
|
return bufferSource;
|
|
632
648
|
}
|
|
633
|
-
|
|
649
|
+
scheduleTimelineEvents(scheduleTime, queueIndex) {
|
|
634
650
|
const timeOffset = this.resumeTime - this.startTime;
|
|
635
651
|
const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
|
|
636
652
|
const schedulingOffset = this.startDelay - timeOffset;
|
|
@@ -642,12 +658,10 @@ class Midy {
|
|
|
642
658
|
const startTime = event.startTime + schedulingOffset;
|
|
643
659
|
switch (event.type) {
|
|
644
660
|
case "noteOn":
|
|
645
|
-
|
|
661
|
+
this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
646
662
|
break;
|
|
647
663
|
case "noteOff": {
|
|
648
|
-
|
|
649
|
-
if (notePromise)
|
|
650
|
-
this.notePromises.push(notePromise);
|
|
664
|
+
this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
651
665
|
break;
|
|
652
666
|
}
|
|
653
667
|
case "noteAftertouch":
|
|
@@ -691,22 +705,23 @@ class Midy {
|
|
|
691
705
|
}
|
|
692
706
|
}
|
|
693
707
|
updateStates(queueIndex, nextQueueIndex) {
|
|
708
|
+
const now = this.audioContext.currentTime;
|
|
694
709
|
if (nextQueueIndex < queueIndex)
|
|
695
710
|
queueIndex = 0;
|
|
696
711
|
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
697
712
|
const event = this.timeline[i];
|
|
698
713
|
switch (event.type) {
|
|
699
714
|
case "controller":
|
|
700
|
-
this.setControlChange(event.channel, event.controllerType, event.value,
|
|
715
|
+
this.setControlChange(event.channel, event.controllerType, event.value, now - this.resumeTime + event.startTime);
|
|
701
716
|
break;
|
|
702
717
|
case "programChange":
|
|
703
|
-
this.setProgramChange(event.channel, event.programNumber,
|
|
718
|
+
this.setProgramChange(event.channel, event.programNumber, now - this.resumeTime + event.startTime);
|
|
704
719
|
break;
|
|
705
720
|
case "pitchBend":
|
|
706
|
-
this.setPitchBend(event.channel, event.value + 8192,
|
|
721
|
+
this.setPitchBend(event.channel, event.value + 8192, now - this.resumeTime + event.startTime);
|
|
707
722
|
break;
|
|
708
723
|
case "sysEx":
|
|
709
|
-
this.handleSysEx(event.data,
|
|
724
|
+
this.handleSysEx(event.data, now - this.resumeTime + event.startTime);
|
|
710
725
|
}
|
|
711
726
|
}
|
|
712
727
|
}
|
|
@@ -714,52 +729,95 @@ class Midy {
|
|
|
714
729
|
if (this.audioContext.state === "suspended") {
|
|
715
730
|
await this.audioContext.resume();
|
|
716
731
|
}
|
|
732
|
+
const paused = this.isPaused;
|
|
717
733
|
this.isPlaying = true;
|
|
718
734
|
this.isPaused = false;
|
|
719
735
|
this.startTime = this.audioContext.currentTime;
|
|
736
|
+
if (paused) {
|
|
737
|
+
this.dispatchEvent(new Event("resumed"));
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
this.dispatchEvent(new Event("started"));
|
|
741
|
+
}
|
|
720
742
|
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
721
|
-
let
|
|
743
|
+
let exitReason;
|
|
722
744
|
this.notePromises = [];
|
|
723
|
-
while (
|
|
745
|
+
while (true) {
|
|
724
746
|
const now = this.audioContext.currentTime;
|
|
725
747
|
if (0 < this.lastActiveSensing &&
|
|
726
748
|
this.activeSensingThreshold < performance.now() - this.lastActiveSensing) {
|
|
727
749
|
await this.stopNotes(0, true, now);
|
|
728
750
|
await this.audioContext.suspend();
|
|
729
|
-
|
|
751
|
+
exitReason = "aborted";
|
|
730
752
|
break;
|
|
731
753
|
}
|
|
754
|
+
if (this.timeline.length <= queueIndex) {
|
|
755
|
+
await this.stopNotes(0, true, now);
|
|
756
|
+
if (this.loop) {
|
|
757
|
+
this.notePromises = [];
|
|
758
|
+
this.resetAllStates();
|
|
759
|
+
this.startTime = this.audioContext.currentTime;
|
|
760
|
+
this.resumeTime = this.loopStart;
|
|
761
|
+
if (0 < this.loopStart) {
|
|
762
|
+
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
763
|
+
this.updateStates(queueIndex, nextQueueIndex);
|
|
764
|
+
queueIndex = nextQueueIndex;
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
queueIndex = 0;
|
|
768
|
+
}
|
|
769
|
+
this.dispatchEvent(new Event("looped"));
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
await this.audioContext.suspend();
|
|
774
|
+
exitReason = "ended";
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
732
778
|
if (this.isPausing) {
|
|
733
779
|
await this.stopNotes(0, true, now);
|
|
734
780
|
await this.audioContext.suspend();
|
|
735
781
|
this.notePromises = [];
|
|
782
|
+
this.isPausing = false;
|
|
783
|
+
exitReason = "paused";
|
|
736
784
|
break;
|
|
737
785
|
}
|
|
738
786
|
else if (this.isStopping) {
|
|
739
787
|
await this.stopNotes(0, true, now);
|
|
740
788
|
await this.audioContext.suspend();
|
|
741
|
-
|
|
789
|
+
this.isStopping = false;
|
|
790
|
+
exitReason = "stopped";
|
|
742
791
|
break;
|
|
743
792
|
}
|
|
744
793
|
else if (this.isSeeking) {
|
|
745
|
-
|
|
794
|
+
this.stopNotes(0, true, now);
|
|
746
795
|
this.startTime = this.audioContext.currentTime;
|
|
747
796
|
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
748
797
|
this.updateStates(queueIndex, nextQueueIndex);
|
|
749
798
|
queueIndex = nextQueueIndex;
|
|
750
799
|
this.isSeeking = false;
|
|
800
|
+
this.dispatchEvent(new Event("seeked"));
|
|
751
801
|
continue;
|
|
752
802
|
}
|
|
753
|
-
queueIndex =
|
|
803
|
+
queueIndex = this.scheduleTimelineEvents(now, queueIndex);
|
|
754
804
|
const waitTime = now + this.noteCheckInterval;
|
|
755
805
|
await this.scheduleTask(() => { }, waitTime);
|
|
756
806
|
}
|
|
757
|
-
if (
|
|
807
|
+
if (exitReason !== "paused") {
|
|
758
808
|
this.notePromises = [];
|
|
759
809
|
this.resetAllStates();
|
|
760
810
|
this.lastActiveSensing = 0;
|
|
761
811
|
}
|
|
762
812
|
this.isPlaying = false;
|
|
813
|
+
if (exitReason === "paused") {
|
|
814
|
+
this.isPaused = true;
|
|
815
|
+
this.dispatchEvent(new Event("paused"));
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
this.isPaused = false;
|
|
819
|
+
this.dispatchEvent(new Event(exitReason));
|
|
820
|
+
}
|
|
763
821
|
}
|
|
764
822
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
765
823
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -896,24 +954,20 @@ class Midy {
|
|
|
896
954
|
return;
|
|
897
955
|
this.isStopping = true;
|
|
898
956
|
await this.playPromise;
|
|
899
|
-
this.isStopping = false;
|
|
900
957
|
}
|
|
901
958
|
async pause() {
|
|
902
959
|
if (!this.isPlaying || this.isPaused)
|
|
903
960
|
return;
|
|
904
961
|
const now = this.audioContext.currentTime;
|
|
905
|
-
this.resumeTime = now
|
|
962
|
+
this.resumeTime = now + this.resumeTime - this.startTime;
|
|
906
963
|
this.isPausing = true;
|
|
907
964
|
await this.playPromise;
|
|
908
|
-
this.isPausing = false;
|
|
909
|
-
this.isPaused = true;
|
|
910
965
|
}
|
|
911
966
|
async resume() {
|
|
912
967
|
if (!this.isPaused)
|
|
913
968
|
return;
|
|
914
969
|
this.playPromise = this.playNotes();
|
|
915
970
|
await this.playPromise;
|
|
916
|
-
this.isPaused = false;
|
|
917
971
|
}
|
|
918
972
|
seekTo(second) {
|
|
919
973
|
this.resumeTime = second;
|
|
@@ -936,19 +990,23 @@ class Midy {
|
|
|
936
990
|
const now = this.audioContext.currentTime;
|
|
937
991
|
return now + this.resumeTime - this.startTime;
|
|
938
992
|
}
|
|
939
|
-
processScheduledNotes(channel, callback) {
|
|
993
|
+
async processScheduledNotes(channel, callback) {
|
|
940
994
|
const scheduledNotes = channel.scheduledNotes;
|
|
995
|
+
const tasks = [];
|
|
941
996
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
942
997
|
const note = scheduledNotes[i];
|
|
943
998
|
if (!note)
|
|
944
999
|
continue;
|
|
945
1000
|
if (note.ending)
|
|
946
1001
|
continue;
|
|
947
|
-
callback(note);
|
|
1002
|
+
const task = note.ready.then(() => callback(note));
|
|
1003
|
+
tasks.push(task);
|
|
948
1004
|
}
|
|
1005
|
+
await Promise.all(tasks);
|
|
949
1006
|
}
|
|
950
|
-
processActiveNotes(channel, scheduleTime, callback) {
|
|
1007
|
+
async processActiveNotes(channel, scheduleTime, callback) {
|
|
951
1008
|
const scheduledNotes = channel.scheduledNotes;
|
|
1009
|
+
const tasks = [];
|
|
952
1010
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
953
1011
|
const note = scheduledNotes[i];
|
|
954
1012
|
if (!note)
|
|
@@ -957,8 +1015,10 @@ class Midy {
|
|
|
957
1015
|
continue;
|
|
958
1016
|
if (scheduleTime < note.startTime)
|
|
959
1017
|
break;
|
|
960
|
-
callback(note);
|
|
1018
|
+
const task = note.ready.then(() => callback(note));
|
|
1019
|
+
tasks.push(task);
|
|
961
1020
|
}
|
|
1021
|
+
await Promise.all(tasks);
|
|
962
1022
|
}
|
|
963
1023
|
createConvolutionReverbImpulse(audioContext, decay, preDecay) {
|
|
964
1024
|
const sampleRate = audioContext.sampleRate;
|
|
@@ -1146,6 +1206,13 @@ class Midy {
|
|
|
1146
1206
|
const noteDetune = this.calcNoteDetune(channel, note);
|
|
1147
1207
|
const pitchControl = this.getPitchControl(channel, note);
|
|
1148
1208
|
const detune = channel.detune + noteDetune + pitchControl;
|
|
1209
|
+
if (channel.portamentoControl) {
|
|
1210
|
+
const state = channel.state;
|
|
1211
|
+
const portamentoNoteNumber = Math.ceil(state.portamentoNoteNumber * 127);
|
|
1212
|
+
note.portamentoNoteNumber = portamentoNoteNumber;
|
|
1213
|
+
channel.portamentoControl = false;
|
|
1214
|
+
state.portamentoNoteNumber = 0;
|
|
1215
|
+
}
|
|
1149
1216
|
if (this.isPortamento(channel, note)) {
|
|
1150
1217
|
const startTime = note.startTime;
|
|
1151
1218
|
const deltaCent = (note.noteNumber - note.portamentoNoteNumber) * 100;
|
|
@@ -1162,8 +1229,10 @@ class Midy {
|
|
|
1162
1229
|
}
|
|
1163
1230
|
}
|
|
1164
1231
|
getPortamentoTime(channel, note) {
|
|
1232
|
+
const { portamentoTimeMSB, portamentoTimeLSB } = channel.state;
|
|
1233
|
+
const portamentoTime = portamentoTimeMSB + portamentoTimeLSB / 128;
|
|
1165
1234
|
const deltaSemitone = Math.abs(note.noteNumber - note.portamentoNoteNumber);
|
|
1166
|
-
const value = Math.ceil(
|
|
1235
|
+
const value = Math.ceil(portamentoTime * 128);
|
|
1167
1236
|
return deltaSemitone / this.getPitchIncrementSpeed(value) / 10;
|
|
1168
1237
|
}
|
|
1169
1238
|
getPitchIncrementSpeed(value) {
|
|
@@ -1427,7 +1496,7 @@ class Midy {
|
|
|
1427
1496
|
if (0 < state.vibratoDepth) {
|
|
1428
1497
|
this.startVibrato(channel, note, now);
|
|
1429
1498
|
}
|
|
1430
|
-
if (0 < state.
|
|
1499
|
+
if (0 < state.modulationDepthMSB + state.modulationDepthLSB) {
|
|
1431
1500
|
this.startModulation(channel, note, now);
|
|
1432
1501
|
}
|
|
1433
1502
|
if (channel.mono && channel.currentBufferSource) {
|
|
@@ -1438,7 +1507,13 @@ class Midy {
|
|
|
1438
1507
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1439
1508
|
this.setChorusSend(channel, note, now);
|
|
1440
1509
|
this.setReverbSend(channel, note, now);
|
|
1441
|
-
|
|
1510
|
+
if (voiceParams.sample.type === "compressed") {
|
|
1511
|
+
const offset = voiceParams.start / audioBuffer.sampleRate;
|
|
1512
|
+
note.bufferSource.start(startTime, offset);
|
|
1513
|
+
}
|
|
1514
|
+
else {
|
|
1515
|
+
note.bufferSource.start(startTime);
|
|
1516
|
+
}
|
|
1442
1517
|
return note;
|
|
1443
1518
|
}
|
|
1444
1519
|
handleExclusiveClass(note, channelNumber, startTime) {
|
|
@@ -1523,11 +1598,7 @@ class Midy {
|
|
|
1523
1598
|
return;
|
|
1524
1599
|
await this.setNoteAudioNode(channel, note, realtime);
|
|
1525
1600
|
this.setNoteRouting(channelNumber, note, startTime);
|
|
1526
|
-
note.
|
|
1527
|
-
const off = note.offEvent;
|
|
1528
|
-
if (off) {
|
|
1529
|
-
this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
|
|
1530
|
-
}
|
|
1601
|
+
note.resolveReady();
|
|
1531
1602
|
}
|
|
1532
1603
|
disconnectNote(note) {
|
|
1533
1604
|
note.bufferSource.disconnect();
|
|
@@ -1572,7 +1643,7 @@ class Midy {
|
|
|
1572
1643
|
}, stopTime);
|
|
1573
1644
|
});
|
|
1574
1645
|
}
|
|
1575
|
-
noteOff(channelNumber, noteNumber,
|
|
1646
|
+
noteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
1576
1647
|
const channel = this.channels[channelNumber];
|
|
1577
1648
|
const state = channel.state;
|
|
1578
1649
|
if (!force) {
|
|
@@ -1591,13 +1662,13 @@ class Midy {
|
|
|
1591
1662
|
if (index < 0)
|
|
1592
1663
|
return;
|
|
1593
1664
|
const note = channel.scheduledNotes[index];
|
|
1594
|
-
if (note.pending) {
|
|
1595
|
-
note.offEvent = { velocity, startTime: endTime };
|
|
1596
|
-
return;
|
|
1597
|
-
}
|
|
1598
1665
|
note.ending = true;
|
|
1599
1666
|
this.setNoteIndex(channel, index);
|
|
1600
|
-
|
|
1667
|
+
const promise = note.ready.then(() => {
|
|
1668
|
+
return this.releaseNote(channel, note, endTime);
|
|
1669
|
+
});
|
|
1670
|
+
this.notePromises.push(promise);
|
|
1671
|
+
return promise;
|
|
1601
1672
|
}
|
|
1602
1673
|
setNoteIndex(channel, index) {
|
|
1603
1674
|
let allEnds = true;
|
|
@@ -1696,7 +1767,7 @@ class Midy {
|
|
|
1696
1767
|
this.setEffects(channel, note, table, scheduleTime);
|
|
1697
1768
|
}
|
|
1698
1769
|
});
|
|
1699
|
-
this.applyVoiceParams(channel, 10);
|
|
1770
|
+
this.applyVoiceParams(channel, 10, scheduleTime);
|
|
1700
1771
|
}
|
|
1701
1772
|
setProgramChange(channelNumber, programNumber, _scheduleTime) {
|
|
1702
1773
|
const channel = this.channels[channelNumber];
|
|
@@ -1729,7 +1800,7 @@ class Midy {
|
|
|
1729
1800
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1730
1801
|
this.setEffects(channel, note, table, scheduleTime);
|
|
1731
1802
|
});
|
|
1732
|
-
this.applyVoiceParams(channel, 13);
|
|
1803
|
+
this.applyVoiceParams(channel, 13, scheduleTime);
|
|
1733
1804
|
}
|
|
1734
1805
|
handlePitchBendMessage(channelNumber, lsb, msb, scheduleTime) {
|
|
1735
1806
|
const pitchBend = msb * 128 + lsb;
|
|
@@ -1739,7 +1810,8 @@ class Midy {
|
|
|
1739
1810
|
const channel = this.channels[channelNumber];
|
|
1740
1811
|
if (channel.isDrum)
|
|
1741
1812
|
return;
|
|
1742
|
-
|
|
1813
|
+
if (!(0 <= scheduleTime))
|
|
1814
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1743
1815
|
const state = channel.state;
|
|
1744
1816
|
const prev = state.pitchWheel * 2 - 1;
|
|
1745
1817
|
const next = (value - 8192) / 8192;
|
|
@@ -1750,9 +1822,11 @@ class Midy {
|
|
|
1750
1822
|
}
|
|
1751
1823
|
setModLfoToPitch(channel, note, scheduleTime) {
|
|
1752
1824
|
if (note.modulationDepth) {
|
|
1825
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1826
|
+
const modulationDepth = modulationDepthMSB + modulationDepthLSB / 128;
|
|
1753
1827
|
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1754
1828
|
this.getLFOPitchDepth(channel, note);
|
|
1755
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1829
|
+
const baseDepth = Math.abs(modLfoToPitch) + modulationDepth;
|
|
1756
1830
|
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1757
1831
|
note.modulationDepth.gain
|
|
1758
1832
|
.cancelScheduledValues(scheduleTime)
|
|
@@ -1885,7 +1959,8 @@ class Midy {
|
|
|
1885
1959
|
createVoiceParamsHandlers() {
|
|
1886
1960
|
return {
|
|
1887
1961
|
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1888
|
-
|
|
1962
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1963
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1889
1964
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1890
1965
|
}
|
|
1891
1966
|
},
|
|
@@ -1895,12 +1970,14 @@ class Midy {
|
|
|
1895
1970
|
}
|
|
1896
1971
|
},
|
|
1897
1972
|
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1898
|
-
|
|
1973
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1974
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1899
1975
|
this.setModLfoToFilterFc(channel, note, scheduleTime);
|
|
1900
1976
|
}
|
|
1901
1977
|
},
|
|
1902
1978
|
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1903
|
-
|
|
1979
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1980
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1904
1981
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
1905
1982
|
}
|
|
1906
1983
|
},
|
|
@@ -1911,12 +1988,14 @@ class Midy {
|
|
|
1911
1988
|
this.setReverbSend(channel, note, scheduleTime);
|
|
1912
1989
|
},
|
|
1913
1990
|
delayModLFO: (_channel, note, _scheduleTime) => {
|
|
1914
|
-
|
|
1991
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1992
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1915
1993
|
this.setDelayModLFO(note);
|
|
1916
1994
|
}
|
|
1917
1995
|
},
|
|
1918
1996
|
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1919
|
-
|
|
1997
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1998
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1920
1999
|
this.setFreqModLFO(note, scheduleTime);
|
|
1921
2000
|
}
|
|
1922
2001
|
},
|
|
@@ -1985,7 +2064,12 @@ class Midy {
|
|
|
1985
2064
|
handlers[10] = this.setPan;
|
|
1986
2065
|
handlers[11] = this.setExpression;
|
|
1987
2066
|
handlers[32] = this.setBankLSB;
|
|
2067
|
+
handlers[33] = this.setModulationDepth;
|
|
2068
|
+
handlers[37] = this.setPortamentoTime;
|
|
1988
2069
|
handlers[38] = this.dataEntryLSB;
|
|
2070
|
+
handlers[39] = this.setVolume;
|
|
2071
|
+
handlers[42] = this.setPan;
|
|
2072
|
+
handlers[43] = this.setExpression;
|
|
1989
2073
|
handlers[64] = this.setSustainPedal;
|
|
1990
2074
|
handlers[65] = this.setPortamento;
|
|
1991
2075
|
handlers[66] = this.setSostenutoPedal;
|
|
@@ -1998,12 +2082,14 @@ class Midy {
|
|
|
1998
2082
|
handlers[76] = this.setVibratoRate;
|
|
1999
2083
|
handlers[77] = this.setVibratoDepth;
|
|
2000
2084
|
handlers[78] = this.setVibratoDelay;
|
|
2085
|
+
handlers[84] = this.setPortamentoNoteNumber;
|
|
2001
2086
|
handlers[91] = this.setReverbSendLevel;
|
|
2002
2087
|
handlers[93] = this.setChorusSendLevel;
|
|
2003
2088
|
handlers[96] = this.dataIncrement;
|
|
2004
2089
|
handlers[97] = this.dataDecrement;
|
|
2005
2090
|
handlers[100] = this.setRPNLSB;
|
|
2006
2091
|
handlers[101] = this.setRPNMSB;
|
|
2092
|
+
handlers[111] = this.setRPGMakerLoop;
|
|
2007
2093
|
handlers[120] = this.allSoundOff;
|
|
2008
2094
|
handlers[121] = this.resetAllControllers;
|
|
2009
2095
|
handlers[123] = this.allNotesOff;
|
|
@@ -2029,7 +2115,9 @@ class Midy {
|
|
|
2029
2115
|
this.channels[channelNumber].bankMSB = msb;
|
|
2030
2116
|
}
|
|
2031
2117
|
updateModulation(channel, scheduleTime) {
|
|
2032
|
-
const
|
|
2118
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
2119
|
+
const modulationDepth = modulationDepthMSB + modulationDepthLSB / 128;
|
|
2120
|
+
const depth = modulationDepth * channel.modulationDepthRange;
|
|
2033
2121
|
this.processScheduledNotes(channel, (note) => {
|
|
2034
2122
|
if (note.modulationDepth) {
|
|
2035
2123
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
@@ -2039,12 +2127,16 @@ class Midy {
|
|
|
2039
2127
|
}
|
|
2040
2128
|
});
|
|
2041
2129
|
}
|
|
2042
|
-
setModulationDepth(channelNumber,
|
|
2130
|
+
setModulationDepth(channelNumber, value, scheduleTime) {
|
|
2043
2131
|
const channel = this.channels[channelNumber];
|
|
2044
2132
|
if (channel.isDrum)
|
|
2045
2133
|
return;
|
|
2046
|
-
|
|
2047
|
-
|
|
2134
|
+
if (!(0 <= scheduleTime))
|
|
2135
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2136
|
+
const state = channel.state;
|
|
2137
|
+
const intPart = Math.trunc(value);
|
|
2138
|
+
state.modulationDepthMSB = intPart / 127;
|
|
2139
|
+
state.modulationDepthLSB = value - intPart;
|
|
2048
2140
|
this.updateModulation(channel, scheduleTime);
|
|
2049
2141
|
}
|
|
2050
2142
|
updatePortamento(channel, scheduleTime) {
|
|
@@ -2065,18 +2157,26 @@ class Midy {
|
|
|
2065
2157
|
}
|
|
2066
2158
|
});
|
|
2067
2159
|
}
|
|
2068
|
-
setPortamentoTime(channelNumber,
|
|
2160
|
+
setPortamentoTime(channelNumber, value, scheduleTime) {
|
|
2161
|
+
if (!(0 <= scheduleTime))
|
|
2162
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2069
2163
|
const channel = this.channels[channelNumber];
|
|
2070
|
-
|
|
2071
|
-
|
|
2164
|
+
const state = channel.state;
|
|
2165
|
+
const intPart = Math.trunc(value);
|
|
2166
|
+
state.portamentoTimeMSB = intPart / 127;
|
|
2167
|
+
state.portamentoTimeLSB = value - 127;
|
|
2072
2168
|
if (channel.isDrum)
|
|
2073
2169
|
return;
|
|
2074
2170
|
this.updatePortamento(channel, scheduleTime);
|
|
2075
2171
|
}
|
|
2076
|
-
setVolume(channelNumber,
|
|
2077
|
-
|
|
2172
|
+
setVolume(channelNumber, value, scheduleTime) {
|
|
2173
|
+
if (!(0 <= scheduleTime))
|
|
2174
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2078
2175
|
const channel = this.channels[channelNumber];
|
|
2079
|
-
|
|
2176
|
+
const state = channel.state;
|
|
2177
|
+
const intPart = Math.trunc(value);
|
|
2178
|
+
state.volumeMSB = intPart / 127;
|
|
2179
|
+
state.volumeLSB = value - intPart;
|
|
2080
2180
|
if (channel.isDrum) {
|
|
2081
2181
|
for (let i = 0; i < 128; i++) {
|
|
2082
2182
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2087,16 +2187,20 @@ class Midy {
|
|
|
2087
2187
|
}
|
|
2088
2188
|
}
|
|
2089
2189
|
panToGain(pan) {
|
|
2090
|
-
const theta = Math.PI / 2 * Math.max(
|
|
2190
|
+
const theta = Math.PI / 2 * Math.max(pan * 127 - 1) / 126;
|
|
2091
2191
|
return {
|
|
2092
2192
|
gainLeft: Math.cos(theta),
|
|
2093
2193
|
gainRight: Math.sin(theta),
|
|
2094
2194
|
};
|
|
2095
2195
|
}
|
|
2096
|
-
setPan(channelNumber,
|
|
2097
|
-
|
|
2196
|
+
setPan(channelNumber, value, scheduleTime) {
|
|
2197
|
+
if (!(0 <= scheduleTime))
|
|
2198
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2098
2199
|
const channel = this.channels[channelNumber];
|
|
2099
|
-
|
|
2200
|
+
const state = channel.state;
|
|
2201
|
+
const intPart = Math.trunc(value);
|
|
2202
|
+
state.panMSB = intPart / 127;
|
|
2203
|
+
state.panLSB = value - intPart;
|
|
2100
2204
|
if (channel.isDrum) {
|
|
2101
2205
|
for (let i = 0; i < 128; i++) {
|
|
2102
2206
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2106,10 +2210,14 @@ class Midy {
|
|
|
2106
2210
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2107
2211
|
}
|
|
2108
2212
|
}
|
|
2109
|
-
setExpression(channelNumber,
|
|
2110
|
-
|
|
2213
|
+
setExpression(channelNumber, value, scheduleTime) {
|
|
2214
|
+
if (!(0 <= scheduleTime))
|
|
2215
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2111
2216
|
const channel = this.channels[channelNumber];
|
|
2112
|
-
|
|
2217
|
+
const state = channel.state;
|
|
2218
|
+
const intPart = Math.trunc(value);
|
|
2219
|
+
state.expressionMSB = intPart / 127;
|
|
2220
|
+
state.expressionLSB = value - intPart;
|
|
2113
2221
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2114
2222
|
}
|
|
2115
2223
|
setBankLSB(channelNumber, lsb) {
|
|
@@ -2120,43 +2228,49 @@ class Midy {
|
|
|
2120
2228
|
this.handleRPN(channelNumber, 0, scheduleTime);
|
|
2121
2229
|
}
|
|
2122
2230
|
updateChannelVolume(channel, scheduleTime) {
|
|
2123
|
-
const
|
|
2124
|
-
const volume =
|
|
2125
|
-
const
|
|
2231
|
+
const { expressionMSB, expressionLSB, volumeMSB, volumeLSB, panMSB, panLSB, } = channel.state;
|
|
2232
|
+
const volume = volumeMSB + volumeLSB / 128;
|
|
2233
|
+
const expression = expressionMSB + expressionLSB / 128;
|
|
2234
|
+
const pan = panMSB + panLSB / 128;
|
|
2235
|
+
const gain = volume * expression;
|
|
2236
|
+
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2126
2237
|
channel.gainL.gain
|
|
2127
2238
|
.cancelScheduledValues(scheduleTime)
|
|
2128
|
-
.setValueAtTime(
|
|
2239
|
+
.setValueAtTime(gain * gainLeft, scheduleTime);
|
|
2129
2240
|
channel.gainR.gain
|
|
2130
2241
|
.cancelScheduledValues(scheduleTime)
|
|
2131
|
-
.setValueAtTime(
|
|
2242
|
+
.setValueAtTime(gain * gainRight, scheduleTime);
|
|
2132
2243
|
}
|
|
2133
2244
|
updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
|
|
2134
2245
|
const gainL = channel.keyBasedGainLs[keyNumber];
|
|
2135
2246
|
if (!gainL)
|
|
2136
2247
|
return;
|
|
2137
2248
|
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
2138
|
-
const
|
|
2139
|
-
const
|
|
2140
|
-
const
|
|
2249
|
+
const { expressionMSB, expressionLSB, volumeMSB, volumeLSB, panMSB, panLSB, } = channel.state;
|
|
2250
|
+
const volume = volumeMSB + volumeLSB / 128;
|
|
2251
|
+
const expression = expressionMSB + expressionLSB / 128;
|
|
2252
|
+
const defaultGain = volume * expression;
|
|
2253
|
+
const defaultPan = panMSB + panLSB / 128;
|
|
2141
2254
|
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
2142
|
-
const
|
|
2143
|
-
?
|
|
2144
|
-
:
|
|
2255
|
+
const gain = (0 <= keyBasedVolume)
|
|
2256
|
+
? defaultGain * keyBasedVolume / 64
|
|
2257
|
+
: defaultGain;
|
|
2145
2258
|
const keyBasedPan = this.getKeyBasedValue(channel, keyNumber, 10);
|
|
2146
2259
|
const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
|
|
2147
2260
|
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2148
2261
|
gainL.gain
|
|
2149
2262
|
.cancelScheduledValues(scheduleTime)
|
|
2150
|
-
.setValueAtTime(
|
|
2263
|
+
.setValueAtTime(gain * gainLeft, scheduleTime);
|
|
2151
2264
|
gainR.gain
|
|
2152
2265
|
.cancelScheduledValues(scheduleTime)
|
|
2153
|
-
.setValueAtTime(
|
|
2266
|
+
.setValueAtTime(gain * gainRight, scheduleTime);
|
|
2154
2267
|
}
|
|
2155
2268
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
2156
2269
|
const channel = this.channels[channelNumber];
|
|
2157
2270
|
if (channel.isDrum)
|
|
2158
2271
|
return;
|
|
2159
|
-
|
|
2272
|
+
if (!(0 <= scheduleTime))
|
|
2273
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2160
2274
|
channel.state.sustainPedal = value / 127;
|
|
2161
2275
|
if (64 <= value) {
|
|
2162
2276
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -2174,7 +2288,8 @@ class Midy {
|
|
|
2174
2288
|
const channel = this.channels[channelNumber];
|
|
2175
2289
|
if (channel.isDrum)
|
|
2176
2290
|
return;
|
|
2177
|
-
|
|
2291
|
+
if (!(0 <= scheduleTime))
|
|
2292
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2178
2293
|
channel.state.portamento = value / 127;
|
|
2179
2294
|
this.updatePortamento(channel, scheduleTime);
|
|
2180
2295
|
}
|
|
@@ -2182,7 +2297,8 @@ class Midy {
|
|
|
2182
2297
|
const channel = this.channels[channelNumber];
|
|
2183
2298
|
if (channel.isDrum)
|
|
2184
2299
|
return;
|
|
2185
|
-
|
|
2300
|
+
if (!(0 <= scheduleTime))
|
|
2301
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2186
2302
|
channel.state.sostenutoPedal = value / 127;
|
|
2187
2303
|
if (64 <= value) {
|
|
2188
2304
|
const sostenutoNotes = [];
|
|
@@ -2203,7 +2319,8 @@ class Midy {
|
|
|
2203
2319
|
if (channel.isDrum)
|
|
2204
2320
|
return;
|
|
2205
2321
|
const state = channel.state;
|
|
2206
|
-
|
|
2322
|
+
if (!(0 <= scheduleTime))
|
|
2323
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2207
2324
|
state.softPedal = softPedal / 127;
|
|
2208
2325
|
this.processScheduledNotes(channel, (note) => {
|
|
2209
2326
|
if (this.isPortamento(channel, note)) {
|
|
@@ -2220,11 +2337,12 @@ class Midy {
|
|
|
2220
2337
|
const channel = this.channels[channelNumber];
|
|
2221
2338
|
if (channel.isDrum)
|
|
2222
2339
|
return;
|
|
2223
|
-
|
|
2340
|
+
if (!(0 <= scheduleTime))
|
|
2341
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2224
2342
|
const state = channel.state;
|
|
2225
2343
|
state.filterResonance = ccValue / 127;
|
|
2226
|
-
const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
|
|
2227
2344
|
this.processScheduledNotes(channel, (note) => {
|
|
2345
|
+
const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
|
|
2228
2346
|
const Q = note.voiceParams.initialFilterQ / 5 * filterResonance;
|
|
2229
2347
|
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
2230
2348
|
});
|
|
@@ -2241,14 +2359,16 @@ class Midy {
|
|
|
2241
2359
|
const channel = this.channels[channelNumber];
|
|
2242
2360
|
if (channel.isDrum)
|
|
2243
2361
|
return;
|
|
2244
|
-
|
|
2362
|
+
if (!(0 <= scheduleTime))
|
|
2363
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2245
2364
|
channel.state.releaseTime = releaseTime / 127;
|
|
2246
2365
|
}
|
|
2247
2366
|
setAttackTime(channelNumber, attackTime, scheduleTime) {
|
|
2248
2367
|
const channel = this.channels[channelNumber];
|
|
2249
2368
|
if (channel.isDrum)
|
|
2250
2369
|
return;
|
|
2251
|
-
|
|
2370
|
+
if (!(0 <= scheduleTime))
|
|
2371
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2252
2372
|
channel.state.attackTime = attackTime / 127;
|
|
2253
2373
|
this.processScheduledNotes(channel, (note) => {
|
|
2254
2374
|
if (scheduleTime < note.startTime) {
|
|
@@ -2261,7 +2381,8 @@ class Midy {
|
|
|
2261
2381
|
if (channel.isDrum)
|
|
2262
2382
|
return;
|
|
2263
2383
|
const state = channel.state;
|
|
2264
|
-
|
|
2384
|
+
if (!(0 <= scheduleTime))
|
|
2385
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2265
2386
|
state.brightness = brightness / 127;
|
|
2266
2387
|
this.processScheduledNotes(channel, (note) => {
|
|
2267
2388
|
if (this.isPortamento(channel, note)) {
|
|
@@ -2276,7 +2397,8 @@ class Midy {
|
|
|
2276
2397
|
const channel = this.channels[channelNumber];
|
|
2277
2398
|
if (channel.isDrum)
|
|
2278
2399
|
return;
|
|
2279
|
-
|
|
2400
|
+
if (!(0 <= scheduleTime))
|
|
2401
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2280
2402
|
channel.state.decayTime = dacayTime / 127;
|
|
2281
2403
|
this.processScheduledNotes(channel, (note) => {
|
|
2282
2404
|
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
@@ -2286,7 +2408,8 @@ class Midy {
|
|
|
2286
2408
|
const channel = this.channels[channelNumber];
|
|
2287
2409
|
if (channel.isDrum)
|
|
2288
2410
|
return;
|
|
2289
|
-
|
|
2411
|
+
if (!(0 <= scheduleTime))
|
|
2412
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2290
2413
|
channel.state.vibratoRate = vibratoRate / 127;
|
|
2291
2414
|
if (channel.vibratoDepth <= 0)
|
|
2292
2415
|
return;
|
|
@@ -2298,7 +2421,8 @@ class Midy {
|
|
|
2298
2421
|
const channel = this.channels[channelNumber];
|
|
2299
2422
|
if (channel.isDrum)
|
|
2300
2423
|
return;
|
|
2301
|
-
|
|
2424
|
+
if (!(0 <= scheduleTime))
|
|
2425
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2302
2426
|
const prev = channel.state.vibratoDepth;
|
|
2303
2427
|
channel.state.vibratoDepth = vibratoDepth / 127;
|
|
2304
2428
|
if (0 < prev) {
|
|
@@ -2316,7 +2440,8 @@ class Midy {
|
|
|
2316
2440
|
const channel = this.channels[channelNumber];
|
|
2317
2441
|
if (channel.isDrum)
|
|
2318
2442
|
return;
|
|
2319
|
-
|
|
2443
|
+
if (!(0 <= scheduleTime))
|
|
2444
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2320
2445
|
channel.state.vibratoDelay = vibratoDelay / 127;
|
|
2321
2446
|
if (0 < channel.state.vibratoDepth) {
|
|
2322
2447
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -2324,8 +2449,16 @@ class Midy {
|
|
|
2324
2449
|
});
|
|
2325
2450
|
}
|
|
2326
2451
|
}
|
|
2452
|
+
setPortamentoNoteNumber(channelNumber, value, scheduleTime) {
|
|
2453
|
+
if (!(0 <= scheduleTime))
|
|
2454
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2455
|
+
const channel = this.channels[channelNumber];
|
|
2456
|
+
channel.portamentoControl = true;
|
|
2457
|
+
channel.state.portamentoNoteNumber = value / 127;
|
|
2458
|
+
}
|
|
2327
2459
|
setReverbSendLevel(channelNumber, reverbSendLevel, scheduleTime) {
|
|
2328
|
-
|
|
2460
|
+
if (!(0 <= scheduleTime))
|
|
2461
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2329
2462
|
const channel = this.channels[channelNumber];
|
|
2330
2463
|
const state = channel.state;
|
|
2331
2464
|
state.reverbSendLevel = reverbSendLevel / 127;
|
|
@@ -2334,7 +2467,8 @@ class Midy {
|
|
|
2334
2467
|
});
|
|
2335
2468
|
}
|
|
2336
2469
|
setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
|
|
2337
|
-
|
|
2470
|
+
if (!(0 <= scheduleTime))
|
|
2471
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2338
2472
|
const channel = this.channels[channelNumber];
|
|
2339
2473
|
const state = channel.state;
|
|
2340
2474
|
state.chorusSendLevel = chorusSendLevel / 127;
|
|
@@ -2394,12 +2528,14 @@ class Midy {
|
|
|
2394
2528
|
}
|
|
2395
2529
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
|
|
2396
2530
|
dataIncrement(channelNumber, scheduleTime) {
|
|
2397
|
-
|
|
2531
|
+
if (!(0 <= scheduleTime))
|
|
2532
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2398
2533
|
this.handleRPN(channelNumber, 1, scheduleTime);
|
|
2399
2534
|
}
|
|
2400
2535
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
|
|
2401
2536
|
dataDecrement(channelNumber, scheduleTime) {
|
|
2402
|
-
|
|
2537
|
+
if (!(0 <= scheduleTime))
|
|
2538
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2403
2539
|
this.handleRPN(channelNumber, -1, scheduleTime);
|
|
2404
2540
|
}
|
|
2405
2541
|
setRPNMSB(channelNumber, value) {
|
|
@@ -2422,7 +2558,8 @@ class Midy {
|
|
|
2422
2558
|
const channel = this.channels[channelNumber];
|
|
2423
2559
|
if (channel.isDrum)
|
|
2424
2560
|
return;
|
|
2425
|
-
|
|
2561
|
+
if (!(0 <= scheduleTime))
|
|
2562
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2426
2563
|
const state = channel.state;
|
|
2427
2564
|
const prev = state.pitchWheelSensitivity;
|
|
2428
2565
|
const next = value / 12800;
|
|
@@ -2442,7 +2579,8 @@ class Midy {
|
|
|
2442
2579
|
const channel = this.channels[channelNumber];
|
|
2443
2580
|
if (channel.isDrum)
|
|
2444
2581
|
return;
|
|
2445
|
-
|
|
2582
|
+
if (!(0 <= scheduleTime))
|
|
2583
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2446
2584
|
const prev = channel.fineTuning;
|
|
2447
2585
|
const next = value;
|
|
2448
2586
|
channel.fineTuning = next;
|
|
@@ -2459,7 +2597,8 @@ class Midy {
|
|
|
2459
2597
|
const channel = this.channels[channelNumber];
|
|
2460
2598
|
if (channel.isDrum)
|
|
2461
2599
|
return;
|
|
2462
|
-
|
|
2600
|
+
if (!(0 <= scheduleTime))
|
|
2601
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2463
2602
|
const prev = channel.coarseTuning;
|
|
2464
2603
|
const next = value;
|
|
2465
2604
|
channel.coarseTuning = next;
|
|
@@ -2476,12 +2615,18 @@ class Midy {
|
|
|
2476
2615
|
const channel = this.channels[channelNumber];
|
|
2477
2616
|
if (channel.isDrum)
|
|
2478
2617
|
return;
|
|
2479
|
-
|
|
2618
|
+
if (!(0 <= scheduleTime))
|
|
2619
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2480
2620
|
channel.modulationDepthRange = value;
|
|
2481
2621
|
this.updateModulation(channel, scheduleTime);
|
|
2482
2622
|
}
|
|
2483
|
-
|
|
2623
|
+
setRPGMakerLoop(_channelNumber, _value, scheduleTime) {
|
|
2484
2624
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2625
|
+
this.loopStart = scheduleTime + this.resumeTime - this.startTime;
|
|
2626
|
+
}
|
|
2627
|
+
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
2628
|
+
if (!(0 <= scheduleTime))
|
|
2629
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2485
2630
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
2486
2631
|
}
|
|
2487
2632
|
resetChannelStates(channelNumber) {
|
|
@@ -2511,8 +2656,10 @@ class Midy {
|
|
|
2511
2656
|
"polyphonicKeyPressure",
|
|
2512
2657
|
"channelPressure",
|
|
2513
2658
|
"pitchWheel",
|
|
2514
|
-
"
|
|
2515
|
-
"
|
|
2659
|
+
"expressionMSB",
|
|
2660
|
+
"expressionLSB",
|
|
2661
|
+
"modulationDepthMSB",
|
|
2662
|
+
"modulationDepthLSB",
|
|
2516
2663
|
"sustainPedal",
|
|
2517
2664
|
"portamento",
|
|
2518
2665
|
"sostenutoPedal",
|
|
@@ -2541,7 +2688,8 @@ class Midy {
|
|
|
2541
2688
|
}
|
|
2542
2689
|
}
|
|
2543
2690
|
allNotesOff(channelNumber, _value, scheduleTime) {
|
|
2544
|
-
|
|
2691
|
+
if (!(0 <= scheduleTime))
|
|
2692
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2545
2693
|
return this.stopActiveNotes(channelNumber, 0, false, scheduleTime);
|
|
2546
2694
|
}
|
|
2547
2695
|
omniOff(channelNumber, value, scheduleTime) {
|
|
@@ -2593,7 +2741,8 @@ class Midy {
|
|
|
2593
2741
|
}
|
|
2594
2742
|
}
|
|
2595
2743
|
GM1SystemOn(scheduleTime) {
|
|
2596
|
-
|
|
2744
|
+
if (!(0 <= scheduleTime))
|
|
2745
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2597
2746
|
this.mode = "GM1";
|
|
2598
2747
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2599
2748
|
this.allSoundOff(i, 0, scheduleTime);
|
|
@@ -2606,7 +2755,8 @@ class Midy {
|
|
|
2606
2755
|
this.channels[9].isDrum = true;
|
|
2607
2756
|
}
|
|
2608
2757
|
GM2SystemOn(scheduleTime) {
|
|
2609
|
-
|
|
2758
|
+
if (!(0 <= scheduleTime))
|
|
2759
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2610
2760
|
this.mode = "GM2";
|
|
2611
2761
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2612
2762
|
this.allSoundOff(i, 0, scheduleTime);
|
|
@@ -2674,7 +2824,8 @@ class Midy {
|
|
|
2674
2824
|
this.setMasterVolume(volume, scheduleTime);
|
|
2675
2825
|
}
|
|
2676
2826
|
setMasterVolume(value, scheduleTime) {
|
|
2677
|
-
|
|
2827
|
+
if (!(0 <= scheduleTime))
|
|
2828
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2678
2829
|
this.masterVolume.gain
|
|
2679
2830
|
.cancelScheduledValues(scheduleTime)
|
|
2680
2831
|
.setValueAtTime(value * value, scheduleTime);
|
|
@@ -3222,5 +3373,6 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
3222
3373
|
modulationDepthRange: 50, // cent
|
|
3223
3374
|
fineTuning: 0, // cent
|
|
3224
3375
|
coarseTuning: 0, // cent
|
|
3376
|
+
portamentoControl: false,
|
|
3225
3377
|
}
|
|
3226
3378
|
});
|