@marmooo/midy 0.1.1 → 0.1.3
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/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts +153 -0
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +1 -0
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.2 → soundfont-parser@0.0.4}/+esm.js +73 -66
- package/esm/midy-GM1.d.ts +18 -14
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +133 -110
- package/esm/midy-GM2.d.ts +36 -30
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +190 -158
- package/esm/midy-GMLite.d.ts +16 -14
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +123 -112
- package/esm/midy.d.ts +33 -31
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +191 -185
- package/package.json +1 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts +153 -0
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +1 -0
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.2 → soundfont-parser@0.0.4}/+esm.js +75 -68
- package/script/midy-GM1.d.ts +18 -14
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +133 -110
- package/script/midy-GM2.d.ts +36 -30
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +190 -158
- package/script/midy-GMLite.d.ts +16 -14
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +123 -112
- package/script/midy.d.ts +33 -31
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +191 -185
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts +0 -135
- package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +0 -1
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts +0 -135
- package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +0 -1
package/script/midy-GM2.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MidyGM2 = void 0;
|
|
4
4
|
const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
|
|
5
|
-
const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.
|
|
5
|
+
const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.js");
|
|
6
6
|
class Note {
|
|
7
7
|
constructor(noteNumber, velocity, startTime, instrumentKey) {
|
|
8
8
|
Object.defineProperty(this, "bufferSource", {
|
|
@@ -11,37 +11,43 @@ class Note {
|
|
|
11
11
|
writable: true,
|
|
12
12
|
value: void 0
|
|
13
13
|
});
|
|
14
|
-
Object.defineProperty(this, "
|
|
14
|
+
Object.defineProperty(this, "filterNode", {
|
|
15
15
|
enumerable: true,
|
|
16
16
|
configurable: true,
|
|
17
17
|
writable: true,
|
|
18
18
|
value: void 0
|
|
19
19
|
});
|
|
20
|
-
Object.defineProperty(this, "
|
|
20
|
+
Object.defineProperty(this, "volumeNode", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(this, "volumeDepth", {
|
|
21
27
|
enumerable: true,
|
|
22
28
|
configurable: true,
|
|
23
29
|
writable: true,
|
|
24
30
|
value: void 0
|
|
25
31
|
});
|
|
26
|
-
Object.defineProperty(this, "
|
|
32
|
+
Object.defineProperty(this, "modulationLFO", {
|
|
27
33
|
enumerable: true,
|
|
28
34
|
configurable: true,
|
|
29
35
|
writable: true,
|
|
30
36
|
value: void 0
|
|
31
37
|
});
|
|
32
|
-
Object.defineProperty(this, "
|
|
38
|
+
Object.defineProperty(this, "modulationDepth", {
|
|
33
39
|
enumerable: true,
|
|
34
40
|
configurable: true,
|
|
35
41
|
writable: true,
|
|
36
42
|
value: void 0
|
|
37
43
|
});
|
|
38
|
-
Object.defineProperty(this, "
|
|
44
|
+
Object.defineProperty(this, "vibratoLFO", {
|
|
39
45
|
enumerable: true,
|
|
40
46
|
configurable: true,
|
|
41
47
|
writable: true,
|
|
42
48
|
value: void 0
|
|
43
49
|
});
|
|
44
|
-
Object.defineProperty(this, "
|
|
50
|
+
Object.defineProperty(this, "vibratoDepth", {
|
|
45
51
|
enumerable: true,
|
|
46
52
|
configurable: true,
|
|
47
53
|
writable: true,
|
|
@@ -85,7 +91,7 @@ class MidyGM2 {
|
|
|
85
91
|
writable: true,
|
|
86
92
|
value: {
|
|
87
93
|
time: this.getReverbTime(64),
|
|
88
|
-
feedback: 0.
|
|
94
|
+
feedback: 0.8,
|
|
89
95
|
}
|
|
90
96
|
});
|
|
91
97
|
Object.defineProperty(this, "chorus", {
|
|
@@ -227,8 +233,12 @@ class MidyGM2 {
|
|
|
227
233
|
this.audioContext = audioContext;
|
|
228
234
|
this.options = { ...this.defaultOptions, ...options };
|
|
229
235
|
this.masterGain = new GainNode(audioContext);
|
|
230
|
-
this.masterGain.connect(audioContext.destination);
|
|
231
236
|
this.channels = this.createChannels(audioContext);
|
|
237
|
+
this.reverbEffect = this.options.reverbAlgorithm(audioContext);
|
|
238
|
+
this.chorusEffect = this.createChorusEffect(audioContext);
|
|
239
|
+
this.chorusEffect.output.connect(this.masterGain);
|
|
240
|
+
this.reverbEffect.output.connect(this.masterGain);
|
|
241
|
+
this.masterGain.connect(audioContext.destination);
|
|
232
242
|
this.GM2SystemOn();
|
|
233
243
|
}
|
|
234
244
|
initSoundFontTable() {
|
|
@@ -272,14 +282,11 @@ class MidyGM2 {
|
|
|
272
282
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
273
283
|
gainL.connect(merger, 0, 0);
|
|
274
284
|
gainR.connect(merger, 0, 1);
|
|
275
|
-
|
|
276
|
-
const chorusEffect = this.createChorusEffect(audioContext);
|
|
285
|
+
merger.connect(this.masterGain);
|
|
277
286
|
return {
|
|
278
287
|
gainL,
|
|
279
288
|
gainR,
|
|
280
289
|
merger,
|
|
281
|
-
reverbEffect,
|
|
282
|
-
chorusEffect,
|
|
283
290
|
};
|
|
284
291
|
}
|
|
285
292
|
createChannels(audioContext) {
|
|
@@ -406,7 +413,7 @@ class MidyGM2 {
|
|
|
406
413
|
const t = this.audioContext.currentTime + offset;
|
|
407
414
|
queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
|
|
408
415
|
if (this.isPausing) {
|
|
409
|
-
await this.stopNotes();
|
|
416
|
+
await this.stopNotes(0, true);
|
|
410
417
|
this.notePromises = [];
|
|
411
418
|
resolve();
|
|
412
419
|
this.isPausing = false;
|
|
@@ -414,7 +421,7 @@ class MidyGM2 {
|
|
|
414
421
|
return;
|
|
415
422
|
}
|
|
416
423
|
else if (this.isStopping) {
|
|
417
|
-
await this.stopNotes();
|
|
424
|
+
await this.stopNotes(0, true);
|
|
418
425
|
this.notePromises = [];
|
|
419
426
|
resolve();
|
|
420
427
|
this.isStopping = false;
|
|
@@ -422,7 +429,7 @@ class MidyGM2 {
|
|
|
422
429
|
return;
|
|
423
430
|
}
|
|
424
431
|
else if (this.isSeeking) {
|
|
425
|
-
this.stopNotes();
|
|
432
|
+
this.stopNotes(0, true);
|
|
426
433
|
this.startTime = this.audioContext.currentTime;
|
|
427
434
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
428
435
|
offset = this.resumeTime - this.startTime;
|
|
@@ -537,21 +544,24 @@ class MidyGM2 {
|
|
|
537
544
|
}
|
|
538
545
|
return { instruments, timeline };
|
|
539
546
|
}
|
|
540
|
-
|
|
547
|
+
async stopChannelNotes(channelNumber, velocity, stopPedal) {
|
|
541
548
|
const now = this.audioContext.currentTime;
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
this.notePromises.push(promise);
|
|
550
|
-
}
|
|
551
|
-
});
|
|
549
|
+
const channel = this.channels[channelNumber];
|
|
550
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
551
|
+
noteList.forEach((note) => {
|
|
552
|
+
if (note) {
|
|
553
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
|
|
554
|
+
this.notePromises.push(promise);
|
|
555
|
+
}
|
|
552
556
|
});
|
|
553
|
-
channel.scheduledNotes.clear();
|
|
554
557
|
});
|
|
558
|
+
channel.scheduledNotes.clear();
|
|
559
|
+
await Promise.all(this.notePromises);
|
|
560
|
+
}
|
|
561
|
+
stopNotes(velocity, stopPedal) {
|
|
562
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
563
|
+
this.stopChannelNotes(i, velocity, stopPedal);
|
|
564
|
+
}
|
|
555
565
|
return Promise.all(this.notePromises);
|
|
556
566
|
}
|
|
557
567
|
async start() {
|
|
@@ -689,7 +699,7 @@ class MidyGM2 {
|
|
|
689
699
|
}
|
|
690
700
|
// https://hajim.rochester.edu/ece/sites/zduan/teaching/ece472/reading/Schroeder_1962.pdf
|
|
691
701
|
// M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
|
|
692
|
-
createSchroederReverb(audioContext, combDelays,
|
|
702
|
+
createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
|
|
693
703
|
const input = new GainNode(audioContext);
|
|
694
704
|
const output = new GainNode(audioContext);
|
|
695
705
|
const mergerGain = new GainNode(audioContext);
|
|
@@ -748,21 +758,6 @@ class MidyGM2 {
|
|
|
748
758
|
feedbackGains,
|
|
749
759
|
};
|
|
750
760
|
}
|
|
751
|
-
connectEffects(channel, gainNode) {
|
|
752
|
-
gainNode.connect(channel.merger);
|
|
753
|
-
channel.merger.connect(this.masterGain);
|
|
754
|
-
if (0 < channel.reverbSendLevel) {
|
|
755
|
-
channel.merger.connect(channel.reverbEffect.input);
|
|
756
|
-
channel.reverbEffect.output.connect(this.masterGain);
|
|
757
|
-
}
|
|
758
|
-
if (0 < channel.chorusSendLevel) {
|
|
759
|
-
channel.merger.connect(channel.chorusEffect.input);
|
|
760
|
-
channel.reverbEffect.output.connect(this.masterGain);
|
|
761
|
-
}
|
|
762
|
-
if (0 < this.chorus.sendToReverb) {
|
|
763
|
-
channel.chorusEffect.sendGain.connect(channel.reverbEffect.input);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
761
|
cbToRatio(cb) {
|
|
767
762
|
return Math.pow(10, cb / 200);
|
|
768
763
|
}
|
|
@@ -779,42 +774,56 @@ class MidyGM2 {
|
|
|
779
774
|
return instrumentKey.playbackRate(noteNumber) *
|
|
780
775
|
Math.pow(2, semitoneOffset / 12);
|
|
781
776
|
}
|
|
782
|
-
setVolumeEnvelope(
|
|
783
|
-
const { instrumentKey, startTime
|
|
784
|
-
note.
|
|
785
|
-
|
|
786
|
-
if (volume === 0)
|
|
787
|
-
volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
|
|
788
|
-
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
|
|
789
|
-
volume;
|
|
777
|
+
setVolumeEnvelope(note) {
|
|
778
|
+
const { instrumentKey, startTime } = note;
|
|
779
|
+
note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
|
|
780
|
+
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
790
781
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
791
782
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
792
783
|
const volAttack = volDelay + instrumentKey.volAttack;
|
|
793
784
|
const volHold = volAttack + instrumentKey.volHold;
|
|
794
785
|
const volDecay = volHold + instrumentKey.volDecay;
|
|
795
|
-
note.
|
|
786
|
+
note.volumeNode.gain
|
|
796
787
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
797
788
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
798
789
|
.setValueAtTime(attackVolume, volHold)
|
|
799
790
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
800
791
|
}
|
|
801
|
-
|
|
802
|
-
const { instrumentKey,
|
|
792
|
+
setPitch(note, semitoneOffset) {
|
|
793
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
794
|
+
const modEnvToPitch = instrumentKey.modEnvToPitch / 100;
|
|
795
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
796
|
+
if (modEnvToPitch === 0)
|
|
797
|
+
return;
|
|
798
|
+
const basePitch = note.bufferSource.playbackRate.value;
|
|
799
|
+
const peekPitch = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset + modEnvToPitch);
|
|
800
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
801
|
+
const modAttack = modDelay + instrumentKey.modAttack;
|
|
802
|
+
const modHold = modAttack + instrumentKey.modHold;
|
|
803
|
+
const modDecay = modHold + instrumentKey.modDecay;
|
|
804
|
+
note.bufferSource.playbackRate.value
|
|
805
|
+
.setValueAtTime(basePitch, modDelay)
|
|
806
|
+
.exponentialRampToValueAtTime(peekPitch, modAttack)
|
|
807
|
+
.setValueAtTime(peekPitch, modHold)
|
|
808
|
+
.linearRampToValueAtTime(basePitch, modDecay);
|
|
809
|
+
}
|
|
810
|
+
setFilterNode(channel, note) {
|
|
811
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
803
812
|
const softPedalFactor = 1 -
|
|
804
813
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
805
814
|
const maxFreq = this.audioContext.sampleRate / 2;
|
|
806
815
|
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
807
816
|
softPedalFactor;
|
|
808
817
|
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
809
|
-
const sustainFreq =
|
|
810
|
-
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain)
|
|
818
|
+
const sustainFreq = baseFreq +
|
|
819
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
820
|
+
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
821
|
+
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
822
|
+
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
811
823
|
const modDelay = startTime + instrumentKey.modDelay;
|
|
812
824
|
const modAttack = modDelay + instrumentKey.modAttack;
|
|
813
825
|
const modHold = modAttack + instrumentKey.modHold;
|
|
814
826
|
const modDecay = modHold + instrumentKey.modDecay;
|
|
815
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
816
|
-
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
817
|
-
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
818
827
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
819
828
|
type: "lowpass",
|
|
820
829
|
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
@@ -825,39 +834,72 @@ class MidyGM2 {
|
|
|
825
834
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
826
835
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
827
836
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
828
|
-
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
|
|
829
837
|
}
|
|
830
|
-
startModulation(channel, note,
|
|
838
|
+
startModulation(channel, note, startTime) {
|
|
831
839
|
const { instrumentKey } = note;
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
});
|
|
835
|
-
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
840
|
+
const { modLfoToPitch, modLfoToVolume } = instrumentKey;
|
|
841
|
+
note.modulationLFO = new OscillatorNode(this.audioContext, {
|
|
836
842
|
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
837
843
|
});
|
|
838
|
-
note.
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
844
|
+
note.filterDepth = new GainNode(this.audioContext, {
|
|
845
|
+
gain: instrumentKey.modLfoToFilterFc,
|
|
846
|
+
});
|
|
847
|
+
const modulationDepth = Math.abs(modLfoToPitch) + channel.modulationDepth;
|
|
848
|
+
const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
|
|
849
|
+
note.modulationDepth = new GainNode(this.audioContext, {
|
|
850
|
+
gain: modulationDepth * modulationDepthSign,
|
|
851
|
+
});
|
|
852
|
+
const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
853
|
+
const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
|
|
854
|
+
note.volumeDepth = new GainNode(this.audioContext, {
|
|
855
|
+
gain: volumeDepth * volumeDepthSign,
|
|
856
|
+
});
|
|
857
|
+
note.modulationLFO.start(startTime + instrumentKey.delayModLFO);
|
|
858
|
+
note.modulationLFO.connect(note.filterDepth);
|
|
859
|
+
note.filterDepth.connect(note.filterNode.frequency);
|
|
860
|
+
note.modulationLFO.connect(note.modulationDepth);
|
|
861
|
+
note.modulationDepth.connect(note.bufferSource.detune);
|
|
862
|
+
note.modulationLFO.connect(note.volumeDepth);
|
|
863
|
+
note.volumeDepth.connect(note.volumeNode.gain);
|
|
864
|
+
}
|
|
865
|
+
startVibrato(channel, note, startTime) {
|
|
866
|
+
const { instrumentKey } = note;
|
|
867
|
+
const { vibLfoToPitch } = instrumentKey;
|
|
868
|
+
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
869
|
+
frequency: this.centToHz(instrumentKey.freqVibLFO) *
|
|
870
|
+
channel.vibratoRate,
|
|
871
|
+
});
|
|
872
|
+
const vibratoDepth = Math.abs(vibLfoToPitch) * channel.vibratoDepth;
|
|
873
|
+
const vibratoDepthSign = 0 < vibLfoToPitch;
|
|
874
|
+
note.vibratoDepth = new GainNode(this.audioContext, {
|
|
875
|
+
gain: vibratoDepth * vibratoDepthSign,
|
|
876
|
+
});
|
|
877
|
+
note.vibratoLFO.start(startTime + instrumentKey.delayVibLFO * channel.vibratoDelay);
|
|
878
|
+
note.vibratoLFO.connect(note.vibratoDepth);
|
|
879
|
+
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
843
880
|
}
|
|
844
881
|
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
845
882
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
846
883
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
847
884
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
848
|
-
|
|
849
|
-
this.setVolumeEnvelope(
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
885
|
+
this.setFilterNode(channel, note);
|
|
886
|
+
this.setVolumeEnvelope(note);
|
|
887
|
+
if (0 < channel.vibratoDepth) {
|
|
888
|
+
this.startVibrato(channel, note, startTime);
|
|
889
|
+
}
|
|
890
|
+
if (0 < channel.modulationDepth) {
|
|
891
|
+
this.setPitch(note, semitoneOffset);
|
|
892
|
+
this.startModulation(channel, note, startTime);
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
854
896
|
}
|
|
855
897
|
if (this.mono && channel.currentBufferSource) {
|
|
856
898
|
channel.currentBufferSource.stop(startTime);
|
|
857
899
|
channel.currentBufferSource = note.bufferSource;
|
|
858
900
|
}
|
|
859
901
|
note.bufferSource.connect(note.filterNode);
|
|
860
|
-
note.filterNode.connect(note.
|
|
902
|
+
note.filterNode.connect(note.volumeNode);
|
|
861
903
|
note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
|
|
862
904
|
return note;
|
|
863
905
|
}
|
|
@@ -882,7 +924,8 @@ class MidyGM2 {
|
|
|
882
924
|
if (!instrumentKey)
|
|
883
925
|
return;
|
|
884
926
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
885
|
-
|
|
927
|
+
note.volumeNode.connect(channel.gainL);
|
|
928
|
+
note.volumeNode.connect(channel.gainR);
|
|
886
929
|
if (channel.sostenutoPedal) {
|
|
887
930
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
888
931
|
}
|
|
@@ -916,17 +959,14 @@ class MidyGM2 {
|
|
|
916
959
|
const velocityRate = (velocity + 127) / 127;
|
|
917
960
|
const volEndTime = stopTime +
|
|
918
961
|
note.instrumentKey.volRelease * velocityRate;
|
|
919
|
-
note.
|
|
962
|
+
note.volumeNode.gain
|
|
920
963
|
.cancelScheduledValues(stopTime)
|
|
921
964
|
.linearRampToValueAtTime(0, volEndTime);
|
|
922
|
-
const
|
|
923
|
-
const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
|
|
924
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
925
|
-
const modEndTime = stopTime +
|
|
965
|
+
const modRelease = stopTime +
|
|
926
966
|
note.instrumentKey.modRelease * velocityRate;
|
|
927
967
|
note.filterNode.frequency
|
|
928
968
|
.cancelScheduledValues(stopTime)
|
|
929
|
-
.linearRampToValueAtTime(
|
|
969
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
930
970
|
note.ending = true;
|
|
931
971
|
this.scheduleTask(() => {
|
|
932
972
|
note.bufferSource.loop = false;
|
|
@@ -935,16 +975,18 @@ class MidyGM2 {
|
|
|
935
975
|
note.bufferSource.onended = () => {
|
|
936
976
|
scheduledNotes[i] = null;
|
|
937
977
|
note.bufferSource.disconnect();
|
|
978
|
+
note.volumeNode.disconnect();
|
|
938
979
|
note.filterNode.disconnect();
|
|
939
|
-
note.
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
980
|
+
if (note.volumeDepth)
|
|
981
|
+
note.volumeDepth.disconnect();
|
|
982
|
+
if (note.modulationDepth)
|
|
983
|
+
note.modulationDepth.disconnect();
|
|
984
|
+
if (note.modulationLFO)
|
|
985
|
+
note.modulationLFO.stop();
|
|
986
|
+
if (note.vibratoDepth)
|
|
987
|
+
note.vibratoDepth.disconnect();
|
|
988
|
+
if (note.vibratoLFO)
|
|
989
|
+
note.vibratoLFO.stop();
|
|
948
990
|
resolve();
|
|
949
991
|
};
|
|
950
992
|
note.bufferSource.stop(volEndTime);
|
|
@@ -960,10 +1002,10 @@ class MidyGM2 {
|
|
|
960
1002
|
const channel = this.channels[channelNumber];
|
|
961
1003
|
const promises = [];
|
|
962
1004
|
channel.sustainPedal = false;
|
|
963
|
-
channel.scheduledNotes.forEach((
|
|
964
|
-
|
|
965
|
-
if (
|
|
966
|
-
const { noteNumber } =
|
|
1005
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1006
|
+
noteList.forEach((note) => {
|
|
1007
|
+
if (note) {
|
|
1008
|
+
const { noteNumber } = note;
|
|
967
1009
|
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
968
1010
|
promises.push(promise);
|
|
969
1011
|
}
|
|
@@ -1017,21 +1059,21 @@ class MidyGM2 {
|
|
|
1017
1059
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1018
1060
|
if (channel.channelPressure.amplitudeControl !== 1) {
|
|
1019
1061
|
activeNotes.forEach((activeNote) => {
|
|
1020
|
-
const gain = activeNote.
|
|
1021
|
-
activeNote.
|
|
1062
|
+
const gain = activeNote.volumeNode.gain.value;
|
|
1063
|
+
activeNote.volumeNode.gain
|
|
1022
1064
|
.cancelScheduledValues(now)
|
|
1023
1065
|
.setValueAtTime(gain * pressure, now);
|
|
1024
1066
|
});
|
|
1025
1067
|
}
|
|
1026
1068
|
}
|
|
1027
1069
|
handlePitchBendMessage(channelNumber, lsb, msb) {
|
|
1028
|
-
const pitchBend = msb * 128 + lsb;
|
|
1070
|
+
const pitchBend = msb * 128 + lsb - 8192;
|
|
1029
1071
|
this.setPitchBend(channelNumber, pitchBend);
|
|
1030
1072
|
}
|
|
1031
1073
|
setPitchBend(channelNumber, pitchBend) {
|
|
1032
1074
|
const channel = this.channels[channelNumber];
|
|
1033
1075
|
const prevPitchBend = channel.pitchBend;
|
|
1034
|
-
channel.pitchBend =
|
|
1076
|
+
channel.pitchBend = pitchBend / 8192;
|
|
1035
1077
|
const detuneChange = (channel.pitchBend - prevPitchBend) *
|
|
1036
1078
|
channel.pitchBendRange * 100;
|
|
1037
1079
|
this.updateDetune(channel, detuneChange);
|
|
@@ -1041,7 +1083,7 @@ class MidyGM2 {
|
|
|
1041
1083
|
case 0:
|
|
1042
1084
|
return this.setBankMSB(channelNumber, value);
|
|
1043
1085
|
case 1:
|
|
1044
|
-
return this.
|
|
1086
|
+
return this.setModulationDepth(channelNumber, value);
|
|
1045
1087
|
case 5:
|
|
1046
1088
|
return this.setPortamentoTime(channelNumber, value);
|
|
1047
1089
|
case 6:
|
|
@@ -1097,18 +1139,19 @@ class MidyGM2 {
|
|
|
1097
1139
|
const now = this.audioContext.currentTime;
|
|
1098
1140
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1099
1141
|
activeNotes.forEach((activeNote) => {
|
|
1100
|
-
if (activeNote.
|
|
1101
|
-
|
|
1102
|
-
gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
|
|
1142
|
+
if (activeNote.modulationDepth) {
|
|
1143
|
+
activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
|
|
1103
1144
|
}
|
|
1104
1145
|
else {
|
|
1146
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
1147
|
+
this.setPitch(activeNote, semitoneOffset);
|
|
1105
1148
|
this.startModulation(channel, activeNote, now);
|
|
1106
1149
|
}
|
|
1107
1150
|
});
|
|
1108
1151
|
}
|
|
1109
|
-
|
|
1152
|
+
setModulationDepth(channelNumber, modulation) {
|
|
1110
1153
|
const channel = this.channels[channelNumber];
|
|
1111
|
-
channel.
|
|
1154
|
+
channel.modulationDepth = (modulation / 127) * channel.modulationDepthRange;
|
|
1112
1155
|
this.updateModulation(channel);
|
|
1113
1156
|
}
|
|
1114
1157
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
@@ -1166,20 +1209,30 @@ class MidyGM2 {
|
|
|
1166
1209
|
this.channels[channelNumber].portamento = value >= 64;
|
|
1167
1210
|
}
|
|
1168
1211
|
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1169
|
-
const now = this.audioContext.currentTime;
|
|
1170
1212
|
const channel = this.channels[channelNumber];
|
|
1171
|
-
const reverbEffect =
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1213
|
+
const reverbEffect = this.reverbEffect;
|
|
1214
|
+
if (0 < reverbSendLevel) {
|
|
1215
|
+
const now = this.audioContext.currentTime;
|
|
1216
|
+
channel.reverbSendLevel = reverbSendLevel / 127;
|
|
1217
|
+
reverbEffect.output.gain.cancelScheduledValues(now);
|
|
1218
|
+
reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
|
|
1219
|
+
}
|
|
1220
|
+
else if (channel.reverbSendLevel !== 0) {
|
|
1221
|
+
channel.merger.disconnect(reverbEffect.input);
|
|
1222
|
+
}
|
|
1175
1223
|
}
|
|
1176
1224
|
setChorusSendLevel(channelNumber, chorusSendLevel) {
|
|
1177
|
-
const now = this.audioContext.currentTime;
|
|
1178
1225
|
const channel = this.channels[channelNumber];
|
|
1179
|
-
const chorusEffect =
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1226
|
+
const chorusEffect = this.chorusEffect;
|
|
1227
|
+
if (0 < chorusSendLevel) {
|
|
1228
|
+
const now = this.audioContext.currentTime;
|
|
1229
|
+
channel.chorusSendLevel = chorusSendLevel / 127;
|
|
1230
|
+
chorusEffect.output.gain.cancelScheduledValues(now);
|
|
1231
|
+
chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
|
|
1232
|
+
}
|
|
1233
|
+
else if (channel.chorusSendLevel !== 0) {
|
|
1234
|
+
channel.merger.disconnect(chorusEffect.input);
|
|
1235
|
+
}
|
|
1183
1236
|
}
|
|
1184
1237
|
setSostenutoPedal(channelNumber, value) {
|
|
1185
1238
|
const isOn = value >= 64;
|
|
@@ -1308,47 +1361,23 @@ class MidyGM2 {
|
|
|
1308
1361
|
handleModulationDepthRangeRPN(channelNumber) {
|
|
1309
1362
|
const channel = this.channels[channelNumber];
|
|
1310
1363
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1311
|
-
const modulationDepthRange = dataMSB + dataLSB / 128;
|
|
1364
|
+
const modulationDepthRange = (dataMSB + dataLSB / 128) * 100;
|
|
1312
1365
|
this.setModulationDepthRange(channelNumber, modulationDepthRange);
|
|
1313
1366
|
}
|
|
1314
1367
|
setModulationDepthRange(channelNumber, modulationDepthRange) {
|
|
1315
1368
|
const channel = this.channels[channelNumber];
|
|
1316
1369
|
channel.modulationDepthRange = modulationDepthRange;
|
|
1317
|
-
channel.
|
|
1370
|
+
channel.modulationDepth = (modulation / 127) * modulationDepthRange;
|
|
1318
1371
|
this.updateModulation(channel);
|
|
1319
1372
|
}
|
|
1320
1373
|
allSoundOff(channelNumber) {
|
|
1321
|
-
|
|
1322
|
-
const channel = this.channels[channelNumber];
|
|
1323
|
-
const velocity = 0;
|
|
1324
|
-
const stopPedal = true;
|
|
1325
|
-
const promises = [];
|
|
1326
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1327
|
-
const activeNote = this.getActiveNote(noteList, now);
|
|
1328
|
-
if (activeNote) {
|
|
1329
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1330
|
-
promises.push(notePromise);
|
|
1331
|
-
}
|
|
1332
|
-
});
|
|
1333
|
-
return promises;
|
|
1374
|
+
return this.stopChannelNotes(channelNumber, 0, true);
|
|
1334
1375
|
}
|
|
1335
1376
|
resetAllControllers(channelNumber) {
|
|
1336
1377
|
Object.assign(this.channels[channelNumber], this.effectSettings);
|
|
1337
1378
|
}
|
|
1338
1379
|
allNotesOff(channelNumber) {
|
|
1339
|
-
|
|
1340
|
-
const channel = this.channels[channelNumber];
|
|
1341
|
-
const velocity = 0;
|
|
1342
|
-
const stopPedal = false;
|
|
1343
|
-
const promises = [];
|
|
1344
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1345
|
-
const activeNote = this.getActiveNote(noteList, now);
|
|
1346
|
-
if (activeNote) {
|
|
1347
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1348
|
-
promises.push(notePromise);
|
|
1349
|
-
}
|
|
1350
|
-
});
|
|
1351
|
-
return promises;
|
|
1380
|
+
return this.stopChannelNotes(channelNumber, 0, false);
|
|
1352
1381
|
}
|
|
1353
1382
|
omniOff() {
|
|
1354
1383
|
this.omni = false;
|
|
@@ -1513,11 +1542,9 @@ class MidyGM2 {
|
|
|
1513
1542
|
}
|
|
1514
1543
|
setReverbType(type) {
|
|
1515
1544
|
this.reverb.time = this.getReverbTimeFromType(type);
|
|
1516
|
-
this.reverb.feedback = (type === 8) ? 0.
|
|
1517
|
-
const { audioContext,
|
|
1518
|
-
|
|
1519
|
-
channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1520
|
-
}
|
|
1545
|
+
this.reverb.feedback = (type === 8) ? 0.9 : 0.8;
|
|
1546
|
+
const { audioContext, options } = this;
|
|
1547
|
+
this.reverbEffect = options.reverbAlgorithm(audioContext);
|
|
1521
1548
|
}
|
|
1522
1549
|
getReverbTimeFromType(type) {
|
|
1523
1550
|
switch (type) {
|
|
@@ -1659,15 +1686,17 @@ class MidyGM2 {
|
|
|
1659
1686
|
return value * 0.00763;
|
|
1660
1687
|
}
|
|
1661
1688
|
setChorusSendToReverb(value) {
|
|
1662
|
-
const now = this.audioContext.currentTime;
|
|
1663
1689
|
const sendToReverb = this.getChorusSendToReverb(value);
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
chorusEffect.sendGain.gain
|
|
1690
|
+
if (0 < sendToReverb) {
|
|
1691
|
+
const now = this.audioContext.currentTime;
|
|
1692
|
+
this.chorus.sendToReverb = sendToReverb;
|
|
1693
|
+
this.chorusEffect.sendGain.gain
|
|
1668
1694
|
.cancelScheduledValues(now)
|
|
1669
1695
|
.setValueAtTime(sendToReverb, now);
|
|
1670
1696
|
}
|
|
1697
|
+
else if (this.chorus.sendToReverb !== 0) {
|
|
1698
|
+
this.chorusEffect.sendGain.disconnect(this.reverbEffect.input);
|
|
1699
|
+
}
|
|
1671
1700
|
}
|
|
1672
1701
|
getChorusSendToReverb(value) {
|
|
1673
1702
|
return value * 0.00787;
|
|
@@ -1709,6 +1738,9 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1709
1738
|
portamentoTime: 0,
|
|
1710
1739
|
reverbSendLevel: 0,
|
|
1711
1740
|
chorusSendLevel: 0,
|
|
1741
|
+
vibratoRate: 1,
|
|
1742
|
+
vibratoDepth: 1,
|
|
1743
|
+
vibratoDelay: 1,
|
|
1712
1744
|
bank: 121 * 128,
|
|
1713
1745
|
bankMSB: 121,
|
|
1714
1746
|
bankLSB: 0,
|
|
@@ -1718,7 +1750,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
1718
1750
|
pitchBend: 0,
|
|
1719
1751
|
fineTuning: 0, // cb
|
|
1720
1752
|
coarseTuning: 0, // cb
|
|
1721
|
-
modulationDepthRange:
|
|
1753
|
+
modulationDepthRange: 50, // cent
|
|
1722
1754
|
}
|
|
1723
1755
|
});
|
|
1724
1756
|
Object.defineProperty(MidyGM2, "effectSettings", {
|
|
@@ -1727,7 +1759,7 @@ Object.defineProperty(MidyGM2, "effectSettings", {
|
|
|
1727
1759
|
writable: true,
|
|
1728
1760
|
value: {
|
|
1729
1761
|
expression: 1,
|
|
1730
|
-
|
|
1762
|
+
modulationDepth: 0,
|
|
1731
1763
|
sustainPedal: false,
|
|
1732
1764
|
portamento: false,
|
|
1733
1765
|
sostenutoPedal: false,
|