@marmooo/midy 0.0.8 → 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 +6 -43
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +38 -32
- package/esm/midy-GM2.d.ts +41 -102
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +130 -61
- package/esm/midy-GMLite.d.ts +4 -39
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +34 -32
- package/esm/midy.d.ts +41 -124
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +130 -65
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +6 -43
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +38 -32
- package/script/midy-GM2.d.ts +41 -102
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +130 -61
- package/script/midy-GMLite.d.ts +4 -39
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +34 -32
- package/script/midy.d.ts +41 -124
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +130 -65
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,
|
|
@@ -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
|
}
|
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;
|
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"}
|
package/esm/midy-GMLite.js
CHANGED
|
@@ -183,23 +183,23 @@ export class MidyGMLite {
|
|
|
183
183
|
this.totalTime = this.calcTotalTime();
|
|
184
184
|
}
|
|
185
185
|
setChannelAudioNodes(audioContext) {
|
|
186
|
-
const { gainLeft, gainRight } = this.panToGain(
|
|
186
|
+
const { gainLeft, gainRight } = this.panToGain(this.constructor.channelSettings.pan);
|
|
187
187
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
188
188
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
189
189
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
190
190
|
gainL.connect(merger, 0, 0);
|
|
191
191
|
gainR.connect(merger, 0, 1);
|
|
192
|
-
merger.connect(this.masterGain);
|
|
193
192
|
return {
|
|
194
193
|
gainL,
|
|
195
194
|
gainR,
|
|
195
|
+
merger,
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
198
|
createChannels(audioContext) {
|
|
199
199
|
const channels = Array.from({ length: 16 }, () => {
|
|
200
200
|
return {
|
|
201
|
-
...
|
|
202
|
-
...
|
|
201
|
+
...this.constructor.channelSettings,
|
|
202
|
+
...this.constructor.effectSettings,
|
|
203
203
|
...this.setChannelAudioNodes(audioContext),
|
|
204
204
|
scheduledNotes: new Map(),
|
|
205
205
|
};
|
|
@@ -491,9 +491,9 @@ export class MidyGMLite {
|
|
|
491
491
|
}
|
|
492
492
|
return noteList[0];
|
|
493
493
|
}
|
|
494
|
-
|
|
495
|
-
gainNode.connect(channel.
|
|
496
|
-
|
|
494
|
+
connectEffects(channel, gainNode) {
|
|
495
|
+
gainNode.connect(channel.merger);
|
|
496
|
+
merger.connect(this.masterGain);
|
|
497
497
|
}
|
|
498
498
|
cbToRatio(cb) {
|
|
499
499
|
return Math.pow(10, cb / 200);
|
|
@@ -559,7 +559,7 @@ export class MidyGMLite {
|
|
|
559
559
|
startModulation(channel, note, time) {
|
|
560
560
|
const { instrumentKey } = note;
|
|
561
561
|
note.modLFOGain = new GainNode(this.audioContext, {
|
|
562
|
-
gain: this.cbToRatio(instrumentKey.modLfoToVolume
|
|
562
|
+
gain: this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation),
|
|
563
563
|
});
|
|
564
564
|
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
565
565
|
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
@@ -598,7 +598,7 @@ export class MidyGMLite {
|
|
|
598
598
|
if (!instrumentKey)
|
|
599
599
|
return;
|
|
600
600
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
601
|
-
this.
|
|
601
|
+
this.connectEffects(channel, note.gainNode);
|
|
602
602
|
const scheduledNotes = channel.scheduledNotes;
|
|
603
603
|
if (scheduledNotes.has(noteNumber)) {
|
|
604
604
|
scheduledNotes.get(noteNumber).push(note);
|
|
@@ -617,39 +617,41 @@ export class MidyGMLite {
|
|
|
617
617
|
return;
|
|
618
618
|
if (!channel.scheduledNotes.has(noteNumber))
|
|
619
619
|
return;
|
|
620
|
-
const
|
|
621
|
-
for (let i = 0; i <
|
|
622
|
-
const
|
|
623
|
-
if (!
|
|
620
|
+
const scheduledNotes = channel.scheduledNotes.get(noteNumber);
|
|
621
|
+
for (let i = 0; i < scheduledNotes.length; i++) {
|
|
622
|
+
const note = scheduledNotes[i];
|
|
623
|
+
if (!note)
|
|
624
624
|
continue;
|
|
625
|
-
if (
|
|
625
|
+
if (note.ending)
|
|
626
626
|
continue;
|
|
627
|
-
const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, instrumentKey, } = targetNote;
|
|
628
627
|
const velocityRate = (velocity + 127) / 127;
|
|
629
|
-
const volEndTime = stopTime +
|
|
630
|
-
|
|
631
|
-
gainNode.gain
|
|
628
|
+
const volEndTime = stopTime +
|
|
629
|
+
note.instrumentKey.volRelease * velocityRate;
|
|
630
|
+
note.gainNode.gain
|
|
631
|
+
.cancelScheduledValues(stopTime)
|
|
632
|
+
.linearRampToValueAtTime(0, volEndTime);
|
|
632
633
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
633
|
-
const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
|
|
634
|
+
const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
|
|
634
635
|
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
635
|
-
const modEndTime = stopTime +
|
|
636
|
-
|
|
636
|
+
const modEndTime = stopTime +
|
|
637
|
+
note.instrumentKey.modRelease * velocityRate;
|
|
638
|
+
note.filterNode.frequency
|
|
637
639
|
.cancelScheduledValues(stopTime)
|
|
638
640
|
.linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
|
|
639
|
-
|
|
641
|
+
note.ending = true;
|
|
640
642
|
this.scheduleTask(() => {
|
|
641
|
-
bufferSource.loop = false;
|
|
643
|
+
note.bufferSource.loop = false;
|
|
642
644
|
}, stopTime);
|
|
643
645
|
return new Promise((resolve) => {
|
|
644
|
-
bufferSource.onended = () => {
|
|
645
|
-
|
|
646
|
-
bufferSource.disconnect(
|
|
647
|
-
filterNode.disconnect(
|
|
648
|
-
gainNode.disconnect(
|
|
649
|
-
if (modLFOGain)
|
|
650
|
-
modLFOGain.disconnect(
|
|
651
|
-
if (modLFO)
|
|
652
|
-
modLFO.stop();
|
|
646
|
+
note.bufferSource.onended = () => {
|
|
647
|
+
scheduledNotes[i] = null;
|
|
648
|
+
note.bufferSource.disconnect();
|
|
649
|
+
note.filterNode.disconnect();
|
|
650
|
+
note.gainNode.disconnect();
|
|
651
|
+
if (note.modLFOGain)
|
|
652
|
+
note.modLFOGain.disconnect();
|
|
653
|
+
if (note.modLFO)
|
|
654
|
+
note.modLFO.stop();
|
|
653
655
|
resolve();
|
|
654
656
|
};
|
|
655
657
|
bufferSource.stop(volEndTime);
|