@marmooo/midy 0.0.7 → 0.0.9
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/esm/midy-GM1.d.ts +12 -47
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +70 -53
- package/esm/midy-GM2.d.ts +52 -109
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +187 -95
- package/esm/midy-GMLite.d.ts +8 -41
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +53 -46
- package/esm/midy.d.ts +55 -134
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +191 -100
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +12 -47
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +70 -53
- package/script/midy-GM2.d.ts +52 -109
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +187 -95
- package/script/midy-GMLite.d.ts +8 -41
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +53 -46
- package/script/midy.d.ts +55 -134
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +191 -100
package/script/midy-GM2.js
CHANGED
|
@@ -54,7 +54,7 @@ class Note {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
class MidyGM2 {
|
|
57
|
-
constructor(audioContext) {
|
|
57
|
+
constructor(audioContext, options = this.defaultOptions) {
|
|
58
58
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
59
59
|
enumerable: true,
|
|
60
60
|
configurable: true,
|
|
@@ -78,13 +78,13 @@ class MidyGM2 {
|
|
|
78
78
|
configurable: true,
|
|
79
79
|
writable: true,
|
|
80
80
|
value: 0
|
|
81
|
-
});
|
|
81
|
+
}); // cb
|
|
82
82
|
Object.defineProperty(this, "masterCoarseTuning", {
|
|
83
83
|
enumerable: true,
|
|
84
84
|
configurable: true,
|
|
85
85
|
writable: true,
|
|
86
86
|
value: 0
|
|
87
|
-
});
|
|
87
|
+
}); // cb
|
|
88
88
|
Object.defineProperty(this, "mono", {
|
|
89
89
|
enumerable: true,
|
|
90
90
|
configurable: true,
|
|
@@ -187,7 +187,19 @@ class MidyGM2 {
|
|
|
187
187
|
writable: true,
|
|
188
188
|
value: []
|
|
189
189
|
});
|
|
190
|
+
Object.defineProperty(this, "defaultOptions", {
|
|
191
|
+
enumerable: true,
|
|
192
|
+
configurable: true,
|
|
193
|
+
writable: true,
|
|
194
|
+
value: {
|
|
195
|
+
reverbAlgorithm: (audioContext) => {
|
|
196
|
+
// return this.createConvolutionReverb(audioContext);
|
|
197
|
+
return this.createSchroederReverb(audioContext);
|
|
198
|
+
},
|
|
199
|
+
}
|
|
200
|
+
});
|
|
190
201
|
this.audioContext = audioContext;
|
|
202
|
+
this.options = { ...this.defaultOptions, ...options };
|
|
191
203
|
this.masterGain = new GainNode(audioContext);
|
|
192
204
|
this.masterGain.connect(audioContext.destination);
|
|
193
205
|
this.channels = this.createChannels(audioContext);
|
|
@@ -228,23 +240,18 @@ class MidyGM2 {
|
|
|
228
240
|
this.totalTime = this.calcTotalTime();
|
|
229
241
|
}
|
|
230
242
|
setChannelAudioNodes(audioContext) {
|
|
231
|
-
const { gainLeft, gainRight } = this.panToGain(
|
|
243
|
+
const { gainLeft, gainRight } = this.panToGain(this.constructor.channelSettings.pan);
|
|
232
244
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
233
245
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
234
246
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
235
247
|
gainL.connect(merger, 0, 0);
|
|
236
248
|
gainR.connect(merger, 0, 1);
|
|
237
|
-
|
|
238
|
-
const reverbEffect = this.createReverbEffect(audioContext);
|
|
249
|
+
const reverbEffect = this.options.reverbAlgorithm(audioContext);
|
|
239
250
|
const chorusEffect = this.createChorusEffect(audioContext);
|
|
240
|
-
chorusEffect.lfo.start();
|
|
241
|
-
reverbEffect.dryGain.connect(gainL);
|
|
242
|
-
reverbEffect.dryGain.connect(gainR);
|
|
243
|
-
reverbEffect.wetGain.connect(gainL);
|
|
244
|
-
reverbEffect.wetGain.connect(gainR);
|
|
245
251
|
return {
|
|
246
252
|
gainL,
|
|
247
253
|
gainR,
|
|
254
|
+
merger,
|
|
248
255
|
reverbEffect,
|
|
249
256
|
chorusEffect,
|
|
250
257
|
};
|
|
@@ -252,13 +259,13 @@ class MidyGM2 {
|
|
|
252
259
|
createChannels(audioContext) {
|
|
253
260
|
const channels = Array.from({ length: 16 }, () => {
|
|
254
261
|
return {
|
|
255
|
-
...
|
|
256
|
-
...
|
|
262
|
+
...this.constructor.channelSettings,
|
|
263
|
+
...this.constructor.effectSettings,
|
|
257
264
|
...this.setChannelAudioNodes(audioContext),
|
|
258
265
|
scheduledNotes: new Map(),
|
|
259
266
|
sostenutoNotes: new Map(),
|
|
260
267
|
channelPressure: {
|
|
261
|
-
...
|
|
268
|
+
...this.constructor.controllerDestinationSettings,
|
|
262
269
|
},
|
|
263
270
|
};
|
|
264
271
|
});
|
|
@@ -586,8 +593,12 @@ class MidyGM2 {
|
|
|
586
593
|
}
|
|
587
594
|
return noteList[0];
|
|
588
595
|
}
|
|
589
|
-
|
|
596
|
+
createConvolutionReverb(audioContext, options = {}) {
|
|
590
597
|
const { decay = 0.8, preDecay = 0, } = options;
|
|
598
|
+
const input = new GainNode(audioContext);
|
|
599
|
+
const output = new GainNode(audioContext);
|
|
600
|
+
const dryGain = new GainNode(audioContext);
|
|
601
|
+
const wetGain = new GainNode(audioContext);
|
|
591
602
|
const sampleRate = audioContext.sampleRate;
|
|
592
603
|
const length = sampleRate * decay;
|
|
593
604
|
const impulse = new AudioBuffer({
|
|
@@ -601,27 +612,82 @@ class MidyGM2 {
|
|
|
601
612
|
for (let i = 0; i < preDecayLength; i++) {
|
|
602
613
|
channelData[i] = Math.random() * 2 - 1;
|
|
603
614
|
}
|
|
615
|
+
const attenuationFactor = 1 / (sampleRate * decay);
|
|
604
616
|
for (let i = preDecayLength; i < length; i++) {
|
|
605
|
-
const attenuation = Math.exp(-(i - preDecayLength)
|
|
617
|
+
const attenuation = Math.exp(-(i - preDecayLength) * attenuationFactor);
|
|
606
618
|
channelData[i] = (Math.random() * 2 - 1) * attenuation;
|
|
607
619
|
}
|
|
608
620
|
}
|
|
609
621
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
610
622
|
buffer: impulse,
|
|
611
623
|
});
|
|
612
|
-
|
|
613
|
-
const wetGain = new GainNode(audioContext);
|
|
624
|
+
input.connect(convolverNode);
|
|
614
625
|
convolverNode.connect(wetGain);
|
|
626
|
+
wetGain.connect(output);
|
|
627
|
+
dryGain.connect(output);
|
|
615
628
|
return {
|
|
616
|
-
|
|
629
|
+
input,
|
|
630
|
+
output,
|
|
617
631
|
dryGain,
|
|
618
632
|
wetGain,
|
|
633
|
+
convolverNode,
|
|
619
634
|
};
|
|
620
635
|
}
|
|
636
|
+
createCombFilter(audioContext, input, delay, feedback) {
|
|
637
|
+
const delayNode = new DelayNode(audioContext, {
|
|
638
|
+
maxDelayTime: delay,
|
|
639
|
+
delayTime: delay,
|
|
640
|
+
});
|
|
641
|
+
const feedbackGain = new GainNode(audioContext, { gain: feedback });
|
|
642
|
+
input.connect(delayNode);
|
|
643
|
+
delayNode.connect(feedbackGain);
|
|
644
|
+
feedbackGain.connect(delayNode);
|
|
645
|
+
return delayNode;
|
|
646
|
+
}
|
|
647
|
+
createAllpassFilter(audioContext, input, delay, feedback) {
|
|
648
|
+
const delayNode = new DelayNode(audioContext, {
|
|
649
|
+
maxDelayTime: delay,
|
|
650
|
+
delayTime: delay,
|
|
651
|
+
});
|
|
652
|
+
const feedbackGain = new GainNode(audioContext, { gain: feedback });
|
|
653
|
+
const passGain = new GainNode(audioContext, { gain: 1 - feedback });
|
|
654
|
+
input.connect(delayNode);
|
|
655
|
+
delayNode.connect(feedbackGain);
|
|
656
|
+
feedbackGain.connect(delayNode);
|
|
657
|
+
delayNode.connect(passGain);
|
|
658
|
+
return passGain;
|
|
659
|
+
}
|
|
660
|
+
// https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
|
|
661
|
+
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
662
|
+
createSchroederReverb(audioContext, options = {}) {
|
|
663
|
+
const { combDelays = [0.31, 0.34, 0.37, 0.40], combFeedbacks = [0.86, 0.87, 0.88, 0.89], allpassDelays = [0.02, 0.05], allpassFeedbacks = [0.7, 0.7], mix = 0.5, } = options;
|
|
664
|
+
const input = new GainNode(audioContext);
|
|
665
|
+
const output = new GainNode(audioContext);
|
|
666
|
+
const mergerGain = new GainNode(audioContext, {
|
|
667
|
+
gain: 1 / (combDelays.length * 2),
|
|
668
|
+
});
|
|
669
|
+
const dryGain = new GainNode(audioContext, { gain: 1 - mix });
|
|
670
|
+
const wetGain = new GainNode(audioContext, { gain: mix });
|
|
671
|
+
for (let i = 0; i < combDelays.length; i++) {
|
|
672
|
+
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
673
|
+
comb.connect(mergerGain);
|
|
674
|
+
}
|
|
675
|
+
const allpasses = [];
|
|
676
|
+
for (let i = 0; i < allpassDelays.length; i++) {
|
|
677
|
+
const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
|
|
678
|
+
allpasses.push(allpass);
|
|
679
|
+
}
|
|
680
|
+
allpasses.at(-1).connect(wetGain);
|
|
681
|
+
input.connect(dryGain);
|
|
682
|
+
dryGain.connect(output);
|
|
683
|
+
wetGain.connect(output);
|
|
684
|
+
return { input, output, dryGain, wetGain };
|
|
685
|
+
}
|
|
621
686
|
createChorusEffect(audioContext, options = {}) {
|
|
622
687
|
const { chorusCount = 2, chorusRate = 0.6, chorusDepth = 0.15, delay = 0.01, variance = delay * 0.1, } = options;
|
|
623
688
|
const lfo = new OscillatorNode(audioContext, { frequency: chorusRate });
|
|
624
689
|
const lfoGain = new GainNode(audioContext, { gain: chorusDepth });
|
|
690
|
+
const output = new GainNode(audioContext);
|
|
625
691
|
const chorusGains = [];
|
|
626
692
|
const delayNodes = [];
|
|
627
693
|
const baseGain = 1 / chorusCount;
|
|
@@ -631,50 +697,47 @@ class MidyGM2 {
|
|
|
631
697
|
const delayNode = new DelayNode(audioContext, {
|
|
632
698
|
maxDelayTime: delayTime,
|
|
633
699
|
});
|
|
634
|
-
delayNodes.push(delayNode);
|
|
635
700
|
const chorusGain = new GainNode(audioContext, { gain: baseGain });
|
|
701
|
+
delayNodes.push(delayNode);
|
|
636
702
|
chorusGains.push(chorusGain);
|
|
637
|
-
lfo.connect(lfoGain);
|
|
638
703
|
lfoGain.connect(delayNode.delayTime);
|
|
639
704
|
delayNode.connect(chorusGain);
|
|
705
|
+
chorusGain.connect(output);
|
|
640
706
|
}
|
|
707
|
+
lfo.connect(lfoGain);
|
|
708
|
+
lfo.start();
|
|
641
709
|
return {
|
|
642
710
|
lfo,
|
|
643
711
|
lfoGain,
|
|
644
712
|
delayNodes,
|
|
645
713
|
chorusGains,
|
|
714
|
+
output,
|
|
646
715
|
};
|
|
647
716
|
}
|
|
648
|
-
|
|
717
|
+
connectEffects(channel, gainNode) {
|
|
718
|
+
gainNode.connect(channel.merger);
|
|
649
719
|
if (channel.reverb === 0) {
|
|
650
720
|
if (channel.chorus === 0) { // no effect
|
|
651
|
-
|
|
652
|
-
gainNode.connect(channel.gainR);
|
|
721
|
+
channel.merger.connect(this.masterGain);
|
|
653
722
|
}
|
|
654
723
|
else { // chorus
|
|
655
724
|
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
656
|
-
|
|
657
|
-
});
|
|
658
|
-
channel.chorusEffect.chorusGains.forEach((chorusGain) => {
|
|
659
|
-
chorusGain.connect(channel.gainL);
|
|
660
|
-
chorusGain.connect(channel.gainR);
|
|
725
|
+
channel.merger.connect(delayNode);
|
|
661
726
|
});
|
|
727
|
+
channel.chorusEffect.output.connect(this.masterGain);
|
|
662
728
|
}
|
|
663
729
|
}
|
|
664
730
|
else {
|
|
665
731
|
if (channel.chorus === 0) { // reverb
|
|
666
|
-
|
|
667
|
-
|
|
732
|
+
channel.merger.connect(channel.reverbEffect.input);
|
|
733
|
+
channel.reverbEffect.output.connect(this.masterGain);
|
|
668
734
|
}
|
|
669
735
|
else { // reverb + chorus
|
|
670
|
-
gainNode.connect(channel.reverbEffect.convolverNode);
|
|
671
|
-
gainNode.connect(channel.reverbEffect.dryGain);
|
|
672
736
|
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
673
|
-
|
|
674
|
-
});
|
|
675
|
-
channel.chorusEffect.chorusGains.forEach((chorusGain) => {
|
|
676
|
-
chorusGain.connect(channel.reverbEffect.convolverNode);
|
|
737
|
+
channel.merger.connect(delayNode);
|
|
677
738
|
});
|
|
739
|
+
channel.merger.connect(channel.reverbEffect.input);
|
|
740
|
+
channel.reverbEffect.output.connect(this.masterGain);
|
|
678
741
|
}
|
|
679
742
|
}
|
|
680
743
|
}
|
|
@@ -745,7 +808,7 @@ class MidyGM2 {
|
|
|
745
808
|
startModulation(channel, note, time) {
|
|
746
809
|
const { instrumentKey } = note;
|
|
747
810
|
note.modLFOGain = new GainNode(this.audioContext, {
|
|
748
|
-
gain: this.cbToRatio(instrumentKey.modLfoToVolume
|
|
811
|
+
gain: this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation),
|
|
749
812
|
});
|
|
750
813
|
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
751
814
|
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
@@ -797,7 +860,7 @@ class MidyGM2 {
|
|
|
797
860
|
if (!instrumentKey)
|
|
798
861
|
return;
|
|
799
862
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
800
|
-
this.
|
|
863
|
+
this.connectEffects(channel, note.gainNode);
|
|
801
864
|
if (channel.sostenutoPedal) {
|
|
802
865
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
803
866
|
}
|
|
@@ -821,42 +884,48 @@ class MidyGM2 {
|
|
|
821
884
|
return;
|
|
822
885
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
823
886
|
return;
|
|
824
|
-
const
|
|
825
|
-
for (let i = 0; i <
|
|
826
|
-
const
|
|
827
|
-
if (!
|
|
887
|
+
const scheduledNotes = channel.scheduledNotes.get(noteNumber);
|
|
888
|
+
for (let i = 0; i < scheduledNotes.length; i++) {
|
|
889
|
+
const note = scheduledNotes[i];
|
|
890
|
+
if (!note)
|
|
828
891
|
continue;
|
|
829
|
-
if (
|
|
892
|
+
if (note.ending)
|
|
830
893
|
continue;
|
|
831
|
-
const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
|
|
832
894
|
const velocityRate = (velocity + 127) / 127;
|
|
833
|
-
const volEndTime = stopTime +
|
|
834
|
-
|
|
835
|
-
gainNode.gain
|
|
895
|
+
const volEndTime = stopTime +
|
|
896
|
+
note.instrumentKey.volRelease * velocityRate;
|
|
897
|
+
note.gainNode.gain
|
|
898
|
+
.cancelScheduledValues(stopTime)
|
|
899
|
+
.linearRampToValueAtTime(0, volEndTime);
|
|
836
900
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
837
|
-
const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
|
|
901
|
+
const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
|
|
838
902
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
839
|
-
const modEndTime = stopTime +
|
|
840
|
-
|
|
903
|
+
const modEndTime = stopTime +
|
|
904
|
+
note.instrumentKey.modRelease * velocityRate;
|
|
905
|
+
note.filterNode.frequency
|
|
841
906
|
.cancelScheduledValues(stopTime)
|
|
842
907
|
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
843
|
-
|
|
908
|
+
note.ending = true;
|
|
844
909
|
this.scheduleTask(() => {
|
|
845
|
-
bufferSource.loop = false;
|
|
910
|
+
note.bufferSource.loop = false;
|
|
846
911
|
}, stopTime);
|
|
847
912
|
return new Promise((resolve) => {
|
|
848
|
-
bufferSource.onended = () => {
|
|
849
|
-
|
|
850
|
-
bufferSource.disconnect(
|
|
851
|
-
filterNode.disconnect(
|
|
852
|
-
gainNode.disconnect(
|
|
853
|
-
if (modLFOGain)
|
|
854
|
-
modLFOGain.disconnect(
|
|
855
|
-
if (
|
|
856
|
-
|
|
913
|
+
note.bufferSource.onended = () => {
|
|
914
|
+
scheduledNotes[i] = null;
|
|
915
|
+
note.bufferSource.disconnect();
|
|
916
|
+
note.filterNode.disconnect();
|
|
917
|
+
note.gainNode.disconnect();
|
|
918
|
+
if (note.modLFOGain)
|
|
919
|
+
note.modLFOGain.disconnect();
|
|
920
|
+
if (note.vibLFOGain)
|
|
921
|
+
note.vibLFOGain.disconnect();
|
|
922
|
+
if (note.modLFO)
|
|
923
|
+
note.modLFO.stop();
|
|
924
|
+
if (note.vibLFO)
|
|
925
|
+
note.vibLFO.stop();
|
|
857
926
|
resolve();
|
|
858
927
|
};
|
|
859
|
-
bufferSource.stop(volEndTime);
|
|
928
|
+
note.bufferSource.stop(volEndTime);
|
|
860
929
|
});
|
|
861
930
|
}
|
|
862
931
|
}
|
|
@@ -954,7 +1023,7 @@ class MidyGM2 {
|
|
|
954
1023
|
case 5:
|
|
955
1024
|
return this.setPortamentoTime(channelNumber, value);
|
|
956
1025
|
case 6:
|
|
957
|
-
return this.
|
|
1026
|
+
return this.dataEntryMSB(channelNumber, value);
|
|
958
1027
|
case 7:
|
|
959
1028
|
return this.setVolume(channelNumber, value);
|
|
960
1029
|
case 10:
|
|
@@ -964,7 +1033,7 @@ class MidyGM2 {
|
|
|
964
1033
|
case 32:
|
|
965
1034
|
return this.setBankLSB(channelNumber, value);
|
|
966
1035
|
case 38:
|
|
967
|
-
return this.
|
|
1036
|
+
return this.dataEntryLSB(channelNumber, value);
|
|
968
1037
|
case 64:
|
|
969
1038
|
return this.setSustainPedal(channelNumber, value);
|
|
970
1039
|
case 65:
|
|
@@ -974,9 +1043,9 @@ class MidyGM2 {
|
|
|
974
1043
|
case 67:
|
|
975
1044
|
return this.setSoftPedal(channelNumber, value);
|
|
976
1045
|
case 91:
|
|
977
|
-
return this.
|
|
1046
|
+
return this.setReverbSendLevel(channelNumber, value);
|
|
978
1047
|
case 93:
|
|
979
|
-
return this.
|
|
1048
|
+
return this.setChorusSendLevel(channelNumber, value);
|
|
980
1049
|
case 100:
|
|
981
1050
|
return this.setRPNLSB(channelNumber, value);
|
|
982
1051
|
case 101:
|
|
@@ -1002,22 +1071,24 @@ class MidyGM2 {
|
|
|
1002
1071
|
setBankMSB(channelNumber, msb) {
|
|
1003
1072
|
this.channels[channelNumber].bankMSB = msb;
|
|
1004
1073
|
}
|
|
1005
|
-
|
|
1074
|
+
updateModulation(channel) {
|
|
1006
1075
|
const now = this.audioContext.currentTime;
|
|
1007
|
-
const channel = this.channels[channelNumber];
|
|
1008
|
-
channel.modulation = (modulation / 127) *
|
|
1009
|
-
(channel.modulationDepthRange * 100);
|
|
1010
1076
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1011
1077
|
activeNotes.forEach((activeNote) => {
|
|
1012
1078
|
if (activeNote.modLFO) {
|
|
1013
|
-
|
|
1014
|
-
|
|
1079
|
+
const { gainNode, instrumentKey } = activeNote;
|
|
1080
|
+
gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
|
|
1015
1081
|
}
|
|
1016
1082
|
else {
|
|
1017
1083
|
this.startModulation(channel, activeNote, now);
|
|
1018
1084
|
}
|
|
1019
1085
|
});
|
|
1020
1086
|
}
|
|
1087
|
+
setModulation(channelNumber, modulation) {
|
|
1088
|
+
const channel = this.channels[channelNumber];
|
|
1089
|
+
channel.modulation = (modulation / 127) * channel.modulationDepthRange;
|
|
1090
|
+
this.updateModulation(channel);
|
|
1091
|
+
}
|
|
1021
1092
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1022
1093
|
this.channels[channelNumber].portamentoTime = portamentoTime / 127;
|
|
1023
1094
|
}
|
|
@@ -1046,6 +1117,10 @@ class MidyGM2 {
|
|
|
1046
1117
|
setBankLSB(channelNumber, lsb) {
|
|
1047
1118
|
this.channels[channelNumber].bankLSB = lsb;
|
|
1048
1119
|
}
|
|
1120
|
+
dataEntryLSB(channelNumber, value) {
|
|
1121
|
+
this.channels[channelNumber].dataLSB = value;
|
|
1122
|
+
this.handleRPN(channelNumber);
|
|
1123
|
+
}
|
|
1049
1124
|
updateChannelGain(channel) {
|
|
1050
1125
|
const now = this.audioContext.currentTime;
|
|
1051
1126
|
const volume = channel.volume * channel.expression;
|
|
@@ -1067,7 +1142,7 @@ class MidyGM2 {
|
|
|
1067
1142
|
setPortamento(channelNumber, value) {
|
|
1068
1143
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1069
1144
|
}
|
|
1070
|
-
|
|
1145
|
+
setReverbSendLevel(channelNumber, reverb) {
|
|
1071
1146
|
const now = this.audioContext.currentTime;
|
|
1072
1147
|
const channel = this.channels[channelNumber];
|
|
1073
1148
|
const reverbEffect = channel.reverbEffect;
|
|
@@ -1077,7 +1152,7 @@ class MidyGM2 {
|
|
|
1077
1152
|
reverbEffect.wetGain.gain.cancelScheduledValues(now);
|
|
1078
1153
|
reverbEffect.wetGain.gain.setValueAtTime(channel.reverb, now);
|
|
1079
1154
|
}
|
|
1080
|
-
|
|
1155
|
+
setChorusSendLevel(channelNumber, chorus) {
|
|
1081
1156
|
const channel = this.channels[channelNumber];
|
|
1082
1157
|
channel.chorus = chorus / 127;
|
|
1083
1158
|
channel.chorusEffect.lfoGain = channel.chorus;
|
|
@@ -1125,21 +1200,21 @@ class MidyGM2 {
|
|
|
1125
1200
|
channel.dataMSB = minMSB;
|
|
1126
1201
|
}
|
|
1127
1202
|
}
|
|
1128
|
-
handleRPN(channelNumber
|
|
1203
|
+
handleRPN(channelNumber) {
|
|
1129
1204
|
const channel = this.channels[channelNumber];
|
|
1130
1205
|
const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
|
|
1131
1206
|
switch (rpn) {
|
|
1132
1207
|
case 0:
|
|
1133
|
-
|
|
1134
|
-
this.handlePitchBendRangeMessage(channelNumber);
|
|
1208
|
+
this.handlePitchBendRangeRPN(channelNumber);
|
|
1135
1209
|
break;
|
|
1136
1210
|
case 1:
|
|
1137
|
-
|
|
1138
|
-
this.handleFineTuningMessage(channelNumber);
|
|
1211
|
+
this.handleFineTuningRPN(channelNumber);
|
|
1139
1212
|
break;
|
|
1140
1213
|
case 2:
|
|
1141
|
-
|
|
1142
|
-
|
|
1214
|
+
this.handleCoarseTuningRPN(channelNumber);
|
|
1215
|
+
break;
|
|
1216
|
+
case 5:
|
|
1217
|
+
this.handleModulationDepthRangeRPN(channelNumber);
|
|
1143
1218
|
break;
|
|
1144
1219
|
default:
|
|
1145
1220
|
console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
|
|
@@ -1151,9 +1226,8 @@ class MidyGM2 {
|
|
|
1151
1226
|
setRPNLSB(channelNumber, value) {
|
|
1152
1227
|
this.channels[channelNumber].rpnLSB = value;
|
|
1153
1228
|
}
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
isMSB ? channel.dataMSB = value : channel.dataLSB = value;
|
|
1229
|
+
dataEntryMSB(channelNumber, value) {
|
|
1230
|
+
this.channels[channelNumber].dataMSB = value;
|
|
1157
1231
|
this.handleRPN(channelNumber);
|
|
1158
1232
|
}
|
|
1159
1233
|
updateDetune(channel, detuneChange) {
|
|
@@ -1167,7 +1241,7 @@ class MidyGM2 {
|
|
|
1167
1241
|
.setValueAtTime(detune, now);
|
|
1168
1242
|
});
|
|
1169
1243
|
}
|
|
1170
|
-
|
|
1244
|
+
handlePitchBendRangeRPN(channelNumber) {
|
|
1171
1245
|
const channel = this.channels[channelNumber];
|
|
1172
1246
|
this.limitData(channel, 0, 127, 0, 99);
|
|
1173
1247
|
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
@@ -1181,7 +1255,7 @@ class MidyGM2 {
|
|
|
1181
1255
|
channel.pitchBend * 100;
|
|
1182
1256
|
this.updateDetune(channel, detuneChange);
|
|
1183
1257
|
}
|
|
1184
|
-
|
|
1258
|
+
handleFineTuningRPN(channelNumber) {
|
|
1185
1259
|
const channel = this.channels[channelNumber];
|
|
1186
1260
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1187
1261
|
const fineTuning = (channel.dataMSB * 128 + channel.dataLSB - 8192) / 8192;
|
|
@@ -1189,9 +1263,12 @@ class MidyGM2 {
|
|
|
1189
1263
|
}
|
|
1190
1264
|
setFineTuning(channelNumber, fineTuning) {
|
|
1191
1265
|
const channel = this.channels[channelNumber];
|
|
1266
|
+
const prevFineTuning = channel.fineTuning;
|
|
1192
1267
|
channel.fineTuning = fineTuning;
|
|
1268
|
+
const detuneChange = channel.fineTuning - prevFineTuning;
|
|
1269
|
+
this.updateDetune(channel, detuneChange);
|
|
1193
1270
|
}
|
|
1194
|
-
|
|
1271
|
+
handleCoarseTuningRPN(channelNumber) {
|
|
1195
1272
|
const channel = this.channels[channelNumber];
|
|
1196
1273
|
this.limitDataMSB(channel, 0, 127);
|
|
1197
1274
|
const coarseTuning = channel.dataMSB - 64;
|
|
@@ -1199,7 +1276,22 @@ class MidyGM2 {
|
|
|
1199
1276
|
}
|
|
1200
1277
|
setCoarseTuning(channelNumber, coarseTuning) {
|
|
1201
1278
|
const channel = this.channels[channelNumber];
|
|
1202
|
-
|
|
1279
|
+
const prevCoarseTuning = channel.coarseTuning;
|
|
1280
|
+
channel.coarseTuning = coarseTuning;
|
|
1281
|
+
const detuneChange = channel.coarseTuning - prevCoarseTuning;
|
|
1282
|
+
this.updateDetune(channel, detuneChange);
|
|
1283
|
+
}
|
|
1284
|
+
handleModulationDepthRangeRPN(channelNumber) {
|
|
1285
|
+
const channel = this.channels[channelNumber];
|
|
1286
|
+
this.limitData(channel, 0, 127, 0, 127);
|
|
1287
|
+
const modulationDepthRange = dataMSB + dataLSB / 128;
|
|
1288
|
+
this.setModulationDepthRange(channelNumber, modulationDepthRange);
|
|
1289
|
+
}
|
|
1290
|
+
setModulationDepthRange(channelNumber, modulationDepthRange) {
|
|
1291
|
+
const channel = this.channels[channelNumber];
|
|
1292
|
+
channel.modulationDepthRange = modulationDepthRange;
|
|
1293
|
+
channel.modulation = (modulation / 127) * channel.modulationDepthRange;
|
|
1294
|
+
this.updateModulation(channel);
|
|
1203
1295
|
}
|
|
1204
1296
|
allSoundOff(channelNumber) {
|
|
1205
1297
|
const now = this.audioContext.currentTime;
|
|
@@ -1290,9 +1382,9 @@ class MidyGM2 {
|
|
|
1290
1382
|
switch (data[3]) {
|
|
1291
1383
|
case 1:
|
|
1292
1384
|
return this.handleMasterVolumeSysEx(data);
|
|
1293
|
-
case 3:
|
|
1385
|
+
case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
|
|
1294
1386
|
return this.handleMasterFineTuningSysEx(data);
|
|
1295
|
-
case 4:
|
|
1387
|
+
case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
|
|
1296
1388
|
return this.handleMasterCoarseTuningSysEx(data);
|
|
1297
1389
|
// case 5: // TODO: Global Parameter Control
|
|
1298
1390
|
default:
|
|
@@ -1404,7 +1496,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1404
1496
|
value: {
|
|
1405
1497
|
currentBufferSource: null,
|
|
1406
1498
|
volume: 100 / 127,
|
|
1407
|
-
pan:
|
|
1499
|
+
pan: 64,
|
|
1408
1500
|
portamentoTime: 0,
|
|
1409
1501
|
reverb: 0,
|
|
1410
1502
|
chorus: 0,
|
|
@@ -1415,9 +1507,9 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1415
1507
|
dataLSB: 0,
|
|
1416
1508
|
program: 0,
|
|
1417
1509
|
pitchBend: 0,
|
|
1418
|
-
fineTuning: 0,
|
|
1419
|
-
coarseTuning: 0,
|
|
1420
|
-
modulationDepthRange: 0.5,
|
|
1510
|
+
fineTuning: 0, // cb
|
|
1511
|
+
coarseTuning: 0, // cb
|
|
1512
|
+
modulationDepthRange: 0.5, // cb
|
|
1421
1513
|
}
|
|
1422
1514
|
});
|
|
1423
1515
|
Object.defineProperty(MidyGM2, "effectSettings", {
|
package/script/midy-GMLite.d.ts
CHANGED
|
@@ -37,25 +37,7 @@ export class MidyGMLite {
|
|
|
37
37
|
notePromises: any[];
|
|
38
38
|
audioContext: any;
|
|
39
39
|
masterGain: any;
|
|
40
|
-
channels:
|
|
41
|
-
scheduledNotes: Map<any, any>;
|
|
42
|
-
gainL: any;
|
|
43
|
-
gainR: any;
|
|
44
|
-
expression: number;
|
|
45
|
-
modulation: number;
|
|
46
|
-
sustainPedal: boolean;
|
|
47
|
-
rpnMSB: number;
|
|
48
|
-
rpnLSB: number;
|
|
49
|
-
pitchBendRange: number;
|
|
50
|
-
volume: number;
|
|
51
|
-
pan: number;
|
|
52
|
-
bank: number;
|
|
53
|
-
dataMSB: number;
|
|
54
|
-
dataLSB: number;
|
|
55
|
-
program: number;
|
|
56
|
-
pitchBend: number;
|
|
57
|
-
modulationDepthRange: number;
|
|
58
|
-
}[];
|
|
40
|
+
channels: any[];
|
|
59
41
|
initSoundFontTable(): any[];
|
|
60
42
|
addSoundFont(soundFont: any): void;
|
|
61
43
|
loadSoundFont(soundFontUrl: any): Promise<void>;
|
|
@@ -63,26 +45,9 @@ export class MidyGMLite {
|
|
|
63
45
|
setChannelAudioNodes(audioContext: any): {
|
|
64
46
|
gainL: any;
|
|
65
47
|
gainR: any;
|
|
48
|
+
merger: any;
|
|
66
49
|
};
|
|
67
|
-
createChannels(audioContext: any):
|
|
68
|
-
scheduledNotes: Map<any, any>;
|
|
69
|
-
gainL: any;
|
|
70
|
-
gainR: any;
|
|
71
|
-
expression: number;
|
|
72
|
-
modulation: number;
|
|
73
|
-
sustainPedal: boolean;
|
|
74
|
-
rpnMSB: number;
|
|
75
|
-
rpnLSB: number;
|
|
76
|
-
pitchBendRange: number;
|
|
77
|
-
volume: number;
|
|
78
|
-
pan: number;
|
|
79
|
-
bank: number;
|
|
80
|
-
dataMSB: number;
|
|
81
|
-
dataLSB: number;
|
|
82
|
-
program: number;
|
|
83
|
-
pitchBend: number;
|
|
84
|
-
modulationDepthRange: number;
|
|
85
|
-
}[];
|
|
50
|
+
createChannels(audioContext: any): any[];
|
|
86
51
|
createNoteBuffer(instrumentKey: any, isSF3: any): Promise<any>;
|
|
87
52
|
createNoteBufferNode(instrumentKey: any, isSF3: any): Promise<any>;
|
|
88
53
|
convertToFloat32Array(uint8Array: any): Float32Array;
|
|
@@ -105,7 +70,7 @@ export class MidyGMLite {
|
|
|
105
70
|
currentTime(): number;
|
|
106
71
|
getActiveNotes(channel: any, time: any): Map<any, any>;
|
|
107
72
|
getActiveNote(noteList: any, time: any): any;
|
|
108
|
-
|
|
73
|
+
connectEffects(channel: any, gainNode: any): void;
|
|
109
74
|
cbToRatio(cb: any): number;
|
|
110
75
|
centToHz(cent: any): number;
|
|
111
76
|
calcSemitoneOffset(channel: any): number;
|
|
@@ -124,6 +89,7 @@ export class MidyGMLite {
|
|
|
124
89
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
|
|
125
90
|
setPitchBend(channelNumber: any, pitchBend: any): void;
|
|
126
91
|
handleControlChange(channelNumber: any, controller: any, value: any): void | any[];
|
|
92
|
+
updateModulation(channel: any): void;
|
|
127
93
|
setModulation(channelNumber: any, modulation: any): void;
|
|
128
94
|
setVolume(channelNumber: any, volume: any): void;
|
|
129
95
|
panToGain(pan: any): {
|
|
@@ -132,14 +98,15 @@ export class MidyGMLite {
|
|
|
132
98
|
};
|
|
133
99
|
setPan(channelNumber: any, pan: any): void;
|
|
134
100
|
setExpression(channelNumber: any, expression: any): void;
|
|
101
|
+
dataEntryLSB(channelNumber: any, value: any): void;
|
|
135
102
|
updateChannelGain(channel: any): void;
|
|
136
103
|
setSustainPedal(channelNumber: any, value: any): void;
|
|
137
104
|
handleRPN(channelNumber: any): void;
|
|
138
105
|
setRPNMSB(channelNumber: any, value: any): void;
|
|
139
106
|
setRPNLSB(channelNumber: any, value: any): void;
|
|
140
|
-
|
|
107
|
+
dataEntryMSB(channelNumber: any, value: any): void;
|
|
141
108
|
updateDetune(channel: any, detuneChange: any): void;
|
|
142
|
-
|
|
109
|
+
handlePitchBendRangeRPN(channelNumber: any): void;
|
|
143
110
|
setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
|
|
144
111
|
allSoundOff(channelNumber: any): any[];
|
|
145
112
|
resetAllControllers(channelNumber: any): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAuBhB,kBAAgC;IAChC,gBAA4C;IAE5C
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAuBhB,kBAAgC;IAChC,gBAA4C;IAE5C,gBAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAcC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,kDAGC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA8CC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,mFA+BC;IAED,qCAcC;IAED,yDAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAn/BD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
|