@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/esm/midy-GM2.js
CHANGED
|
@@ -51,7 +51,7 @@ class Note {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
export class MidyGM2 {
|
|
54
|
-
constructor(audioContext) {
|
|
54
|
+
constructor(audioContext, options = this.defaultOptions) {
|
|
55
55
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
56
56
|
enumerable: true,
|
|
57
57
|
configurable: true,
|
|
@@ -75,13 +75,13 @@ export class MidyGM2 {
|
|
|
75
75
|
configurable: true,
|
|
76
76
|
writable: true,
|
|
77
77
|
value: 0
|
|
78
|
-
});
|
|
78
|
+
}); // cb
|
|
79
79
|
Object.defineProperty(this, "masterCoarseTuning", {
|
|
80
80
|
enumerable: true,
|
|
81
81
|
configurable: true,
|
|
82
82
|
writable: true,
|
|
83
83
|
value: 0
|
|
84
|
-
});
|
|
84
|
+
}); // cb
|
|
85
85
|
Object.defineProperty(this, "mono", {
|
|
86
86
|
enumerable: true,
|
|
87
87
|
configurable: true,
|
|
@@ -184,7 +184,19 @@ export class MidyGM2 {
|
|
|
184
184
|
writable: true,
|
|
185
185
|
value: []
|
|
186
186
|
});
|
|
187
|
+
Object.defineProperty(this, "defaultOptions", {
|
|
188
|
+
enumerable: true,
|
|
189
|
+
configurable: true,
|
|
190
|
+
writable: true,
|
|
191
|
+
value: {
|
|
192
|
+
reverbAlgorithm: (audioContext) => {
|
|
193
|
+
// return this.createConvolutionReverb(audioContext);
|
|
194
|
+
return this.createSchroederReverb(audioContext);
|
|
195
|
+
},
|
|
196
|
+
}
|
|
197
|
+
});
|
|
187
198
|
this.audioContext = audioContext;
|
|
199
|
+
this.options = { ...this.defaultOptions, ...options };
|
|
188
200
|
this.masterGain = new GainNode(audioContext);
|
|
189
201
|
this.masterGain.connect(audioContext.destination);
|
|
190
202
|
this.channels = this.createChannels(audioContext);
|
|
@@ -225,23 +237,18 @@ export class MidyGM2 {
|
|
|
225
237
|
this.totalTime = this.calcTotalTime();
|
|
226
238
|
}
|
|
227
239
|
setChannelAudioNodes(audioContext) {
|
|
228
|
-
const { gainLeft, gainRight } = this.panToGain(
|
|
240
|
+
const { gainLeft, gainRight } = this.panToGain(this.constructor.channelSettings.pan);
|
|
229
241
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
230
242
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
231
243
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
232
244
|
gainL.connect(merger, 0, 0);
|
|
233
245
|
gainR.connect(merger, 0, 1);
|
|
234
|
-
|
|
235
|
-
const reverbEffect = this.createReverbEffect(audioContext);
|
|
246
|
+
const reverbEffect = this.options.reverbAlgorithm(audioContext);
|
|
236
247
|
const chorusEffect = this.createChorusEffect(audioContext);
|
|
237
|
-
chorusEffect.lfo.start();
|
|
238
|
-
reverbEffect.dryGain.connect(gainL);
|
|
239
|
-
reverbEffect.dryGain.connect(gainR);
|
|
240
|
-
reverbEffect.wetGain.connect(gainL);
|
|
241
|
-
reverbEffect.wetGain.connect(gainR);
|
|
242
248
|
return {
|
|
243
249
|
gainL,
|
|
244
250
|
gainR,
|
|
251
|
+
merger,
|
|
245
252
|
reverbEffect,
|
|
246
253
|
chorusEffect,
|
|
247
254
|
};
|
|
@@ -249,13 +256,13 @@ export class MidyGM2 {
|
|
|
249
256
|
createChannels(audioContext) {
|
|
250
257
|
const channels = Array.from({ length: 16 }, () => {
|
|
251
258
|
return {
|
|
252
|
-
...
|
|
253
|
-
...
|
|
259
|
+
...this.constructor.channelSettings,
|
|
260
|
+
...this.constructor.effectSettings,
|
|
254
261
|
...this.setChannelAudioNodes(audioContext),
|
|
255
262
|
scheduledNotes: new Map(),
|
|
256
263
|
sostenutoNotes: new Map(),
|
|
257
264
|
channelPressure: {
|
|
258
|
-
...
|
|
265
|
+
...this.constructor.controllerDestinationSettings,
|
|
259
266
|
},
|
|
260
267
|
};
|
|
261
268
|
});
|
|
@@ -583,8 +590,12 @@ export class MidyGM2 {
|
|
|
583
590
|
}
|
|
584
591
|
return noteList[0];
|
|
585
592
|
}
|
|
586
|
-
|
|
593
|
+
createConvolutionReverb(audioContext, options = {}) {
|
|
587
594
|
const { decay = 0.8, preDecay = 0, } = options;
|
|
595
|
+
const input = new GainNode(audioContext);
|
|
596
|
+
const output = new GainNode(audioContext);
|
|
597
|
+
const dryGain = new GainNode(audioContext);
|
|
598
|
+
const wetGain = new GainNode(audioContext);
|
|
588
599
|
const sampleRate = audioContext.sampleRate;
|
|
589
600
|
const length = sampleRate * decay;
|
|
590
601
|
const impulse = new AudioBuffer({
|
|
@@ -598,27 +609,82 @@ export class MidyGM2 {
|
|
|
598
609
|
for (let i = 0; i < preDecayLength; i++) {
|
|
599
610
|
channelData[i] = Math.random() * 2 - 1;
|
|
600
611
|
}
|
|
612
|
+
const attenuationFactor = 1 / (sampleRate * decay);
|
|
601
613
|
for (let i = preDecayLength; i < length; i++) {
|
|
602
|
-
const attenuation = Math.exp(-(i - preDecayLength)
|
|
614
|
+
const attenuation = Math.exp(-(i - preDecayLength) * attenuationFactor);
|
|
603
615
|
channelData[i] = (Math.random() * 2 - 1) * attenuation;
|
|
604
616
|
}
|
|
605
617
|
}
|
|
606
618
|
const convolverNode = new ConvolverNode(audioContext, {
|
|
607
619
|
buffer: impulse,
|
|
608
620
|
});
|
|
609
|
-
|
|
610
|
-
const wetGain = new GainNode(audioContext);
|
|
621
|
+
input.connect(convolverNode);
|
|
611
622
|
convolverNode.connect(wetGain);
|
|
623
|
+
wetGain.connect(output);
|
|
624
|
+
dryGain.connect(output);
|
|
612
625
|
return {
|
|
613
|
-
|
|
626
|
+
input,
|
|
627
|
+
output,
|
|
614
628
|
dryGain,
|
|
615
629
|
wetGain,
|
|
630
|
+
convolverNode,
|
|
616
631
|
};
|
|
617
632
|
}
|
|
633
|
+
createCombFilter(audioContext, input, delay, feedback) {
|
|
634
|
+
const delayNode = new DelayNode(audioContext, {
|
|
635
|
+
maxDelayTime: delay,
|
|
636
|
+
delayTime: delay,
|
|
637
|
+
});
|
|
638
|
+
const feedbackGain = new GainNode(audioContext, { gain: feedback });
|
|
639
|
+
input.connect(delayNode);
|
|
640
|
+
delayNode.connect(feedbackGain);
|
|
641
|
+
feedbackGain.connect(delayNode);
|
|
642
|
+
return delayNode;
|
|
643
|
+
}
|
|
644
|
+
createAllpassFilter(audioContext, input, delay, feedback) {
|
|
645
|
+
const delayNode = new DelayNode(audioContext, {
|
|
646
|
+
maxDelayTime: delay,
|
|
647
|
+
delayTime: delay,
|
|
648
|
+
});
|
|
649
|
+
const feedbackGain = new GainNode(audioContext, { gain: feedback });
|
|
650
|
+
const passGain = new GainNode(audioContext, { gain: 1 - feedback });
|
|
651
|
+
input.connect(delayNode);
|
|
652
|
+
delayNode.connect(feedbackGain);
|
|
653
|
+
feedbackGain.connect(delayNode);
|
|
654
|
+
delayNode.connect(passGain);
|
|
655
|
+
return passGain;
|
|
656
|
+
}
|
|
657
|
+
// https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
|
|
658
|
+
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
659
|
+
createSchroederReverb(audioContext, options = {}) {
|
|
660
|
+
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;
|
|
661
|
+
const input = new GainNode(audioContext);
|
|
662
|
+
const output = new GainNode(audioContext);
|
|
663
|
+
const mergerGain = new GainNode(audioContext, {
|
|
664
|
+
gain: 1 / (combDelays.length * 2),
|
|
665
|
+
});
|
|
666
|
+
const dryGain = new GainNode(audioContext, { gain: 1 - mix });
|
|
667
|
+
const wetGain = new GainNode(audioContext, { gain: mix });
|
|
668
|
+
for (let i = 0; i < combDelays.length; i++) {
|
|
669
|
+
const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
|
|
670
|
+
comb.connect(mergerGain);
|
|
671
|
+
}
|
|
672
|
+
const allpasses = [];
|
|
673
|
+
for (let i = 0; i < allpassDelays.length; i++) {
|
|
674
|
+
const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
|
|
675
|
+
allpasses.push(allpass);
|
|
676
|
+
}
|
|
677
|
+
allpasses.at(-1).connect(wetGain);
|
|
678
|
+
input.connect(dryGain);
|
|
679
|
+
dryGain.connect(output);
|
|
680
|
+
wetGain.connect(output);
|
|
681
|
+
return { input, output, dryGain, wetGain };
|
|
682
|
+
}
|
|
618
683
|
createChorusEffect(audioContext, options = {}) {
|
|
619
684
|
const { chorusCount = 2, chorusRate = 0.6, chorusDepth = 0.15, delay = 0.01, variance = delay * 0.1, } = options;
|
|
620
685
|
const lfo = new OscillatorNode(audioContext, { frequency: chorusRate });
|
|
621
686
|
const lfoGain = new GainNode(audioContext, { gain: chorusDepth });
|
|
687
|
+
const output = new GainNode(audioContext);
|
|
622
688
|
const chorusGains = [];
|
|
623
689
|
const delayNodes = [];
|
|
624
690
|
const baseGain = 1 / chorusCount;
|
|
@@ -628,50 +694,47 @@ export class MidyGM2 {
|
|
|
628
694
|
const delayNode = new DelayNode(audioContext, {
|
|
629
695
|
maxDelayTime: delayTime,
|
|
630
696
|
});
|
|
631
|
-
delayNodes.push(delayNode);
|
|
632
697
|
const chorusGain = new GainNode(audioContext, { gain: baseGain });
|
|
698
|
+
delayNodes.push(delayNode);
|
|
633
699
|
chorusGains.push(chorusGain);
|
|
634
|
-
lfo.connect(lfoGain);
|
|
635
700
|
lfoGain.connect(delayNode.delayTime);
|
|
636
701
|
delayNode.connect(chorusGain);
|
|
702
|
+
chorusGain.connect(output);
|
|
637
703
|
}
|
|
704
|
+
lfo.connect(lfoGain);
|
|
705
|
+
lfo.start();
|
|
638
706
|
return {
|
|
639
707
|
lfo,
|
|
640
708
|
lfoGain,
|
|
641
709
|
delayNodes,
|
|
642
710
|
chorusGains,
|
|
711
|
+
output,
|
|
643
712
|
};
|
|
644
713
|
}
|
|
645
|
-
|
|
714
|
+
connectEffects(channel, gainNode) {
|
|
715
|
+
gainNode.connect(channel.merger);
|
|
646
716
|
if (channel.reverb === 0) {
|
|
647
717
|
if (channel.chorus === 0) { // no effect
|
|
648
|
-
|
|
649
|
-
gainNode.connect(channel.gainR);
|
|
718
|
+
channel.merger.connect(this.masterGain);
|
|
650
719
|
}
|
|
651
720
|
else { // chorus
|
|
652
721
|
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
653
|
-
|
|
654
|
-
});
|
|
655
|
-
channel.chorusEffect.chorusGains.forEach((chorusGain) => {
|
|
656
|
-
chorusGain.connect(channel.gainL);
|
|
657
|
-
chorusGain.connect(channel.gainR);
|
|
722
|
+
channel.merger.connect(delayNode);
|
|
658
723
|
});
|
|
724
|
+
channel.chorusEffect.output.connect(this.masterGain);
|
|
659
725
|
}
|
|
660
726
|
}
|
|
661
727
|
else {
|
|
662
728
|
if (channel.chorus === 0) { // reverb
|
|
663
|
-
|
|
664
|
-
|
|
729
|
+
channel.merger.connect(channel.reverbEffect.input);
|
|
730
|
+
channel.reverbEffect.output.connect(this.masterGain);
|
|
665
731
|
}
|
|
666
732
|
else { // reverb + chorus
|
|
667
|
-
gainNode.connect(channel.reverbEffect.convolverNode);
|
|
668
|
-
gainNode.connect(channel.reverbEffect.dryGain);
|
|
669
733
|
channel.chorusEffect.delayNodes.forEach((delayNode) => {
|
|
670
|
-
|
|
671
|
-
});
|
|
672
|
-
channel.chorusEffect.chorusGains.forEach((chorusGain) => {
|
|
673
|
-
chorusGain.connect(channel.reverbEffect.convolverNode);
|
|
734
|
+
channel.merger.connect(delayNode);
|
|
674
735
|
});
|
|
736
|
+
channel.merger.connect(channel.reverbEffect.input);
|
|
737
|
+
channel.reverbEffect.output.connect(this.masterGain);
|
|
675
738
|
}
|
|
676
739
|
}
|
|
677
740
|
}
|
|
@@ -742,7 +805,7 @@ export class MidyGM2 {
|
|
|
742
805
|
startModulation(channel, note, time) {
|
|
743
806
|
const { instrumentKey } = note;
|
|
744
807
|
note.modLFOGain = new GainNode(this.audioContext, {
|
|
745
|
-
gain: this.cbToRatio(instrumentKey.modLfoToVolume
|
|
808
|
+
gain: this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation),
|
|
746
809
|
});
|
|
747
810
|
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
748
811
|
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
@@ -794,7 +857,7 @@ export class MidyGM2 {
|
|
|
794
857
|
if (!instrumentKey)
|
|
795
858
|
return;
|
|
796
859
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
797
|
-
this.
|
|
860
|
+
this.connectEffects(channel, note.gainNode);
|
|
798
861
|
if (channel.sostenutoPedal) {
|
|
799
862
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
800
863
|
}
|
|
@@ -818,42 +881,48 @@ export class MidyGM2 {
|
|
|
818
881
|
return;
|
|
819
882
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
820
883
|
return;
|
|
821
|
-
const
|
|
822
|
-
for (let i = 0; i <
|
|
823
|
-
const
|
|
824
|
-
if (!
|
|
884
|
+
const scheduledNotes = channel.scheduledNotes.get(noteNumber);
|
|
885
|
+
for (let i = 0; i < scheduledNotes.length; i++) {
|
|
886
|
+
const note = scheduledNotes[i];
|
|
887
|
+
if (!note)
|
|
825
888
|
continue;
|
|
826
|
-
if (
|
|
889
|
+
if (note.ending)
|
|
827
890
|
continue;
|
|
828
|
-
const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
|
|
829
891
|
const velocityRate = (velocity + 127) / 127;
|
|
830
|
-
const volEndTime = stopTime +
|
|
831
|
-
|
|
832
|
-
gainNode.gain
|
|
892
|
+
const volEndTime = stopTime +
|
|
893
|
+
note.instrumentKey.volRelease * velocityRate;
|
|
894
|
+
note.gainNode.gain
|
|
895
|
+
.cancelScheduledValues(stopTime)
|
|
896
|
+
.linearRampToValueAtTime(0, volEndTime);
|
|
833
897
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
834
|
-
const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
|
|
898
|
+
const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
|
|
835
899
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
836
|
-
const modEndTime = stopTime +
|
|
837
|
-
|
|
900
|
+
const modEndTime = stopTime +
|
|
901
|
+
note.instrumentKey.modRelease * velocityRate;
|
|
902
|
+
note.filterNode.frequency
|
|
838
903
|
.cancelScheduledValues(stopTime)
|
|
839
904
|
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
840
|
-
|
|
905
|
+
note.ending = true;
|
|
841
906
|
this.scheduleTask(() => {
|
|
842
|
-
bufferSource.loop = false;
|
|
907
|
+
note.bufferSource.loop = false;
|
|
843
908
|
}, stopTime);
|
|
844
909
|
return new Promise((resolve) => {
|
|
845
|
-
bufferSource.onended = () => {
|
|
846
|
-
|
|
847
|
-
bufferSource.disconnect(
|
|
848
|
-
filterNode.disconnect(
|
|
849
|
-
gainNode.disconnect(
|
|
850
|
-
if (modLFOGain)
|
|
851
|
-
modLFOGain.disconnect(
|
|
852
|
-
if (
|
|
853
|
-
|
|
910
|
+
note.bufferSource.onended = () => {
|
|
911
|
+
scheduledNotes[i] = null;
|
|
912
|
+
note.bufferSource.disconnect();
|
|
913
|
+
note.filterNode.disconnect();
|
|
914
|
+
note.gainNode.disconnect();
|
|
915
|
+
if (note.modLFOGain)
|
|
916
|
+
note.modLFOGain.disconnect();
|
|
917
|
+
if (note.vibLFOGain)
|
|
918
|
+
note.vibLFOGain.disconnect();
|
|
919
|
+
if (note.modLFO)
|
|
920
|
+
note.modLFO.stop();
|
|
921
|
+
if (note.vibLFO)
|
|
922
|
+
note.vibLFO.stop();
|
|
854
923
|
resolve();
|
|
855
924
|
};
|
|
856
|
-
bufferSource.stop(volEndTime);
|
|
925
|
+
note.bufferSource.stop(volEndTime);
|
|
857
926
|
});
|
|
858
927
|
}
|
|
859
928
|
}
|
|
@@ -951,7 +1020,7 @@ export class MidyGM2 {
|
|
|
951
1020
|
case 5:
|
|
952
1021
|
return this.setPortamentoTime(channelNumber, value);
|
|
953
1022
|
case 6:
|
|
954
|
-
return this.
|
|
1023
|
+
return this.dataEntryMSB(channelNumber, value);
|
|
955
1024
|
case 7:
|
|
956
1025
|
return this.setVolume(channelNumber, value);
|
|
957
1026
|
case 10:
|
|
@@ -961,7 +1030,7 @@ export class MidyGM2 {
|
|
|
961
1030
|
case 32:
|
|
962
1031
|
return this.setBankLSB(channelNumber, value);
|
|
963
1032
|
case 38:
|
|
964
|
-
return this.
|
|
1033
|
+
return this.dataEntryLSB(channelNumber, value);
|
|
965
1034
|
case 64:
|
|
966
1035
|
return this.setSustainPedal(channelNumber, value);
|
|
967
1036
|
case 65:
|
|
@@ -971,9 +1040,9 @@ export class MidyGM2 {
|
|
|
971
1040
|
case 67:
|
|
972
1041
|
return this.setSoftPedal(channelNumber, value);
|
|
973
1042
|
case 91:
|
|
974
|
-
return this.
|
|
1043
|
+
return this.setReverbSendLevel(channelNumber, value);
|
|
975
1044
|
case 93:
|
|
976
|
-
return this.
|
|
1045
|
+
return this.setChorusSendLevel(channelNumber, value);
|
|
977
1046
|
case 100:
|
|
978
1047
|
return this.setRPNLSB(channelNumber, value);
|
|
979
1048
|
case 101:
|
|
@@ -999,22 +1068,24 @@ export class MidyGM2 {
|
|
|
999
1068
|
setBankMSB(channelNumber, msb) {
|
|
1000
1069
|
this.channels[channelNumber].bankMSB = msb;
|
|
1001
1070
|
}
|
|
1002
|
-
|
|
1071
|
+
updateModulation(channel) {
|
|
1003
1072
|
const now = this.audioContext.currentTime;
|
|
1004
|
-
const channel = this.channels[channelNumber];
|
|
1005
|
-
channel.modulation = (modulation / 127) *
|
|
1006
|
-
(channel.modulationDepthRange * 100);
|
|
1007
1073
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1008
1074
|
activeNotes.forEach((activeNote) => {
|
|
1009
1075
|
if (activeNote.modLFO) {
|
|
1010
|
-
|
|
1011
|
-
|
|
1076
|
+
const { gainNode, instrumentKey } = activeNote;
|
|
1077
|
+
gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
|
|
1012
1078
|
}
|
|
1013
1079
|
else {
|
|
1014
1080
|
this.startModulation(channel, activeNote, now);
|
|
1015
1081
|
}
|
|
1016
1082
|
});
|
|
1017
1083
|
}
|
|
1084
|
+
setModulation(channelNumber, modulation) {
|
|
1085
|
+
const channel = this.channels[channelNumber];
|
|
1086
|
+
channel.modulation = (modulation / 127) * channel.modulationDepthRange;
|
|
1087
|
+
this.updateModulation(channel);
|
|
1088
|
+
}
|
|
1018
1089
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1019
1090
|
this.channels[channelNumber].portamentoTime = portamentoTime / 127;
|
|
1020
1091
|
}
|
|
@@ -1043,6 +1114,10 @@ export class MidyGM2 {
|
|
|
1043
1114
|
setBankLSB(channelNumber, lsb) {
|
|
1044
1115
|
this.channels[channelNumber].bankLSB = lsb;
|
|
1045
1116
|
}
|
|
1117
|
+
dataEntryLSB(channelNumber, value) {
|
|
1118
|
+
this.channels[channelNumber].dataLSB = value;
|
|
1119
|
+
this.handleRPN(channelNumber);
|
|
1120
|
+
}
|
|
1046
1121
|
updateChannelGain(channel) {
|
|
1047
1122
|
const now = this.audioContext.currentTime;
|
|
1048
1123
|
const volume = channel.volume * channel.expression;
|
|
@@ -1064,7 +1139,7 @@ export class MidyGM2 {
|
|
|
1064
1139
|
setPortamento(channelNumber, value) {
|
|
1065
1140
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1066
1141
|
}
|
|
1067
|
-
|
|
1142
|
+
setReverbSendLevel(channelNumber, reverb) {
|
|
1068
1143
|
const now = this.audioContext.currentTime;
|
|
1069
1144
|
const channel = this.channels[channelNumber];
|
|
1070
1145
|
const reverbEffect = channel.reverbEffect;
|
|
@@ -1074,7 +1149,7 @@ export class MidyGM2 {
|
|
|
1074
1149
|
reverbEffect.wetGain.gain.cancelScheduledValues(now);
|
|
1075
1150
|
reverbEffect.wetGain.gain.setValueAtTime(channel.reverb, now);
|
|
1076
1151
|
}
|
|
1077
|
-
|
|
1152
|
+
setChorusSendLevel(channelNumber, chorus) {
|
|
1078
1153
|
const channel = this.channels[channelNumber];
|
|
1079
1154
|
channel.chorus = chorus / 127;
|
|
1080
1155
|
channel.chorusEffect.lfoGain = channel.chorus;
|
|
@@ -1122,21 +1197,21 @@ export class MidyGM2 {
|
|
|
1122
1197
|
channel.dataMSB = minMSB;
|
|
1123
1198
|
}
|
|
1124
1199
|
}
|
|
1125
|
-
handleRPN(channelNumber
|
|
1200
|
+
handleRPN(channelNumber) {
|
|
1126
1201
|
const channel = this.channels[channelNumber];
|
|
1127
1202
|
const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
|
|
1128
1203
|
switch (rpn) {
|
|
1129
1204
|
case 0:
|
|
1130
|
-
|
|
1131
|
-
this.handlePitchBendRangeMessage(channelNumber);
|
|
1205
|
+
this.handlePitchBendRangeRPN(channelNumber);
|
|
1132
1206
|
break;
|
|
1133
1207
|
case 1:
|
|
1134
|
-
|
|
1135
|
-
this.handleFineTuningMessage(channelNumber);
|
|
1208
|
+
this.handleFineTuningRPN(channelNumber);
|
|
1136
1209
|
break;
|
|
1137
1210
|
case 2:
|
|
1138
|
-
|
|
1139
|
-
|
|
1211
|
+
this.handleCoarseTuningRPN(channelNumber);
|
|
1212
|
+
break;
|
|
1213
|
+
case 5:
|
|
1214
|
+
this.handleModulationDepthRangeRPN(channelNumber);
|
|
1140
1215
|
break;
|
|
1141
1216
|
default:
|
|
1142
1217
|
console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
|
|
@@ -1148,9 +1223,8 @@ export class MidyGM2 {
|
|
|
1148
1223
|
setRPNLSB(channelNumber, value) {
|
|
1149
1224
|
this.channels[channelNumber].rpnLSB = value;
|
|
1150
1225
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
isMSB ? channel.dataMSB = value : channel.dataLSB = value;
|
|
1226
|
+
dataEntryMSB(channelNumber, value) {
|
|
1227
|
+
this.channels[channelNumber].dataMSB = value;
|
|
1154
1228
|
this.handleRPN(channelNumber);
|
|
1155
1229
|
}
|
|
1156
1230
|
updateDetune(channel, detuneChange) {
|
|
@@ -1164,7 +1238,7 @@ export class MidyGM2 {
|
|
|
1164
1238
|
.setValueAtTime(detune, now);
|
|
1165
1239
|
});
|
|
1166
1240
|
}
|
|
1167
|
-
|
|
1241
|
+
handlePitchBendRangeRPN(channelNumber) {
|
|
1168
1242
|
const channel = this.channels[channelNumber];
|
|
1169
1243
|
this.limitData(channel, 0, 127, 0, 99);
|
|
1170
1244
|
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
@@ -1178,7 +1252,7 @@ export class MidyGM2 {
|
|
|
1178
1252
|
channel.pitchBend * 100;
|
|
1179
1253
|
this.updateDetune(channel, detuneChange);
|
|
1180
1254
|
}
|
|
1181
|
-
|
|
1255
|
+
handleFineTuningRPN(channelNumber) {
|
|
1182
1256
|
const channel = this.channels[channelNumber];
|
|
1183
1257
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1184
1258
|
const fineTuning = (channel.dataMSB * 128 + channel.dataLSB - 8192) / 8192;
|
|
@@ -1186,9 +1260,12 @@ export class MidyGM2 {
|
|
|
1186
1260
|
}
|
|
1187
1261
|
setFineTuning(channelNumber, fineTuning) {
|
|
1188
1262
|
const channel = this.channels[channelNumber];
|
|
1263
|
+
const prevFineTuning = channel.fineTuning;
|
|
1189
1264
|
channel.fineTuning = fineTuning;
|
|
1265
|
+
const detuneChange = channel.fineTuning - prevFineTuning;
|
|
1266
|
+
this.updateDetune(channel, detuneChange);
|
|
1190
1267
|
}
|
|
1191
|
-
|
|
1268
|
+
handleCoarseTuningRPN(channelNumber) {
|
|
1192
1269
|
const channel = this.channels[channelNumber];
|
|
1193
1270
|
this.limitDataMSB(channel, 0, 127);
|
|
1194
1271
|
const coarseTuning = channel.dataMSB - 64;
|
|
@@ -1196,7 +1273,22 @@ export class MidyGM2 {
|
|
|
1196
1273
|
}
|
|
1197
1274
|
setCoarseTuning(channelNumber, coarseTuning) {
|
|
1198
1275
|
const channel = this.channels[channelNumber];
|
|
1199
|
-
|
|
1276
|
+
const prevCoarseTuning = channel.coarseTuning;
|
|
1277
|
+
channel.coarseTuning = coarseTuning;
|
|
1278
|
+
const detuneChange = channel.coarseTuning - prevCoarseTuning;
|
|
1279
|
+
this.updateDetune(channel, detuneChange);
|
|
1280
|
+
}
|
|
1281
|
+
handleModulationDepthRangeRPN(channelNumber) {
|
|
1282
|
+
const channel = this.channels[channelNumber];
|
|
1283
|
+
this.limitData(channel, 0, 127, 0, 127);
|
|
1284
|
+
const modulationDepthRange = dataMSB + dataLSB / 128;
|
|
1285
|
+
this.setModulationDepthRange(channelNumber, modulationDepthRange);
|
|
1286
|
+
}
|
|
1287
|
+
setModulationDepthRange(channelNumber, modulationDepthRange) {
|
|
1288
|
+
const channel = this.channels[channelNumber];
|
|
1289
|
+
channel.modulationDepthRange = modulationDepthRange;
|
|
1290
|
+
channel.modulation = (modulation / 127) * channel.modulationDepthRange;
|
|
1291
|
+
this.updateModulation(channel);
|
|
1200
1292
|
}
|
|
1201
1293
|
allSoundOff(channelNumber) {
|
|
1202
1294
|
const now = this.audioContext.currentTime;
|
|
@@ -1287,9 +1379,9 @@ export class MidyGM2 {
|
|
|
1287
1379
|
switch (data[3]) {
|
|
1288
1380
|
case 1:
|
|
1289
1381
|
return this.handleMasterVolumeSysEx(data);
|
|
1290
|
-
case 3:
|
|
1382
|
+
case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
|
|
1291
1383
|
return this.handleMasterFineTuningSysEx(data);
|
|
1292
|
-
case 4:
|
|
1384
|
+
case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
|
|
1293
1385
|
return this.handleMasterCoarseTuningSysEx(data);
|
|
1294
1386
|
// case 5: // TODO: Global Parameter Control
|
|
1295
1387
|
default:
|
|
@@ -1400,7 +1492,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1400
1492
|
value: {
|
|
1401
1493
|
currentBufferSource: null,
|
|
1402
1494
|
volume: 100 / 127,
|
|
1403
|
-
pan:
|
|
1495
|
+
pan: 64,
|
|
1404
1496
|
portamentoTime: 0,
|
|
1405
1497
|
reverb: 0,
|
|
1406
1498
|
chorus: 0,
|
|
@@ -1411,9 +1503,9 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1411
1503
|
dataLSB: 0,
|
|
1412
1504
|
program: 0,
|
|
1413
1505
|
pitchBend: 0,
|
|
1414
|
-
fineTuning: 0,
|
|
1415
|
-
coarseTuning: 0,
|
|
1416
|
-
modulationDepthRange: 0.5,
|
|
1506
|
+
fineTuning: 0, // cb
|
|
1507
|
+
coarseTuning: 0, // cb
|
|
1508
|
+
modulationDepthRange: 0.5, // cb
|
|
1417
1509
|
}
|
|
1418
1510
|
});
|
|
1419
1511
|
Object.defineProperty(MidyGM2, "effectSettings", {
|
package/esm/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;
|
package/esm/midy-GMLite.d.ts.map
CHANGED
|
@@ -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"}
|