@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.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,
|
|
@@ -113,6 +107,9 @@ class Note {
|
|
|
113
107
|
this.noteNumber = noteNumber;
|
|
114
108
|
this.velocity = velocity;
|
|
115
109
|
this.startTime = startTime;
|
|
110
|
+
this.ready = new Promise((resolve) => {
|
|
111
|
+
this.resolveReady = resolve;
|
|
112
|
+
});
|
|
116
113
|
}
|
|
117
114
|
}
|
|
118
115
|
const drumExclusiveClassesByKit = new Array(57);
|
|
@@ -158,14 +155,19 @@ const defaultControllerState = {
|
|
|
158
155
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
159
156
|
link: { type: 127, defaultValue: 0 },
|
|
160
157
|
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
|
|
159
|
+
portamentoTimeMSB: { type: 128 + 5, defaultValue: 0 },
|
|
163
160
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
162
|
+
panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
163
|
+
expressionMSB: { type: 128 + 11, defaultValue: 1 },
|
|
167
164
|
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
165
|
+
modulationDepthLSB: { type: 128 + 33, defaultValue: 0 },
|
|
166
|
+
portamentoTimeLSB: { type: 128 + 37, defaultValue: 0 },
|
|
168
167
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
168
|
+
volumeLSB: { type: 128 + 39, defaultValue: 0 },
|
|
169
|
+
panLSB: { type: 128 + 42, defaultValue: 0 },
|
|
170
|
+
expressionLSB: { type: 128 + 43, defaultValue: 0 },
|
|
169
171
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
170
172
|
portamento: { type: 128 + 65, defaultValue: 0 },
|
|
171
173
|
sostenutoPedal: { type: 128 + 66, defaultValue: 0 },
|
|
@@ -178,12 +180,14 @@ const defaultControllerState = {
|
|
|
178
180
|
vibratoRate: { type: 128 + 76, defaultValue: 64 / 127 },
|
|
179
181
|
vibratoDepth: { type: 128 + 77, defaultValue: 64 / 127 },
|
|
180
182
|
vibratoDelay: { type: 128 + 78, defaultValue: 64 / 127 },
|
|
183
|
+
portamentoNoteNumber: { type: 128 + 84, defaultValue: 0 },
|
|
181
184
|
reverbSendLevel: { type: 128 + 91, defaultValue: 0 },
|
|
182
185
|
chorusSendLevel: { type: 128 + 93, defaultValue: 0 },
|
|
183
186
|
// dataIncrement: { type: 128 + 96, defaultValue: 0 },
|
|
184
187
|
// dataDecrement: { type: 128 + 97, defaultValue: 0 },
|
|
185
188
|
// rpnLSB: { type: 128 + 100, defaultValue: 127 },
|
|
186
189
|
// rpnMSB: { type: 128 + 101, defaultValue: 127 },
|
|
190
|
+
// rpgMakerLoop: { type: 128 + 111, defaultValue: 0 },
|
|
187
191
|
// allSoundOff: { type: 128 + 120, defaultValue: 0 },
|
|
188
192
|
// resetAllControllers: { type: 128 + 121, defaultValue: 0 },
|
|
189
193
|
// allNotesOff: { type: 128 + 123, defaultValue: 0 },
|
|
@@ -243,8 +247,9 @@ const pitchEnvelopeKeys = [
|
|
|
243
247
|
"playbackRate",
|
|
244
248
|
];
|
|
245
249
|
const pitchEnvelopeKeySet = new Set(pitchEnvelopeKeys);
|
|
246
|
-
export class Midy {
|
|
250
|
+
export class Midy extends EventTarget {
|
|
247
251
|
constructor(audioContext) {
|
|
252
|
+
super();
|
|
248
253
|
Object.defineProperty(this, "mode", {
|
|
249
254
|
enumerable: true,
|
|
250
255
|
configurable: true,
|
|
@@ -405,6 +410,18 @@ export class Midy {
|
|
|
405
410
|
writable: true,
|
|
406
411
|
value: false
|
|
407
412
|
});
|
|
413
|
+
Object.defineProperty(this, "loop", {
|
|
414
|
+
enumerable: true,
|
|
415
|
+
configurable: true,
|
|
416
|
+
writable: true,
|
|
417
|
+
value: true
|
|
418
|
+
});
|
|
419
|
+
Object.defineProperty(this, "loopStart", {
|
|
420
|
+
enumerable: true,
|
|
421
|
+
configurable: true,
|
|
422
|
+
writable: true,
|
|
423
|
+
value: 0
|
|
424
|
+
});
|
|
408
425
|
Object.defineProperty(this, "playPromise", {
|
|
409
426
|
enumerable: true,
|
|
410
427
|
configurable: true,
|
|
@@ -561,7 +578,7 @@ export class Midy {
|
|
|
561
578
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
562
579
|
}
|
|
563
580
|
createChannelAudioNodes(audioContext) {
|
|
564
|
-
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.
|
|
581
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
|
|
565
582
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
566
583
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
567
584
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -604,10 +621,9 @@ export class Midy {
|
|
|
604
621
|
return channels;
|
|
605
622
|
}
|
|
606
623
|
async createAudioBuffer(voiceParams) {
|
|
607
|
-
const sample = voiceParams
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
|
|
624
|
+
const { sample, start, end } = voiceParams;
|
|
625
|
+
const sampleEnd = sample.data.length + end;
|
|
626
|
+
const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
|
|
611
627
|
return audioBuffer;
|
|
612
628
|
}
|
|
613
629
|
isLoopDrum(channel, noteNumber) {
|
|
@@ -627,7 +643,7 @@ export class Midy {
|
|
|
627
643
|
}
|
|
628
644
|
return bufferSource;
|
|
629
645
|
}
|
|
630
|
-
|
|
646
|
+
scheduleTimelineEvents(scheduleTime, queueIndex) {
|
|
631
647
|
const timeOffset = this.resumeTime - this.startTime;
|
|
632
648
|
const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
|
|
633
649
|
const schedulingOffset = this.startDelay - timeOffset;
|
|
@@ -639,12 +655,10 @@ export class Midy {
|
|
|
639
655
|
const startTime = event.startTime + schedulingOffset;
|
|
640
656
|
switch (event.type) {
|
|
641
657
|
case "noteOn":
|
|
642
|
-
|
|
658
|
+
this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
643
659
|
break;
|
|
644
660
|
case "noteOff": {
|
|
645
|
-
|
|
646
|
-
if (notePromise)
|
|
647
|
-
this.notePromises.push(notePromise);
|
|
661
|
+
this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
648
662
|
break;
|
|
649
663
|
}
|
|
650
664
|
case "noteAftertouch":
|
|
@@ -688,22 +702,23 @@ export class Midy {
|
|
|
688
702
|
}
|
|
689
703
|
}
|
|
690
704
|
updateStates(queueIndex, nextQueueIndex) {
|
|
705
|
+
const now = this.audioContext.currentTime;
|
|
691
706
|
if (nextQueueIndex < queueIndex)
|
|
692
707
|
queueIndex = 0;
|
|
693
708
|
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
694
709
|
const event = this.timeline[i];
|
|
695
710
|
switch (event.type) {
|
|
696
711
|
case "controller":
|
|
697
|
-
this.setControlChange(event.channel, event.controllerType, event.value,
|
|
712
|
+
this.setControlChange(event.channel, event.controllerType, event.value, now - this.resumeTime + event.startTime);
|
|
698
713
|
break;
|
|
699
714
|
case "programChange":
|
|
700
|
-
this.setProgramChange(event.channel, event.programNumber,
|
|
715
|
+
this.setProgramChange(event.channel, event.programNumber, now - this.resumeTime + event.startTime);
|
|
701
716
|
break;
|
|
702
717
|
case "pitchBend":
|
|
703
|
-
this.setPitchBend(event.channel, event.value + 8192,
|
|
718
|
+
this.setPitchBend(event.channel, event.value + 8192, now - this.resumeTime + event.startTime);
|
|
704
719
|
break;
|
|
705
720
|
case "sysEx":
|
|
706
|
-
this.handleSysEx(event.data,
|
|
721
|
+
this.handleSysEx(event.data, now - this.resumeTime + event.startTime);
|
|
707
722
|
}
|
|
708
723
|
}
|
|
709
724
|
}
|
|
@@ -711,52 +726,95 @@ export class Midy {
|
|
|
711
726
|
if (this.audioContext.state === "suspended") {
|
|
712
727
|
await this.audioContext.resume();
|
|
713
728
|
}
|
|
729
|
+
const paused = this.isPaused;
|
|
714
730
|
this.isPlaying = true;
|
|
715
731
|
this.isPaused = false;
|
|
716
732
|
this.startTime = this.audioContext.currentTime;
|
|
733
|
+
if (paused) {
|
|
734
|
+
this.dispatchEvent(new Event("resumed"));
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
this.dispatchEvent(new Event("started"));
|
|
738
|
+
}
|
|
717
739
|
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
718
|
-
let
|
|
740
|
+
let exitReason;
|
|
719
741
|
this.notePromises = [];
|
|
720
|
-
while (
|
|
742
|
+
while (true) {
|
|
721
743
|
const now = this.audioContext.currentTime;
|
|
722
744
|
if (0 < this.lastActiveSensing &&
|
|
723
745
|
this.activeSensingThreshold < performance.now() - this.lastActiveSensing) {
|
|
724
746
|
await this.stopNotes(0, true, now);
|
|
725
747
|
await this.audioContext.suspend();
|
|
726
|
-
|
|
748
|
+
exitReason = "aborted";
|
|
727
749
|
break;
|
|
728
750
|
}
|
|
751
|
+
if (this.timeline.length <= queueIndex) {
|
|
752
|
+
await this.stopNotes(0, true, now);
|
|
753
|
+
if (this.loop) {
|
|
754
|
+
this.notePromises = [];
|
|
755
|
+
this.resetAllStates();
|
|
756
|
+
this.startTime = this.audioContext.currentTime;
|
|
757
|
+
this.resumeTime = this.loopStart;
|
|
758
|
+
if (0 < this.loopStart) {
|
|
759
|
+
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
760
|
+
this.updateStates(queueIndex, nextQueueIndex);
|
|
761
|
+
queueIndex = nextQueueIndex;
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
queueIndex = 0;
|
|
765
|
+
}
|
|
766
|
+
this.dispatchEvent(new Event("looped"));
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
await this.audioContext.suspend();
|
|
771
|
+
exitReason = "ended";
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
729
775
|
if (this.isPausing) {
|
|
730
776
|
await this.stopNotes(0, true, now);
|
|
731
777
|
await this.audioContext.suspend();
|
|
732
778
|
this.notePromises = [];
|
|
779
|
+
this.isPausing = false;
|
|
780
|
+
exitReason = "paused";
|
|
733
781
|
break;
|
|
734
782
|
}
|
|
735
783
|
else if (this.isStopping) {
|
|
736
784
|
await this.stopNotes(0, true, now);
|
|
737
785
|
await this.audioContext.suspend();
|
|
738
|
-
|
|
786
|
+
this.isStopping = false;
|
|
787
|
+
exitReason = "stopped";
|
|
739
788
|
break;
|
|
740
789
|
}
|
|
741
790
|
else if (this.isSeeking) {
|
|
742
|
-
|
|
791
|
+
this.stopNotes(0, true, now);
|
|
743
792
|
this.startTime = this.audioContext.currentTime;
|
|
744
793
|
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
745
794
|
this.updateStates(queueIndex, nextQueueIndex);
|
|
746
795
|
queueIndex = nextQueueIndex;
|
|
747
796
|
this.isSeeking = false;
|
|
797
|
+
this.dispatchEvent(new Event("seeked"));
|
|
748
798
|
continue;
|
|
749
799
|
}
|
|
750
|
-
queueIndex =
|
|
800
|
+
queueIndex = this.scheduleTimelineEvents(now, queueIndex);
|
|
751
801
|
const waitTime = now + this.noteCheckInterval;
|
|
752
802
|
await this.scheduleTask(() => { }, waitTime);
|
|
753
803
|
}
|
|
754
|
-
if (
|
|
804
|
+
if (exitReason !== "paused") {
|
|
755
805
|
this.notePromises = [];
|
|
756
806
|
this.resetAllStates();
|
|
757
807
|
this.lastActiveSensing = 0;
|
|
758
808
|
}
|
|
759
809
|
this.isPlaying = false;
|
|
810
|
+
if (exitReason === "paused") {
|
|
811
|
+
this.isPaused = true;
|
|
812
|
+
this.dispatchEvent(new Event("paused"));
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
this.isPaused = false;
|
|
816
|
+
this.dispatchEvent(new Event(exitReason));
|
|
817
|
+
}
|
|
760
818
|
}
|
|
761
819
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
762
820
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -893,24 +951,20 @@ export class Midy {
|
|
|
893
951
|
return;
|
|
894
952
|
this.isStopping = true;
|
|
895
953
|
await this.playPromise;
|
|
896
|
-
this.isStopping = false;
|
|
897
954
|
}
|
|
898
955
|
async pause() {
|
|
899
956
|
if (!this.isPlaying || this.isPaused)
|
|
900
957
|
return;
|
|
901
958
|
const now = this.audioContext.currentTime;
|
|
902
|
-
this.resumeTime = now
|
|
959
|
+
this.resumeTime = now + this.resumeTime - this.startTime;
|
|
903
960
|
this.isPausing = true;
|
|
904
961
|
await this.playPromise;
|
|
905
|
-
this.isPausing = false;
|
|
906
|
-
this.isPaused = true;
|
|
907
962
|
}
|
|
908
963
|
async resume() {
|
|
909
964
|
if (!this.isPaused)
|
|
910
965
|
return;
|
|
911
966
|
this.playPromise = this.playNotes();
|
|
912
967
|
await this.playPromise;
|
|
913
|
-
this.isPaused = false;
|
|
914
968
|
}
|
|
915
969
|
seekTo(second) {
|
|
916
970
|
this.resumeTime = second;
|
|
@@ -933,19 +987,23 @@ export class Midy {
|
|
|
933
987
|
const now = this.audioContext.currentTime;
|
|
934
988
|
return now + this.resumeTime - this.startTime;
|
|
935
989
|
}
|
|
936
|
-
processScheduledNotes(channel, callback) {
|
|
990
|
+
async processScheduledNotes(channel, callback) {
|
|
937
991
|
const scheduledNotes = channel.scheduledNotes;
|
|
992
|
+
const tasks = [];
|
|
938
993
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
939
994
|
const note = scheduledNotes[i];
|
|
940
995
|
if (!note)
|
|
941
996
|
continue;
|
|
942
997
|
if (note.ending)
|
|
943
998
|
continue;
|
|
944
|
-
callback(note);
|
|
999
|
+
const task = note.ready.then(() => callback(note));
|
|
1000
|
+
tasks.push(task);
|
|
945
1001
|
}
|
|
1002
|
+
await Promise.all(tasks);
|
|
946
1003
|
}
|
|
947
|
-
processActiveNotes(channel, scheduleTime, callback) {
|
|
1004
|
+
async processActiveNotes(channel, scheduleTime, callback) {
|
|
948
1005
|
const scheduledNotes = channel.scheduledNotes;
|
|
1006
|
+
const tasks = [];
|
|
949
1007
|
for (let i = channel.scheduleIndex; i < scheduledNotes.length; i++) {
|
|
950
1008
|
const note = scheduledNotes[i];
|
|
951
1009
|
if (!note)
|
|
@@ -954,8 +1012,10 @@ export class Midy {
|
|
|
954
1012
|
continue;
|
|
955
1013
|
if (scheduleTime < note.startTime)
|
|
956
1014
|
break;
|
|
957
|
-
callback(note);
|
|
1015
|
+
const task = note.ready.then(() => callback(note));
|
|
1016
|
+
tasks.push(task);
|
|
958
1017
|
}
|
|
1018
|
+
await Promise.all(tasks);
|
|
959
1019
|
}
|
|
960
1020
|
createConvolutionReverbImpulse(audioContext, decay, preDecay) {
|
|
961
1021
|
const sampleRate = audioContext.sampleRate;
|
|
@@ -1143,6 +1203,13 @@ export class Midy {
|
|
|
1143
1203
|
const noteDetune = this.calcNoteDetune(channel, note);
|
|
1144
1204
|
const pitchControl = this.getPitchControl(channel, note);
|
|
1145
1205
|
const detune = channel.detune + noteDetune + pitchControl;
|
|
1206
|
+
if (channel.portamentoControl) {
|
|
1207
|
+
const state = channel.state;
|
|
1208
|
+
const portamentoNoteNumber = Math.ceil(state.portamentoNoteNumber * 127);
|
|
1209
|
+
note.portamentoNoteNumber = portamentoNoteNumber;
|
|
1210
|
+
channel.portamentoControl = false;
|
|
1211
|
+
state.portamentoNoteNumber = 0;
|
|
1212
|
+
}
|
|
1146
1213
|
if (this.isPortamento(channel, note)) {
|
|
1147
1214
|
const startTime = note.startTime;
|
|
1148
1215
|
const deltaCent = (note.noteNumber - note.portamentoNoteNumber) * 100;
|
|
@@ -1159,8 +1226,10 @@ export class Midy {
|
|
|
1159
1226
|
}
|
|
1160
1227
|
}
|
|
1161
1228
|
getPortamentoTime(channel, note) {
|
|
1229
|
+
const { portamentoTimeMSB, portamentoTimeLSB } = channel.state;
|
|
1230
|
+
const portamentoTime = portamentoTimeMSB + portamentoTimeLSB / 128;
|
|
1162
1231
|
const deltaSemitone = Math.abs(note.noteNumber - note.portamentoNoteNumber);
|
|
1163
|
-
const value = Math.ceil(
|
|
1232
|
+
const value = Math.ceil(portamentoTime * 128);
|
|
1164
1233
|
return deltaSemitone / this.getPitchIncrementSpeed(value) / 10;
|
|
1165
1234
|
}
|
|
1166
1235
|
getPitchIncrementSpeed(value) {
|
|
@@ -1424,7 +1493,7 @@ export class Midy {
|
|
|
1424
1493
|
if (0 < state.vibratoDepth) {
|
|
1425
1494
|
this.startVibrato(channel, note, now);
|
|
1426
1495
|
}
|
|
1427
|
-
if (0 < state.
|
|
1496
|
+
if (0 < state.modulationDepthMSB + state.modulationDepthLSB) {
|
|
1428
1497
|
this.startModulation(channel, note, now);
|
|
1429
1498
|
}
|
|
1430
1499
|
if (channel.mono && channel.currentBufferSource) {
|
|
@@ -1435,7 +1504,13 @@ export class Midy {
|
|
|
1435
1504
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1436
1505
|
this.setChorusSend(channel, note, now);
|
|
1437
1506
|
this.setReverbSend(channel, note, now);
|
|
1438
|
-
|
|
1507
|
+
if (voiceParams.sample.type === "compressed") {
|
|
1508
|
+
const offset = voiceParams.start / audioBuffer.sampleRate;
|
|
1509
|
+
note.bufferSource.start(startTime, offset);
|
|
1510
|
+
}
|
|
1511
|
+
else {
|
|
1512
|
+
note.bufferSource.start(startTime);
|
|
1513
|
+
}
|
|
1439
1514
|
return note;
|
|
1440
1515
|
}
|
|
1441
1516
|
handleExclusiveClass(note, channelNumber, startTime) {
|
|
@@ -1520,11 +1595,7 @@ export class Midy {
|
|
|
1520
1595
|
return;
|
|
1521
1596
|
await this.setNoteAudioNode(channel, note, realtime);
|
|
1522
1597
|
this.setNoteRouting(channelNumber, note, startTime);
|
|
1523
|
-
note.
|
|
1524
|
-
const off = note.offEvent;
|
|
1525
|
-
if (off) {
|
|
1526
|
-
this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
|
|
1527
|
-
}
|
|
1598
|
+
note.resolveReady();
|
|
1528
1599
|
}
|
|
1529
1600
|
disconnectNote(note) {
|
|
1530
1601
|
note.bufferSource.disconnect();
|
|
@@ -1569,7 +1640,7 @@ export class Midy {
|
|
|
1569
1640
|
}, stopTime);
|
|
1570
1641
|
});
|
|
1571
1642
|
}
|
|
1572
|
-
noteOff(channelNumber, noteNumber,
|
|
1643
|
+
noteOff(channelNumber, noteNumber, _velocity, endTime, force) {
|
|
1573
1644
|
const channel = this.channels[channelNumber];
|
|
1574
1645
|
const state = channel.state;
|
|
1575
1646
|
if (!force) {
|
|
@@ -1588,13 +1659,13 @@ export class Midy {
|
|
|
1588
1659
|
if (index < 0)
|
|
1589
1660
|
return;
|
|
1590
1661
|
const note = channel.scheduledNotes[index];
|
|
1591
|
-
if (note.pending) {
|
|
1592
|
-
note.offEvent = { velocity, startTime: endTime };
|
|
1593
|
-
return;
|
|
1594
|
-
}
|
|
1595
1662
|
note.ending = true;
|
|
1596
1663
|
this.setNoteIndex(channel, index);
|
|
1597
|
-
|
|
1664
|
+
const promise = note.ready.then(() => {
|
|
1665
|
+
return this.releaseNote(channel, note, endTime);
|
|
1666
|
+
});
|
|
1667
|
+
this.notePromises.push(promise);
|
|
1668
|
+
return promise;
|
|
1598
1669
|
}
|
|
1599
1670
|
setNoteIndex(channel, index) {
|
|
1600
1671
|
let allEnds = true;
|
|
@@ -1693,7 +1764,7 @@ export class Midy {
|
|
|
1693
1764
|
this.setEffects(channel, note, table, scheduleTime);
|
|
1694
1765
|
}
|
|
1695
1766
|
});
|
|
1696
|
-
this.applyVoiceParams(channel, 10);
|
|
1767
|
+
this.applyVoiceParams(channel, 10, scheduleTime);
|
|
1697
1768
|
}
|
|
1698
1769
|
setProgramChange(channelNumber, programNumber, _scheduleTime) {
|
|
1699
1770
|
const channel = this.channels[channelNumber];
|
|
@@ -1726,7 +1797,7 @@ export class Midy {
|
|
|
1726
1797
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1727
1798
|
this.setEffects(channel, note, table, scheduleTime);
|
|
1728
1799
|
});
|
|
1729
|
-
this.applyVoiceParams(channel, 13);
|
|
1800
|
+
this.applyVoiceParams(channel, 13, scheduleTime);
|
|
1730
1801
|
}
|
|
1731
1802
|
handlePitchBendMessage(channelNumber, lsb, msb, scheduleTime) {
|
|
1732
1803
|
const pitchBend = msb * 128 + lsb;
|
|
@@ -1736,7 +1807,8 @@ export class Midy {
|
|
|
1736
1807
|
const channel = this.channels[channelNumber];
|
|
1737
1808
|
if (channel.isDrum)
|
|
1738
1809
|
return;
|
|
1739
|
-
|
|
1810
|
+
if (!(0 <= scheduleTime))
|
|
1811
|
+
scheduleTime = this.audioContext.currentTime;
|
|
1740
1812
|
const state = channel.state;
|
|
1741
1813
|
const prev = state.pitchWheel * 2 - 1;
|
|
1742
1814
|
const next = (value - 8192) / 8192;
|
|
@@ -1747,9 +1819,11 @@ export class Midy {
|
|
|
1747
1819
|
}
|
|
1748
1820
|
setModLfoToPitch(channel, note, scheduleTime) {
|
|
1749
1821
|
if (note.modulationDepth) {
|
|
1822
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1823
|
+
const modulationDepth = modulationDepthMSB + modulationDepthLSB / 128;
|
|
1750
1824
|
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1751
1825
|
this.getLFOPitchDepth(channel, note);
|
|
1752
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1826
|
+
const baseDepth = Math.abs(modLfoToPitch) + modulationDepth;
|
|
1753
1827
|
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1754
1828
|
note.modulationDepth.gain
|
|
1755
1829
|
.cancelScheduledValues(scheduleTime)
|
|
@@ -1882,7 +1956,8 @@ export class Midy {
|
|
|
1882
1956
|
createVoiceParamsHandlers() {
|
|
1883
1957
|
return {
|
|
1884
1958
|
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1885
|
-
|
|
1959
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1960
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1886
1961
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1887
1962
|
}
|
|
1888
1963
|
},
|
|
@@ -1892,12 +1967,14 @@ export class Midy {
|
|
|
1892
1967
|
}
|
|
1893
1968
|
},
|
|
1894
1969
|
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1895
|
-
|
|
1970
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1971
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1896
1972
|
this.setModLfoToFilterFc(channel, note, scheduleTime);
|
|
1897
1973
|
}
|
|
1898
1974
|
},
|
|
1899
1975
|
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1900
|
-
|
|
1976
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1977
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1901
1978
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
1902
1979
|
}
|
|
1903
1980
|
},
|
|
@@ -1908,12 +1985,14 @@ export class Midy {
|
|
|
1908
1985
|
this.setReverbSend(channel, note, scheduleTime);
|
|
1909
1986
|
},
|
|
1910
1987
|
delayModLFO: (_channel, note, _scheduleTime) => {
|
|
1911
|
-
|
|
1988
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1989
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1912
1990
|
this.setDelayModLFO(note);
|
|
1913
1991
|
}
|
|
1914
1992
|
},
|
|
1915
1993
|
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1916
|
-
|
|
1994
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
1995
|
+
if (0 < modulationDepthMSB + modulationDepthLSB) {
|
|
1917
1996
|
this.setFreqModLFO(note, scheduleTime);
|
|
1918
1997
|
}
|
|
1919
1998
|
},
|
|
@@ -1982,7 +2061,12 @@ export class Midy {
|
|
|
1982
2061
|
handlers[10] = this.setPan;
|
|
1983
2062
|
handlers[11] = this.setExpression;
|
|
1984
2063
|
handlers[32] = this.setBankLSB;
|
|
2064
|
+
handlers[33] = this.setModulationDepth;
|
|
2065
|
+
handlers[37] = this.setPortamentoTime;
|
|
1985
2066
|
handlers[38] = this.dataEntryLSB;
|
|
2067
|
+
handlers[39] = this.setVolume;
|
|
2068
|
+
handlers[42] = this.setPan;
|
|
2069
|
+
handlers[43] = this.setExpression;
|
|
1986
2070
|
handlers[64] = this.setSustainPedal;
|
|
1987
2071
|
handlers[65] = this.setPortamento;
|
|
1988
2072
|
handlers[66] = this.setSostenutoPedal;
|
|
@@ -1995,12 +2079,14 @@ export class Midy {
|
|
|
1995
2079
|
handlers[76] = this.setVibratoRate;
|
|
1996
2080
|
handlers[77] = this.setVibratoDepth;
|
|
1997
2081
|
handlers[78] = this.setVibratoDelay;
|
|
2082
|
+
handlers[84] = this.setPortamentoNoteNumber;
|
|
1998
2083
|
handlers[91] = this.setReverbSendLevel;
|
|
1999
2084
|
handlers[93] = this.setChorusSendLevel;
|
|
2000
2085
|
handlers[96] = this.dataIncrement;
|
|
2001
2086
|
handlers[97] = this.dataDecrement;
|
|
2002
2087
|
handlers[100] = this.setRPNLSB;
|
|
2003
2088
|
handlers[101] = this.setRPNMSB;
|
|
2089
|
+
handlers[111] = this.setRPGMakerLoop;
|
|
2004
2090
|
handlers[120] = this.allSoundOff;
|
|
2005
2091
|
handlers[121] = this.resetAllControllers;
|
|
2006
2092
|
handlers[123] = this.allNotesOff;
|
|
@@ -2026,7 +2112,9 @@ export class Midy {
|
|
|
2026
2112
|
this.channels[channelNumber].bankMSB = msb;
|
|
2027
2113
|
}
|
|
2028
2114
|
updateModulation(channel, scheduleTime) {
|
|
2029
|
-
const
|
|
2115
|
+
const { modulationDepthMSB, modulationDepthLSB } = channel.state;
|
|
2116
|
+
const modulationDepth = modulationDepthMSB + modulationDepthLSB / 128;
|
|
2117
|
+
const depth = modulationDepth * channel.modulationDepthRange;
|
|
2030
2118
|
this.processScheduledNotes(channel, (note) => {
|
|
2031
2119
|
if (note.modulationDepth) {
|
|
2032
2120
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
@@ -2036,12 +2124,16 @@ export class Midy {
|
|
|
2036
2124
|
}
|
|
2037
2125
|
});
|
|
2038
2126
|
}
|
|
2039
|
-
setModulationDepth(channelNumber,
|
|
2127
|
+
setModulationDepth(channelNumber, value, scheduleTime) {
|
|
2040
2128
|
const channel = this.channels[channelNumber];
|
|
2041
2129
|
if (channel.isDrum)
|
|
2042
2130
|
return;
|
|
2043
|
-
|
|
2044
|
-
|
|
2131
|
+
if (!(0 <= scheduleTime))
|
|
2132
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2133
|
+
const state = channel.state;
|
|
2134
|
+
const intPart = Math.trunc(value);
|
|
2135
|
+
state.modulationDepthMSB = intPart / 127;
|
|
2136
|
+
state.modulationDepthLSB = value - intPart;
|
|
2045
2137
|
this.updateModulation(channel, scheduleTime);
|
|
2046
2138
|
}
|
|
2047
2139
|
updatePortamento(channel, scheduleTime) {
|
|
@@ -2062,18 +2154,26 @@ export class Midy {
|
|
|
2062
2154
|
}
|
|
2063
2155
|
});
|
|
2064
2156
|
}
|
|
2065
|
-
setPortamentoTime(channelNumber,
|
|
2157
|
+
setPortamentoTime(channelNumber, value, scheduleTime) {
|
|
2158
|
+
if (!(0 <= scheduleTime))
|
|
2159
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2066
2160
|
const channel = this.channels[channelNumber];
|
|
2067
|
-
|
|
2068
|
-
|
|
2161
|
+
const state = channel.state;
|
|
2162
|
+
const intPart = Math.trunc(value);
|
|
2163
|
+
state.portamentoTimeMSB = intPart / 127;
|
|
2164
|
+
state.portamentoTimeLSB = value - 127;
|
|
2069
2165
|
if (channel.isDrum)
|
|
2070
2166
|
return;
|
|
2071
2167
|
this.updatePortamento(channel, scheduleTime);
|
|
2072
2168
|
}
|
|
2073
|
-
setVolume(channelNumber,
|
|
2074
|
-
|
|
2169
|
+
setVolume(channelNumber, value, scheduleTime) {
|
|
2170
|
+
if (!(0 <= scheduleTime))
|
|
2171
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2075
2172
|
const channel = this.channels[channelNumber];
|
|
2076
|
-
|
|
2173
|
+
const state = channel.state;
|
|
2174
|
+
const intPart = Math.trunc(value);
|
|
2175
|
+
state.volumeMSB = intPart / 127;
|
|
2176
|
+
state.volumeLSB = value - intPart;
|
|
2077
2177
|
if (channel.isDrum) {
|
|
2078
2178
|
for (let i = 0; i < 128; i++) {
|
|
2079
2179
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2084,16 +2184,20 @@ export class Midy {
|
|
|
2084
2184
|
}
|
|
2085
2185
|
}
|
|
2086
2186
|
panToGain(pan) {
|
|
2087
|
-
const theta = Math.PI / 2 * Math.max(
|
|
2187
|
+
const theta = Math.PI / 2 * Math.max(pan * 127 - 1) / 126;
|
|
2088
2188
|
return {
|
|
2089
2189
|
gainLeft: Math.cos(theta),
|
|
2090
2190
|
gainRight: Math.sin(theta),
|
|
2091
2191
|
};
|
|
2092
2192
|
}
|
|
2093
|
-
setPan(channelNumber,
|
|
2094
|
-
|
|
2193
|
+
setPan(channelNumber, value, scheduleTime) {
|
|
2194
|
+
if (!(0 <= scheduleTime))
|
|
2195
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2095
2196
|
const channel = this.channels[channelNumber];
|
|
2096
|
-
|
|
2197
|
+
const state = channel.state;
|
|
2198
|
+
const intPart = Math.trunc(value);
|
|
2199
|
+
state.panMSB = intPart / 127;
|
|
2200
|
+
state.panLSB = value - intPart;
|
|
2097
2201
|
if (channel.isDrum) {
|
|
2098
2202
|
for (let i = 0; i < 128; i++) {
|
|
2099
2203
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2103,10 +2207,14 @@ export class Midy {
|
|
|
2103
2207
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2104
2208
|
}
|
|
2105
2209
|
}
|
|
2106
|
-
setExpression(channelNumber,
|
|
2107
|
-
|
|
2210
|
+
setExpression(channelNumber, value, scheduleTime) {
|
|
2211
|
+
if (!(0 <= scheduleTime))
|
|
2212
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2108
2213
|
const channel = this.channels[channelNumber];
|
|
2109
|
-
|
|
2214
|
+
const state = channel.state;
|
|
2215
|
+
const intPart = Math.trunc(value);
|
|
2216
|
+
state.expressionMSB = intPart / 127;
|
|
2217
|
+
state.expressionLSB = value - intPart;
|
|
2110
2218
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2111
2219
|
}
|
|
2112
2220
|
setBankLSB(channelNumber, lsb) {
|
|
@@ -2117,43 +2225,49 @@ export class Midy {
|
|
|
2117
2225
|
this.handleRPN(channelNumber, 0, scheduleTime);
|
|
2118
2226
|
}
|
|
2119
2227
|
updateChannelVolume(channel, scheduleTime) {
|
|
2120
|
-
const
|
|
2121
|
-
const volume =
|
|
2122
|
-
const
|
|
2228
|
+
const { expressionMSB, expressionLSB, volumeMSB, volumeLSB, panMSB, panLSB, } = channel.state;
|
|
2229
|
+
const volume = volumeMSB + volumeLSB / 128;
|
|
2230
|
+
const expression = expressionMSB + expressionLSB / 128;
|
|
2231
|
+
const pan = panMSB + panLSB / 128;
|
|
2232
|
+
const gain = volume * expression;
|
|
2233
|
+
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2123
2234
|
channel.gainL.gain
|
|
2124
2235
|
.cancelScheduledValues(scheduleTime)
|
|
2125
|
-
.setValueAtTime(
|
|
2236
|
+
.setValueAtTime(gain * gainLeft, scheduleTime);
|
|
2126
2237
|
channel.gainR.gain
|
|
2127
2238
|
.cancelScheduledValues(scheduleTime)
|
|
2128
|
-
.setValueAtTime(
|
|
2239
|
+
.setValueAtTime(gain * gainRight, scheduleTime);
|
|
2129
2240
|
}
|
|
2130
2241
|
updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
|
|
2131
2242
|
const gainL = channel.keyBasedGainLs[keyNumber];
|
|
2132
2243
|
if (!gainL)
|
|
2133
2244
|
return;
|
|
2134
2245
|
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
2135
|
-
const
|
|
2136
|
-
const
|
|
2137
|
-
const
|
|
2246
|
+
const { expressionMSB, expressionLSB, volumeMSB, volumeLSB, panMSB, panLSB, } = channel.state;
|
|
2247
|
+
const volume = volumeMSB + volumeLSB / 128;
|
|
2248
|
+
const expression = expressionMSB + expressionLSB / 128;
|
|
2249
|
+
const defaultGain = volume * expression;
|
|
2250
|
+
const defaultPan = panMSB + panLSB / 128;
|
|
2138
2251
|
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
2139
|
-
const
|
|
2140
|
-
?
|
|
2141
|
-
:
|
|
2252
|
+
const gain = (0 <= keyBasedVolume)
|
|
2253
|
+
? defaultGain * keyBasedVolume / 64
|
|
2254
|
+
: defaultGain;
|
|
2142
2255
|
const keyBasedPan = this.getKeyBasedValue(channel, keyNumber, 10);
|
|
2143
2256
|
const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
|
|
2144
2257
|
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2145
2258
|
gainL.gain
|
|
2146
2259
|
.cancelScheduledValues(scheduleTime)
|
|
2147
|
-
.setValueAtTime(
|
|
2260
|
+
.setValueAtTime(gain * gainLeft, scheduleTime);
|
|
2148
2261
|
gainR.gain
|
|
2149
2262
|
.cancelScheduledValues(scheduleTime)
|
|
2150
|
-
.setValueAtTime(
|
|
2263
|
+
.setValueAtTime(gain * gainRight, scheduleTime);
|
|
2151
2264
|
}
|
|
2152
2265
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
2153
2266
|
const channel = this.channels[channelNumber];
|
|
2154
2267
|
if (channel.isDrum)
|
|
2155
2268
|
return;
|
|
2156
|
-
|
|
2269
|
+
if (!(0 <= scheduleTime))
|
|
2270
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2157
2271
|
channel.state.sustainPedal = value / 127;
|
|
2158
2272
|
if (64 <= value) {
|
|
2159
2273
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -2171,7 +2285,8 @@ export class Midy {
|
|
|
2171
2285
|
const channel = this.channels[channelNumber];
|
|
2172
2286
|
if (channel.isDrum)
|
|
2173
2287
|
return;
|
|
2174
|
-
|
|
2288
|
+
if (!(0 <= scheduleTime))
|
|
2289
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2175
2290
|
channel.state.portamento = value / 127;
|
|
2176
2291
|
this.updatePortamento(channel, scheduleTime);
|
|
2177
2292
|
}
|
|
@@ -2179,7 +2294,8 @@ export class Midy {
|
|
|
2179
2294
|
const channel = this.channels[channelNumber];
|
|
2180
2295
|
if (channel.isDrum)
|
|
2181
2296
|
return;
|
|
2182
|
-
|
|
2297
|
+
if (!(0 <= scheduleTime))
|
|
2298
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2183
2299
|
channel.state.sostenutoPedal = value / 127;
|
|
2184
2300
|
if (64 <= value) {
|
|
2185
2301
|
const sostenutoNotes = [];
|
|
@@ -2200,7 +2316,8 @@ export class Midy {
|
|
|
2200
2316
|
if (channel.isDrum)
|
|
2201
2317
|
return;
|
|
2202
2318
|
const state = channel.state;
|
|
2203
|
-
|
|
2319
|
+
if (!(0 <= scheduleTime))
|
|
2320
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2204
2321
|
state.softPedal = softPedal / 127;
|
|
2205
2322
|
this.processScheduledNotes(channel, (note) => {
|
|
2206
2323
|
if (this.isPortamento(channel, note)) {
|
|
@@ -2217,11 +2334,12 @@ export class Midy {
|
|
|
2217
2334
|
const channel = this.channels[channelNumber];
|
|
2218
2335
|
if (channel.isDrum)
|
|
2219
2336
|
return;
|
|
2220
|
-
|
|
2337
|
+
if (!(0 <= scheduleTime))
|
|
2338
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2221
2339
|
const state = channel.state;
|
|
2222
2340
|
state.filterResonance = ccValue / 127;
|
|
2223
|
-
const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
|
|
2224
2341
|
this.processScheduledNotes(channel, (note) => {
|
|
2342
|
+
const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
|
|
2225
2343
|
const Q = note.voiceParams.initialFilterQ / 5 * filterResonance;
|
|
2226
2344
|
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
2227
2345
|
});
|
|
@@ -2238,14 +2356,16 @@ export class Midy {
|
|
|
2238
2356
|
const channel = this.channels[channelNumber];
|
|
2239
2357
|
if (channel.isDrum)
|
|
2240
2358
|
return;
|
|
2241
|
-
|
|
2359
|
+
if (!(0 <= scheduleTime))
|
|
2360
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2242
2361
|
channel.state.releaseTime = releaseTime / 127;
|
|
2243
2362
|
}
|
|
2244
2363
|
setAttackTime(channelNumber, attackTime, scheduleTime) {
|
|
2245
2364
|
const channel = this.channels[channelNumber];
|
|
2246
2365
|
if (channel.isDrum)
|
|
2247
2366
|
return;
|
|
2248
|
-
|
|
2367
|
+
if (!(0 <= scheduleTime))
|
|
2368
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2249
2369
|
channel.state.attackTime = attackTime / 127;
|
|
2250
2370
|
this.processScheduledNotes(channel, (note) => {
|
|
2251
2371
|
if (scheduleTime < note.startTime) {
|
|
@@ -2258,7 +2378,8 @@ export class Midy {
|
|
|
2258
2378
|
if (channel.isDrum)
|
|
2259
2379
|
return;
|
|
2260
2380
|
const state = channel.state;
|
|
2261
|
-
|
|
2381
|
+
if (!(0 <= scheduleTime))
|
|
2382
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2262
2383
|
state.brightness = brightness / 127;
|
|
2263
2384
|
this.processScheduledNotes(channel, (note) => {
|
|
2264
2385
|
if (this.isPortamento(channel, note)) {
|
|
@@ -2273,7 +2394,8 @@ export class Midy {
|
|
|
2273
2394
|
const channel = this.channels[channelNumber];
|
|
2274
2395
|
if (channel.isDrum)
|
|
2275
2396
|
return;
|
|
2276
|
-
|
|
2397
|
+
if (!(0 <= scheduleTime))
|
|
2398
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2277
2399
|
channel.state.decayTime = dacayTime / 127;
|
|
2278
2400
|
this.processScheduledNotes(channel, (note) => {
|
|
2279
2401
|
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
@@ -2283,7 +2405,8 @@ export class Midy {
|
|
|
2283
2405
|
const channel = this.channels[channelNumber];
|
|
2284
2406
|
if (channel.isDrum)
|
|
2285
2407
|
return;
|
|
2286
|
-
|
|
2408
|
+
if (!(0 <= scheduleTime))
|
|
2409
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2287
2410
|
channel.state.vibratoRate = vibratoRate / 127;
|
|
2288
2411
|
if (channel.vibratoDepth <= 0)
|
|
2289
2412
|
return;
|
|
@@ -2295,7 +2418,8 @@ export class Midy {
|
|
|
2295
2418
|
const channel = this.channels[channelNumber];
|
|
2296
2419
|
if (channel.isDrum)
|
|
2297
2420
|
return;
|
|
2298
|
-
|
|
2421
|
+
if (!(0 <= scheduleTime))
|
|
2422
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2299
2423
|
const prev = channel.state.vibratoDepth;
|
|
2300
2424
|
channel.state.vibratoDepth = vibratoDepth / 127;
|
|
2301
2425
|
if (0 < prev) {
|
|
@@ -2313,7 +2437,8 @@ export class Midy {
|
|
|
2313
2437
|
const channel = this.channels[channelNumber];
|
|
2314
2438
|
if (channel.isDrum)
|
|
2315
2439
|
return;
|
|
2316
|
-
|
|
2440
|
+
if (!(0 <= scheduleTime))
|
|
2441
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2317
2442
|
channel.state.vibratoDelay = vibratoDelay / 127;
|
|
2318
2443
|
if (0 < channel.state.vibratoDepth) {
|
|
2319
2444
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -2321,8 +2446,16 @@ export class Midy {
|
|
|
2321
2446
|
});
|
|
2322
2447
|
}
|
|
2323
2448
|
}
|
|
2449
|
+
setPortamentoNoteNumber(channelNumber, value, scheduleTime) {
|
|
2450
|
+
if (!(0 <= scheduleTime))
|
|
2451
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2452
|
+
const channel = this.channels[channelNumber];
|
|
2453
|
+
channel.portamentoControl = true;
|
|
2454
|
+
channel.state.portamentoNoteNumber = value / 127;
|
|
2455
|
+
}
|
|
2324
2456
|
setReverbSendLevel(channelNumber, reverbSendLevel, scheduleTime) {
|
|
2325
|
-
|
|
2457
|
+
if (!(0 <= scheduleTime))
|
|
2458
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2326
2459
|
const channel = this.channels[channelNumber];
|
|
2327
2460
|
const state = channel.state;
|
|
2328
2461
|
state.reverbSendLevel = reverbSendLevel / 127;
|
|
@@ -2331,7 +2464,8 @@ export class Midy {
|
|
|
2331
2464
|
});
|
|
2332
2465
|
}
|
|
2333
2466
|
setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
|
|
2334
|
-
|
|
2467
|
+
if (!(0 <= scheduleTime))
|
|
2468
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2335
2469
|
const channel = this.channels[channelNumber];
|
|
2336
2470
|
const state = channel.state;
|
|
2337
2471
|
state.chorusSendLevel = chorusSendLevel / 127;
|
|
@@ -2391,12 +2525,14 @@ export class Midy {
|
|
|
2391
2525
|
}
|
|
2392
2526
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
|
|
2393
2527
|
dataIncrement(channelNumber, scheduleTime) {
|
|
2394
|
-
|
|
2528
|
+
if (!(0 <= scheduleTime))
|
|
2529
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2395
2530
|
this.handleRPN(channelNumber, 1, scheduleTime);
|
|
2396
2531
|
}
|
|
2397
2532
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
|
|
2398
2533
|
dataDecrement(channelNumber, scheduleTime) {
|
|
2399
|
-
|
|
2534
|
+
if (!(0 <= scheduleTime))
|
|
2535
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2400
2536
|
this.handleRPN(channelNumber, -1, scheduleTime);
|
|
2401
2537
|
}
|
|
2402
2538
|
setRPNMSB(channelNumber, value) {
|
|
@@ -2419,7 +2555,8 @@ export class Midy {
|
|
|
2419
2555
|
const channel = this.channels[channelNumber];
|
|
2420
2556
|
if (channel.isDrum)
|
|
2421
2557
|
return;
|
|
2422
|
-
|
|
2558
|
+
if (!(0 <= scheduleTime))
|
|
2559
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2423
2560
|
const state = channel.state;
|
|
2424
2561
|
const prev = state.pitchWheelSensitivity;
|
|
2425
2562
|
const next = value / 12800;
|
|
@@ -2439,7 +2576,8 @@ export class Midy {
|
|
|
2439
2576
|
const channel = this.channels[channelNumber];
|
|
2440
2577
|
if (channel.isDrum)
|
|
2441
2578
|
return;
|
|
2442
|
-
|
|
2579
|
+
if (!(0 <= scheduleTime))
|
|
2580
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2443
2581
|
const prev = channel.fineTuning;
|
|
2444
2582
|
const next = value;
|
|
2445
2583
|
channel.fineTuning = next;
|
|
@@ -2456,7 +2594,8 @@ export class Midy {
|
|
|
2456
2594
|
const channel = this.channels[channelNumber];
|
|
2457
2595
|
if (channel.isDrum)
|
|
2458
2596
|
return;
|
|
2459
|
-
|
|
2597
|
+
if (!(0 <= scheduleTime))
|
|
2598
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2460
2599
|
const prev = channel.coarseTuning;
|
|
2461
2600
|
const next = value;
|
|
2462
2601
|
channel.coarseTuning = next;
|
|
@@ -2473,12 +2612,18 @@ export class Midy {
|
|
|
2473
2612
|
const channel = this.channels[channelNumber];
|
|
2474
2613
|
if (channel.isDrum)
|
|
2475
2614
|
return;
|
|
2476
|
-
|
|
2615
|
+
if (!(0 <= scheduleTime))
|
|
2616
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2477
2617
|
channel.modulationDepthRange = value;
|
|
2478
2618
|
this.updateModulation(channel, scheduleTime);
|
|
2479
2619
|
}
|
|
2480
|
-
|
|
2620
|
+
setRPGMakerLoop(_channelNumber, _value, scheduleTime) {
|
|
2481
2621
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2622
|
+
this.loopStart = scheduleTime + this.resumeTime - this.startTime;
|
|
2623
|
+
}
|
|
2624
|
+
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
2625
|
+
if (!(0 <= scheduleTime))
|
|
2626
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2482
2627
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
2483
2628
|
}
|
|
2484
2629
|
resetChannelStates(channelNumber) {
|
|
@@ -2508,8 +2653,10 @@ export class Midy {
|
|
|
2508
2653
|
"polyphonicKeyPressure",
|
|
2509
2654
|
"channelPressure",
|
|
2510
2655
|
"pitchWheel",
|
|
2511
|
-
"
|
|
2512
|
-
"
|
|
2656
|
+
"expressionMSB",
|
|
2657
|
+
"expressionLSB",
|
|
2658
|
+
"modulationDepthMSB",
|
|
2659
|
+
"modulationDepthLSB",
|
|
2513
2660
|
"sustainPedal",
|
|
2514
2661
|
"portamento",
|
|
2515
2662
|
"sostenutoPedal",
|
|
@@ -2538,7 +2685,8 @@ export class Midy {
|
|
|
2538
2685
|
}
|
|
2539
2686
|
}
|
|
2540
2687
|
allNotesOff(channelNumber, _value, scheduleTime) {
|
|
2541
|
-
|
|
2688
|
+
if (!(0 <= scheduleTime))
|
|
2689
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2542
2690
|
return this.stopActiveNotes(channelNumber, 0, false, scheduleTime);
|
|
2543
2691
|
}
|
|
2544
2692
|
omniOff(channelNumber, value, scheduleTime) {
|
|
@@ -2590,7 +2738,8 @@ export class Midy {
|
|
|
2590
2738
|
}
|
|
2591
2739
|
}
|
|
2592
2740
|
GM1SystemOn(scheduleTime) {
|
|
2593
|
-
|
|
2741
|
+
if (!(0 <= scheduleTime))
|
|
2742
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2594
2743
|
this.mode = "GM1";
|
|
2595
2744
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2596
2745
|
this.allSoundOff(i, 0, scheduleTime);
|
|
@@ -2603,7 +2752,8 @@ export class Midy {
|
|
|
2603
2752
|
this.channels[9].isDrum = true;
|
|
2604
2753
|
}
|
|
2605
2754
|
GM2SystemOn(scheduleTime) {
|
|
2606
|
-
|
|
2755
|
+
if (!(0 <= scheduleTime))
|
|
2756
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2607
2757
|
this.mode = "GM2";
|
|
2608
2758
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2609
2759
|
this.allSoundOff(i, 0, scheduleTime);
|
|
@@ -2671,7 +2821,8 @@ export class Midy {
|
|
|
2671
2821
|
this.setMasterVolume(volume, scheduleTime);
|
|
2672
2822
|
}
|
|
2673
2823
|
setMasterVolume(value, scheduleTime) {
|
|
2674
|
-
|
|
2824
|
+
if (!(0 <= scheduleTime))
|
|
2825
|
+
scheduleTime = this.audioContext.currentTime;
|
|
2675
2826
|
this.masterVolume.gain
|
|
2676
2827
|
.cancelScheduledValues(scheduleTime)
|
|
2677
2828
|
.setValueAtTime(value * value, scheduleTime);
|
|
@@ -3218,5 +3369,6 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
3218
3369
|
modulationDepthRange: 50, // cent
|
|
3219
3370
|
fineTuning: 0, // cent
|
|
3220
3371
|
coarseTuning: 0, // cent
|
|
3372
|
+
portamentoControl: false,
|
|
3221
3373
|
}
|
|
3222
3374
|
});
|