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