@marmooo/midy 0.1.2 → 0.1.4
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 +19 -13
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +171 -131
- package/esm/midy-GM2.d.ts +22 -14
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +186 -133
- package/esm/midy-GMLite.d.ts +17 -13
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +159 -131
- package/esm/midy.d.ts +30 -16
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +266 -166
- 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 +19 -13
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +171 -131
- package/script/midy-GM2.d.ts +22 -14
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +186 -133
- package/script/midy-GMLite.d.ts +17 -13
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +159 -131
- package/script/midy.d.ts +30 -16
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +266 -166
- 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.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Midy = 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,
|
|
@@ -413,7 +419,7 @@ class Midy {
|
|
|
413
419
|
const t = this.audioContext.currentTime + offset;
|
|
414
420
|
queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
|
|
415
421
|
if (this.isPausing) {
|
|
416
|
-
await this.stopNotes();
|
|
422
|
+
await this.stopNotes(0, true);
|
|
417
423
|
this.notePromises = [];
|
|
418
424
|
resolve();
|
|
419
425
|
this.isPausing = false;
|
|
@@ -421,7 +427,7 @@ class Midy {
|
|
|
421
427
|
return;
|
|
422
428
|
}
|
|
423
429
|
else if (this.isStopping) {
|
|
424
|
-
await this.stopNotes();
|
|
430
|
+
await this.stopNotes(0, true);
|
|
425
431
|
this.notePromises = [];
|
|
426
432
|
resolve();
|
|
427
433
|
this.isStopping = false;
|
|
@@ -429,7 +435,7 @@ class Midy {
|
|
|
429
435
|
return;
|
|
430
436
|
}
|
|
431
437
|
else if (this.isSeeking) {
|
|
432
|
-
this.stopNotes();
|
|
438
|
+
this.stopNotes(0, true);
|
|
433
439
|
this.startTime = this.audioContext.currentTime;
|
|
434
440
|
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
435
441
|
offset = this.resumeTime - this.startTime;
|
|
@@ -544,21 +550,25 @@ class Midy {
|
|
|
544
550
|
}
|
|
545
551
|
return { instruments, timeline };
|
|
546
552
|
}
|
|
547
|
-
|
|
553
|
+
async stopChannelNotes(channelNumber, velocity, stopPedal) {
|
|
548
554
|
const now = this.audioContext.currentTime;
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
});
|
|
559
|
-
});
|
|
560
|
-
channel.scheduledNotes.clear();
|
|
555
|
+
const channel = this.channels[channelNumber];
|
|
556
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
557
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
558
|
+
const note = noteList[i];
|
|
559
|
+
if (!note)
|
|
560
|
+
continue;
|
|
561
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
|
|
562
|
+
this.notePromises.push(promise);
|
|
563
|
+
}
|
|
561
564
|
});
|
|
565
|
+
channel.scheduledNotes.clear();
|
|
566
|
+
await Promise.all(this.notePromises);
|
|
567
|
+
}
|
|
568
|
+
stopNotes(velocity, stopPedal) {
|
|
569
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
570
|
+
this.stopChannelNotes(i, velocity, stopPedal);
|
|
571
|
+
}
|
|
562
572
|
return Promise.all(this.notePromises);
|
|
563
573
|
}
|
|
564
574
|
async start() {
|
|
@@ -771,98 +781,139 @@ class Midy {
|
|
|
771
781
|
return instrumentKey.playbackRate(noteNumber) *
|
|
772
782
|
Math.pow(2, semitoneOffset / 12);
|
|
773
783
|
}
|
|
774
|
-
setVolumeEnvelope(note) {
|
|
784
|
+
setVolumeEnvelope(channel, note) {
|
|
775
785
|
const { instrumentKey, startTime } = note;
|
|
776
|
-
note.gainNode = new GainNode(this.audioContext, { gain: 0 });
|
|
777
786
|
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
778
787
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
779
788
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
780
|
-
const volAttack = volDelay + instrumentKey.volAttack;
|
|
789
|
+
const volAttack = volDelay + instrumentKey.volAttack * channel.attackTime;
|
|
781
790
|
const volHold = volAttack + instrumentKey.volHold;
|
|
782
|
-
const volDecay = volHold + instrumentKey.volDecay;
|
|
783
|
-
note.
|
|
791
|
+
const volDecay = volHold + instrumentKey.volDecay * channel.decayTime;
|
|
792
|
+
note.volumeNode.gain
|
|
793
|
+
.cancelScheduledValues(startTime)
|
|
794
|
+
.setValueAtTime(0, startTime)
|
|
784
795
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
785
796
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
786
797
|
.setValueAtTime(attackVolume, volHold)
|
|
787
798
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
788
799
|
}
|
|
800
|
+
setPitch(note, semitoneOffset) {
|
|
801
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
802
|
+
const modEnvToPitch = instrumentKey.modEnvToPitch / 100;
|
|
803
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
804
|
+
if (modEnvToPitch === 0)
|
|
805
|
+
return;
|
|
806
|
+
const basePitch = note.bufferSource.playbackRate.value;
|
|
807
|
+
const peekPitch = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset + modEnvToPitch);
|
|
808
|
+
const modDelay = startTime + instrumentKey.modDelay;
|
|
809
|
+
const modAttack = modDelay + instrumentKey.modAttack;
|
|
810
|
+
const modHold = modAttack + instrumentKey.modHold;
|
|
811
|
+
const modDecay = modHold + instrumentKey.modDecay;
|
|
812
|
+
note.bufferSource.playbackRate.value
|
|
813
|
+
.setValueAtTime(basePitch, modDelay)
|
|
814
|
+
.exponentialRampToValueAtTime(peekPitch, modAttack)
|
|
815
|
+
.setValueAtTime(peekPitch, modHold)
|
|
816
|
+
.linearRampToValueAtTime(basePitch, modDecay);
|
|
817
|
+
}
|
|
818
|
+
clampCutoffFrequency(frequency) {
|
|
819
|
+
const minFrequency = 20; // min Hz of initialFilterFc
|
|
820
|
+
const maxFrequency = 20000; // max Hz of initialFilterFc
|
|
821
|
+
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
822
|
+
}
|
|
789
823
|
setFilterEnvelope(channel, note) {
|
|
790
|
-
const { instrumentKey,
|
|
824
|
+
const { instrumentKey, noteNumber, startTime } = note;
|
|
791
825
|
const softPedalFactor = 1 -
|
|
792
826
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
793
|
-
const maxFreq = this.audioContext.sampleRate / 2;
|
|
794
827
|
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
795
|
-
softPedalFactor;
|
|
796
|
-
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
797
|
-
const sustainFreq =
|
|
798
|
-
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain)
|
|
828
|
+
softPedalFactor * channel.brightness;
|
|
829
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor * channel.brightness;
|
|
830
|
+
const sustainFreq = baseFreq +
|
|
831
|
+
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
832
|
+
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
833
|
+
const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
|
|
834
|
+
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
799
835
|
const modDelay = startTime + instrumentKey.modDelay;
|
|
800
836
|
const modAttack = modDelay + instrumentKey.modAttack;
|
|
801
837
|
const modHold = modAttack + instrumentKey.modHold;
|
|
802
838
|
const modDecay = modHold + instrumentKey.modDecay;
|
|
803
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
804
|
-
const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
|
|
805
|
-
const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
|
|
806
|
-
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
807
|
-
type: "lowpass",
|
|
808
|
-
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
809
|
-
frequency: adjustedBaseFreq,
|
|
810
|
-
});
|
|
811
839
|
note.filterNode.frequency
|
|
840
|
+
.cancelScheduledValues(startTime)
|
|
841
|
+
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
812
842
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
813
843
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
814
844
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
815
845
|
.linearRampToValueAtTime(adjustedSustainFreq, modDecay);
|
|
816
|
-
note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
|
|
817
846
|
}
|
|
818
|
-
startModulation(channel, note,
|
|
847
|
+
startModulation(channel, note, startTime) {
|
|
819
848
|
const { instrumentKey } = note;
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
});
|
|
823
|
-
note.modLFO = new OscillatorNode(this.audioContext, {
|
|
849
|
+
const { modLfoToPitch, modLfoToVolume } = instrumentKey;
|
|
850
|
+
note.modulationLFO = new OscillatorNode(this.audioContext, {
|
|
824
851
|
frequency: this.centToHz(instrumentKey.freqModLFO),
|
|
825
852
|
});
|
|
826
|
-
note.
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
const { instrumentKey } = note;
|
|
834
|
-
note.vibLFOGain = new GainNode(this.audioContext, {
|
|
835
|
-
gain: channel.vibratoDepth,
|
|
853
|
+
note.filterDepth = new GainNode(this.audioContext, {
|
|
854
|
+
gain: instrumentKey.modLfoToFilterFc,
|
|
855
|
+
});
|
|
856
|
+
const modulationDepth = Math.abs(modLfoToPitch) + channel.modulationDepth;
|
|
857
|
+
const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
|
|
858
|
+
note.modulationDepth = new GainNode(this.audioContext, {
|
|
859
|
+
gain: modulationDepth * modulationDepthSign,
|
|
836
860
|
});
|
|
837
|
-
|
|
838
|
-
|
|
861
|
+
const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
862
|
+
const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
|
|
863
|
+
note.volumeDepth = new GainNode(this.audioContext, {
|
|
864
|
+
gain: volumeDepth * volumeDepthSign,
|
|
865
|
+
});
|
|
866
|
+
note.modulationLFO.start(startTime + instrumentKey.delayModLFO);
|
|
867
|
+
note.modulationLFO.connect(note.filterDepth);
|
|
868
|
+
note.filterDepth.connect(note.filterNode.frequency);
|
|
869
|
+
note.modulationLFO.connect(note.modulationDepth);
|
|
870
|
+
note.modulationDepth.connect(note.bufferSource.detune);
|
|
871
|
+
note.modulationLFO.connect(note.volumeDepth);
|
|
872
|
+
note.volumeDepth.connect(note.volumeNode.gain);
|
|
873
|
+
}
|
|
874
|
+
startVibrato(channel, note, startTime) {
|
|
875
|
+
const { instrumentKey } = note;
|
|
876
|
+
const { vibLfoToPitch } = instrumentKey;
|
|
877
|
+
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
878
|
+
frequency: this.centToHz(instrumentKey.freqVibLFO) *
|
|
839
879
|
channel.vibratoRate,
|
|
840
880
|
});
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
note.
|
|
881
|
+
const vibratoDepth = Math.abs(vibLfoToPitch) * channel.vibratoDepth;
|
|
882
|
+
const vibratoDepthSign = 0 < vibLfoToPitch;
|
|
883
|
+
note.vibratoDepth = new GainNode(this.audioContext, {
|
|
884
|
+
gain: vibratoDepth * vibratoDepthSign,
|
|
885
|
+
});
|
|
886
|
+
note.vibratoLFO.start(startTime + instrumentKey.delayVibLFO * channel.vibratoDelay);
|
|
887
|
+
note.vibratoLFO.connect(note.vibratoDepth);
|
|
888
|
+
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
844
889
|
}
|
|
845
890
|
async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
|
|
846
891
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
847
892
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
848
893
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
849
|
-
note.
|
|
850
|
-
this.
|
|
894
|
+
note.volumeNode = new GainNode(this.audioContext);
|
|
895
|
+
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
896
|
+
type: "lowpass",
|
|
897
|
+
Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
|
|
898
|
+
});
|
|
899
|
+
this.setVolumeEnvelope(channel, note);
|
|
851
900
|
this.setFilterEnvelope(channel, note);
|
|
852
|
-
if (channel.
|
|
853
|
-
|
|
854
|
-
|
|
901
|
+
if (0 < channel.vibratoDepth) {
|
|
902
|
+
this.startVibrato(channel, note, startTime);
|
|
903
|
+
}
|
|
904
|
+
if (0 < channel.modulationDepth) {
|
|
905
|
+
this.setPitch(note, semitoneOffset);
|
|
906
|
+
this.startModulation(channel, note, startTime);
|
|
855
907
|
}
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
this.startVibrato(channel, note, delayVibLFO);
|
|
908
|
+
else {
|
|
909
|
+
note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
|
|
859
910
|
}
|
|
860
911
|
if (this.mono && channel.currentBufferSource) {
|
|
861
912
|
channel.currentBufferSource.stop(startTime);
|
|
862
913
|
channel.currentBufferSource = note.bufferSource;
|
|
863
914
|
}
|
|
864
915
|
note.bufferSource.connect(note.filterNode);
|
|
865
|
-
note.filterNode.connect(note.
|
|
916
|
+
note.filterNode.connect(note.volumeNode);
|
|
866
917
|
note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
|
|
867
918
|
return note;
|
|
868
919
|
}
|
|
@@ -887,8 +938,8 @@ class Midy {
|
|
|
887
938
|
if (!instrumentKey)
|
|
888
939
|
return;
|
|
889
940
|
const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
|
|
890
|
-
note.
|
|
891
|
-
note.
|
|
941
|
+
note.volumeNode.connect(channel.gainL);
|
|
942
|
+
note.volumeNode.connect(channel.gainR);
|
|
892
943
|
if (channel.sostenutoPedal) {
|
|
893
944
|
channel.sostenutoNotes.set(noteNumber, note);
|
|
894
945
|
}
|
|
@@ -904,7 +955,7 @@ class Midy {
|
|
|
904
955
|
const now = this.audioContext.currentTime;
|
|
905
956
|
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
|
|
906
957
|
}
|
|
907
|
-
scheduleNoteRelease(channelNumber, noteNumber,
|
|
958
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
|
|
908
959
|
const channel = this.channels[channelNumber];
|
|
909
960
|
if (stopPedal && channel.sustainPedal)
|
|
910
961
|
return;
|
|
@@ -919,20 +970,15 @@ class Midy {
|
|
|
919
970
|
continue;
|
|
920
971
|
if (note.ending)
|
|
921
972
|
continue;
|
|
922
|
-
const velocityRate = (velocity + 127) / 127;
|
|
923
973
|
const volEndTime = stopTime +
|
|
924
|
-
note.instrumentKey.volRelease *
|
|
925
|
-
note.
|
|
974
|
+
note.instrumentKey.volRelease * channel.releaseTime;
|
|
975
|
+
note.volumeNode.gain
|
|
926
976
|
.cancelScheduledValues(stopTime)
|
|
927
977
|
.linearRampToValueAtTime(0, volEndTime);
|
|
928
|
-
const
|
|
929
|
-
const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
|
|
930
|
-
const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
|
|
931
|
-
const modEndTime = stopTime +
|
|
932
|
-
note.instrumentKey.modRelease * velocityRate;
|
|
978
|
+
const modRelease = stopTime + note.instrumentKey.modRelease;
|
|
933
979
|
note.filterNode.frequency
|
|
934
980
|
.cancelScheduledValues(stopTime)
|
|
935
|
-
.linearRampToValueAtTime(
|
|
981
|
+
.linearRampToValueAtTime(0, modRelease);
|
|
936
982
|
note.ending = true;
|
|
937
983
|
this.scheduleTask(() => {
|
|
938
984
|
note.bufferSource.loop = false;
|
|
@@ -941,16 +987,18 @@ class Midy {
|
|
|
941
987
|
note.bufferSource.onended = () => {
|
|
942
988
|
scheduledNotes[i] = null;
|
|
943
989
|
note.bufferSource.disconnect();
|
|
990
|
+
note.volumeNode.disconnect();
|
|
944
991
|
note.filterNode.disconnect();
|
|
945
|
-
note.
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
992
|
+
if (note.volumeDepth)
|
|
993
|
+
note.volumeDepth.disconnect();
|
|
994
|
+
if (note.modulationDepth)
|
|
995
|
+
note.modulationDepth.disconnect();
|
|
996
|
+
if (note.modulationLFO)
|
|
997
|
+
note.modulationLFO.stop();
|
|
998
|
+
if (note.vibratoDepth)
|
|
999
|
+
note.vibratoDepth.disconnect();
|
|
1000
|
+
if (note.vibratoLFO)
|
|
1001
|
+
note.vibratoLFO.stop();
|
|
954
1002
|
resolve();
|
|
955
1003
|
};
|
|
956
1004
|
note.bufferSource.stop(volEndTime);
|
|
@@ -966,14 +1014,15 @@ class Midy {
|
|
|
966
1014
|
const channel = this.channels[channelNumber];
|
|
967
1015
|
const promises = [];
|
|
968
1016
|
channel.sustainPedal = false;
|
|
969
|
-
channel.scheduledNotes.forEach((
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1017
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1018
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1019
|
+
const note = noteList[i];
|
|
1020
|
+
if (!note)
|
|
1021
|
+
continue;
|
|
1022
|
+
const { noteNumber } = note;
|
|
1023
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
1024
|
+
promises.push(promise);
|
|
1025
|
+
}
|
|
977
1026
|
});
|
|
978
1027
|
return promises;
|
|
979
1028
|
}
|
|
@@ -1020,8 +1069,8 @@ class Midy {
|
|
|
1020
1069
|
if (channel.polyphonicKeyPressure.amplitudeControl !== 1) {
|
|
1021
1070
|
if (activeNotes.has(noteNumber)) {
|
|
1022
1071
|
const activeNote = activeNotes.get(noteNumber);
|
|
1023
|
-
const gain = activeNote.
|
|
1024
|
-
activeNote.
|
|
1072
|
+
const gain = activeNote.volumeNode.gain.value;
|
|
1073
|
+
activeNote.volumeNode.gain
|
|
1025
1074
|
.cancelScheduledValues(now)
|
|
1026
1075
|
.setValueAtTime(gain * pressure, now);
|
|
1027
1076
|
}
|
|
@@ -1040,8 +1089,8 @@ class Midy {
|
|
|
1040
1089
|
const activeNotes = this.getActiveNotes(channel, now);
|
|
1041
1090
|
if (channel.channelPressure.amplitudeControl !== 1) {
|
|
1042
1091
|
activeNotes.forEach((activeNote) => {
|
|
1043
|
-
const gain = activeNote.
|
|
1044
|
-
activeNote.
|
|
1092
|
+
const gain = activeNote.volumeNode.gain.value;
|
|
1093
|
+
activeNote.volumeNode.gain
|
|
1045
1094
|
.cancelScheduledValues(now)
|
|
1046
1095
|
.setValueAtTime(gain * pressure, now);
|
|
1047
1096
|
});
|
|
@@ -1064,7 +1113,7 @@ class Midy {
|
|
|
1064
1113
|
case 0:
|
|
1065
1114
|
return this.setBankMSB(channelNumber, value);
|
|
1066
1115
|
case 1:
|
|
1067
|
-
return this.
|
|
1116
|
+
return this.setModulationDepth(channelNumber, value);
|
|
1068
1117
|
case 5:
|
|
1069
1118
|
return this.setPortamentoTime(channelNumber, value);
|
|
1070
1119
|
case 6:
|
|
@@ -1087,7 +1136,16 @@ class Midy {
|
|
|
1087
1136
|
return this.setSostenutoPedal(channelNumber, value);
|
|
1088
1137
|
case 67:
|
|
1089
1138
|
return this.setSoftPedal(channelNumber, value);
|
|
1090
|
-
|
|
1139
|
+
case 71:
|
|
1140
|
+
return this.setFilterResonance(channelNumber, value);
|
|
1141
|
+
case 72:
|
|
1142
|
+
return this.setReleaseTime(channelNumber, value);
|
|
1143
|
+
case 73:
|
|
1144
|
+
return this.setAttackTime(channelNumber, value);
|
|
1145
|
+
case 74:
|
|
1146
|
+
return this.setBrightness(channelNumber, value);
|
|
1147
|
+
case 75:
|
|
1148
|
+
return this.setDecayTime(channelNumber, value);
|
|
1091
1149
|
case 76:
|
|
1092
1150
|
return this.setVibratoRate(channelNumber, value);
|
|
1093
1151
|
case 77:
|
|
@@ -1129,20 +1187,25 @@ class Midy {
|
|
|
1129
1187
|
}
|
|
1130
1188
|
updateModulation(channel) {
|
|
1131
1189
|
const now = this.audioContext.currentTime;
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1190
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1191
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1192
|
+
const note = noteList[i];
|
|
1193
|
+
if (!note)
|
|
1194
|
+
continue;
|
|
1195
|
+
if (note.modulationDepth) {
|
|
1196
|
+
note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
|
|
1197
|
+
}
|
|
1198
|
+
else {
|
|
1199
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
1200
|
+
this.setPitch(note, semitoneOffset);
|
|
1201
|
+
this.startModulation(channel, note, now);
|
|
1202
|
+
}
|
|
1140
1203
|
}
|
|
1141
1204
|
});
|
|
1142
1205
|
}
|
|
1143
|
-
|
|
1206
|
+
setModulationDepth(channelNumber, modulation) {
|
|
1144
1207
|
const channel = this.channels[channelNumber];
|
|
1145
|
-
channel.
|
|
1208
|
+
channel.modulationDepth = (modulation / 127) * channel.modulationDepthRange;
|
|
1146
1209
|
this.updateModulation(channel);
|
|
1147
1210
|
}
|
|
1148
1211
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
@@ -1242,23 +1305,75 @@ class Midy {
|
|
|
1242
1305
|
const channel = this.channels[channelNumber];
|
|
1243
1306
|
channel.softPedal = softPedal / 127;
|
|
1244
1307
|
}
|
|
1308
|
+
setFilterResonance(channelNumber, filterResonance) {
|
|
1309
|
+
const now = this.audioContext.currentTime;
|
|
1310
|
+
const channel = this.channels[channelNumber];
|
|
1311
|
+
channel.filterResonance = filterResonance / 64;
|
|
1312
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1313
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1314
|
+
const note = noteList[i];
|
|
1315
|
+
if (!note)
|
|
1316
|
+
continue;
|
|
1317
|
+
const Q = note.instrumentKey.initialFilterQ / 10 *
|
|
1318
|
+
channel.filterResonance;
|
|
1319
|
+
note.filterNode.Q.setValueAtTime(Q, now);
|
|
1320
|
+
}
|
|
1321
|
+
});
|
|
1322
|
+
}
|
|
1323
|
+
setReleaseTime(channelNumber, releaseTime) {
|
|
1324
|
+
const channel = this.channels[channelNumber];
|
|
1325
|
+
channel.releaseTime = releaseTime / 64;
|
|
1326
|
+
}
|
|
1327
|
+
setAttackTime(channelNumber, attackTime) {
|
|
1328
|
+
const now = this.audioContext.currentTime;
|
|
1329
|
+
const channel = this.channels[channelNumber];
|
|
1330
|
+
channel.attackTime = attackTime / 64;
|
|
1331
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1332
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1333
|
+
const note = noteList[i];
|
|
1334
|
+
if (!note)
|
|
1335
|
+
continue;
|
|
1336
|
+
if (note.startTime < now)
|
|
1337
|
+
continue;
|
|
1338
|
+
this.setVolumeEnvelope(channel, note);
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
setBrightness(channelNumber, brightness) {
|
|
1343
|
+
const channel = this.channels[channelNumber];
|
|
1344
|
+
channel.brightness = brightness / 64;
|
|
1345
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1346
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1347
|
+
const note = noteList[i];
|
|
1348
|
+
if (!note)
|
|
1349
|
+
continue;
|
|
1350
|
+
this.setFilterEnvelope(channel, note);
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
}
|
|
1354
|
+
setDecayTime(channelNumber, dacayTime) {
|
|
1355
|
+
const channel = this.channels[channelNumber];
|
|
1356
|
+
channel.decayTime = dacayTime / 64;
|
|
1357
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1358
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1359
|
+
const note = noteList[i];
|
|
1360
|
+
if (!note)
|
|
1361
|
+
continue;
|
|
1362
|
+
this.setVolumeEnvelope(channel, note);
|
|
1363
|
+
}
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1245
1366
|
setVibratoRate(channelNumber, vibratoRate) {
|
|
1246
1367
|
const channel = this.channels[channelNumber];
|
|
1247
|
-
channel.vibratoRate = vibratoRate /
|
|
1368
|
+
channel.vibratoRate = vibratoRate / 64;
|
|
1248
1369
|
}
|
|
1249
1370
|
setVibratoDepth(channelNumber, vibratoDepth) {
|
|
1250
1371
|
const channel = this.channels[channelNumber];
|
|
1251
|
-
channel.vibratoDepth = vibratoDepth /
|
|
1372
|
+
channel.vibratoDepth = vibratoDepth / 64;
|
|
1252
1373
|
}
|
|
1253
1374
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1254
|
-
// Access Virus: 0-10sec
|
|
1255
|
-
// Elektron: 0-5sec
|
|
1256
|
-
// Korg: 0-5sec
|
|
1257
|
-
// Nord: 0-5sec
|
|
1258
|
-
// Roland: 0-5sec
|
|
1259
|
-
// Yamaha: 0-8sec
|
|
1260
1375
|
const channel = this.channels[channelNumber];
|
|
1261
|
-
channel.vibratoDelay = vibratoDelay /
|
|
1376
|
+
channel.vibratoDelay = vibratoDelay / 64;
|
|
1262
1377
|
}
|
|
1263
1378
|
limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
|
|
1264
1379
|
if (maxLSB < channel.dataLSB) {
|
|
@@ -1328,13 +1443,17 @@ class Midy {
|
|
|
1328
1443
|
}
|
|
1329
1444
|
updateDetune(channel, detuneChange) {
|
|
1330
1445
|
const now = this.audioContext.currentTime;
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
.
|
|
1446
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1447
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1448
|
+
const note = noteList[i];
|
|
1449
|
+
if (!note)
|
|
1450
|
+
continue;
|
|
1451
|
+
const { bufferSource } = note;
|
|
1452
|
+
const detune = bufferSource.detune.value + detuneChange;
|
|
1453
|
+
bufferSource.detune
|
|
1454
|
+
.cancelScheduledValues(now)
|
|
1455
|
+
.setValueAtTime(detune, now);
|
|
1456
|
+
}
|
|
1338
1457
|
});
|
|
1339
1458
|
}
|
|
1340
1459
|
handlePitchBendRangeRPN(channelNumber) {
|
|
@@ -1380,47 +1499,23 @@ class Midy {
|
|
|
1380
1499
|
handleModulationDepthRangeRPN(channelNumber) {
|
|
1381
1500
|
const channel = this.channels[channelNumber];
|
|
1382
1501
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1383
|
-
const modulationDepthRange = dataMSB + dataLSB / 128;
|
|
1502
|
+
const modulationDepthRange = (dataMSB + dataLSB / 128) * 100;
|
|
1384
1503
|
this.setModulationDepthRange(channelNumber, modulationDepthRange);
|
|
1385
1504
|
}
|
|
1386
1505
|
setModulationDepthRange(channelNumber, modulationDepthRange) {
|
|
1387
1506
|
const channel = this.channels[channelNumber];
|
|
1388
1507
|
channel.modulationDepthRange = modulationDepthRange;
|
|
1389
|
-
channel.
|
|
1508
|
+
channel.modulationDepth = (modulation / 127) * modulationDepthRange;
|
|
1390
1509
|
this.updateModulation(channel);
|
|
1391
1510
|
}
|
|
1392
1511
|
allSoundOff(channelNumber) {
|
|
1393
|
-
|
|
1394
|
-
const channel = this.channels[channelNumber];
|
|
1395
|
-
const velocity = 0;
|
|
1396
|
-
const stopPedal = true;
|
|
1397
|
-
const promises = [];
|
|
1398
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1399
|
-
const activeNote = this.getActiveNote(noteList, now);
|
|
1400
|
-
if (activeNote) {
|
|
1401
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
|
|
1402
|
-
promises.push(notePromise);
|
|
1403
|
-
}
|
|
1404
|
-
});
|
|
1405
|
-
return promises;
|
|
1512
|
+
return this.stopChannelNotes(channelNumber, 0, true);
|
|
1406
1513
|
}
|
|
1407
1514
|
resetAllControllers(channelNumber) {
|
|
1408
1515
|
Object.assign(this.channels[channelNumber], this.effectSettings);
|
|
1409
1516
|
}
|
|
1410
1517
|
allNotesOff(channelNumber) {
|
|
1411
|
-
|
|
1412
|
-
const channel = this.channels[channelNumber];
|
|
1413
|
-
const velocity = 0;
|
|
1414
|
-
const stopPedal = false;
|
|
1415
|
-
const promises = [];
|
|
1416
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1417
|
-
const activeNote = this.getActiveNote(noteList, now);
|
|
1418
|
-
if (activeNote) {
|
|
1419
|
-
const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
|
|
1420
|
-
promises.push(notePromise);
|
|
1421
|
-
}
|
|
1422
|
-
});
|
|
1423
|
-
return promises;
|
|
1518
|
+
return this.stopChannelNotes(channelNumber, 0, false);
|
|
1424
1519
|
}
|
|
1425
1520
|
omniOff() {
|
|
1426
1521
|
this.omni = false;
|
|
@@ -1779,11 +1874,16 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
1779
1874
|
volume: 100 / 127,
|
|
1780
1875
|
pan: 64,
|
|
1781
1876
|
portamentoTime: 0,
|
|
1877
|
+
filterResonance: 1,
|
|
1878
|
+
releaseTime: 1,
|
|
1879
|
+
attackTime: 1,
|
|
1880
|
+
brightness: 1,
|
|
1881
|
+
decayTime: 1,
|
|
1782
1882
|
reverbSendLevel: 0,
|
|
1783
1883
|
chorusSendLevel: 0,
|
|
1784
|
-
vibratoRate:
|
|
1785
|
-
vibratoDepth:
|
|
1786
|
-
vibratoDelay:
|
|
1884
|
+
vibratoRate: 1,
|
|
1885
|
+
vibratoDepth: 1,
|
|
1886
|
+
vibratoDelay: 1,
|
|
1787
1887
|
bank: 121 * 128,
|
|
1788
1888
|
bankMSB: 121,
|
|
1789
1889
|
bankLSB: 0,
|
|
@@ -1793,7 +1893,7 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
1793
1893
|
pitchBend: 0,
|
|
1794
1894
|
fineTuning: 0, // cb
|
|
1795
1895
|
coarseTuning: 0, // cb
|
|
1796
|
-
modulationDepthRange:
|
|
1896
|
+
modulationDepthRange: 50, // cent
|
|
1797
1897
|
}
|
|
1798
1898
|
});
|
|
1799
1899
|
Object.defineProperty(Midy, "effectSettings", {
|
|
@@ -1802,7 +1902,7 @@ Object.defineProperty(Midy, "effectSettings", {
|
|
|
1802
1902
|
writable: true,
|
|
1803
1903
|
value: {
|
|
1804
1904
|
expression: 1,
|
|
1805
|
-
|
|
1905
|
+
modulationDepth: 0,
|
|
1806
1906
|
sustainPedal: false,
|
|
1807
1907
|
portamento: false,
|
|
1808
1908
|
sostenutoPedal: false,
|