@marmooo/midy 0.1.3 → 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/midy-GM1.d.ts +3 -2
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +66 -55
- package/esm/midy-GM2.d.ts +3 -2
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +61 -47
- package/esm/midy-GMLite.d.ts +3 -2
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +64 -53
- package/esm/midy.d.ts +14 -3
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +140 -53
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +3 -2
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +66 -55
- package/script/midy-GM2.d.ts +3 -2
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +61 -47
- package/script/midy-GMLite.d.ts +3 -2
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +64 -53
- package/script/midy.d.ts +14 -3
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +140 -53
package/esm/midy-GMLite.js
CHANGED
|
@@ -420,12 +420,13 @@ export class MidyGMLite {
|
|
|
420
420
|
const now = this.audioContext.currentTime;
|
|
421
421
|
const channel = this.channels[channelNumber];
|
|
422
422
|
channel.scheduledNotes.forEach((noteList) => {
|
|
423
|
-
noteList.
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
423
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
424
|
+
const note = noteList[i];
|
|
425
|
+
if (!note)
|
|
426
|
+
continue;
|
|
427
|
+
const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
|
|
428
|
+
this.notePromises.push(promise);
|
|
429
|
+
}
|
|
429
430
|
});
|
|
430
431
|
channel.scheduledNotes.clear();
|
|
431
432
|
await Promise.all(this.notePromises);
|
|
@@ -516,7 +517,6 @@ export class MidyGMLite {
|
|
|
516
517
|
}
|
|
517
518
|
setVolumeEnvelope(note) {
|
|
518
519
|
const { instrumentKey, startTime } = note;
|
|
519
|
-
note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
|
|
520
520
|
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
521
521
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
522
522
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
@@ -524,6 +524,8 @@ export class MidyGMLite {
|
|
|
524
524
|
const volHold = volAttack + instrumentKey.volHold;
|
|
525
525
|
const volDecay = volHold + instrumentKey.volDecay;
|
|
526
526
|
note.volumeNode.gain
|
|
527
|
+
.cancelScheduledValues(startTime)
|
|
528
|
+
.setValueAtTime(0, startTime)
|
|
527
529
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
528
530
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
529
531
|
.setValueAtTime(attackVolume, volHold)
|
|
@@ -547,29 +549,27 @@ export class MidyGMLite {
|
|
|
547
549
|
.setValueAtTime(peekPitch, modHold)
|
|
548
550
|
.linearRampToValueAtTime(basePitch, modDecay);
|
|
549
551
|
}
|
|
550
|
-
|
|
551
|
-
const
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const
|
|
552
|
+
clampCutoffFrequency(frequency) {
|
|
553
|
+
const minFrequency = 20; // min Hz of initialFilterFc
|
|
554
|
+
const maxFrequency = 20000; // max Hz of initialFilterFc
|
|
555
|
+
return Math.max(minFrequency, Math.min(frequency, maxFrequency));
|
|
556
|
+
}
|
|
557
|
+
setFilterEnvelope(note) {
|
|
558
|
+
const { instrumentKey, startTime } = note;
|
|
559
|
+
const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
|
|
560
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc);
|
|
558
561
|
const sustainFreq = baseFreq +
|
|
559
562
|
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
560
|
-
const adjustedBaseFreq =
|
|
561
|
-
const adjustedPeekFreq =
|
|
562
|
-
const adjustedSustainFreq =
|
|
563
|
+
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
564
|
+
const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
|
|
565
|
+
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
563
566
|
const modDelay = startTime + instrumentKey.modDelay;
|
|
564
567
|
const modAttack = modDelay + instrumentKey.modAttack;
|
|
565
568
|
const modHold = modAttack + instrumentKey.modHold;
|
|
566
569
|
const modDecay = modHold + instrumentKey.modDecay;
|
|
567
|
-
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
568
|
-
type: "lowpass",
|
|
569
|
-
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
570
|
-
frequency: adjustedBaseFreq,
|
|
571
|
-
});
|
|
572
570
|
note.filterNode.frequency
|
|
571
|
+
.cancelScheduledValues(startTime)
|
|
572
|
+
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
573
573
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
574
574
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
575
575
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
@@ -606,8 +606,13 @@ export class MidyGMLite {
|
|
|
606
606
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
607
607
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
608
608
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
609
|
-
this.
|
|
609
|
+
note.volumeNode = new GainNode(this.audioContext);
|
|
610
|
+
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
611
|
+
type: "lowpass",
|
|
612
|
+
Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
|
|
613
|
+
});
|
|
610
614
|
this.setVolumeEnvelope(note);
|
|
615
|
+
this.setFilterEnvelope(note);
|
|
611
616
|
if (0 < channel.modulationDepth) {
|
|
612
617
|
this.setPitch(note, semitoneOffset);
|
|
613
618
|
this.startModulation(channel, note, startTime);
|
|
@@ -646,7 +651,7 @@ export class MidyGMLite {
|
|
|
646
651
|
const now = this.audioContext.currentTime;
|
|
647
652
|
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
|
|
648
653
|
}
|
|
649
|
-
scheduleNoteRelease(channelNumber, noteNumber,
|
|
654
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
|
|
650
655
|
const channel = this.channels[channelNumber];
|
|
651
656
|
if (stopPedal && channel.sustainPedal)
|
|
652
657
|
return;
|
|
@@ -659,14 +664,11 @@ export class MidyGMLite {
|
|
|
659
664
|
continue;
|
|
660
665
|
if (note.ending)
|
|
661
666
|
continue;
|
|
662
|
-
const
|
|
663
|
-
const volEndTime = stopTime +
|
|
664
|
-
note.instrumentKey.volRelease * velocityRate;
|
|
667
|
+
const volEndTime = stopTime + note.instrumentKey.volRelease;
|
|
665
668
|
note.volumeNode.gain
|
|
666
669
|
.cancelScheduledValues(stopTime)
|
|
667
670
|
.linearRampToValueAtTime(0, volEndTime);
|
|
668
|
-
const modRelease = stopTime +
|
|
669
|
-
note.instrumentKey.modRelease * velocityRate;
|
|
671
|
+
const modRelease = stopTime + note.instrumentKey.modRelease;
|
|
670
672
|
note.filterNode.frequency
|
|
671
673
|
.cancelScheduledValues(stopTime)
|
|
672
674
|
.linearRampToValueAtTime(0, modRelease);
|
|
@@ -702,13 +704,14 @@ export class MidyGMLite {
|
|
|
702
704
|
const promises = [];
|
|
703
705
|
channel.sustainPedal = false;
|
|
704
706
|
channel.scheduledNotes.forEach((noteList) => {
|
|
705
|
-
noteList.
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
707
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
708
|
+
const note = noteList[i];
|
|
709
|
+
if (!note)
|
|
710
|
+
continue;
|
|
711
|
+
const { noteNumber } = note;
|
|
712
|
+
const promise = this.releaseNote(channelNumber, noteNumber, velocity);
|
|
713
|
+
promises.push(promise);
|
|
714
|
+
}
|
|
712
715
|
});
|
|
713
716
|
return promises;
|
|
714
717
|
}
|
|
@@ -778,15 +781,19 @@ export class MidyGMLite {
|
|
|
778
781
|
}
|
|
779
782
|
updateModulation(channel) {
|
|
780
783
|
const now = this.audioContext.currentTime;
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
784
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
785
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
786
|
+
const note = noteList[i];
|
|
787
|
+
if (!note)
|
|
788
|
+
continue;
|
|
789
|
+
if (note.modulationDepth) {
|
|
790
|
+
note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
794
|
+
this.setPitch(note, semitoneOffset);
|
|
795
|
+
this.startModulation(channel, note, now);
|
|
796
|
+
}
|
|
790
797
|
}
|
|
791
798
|
});
|
|
792
799
|
}
|
|
@@ -862,13 +869,17 @@ export class MidyGMLite {
|
|
|
862
869
|
}
|
|
863
870
|
updateDetune(channel, detuneChange) {
|
|
864
871
|
const now = this.audioContext.currentTime;
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
.
|
|
872
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
873
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
874
|
+
const note = noteList[i];
|
|
875
|
+
if (!note)
|
|
876
|
+
continue;
|
|
877
|
+
const { bufferSource } = note;
|
|
878
|
+
const detune = bufferSource.detune.value + detuneChange;
|
|
879
|
+
bufferSource.detune
|
|
880
|
+
.cancelScheduledValues(now)
|
|
881
|
+
.setValueAtTime(detune, now);
|
|
882
|
+
}
|
|
872
883
|
});
|
|
873
884
|
}
|
|
874
885
|
handlePitchBendRangeRPN(channelNumber) {
|
package/esm/midy.d.ts
CHANGED
|
@@ -4,6 +4,11 @@ export class Midy {
|
|
|
4
4
|
volume: number;
|
|
5
5
|
pan: number;
|
|
6
6
|
portamentoTime: number;
|
|
7
|
+
filterResonance: number;
|
|
8
|
+
releaseTime: number;
|
|
9
|
+
attackTime: number;
|
|
10
|
+
brightness: number;
|
|
11
|
+
decayTime: number;
|
|
7
12
|
reverbSendLevel: number;
|
|
8
13
|
chorusSendLevel: number;
|
|
9
14
|
vibratoRate: number;
|
|
@@ -165,16 +170,17 @@ export class Midy {
|
|
|
165
170
|
centToHz(cent: any): number;
|
|
166
171
|
calcSemitoneOffset(channel: any): any;
|
|
167
172
|
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
168
|
-
setVolumeEnvelope(note: any): void;
|
|
173
|
+
setVolumeEnvelope(channel: any, note: any): void;
|
|
169
174
|
setPitch(note: any, semitoneOffset: any): void;
|
|
170
|
-
|
|
175
|
+
clampCutoffFrequency(frequency: any): number;
|
|
176
|
+
setFilterEnvelope(channel: any, note: any): void;
|
|
171
177
|
startModulation(channel: any, note: any, startTime: any): void;
|
|
172
178
|
startVibrato(channel: any, note: any, startTime: any): void;
|
|
173
179
|
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
174
180
|
calcBank(channel: any, channelNumber: any): any;
|
|
175
181
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
176
182
|
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
177
|
-
scheduleNoteRelease(channelNumber: any, noteNumber: any,
|
|
183
|
+
scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
|
|
178
184
|
releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
|
|
179
185
|
releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
|
|
180
186
|
releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
|
|
@@ -205,6 +211,11 @@ export class Midy {
|
|
|
205
211
|
setChorusSendLevel(channelNumber: any, chorusSendLevel: any): void;
|
|
206
212
|
setSostenutoPedal(channelNumber: any, value: any): void;
|
|
207
213
|
setSoftPedal(channelNumber: any, softPedal: any): void;
|
|
214
|
+
setFilterResonance(channelNumber: any, filterResonance: any): void;
|
|
215
|
+
setReleaseTime(channelNumber: any, releaseTime: any): void;
|
|
216
|
+
setAttackTime(channelNumber: any, attackTime: any): void;
|
|
217
|
+
setBrightness(channelNumber: any, brightness: any): void;
|
|
218
|
+
setDecayTime(channelNumber: any, dacayTime: any): void;
|
|
208
219
|
setVibratoRate(channelNumber: any, vibratoRate: any): void;
|
|
209
220
|
setVibratoDepth(channelNumber: any, vibratoDepth: any): void;
|
|
210
221
|
setVibratoDelay(channelNumber: any, vibratoDelay: any): void;
|
package/esm/midy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAwBA;IAkCE
|
|
1
|
+
{"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"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"}
|
package/esm/midy.js
CHANGED
|
@@ -551,12 +551,13 @@ export class Midy {
|
|
|
551
551
|
const now = this.audioContext.currentTime;
|
|
552
552
|
const channel = this.channels[channelNumber];
|
|
553
553
|
channel.scheduledNotes.forEach((noteList) => {
|
|
554
|
-
noteList.
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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
|
+
}
|
|
560
561
|
});
|
|
561
562
|
channel.scheduledNotes.clear();
|
|
562
563
|
await Promise.all(this.notePromises);
|
|
@@ -777,16 +778,17 @@ export class Midy {
|
|
|
777
778
|
return instrumentKey.playbackRate(noteNumber) *
|
|
778
779
|
Math.pow(2, semitoneOffset / 12);
|
|
779
780
|
}
|
|
780
|
-
setVolumeEnvelope(note) {
|
|
781
|
+
setVolumeEnvelope(channel, note) {
|
|
781
782
|
const { instrumentKey, startTime } = note;
|
|
782
|
-
note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
|
|
783
783
|
const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
|
|
784
784
|
const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
|
|
785
785
|
const volDelay = startTime + instrumentKey.volDelay;
|
|
786
|
-
const volAttack = volDelay + instrumentKey.volAttack;
|
|
786
|
+
const volAttack = volDelay + instrumentKey.volAttack * channel.attackTime;
|
|
787
787
|
const volHold = volAttack + instrumentKey.volHold;
|
|
788
|
-
const volDecay = volHold + instrumentKey.volDecay;
|
|
788
|
+
const volDecay = volHold + instrumentKey.volDecay * channel.decayTime;
|
|
789
789
|
note.volumeNode.gain
|
|
790
|
+
.cancelScheduledValues(startTime)
|
|
791
|
+
.setValueAtTime(0, startTime)
|
|
790
792
|
.setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
|
|
791
793
|
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
792
794
|
.setValueAtTime(attackVolume, volHold)
|
|
@@ -810,29 +812,30 @@ export class Midy {
|
|
|
810
812
|
.setValueAtTime(peekPitch, modHold)
|
|
811
813
|
.linearRampToValueAtTime(basePitch, modDecay);
|
|
812
814
|
}
|
|
813
|
-
|
|
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
|
+
}
|
|
820
|
+
setFilterEnvelope(channel, note) {
|
|
814
821
|
const { instrumentKey, noteNumber, startTime } = note;
|
|
815
822
|
const softPedalFactor = 1 -
|
|
816
823
|
(0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
|
|
817
|
-
const maxFreq = this.audioContext.sampleRate / 2;
|
|
818
824
|
const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
|
|
819
|
-
softPedalFactor;
|
|
820
|
-
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
|
|
825
|
+
softPedalFactor * channel.brightness;
|
|
826
|
+
const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor * channel.brightness;
|
|
821
827
|
const sustainFreq = baseFreq +
|
|
822
828
|
(peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
|
|
823
|
-
const adjustedBaseFreq =
|
|
824
|
-
const adjustedPeekFreq =
|
|
825
|
-
const adjustedSustainFreq =
|
|
829
|
+
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
830
|
+
const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
|
|
831
|
+
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
826
832
|
const modDelay = startTime + instrumentKey.modDelay;
|
|
827
833
|
const modAttack = modDelay + instrumentKey.modAttack;
|
|
828
834
|
const modHold = modAttack + instrumentKey.modHold;
|
|
829
835
|
const modDecay = modHold + instrumentKey.modDecay;
|
|
830
|
-
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
831
|
-
type: "lowpass",
|
|
832
|
-
Q: instrumentKey.initialFilterQ / 10, // dB
|
|
833
|
-
frequency: adjustedBaseFreq,
|
|
834
|
-
});
|
|
835
836
|
note.filterNode.frequency
|
|
837
|
+
.cancelScheduledValues(startTime)
|
|
838
|
+
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
836
839
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
837
840
|
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
838
841
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
@@ -885,8 +888,13 @@ export class Midy {
|
|
|
885
888
|
const semitoneOffset = this.calcSemitoneOffset(channel);
|
|
886
889
|
const note = new Note(noteNumber, velocity, startTime, instrumentKey);
|
|
887
890
|
note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
|
|
888
|
-
this.
|
|
889
|
-
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);
|
|
897
|
+
this.setFilterEnvelope(channel, note);
|
|
890
898
|
if (0 < channel.vibratoDepth) {
|
|
891
899
|
this.startVibrato(channel, note, startTime);
|
|
892
900
|
}
|
|
@@ -944,7 +952,7 @@ export class Midy {
|
|
|
944
952
|
const now = this.audioContext.currentTime;
|
|
945
953
|
return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
|
|
946
954
|
}
|
|
947
|
-
scheduleNoteRelease(channelNumber, noteNumber,
|
|
955
|
+
scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
|
|
948
956
|
const channel = this.channels[channelNumber];
|
|
949
957
|
if (stopPedal && channel.sustainPedal)
|
|
950
958
|
return;
|
|
@@ -959,14 +967,12 @@ export class Midy {
|
|
|
959
967
|
continue;
|
|
960
968
|
if (note.ending)
|
|
961
969
|
continue;
|
|
962
|
-
const velocityRate = (velocity + 127) / 127;
|
|
963
970
|
const volEndTime = stopTime +
|
|
964
|
-
note.instrumentKey.volRelease *
|
|
971
|
+
note.instrumentKey.volRelease * channel.releaseTime;
|
|
965
972
|
note.volumeNode.gain
|
|
966
973
|
.cancelScheduledValues(stopTime)
|
|
967
974
|
.linearRampToValueAtTime(0, volEndTime);
|
|
968
|
-
const modRelease = stopTime +
|
|
969
|
-
note.instrumentKey.modRelease * velocityRate;
|
|
975
|
+
const modRelease = stopTime + note.instrumentKey.modRelease;
|
|
970
976
|
note.filterNode.frequency
|
|
971
977
|
.cancelScheduledValues(stopTime)
|
|
972
978
|
.linearRampToValueAtTime(0, modRelease);
|
|
@@ -1006,13 +1012,14 @@ export class Midy {
|
|
|
1006
1012
|
const promises = [];
|
|
1007
1013
|
channel.sustainPedal = false;
|
|
1008
1014
|
channel.scheduledNotes.forEach((noteList) => {
|
|
1009
|
-
noteList.
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
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
|
+
}
|
|
1016
1023
|
});
|
|
1017
1024
|
return promises;
|
|
1018
1025
|
}
|
|
@@ -1126,7 +1133,16 @@ export class Midy {
|
|
|
1126
1133
|
return this.setSostenutoPedal(channelNumber, value);
|
|
1127
1134
|
case 67:
|
|
1128
1135
|
return this.setSoftPedal(channelNumber, value);
|
|
1129
|
-
|
|
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);
|
|
1130
1146
|
case 76:
|
|
1131
1147
|
return this.setVibratoRate(channelNumber, value);
|
|
1132
1148
|
case 77:
|
|
@@ -1168,15 +1184,19 @@ export class Midy {
|
|
|
1168
1184
|
}
|
|
1169
1185
|
updateModulation(channel) {
|
|
1170
1186
|
const now = this.audioContext.currentTime;
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
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
|
+
}
|
|
1180
1200
|
}
|
|
1181
1201
|
});
|
|
1182
1202
|
}
|
|
@@ -1282,6 +1302,64 @@ export class Midy {
|
|
|
1282
1302
|
const channel = this.channels[channelNumber];
|
|
1283
1303
|
channel.softPedal = softPedal / 127;
|
|
1284
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
|
+
}
|
|
1285
1363
|
setVibratoRate(channelNumber, vibratoRate) {
|
|
1286
1364
|
const channel = this.channels[channelNumber];
|
|
1287
1365
|
channel.vibratoRate = vibratoRate / 64;
|
|
@@ -1362,13 +1440,17 @@ export class Midy {
|
|
|
1362
1440
|
}
|
|
1363
1441
|
updateDetune(channel, detuneChange) {
|
|
1364
1442
|
const now = this.audioContext.currentTime;
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
.
|
|
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
|
+
}
|
|
1372
1454
|
});
|
|
1373
1455
|
}
|
|
1374
1456
|
handlePitchBendRangeRPN(channelNumber) {
|
|
@@ -1788,6 +1870,11 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
1788
1870
|
volume: 100 / 127,
|
|
1789
1871
|
pan: 64,
|
|
1790
1872
|
portamentoTime: 0,
|
|
1873
|
+
filterResonance: 1,
|
|
1874
|
+
releaseTime: 1,
|
|
1875
|
+
attackTime: 1,
|
|
1876
|
+
brightness: 1,
|
|
1877
|
+
decayTime: 1,
|
|
1791
1878
|
reverbSendLevel: 0,
|
|
1792
1879
|
chorusSendLevel: 0,
|
|
1793
1880
|
vibratoRate: 1,
|
package/package.json
CHANGED
package/script/midy-GM1.d.ts
CHANGED
|
@@ -79,12 +79,13 @@ export class MidyGM1 {
|
|
|
79
79
|
calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
|
|
80
80
|
setVolumeEnvelope(note: any): void;
|
|
81
81
|
setPitch(note: any, semitoneOffset: any): void;
|
|
82
|
-
|
|
82
|
+
clampCutoffFrequency(frequency: any): number;
|
|
83
|
+
setFilterEnvelope(note: any): void;
|
|
83
84
|
startModulation(channel: any, note: any, startTime: any): void;
|
|
84
85
|
createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
|
|
85
86
|
scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
86
87
|
noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
|
|
87
|
-
scheduleNoteRelease(channelNumber: any, noteNumber: any,
|
|
88
|
+
scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
|
|
88
89
|
releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
|
|
89
90
|
releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
|
|
90
91
|
handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
|
package/script/midy-GM1.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAwBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;IAC5C,gBAAiD;IAKnD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,mFAmBC;IAED,yDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAwBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;IAC5C,gBAAiD;IAKnD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MA8DC;IAED,mFAmBC;IAED,yDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,sCAGC;IAED,mFAGC;IAED,mCAeC;IAED,+CAwBC;IAED,6CAIC;IAED,mCAsBC;IAED,+DA0BC;IAED,wHAmCC;IAED,kGA6BC;IAED,0EAGC;IAED,uIAyCC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,2FA+BC;IAED,qCAkBC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AApjCD;IAUE,gFAKC;IAdD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
|