@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.
Files changed (35) hide show
  1. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts +153 -0
  2. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +1 -0
  3. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.2 → soundfont-parser@0.0.4}/+esm.js +73 -66
  4. package/esm/midy-GM1.d.ts +19 -13
  5. package/esm/midy-GM1.d.ts.map +1 -1
  6. package/esm/midy-GM1.js +171 -131
  7. package/esm/midy-GM2.d.ts +22 -14
  8. package/esm/midy-GM2.d.ts.map +1 -1
  9. package/esm/midy-GM2.js +186 -133
  10. package/esm/midy-GMLite.d.ts +17 -13
  11. package/esm/midy-GMLite.d.ts.map +1 -1
  12. package/esm/midy-GMLite.js +159 -131
  13. package/esm/midy.d.ts +30 -16
  14. package/esm/midy.d.ts.map +1 -1
  15. package/esm/midy.js +266 -166
  16. package/package.json +1 -1
  17. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts +153 -0
  18. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.4/+esm.d.ts.map +1 -0
  19. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.2 → soundfont-parser@0.0.4}/+esm.js +75 -68
  20. package/script/midy-GM1.d.ts +19 -13
  21. package/script/midy-GM1.d.ts.map +1 -1
  22. package/script/midy-GM1.js +171 -131
  23. package/script/midy-GM2.d.ts +22 -14
  24. package/script/midy-GM2.d.ts.map +1 -1
  25. package/script/midy-GM2.js +186 -133
  26. package/script/midy-GMLite.d.ts +17 -13
  27. package/script/midy-GMLite.d.ts.map +1 -1
  28. package/script/midy-GMLite.js +159 -131
  29. package/script/midy.d.ts +30 -16
  30. package/script/midy.d.ts.map +1 -1
  31. package/script/midy.js +266 -166
  32. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts +0 -135
  33. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +0 -1
  34. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts +0 -135
  35. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +0 -1
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MidyGM2 = void 0;
4
4
  const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
5
- const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.js");
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, "gainNode", {
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, "filterNode", {
20
+ Object.defineProperty(this, "volumeNode", {
21
21
  enumerable: true,
22
22
  configurable: true,
23
23
  writable: true,
24
24
  value: void 0
25
25
  });
26
- Object.defineProperty(this, "modLFO", {
26
+ Object.defineProperty(this, "volumeDepth", {
27
27
  enumerable: true,
28
28
  configurable: true,
29
29
  writable: true,
30
30
  value: void 0
31
31
  });
32
- Object.defineProperty(this, "modLFOGain", {
32
+ Object.defineProperty(this, "modulationLFO", {
33
33
  enumerable: true,
34
34
  configurable: true,
35
35
  writable: true,
36
36
  value: void 0
37
37
  });
38
- Object.defineProperty(this, "vibLFO", {
38
+ Object.defineProperty(this, "modulationDepth", {
39
39
  enumerable: true,
40
40
  configurable: true,
41
41
  writable: true,
42
42
  value: void 0
43
43
  });
44
- Object.defineProperty(this, "vibLFOGain", {
44
+ Object.defineProperty(this, "vibratoLFO", {
45
+ enumerable: true,
46
+ configurable: true,
47
+ writable: true,
48
+ value: void 0
49
+ });
50
+ Object.defineProperty(this, "vibratoDepth", {
45
51
  enumerable: true,
46
52
  configurable: true,
47
53
  writable: true,
@@ -407,7 +413,7 @@ class MidyGM2 {
407
413
  const t = this.audioContext.currentTime + offset;
408
414
  queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
409
415
  if (this.isPausing) {
410
- await this.stopNotes();
416
+ await this.stopNotes(0, true);
411
417
  this.notePromises = [];
412
418
  resolve();
413
419
  this.isPausing = false;
@@ -415,7 +421,7 @@ class MidyGM2 {
415
421
  return;
416
422
  }
417
423
  else if (this.isStopping) {
418
- await this.stopNotes();
424
+ await this.stopNotes(0, true);
419
425
  this.notePromises = [];
420
426
  resolve();
421
427
  this.isStopping = false;
@@ -423,7 +429,7 @@ class MidyGM2 {
423
429
  return;
424
430
  }
425
431
  else if (this.isSeeking) {
426
- this.stopNotes();
432
+ this.stopNotes(0, true);
427
433
  this.startTime = this.audioContext.currentTime;
428
434
  queueIndex = this.getQueueIndex(this.resumeTime);
429
435
  offset = this.resumeTime - this.startTime;
@@ -538,21 +544,25 @@ class MidyGM2 {
538
544
  }
539
545
  return { instruments, timeline };
540
546
  }
541
- stopNotes() {
547
+ async stopChannelNotes(channelNumber, velocity, stopPedal) {
542
548
  const now = this.audioContext.currentTime;
543
- const velocity = 0;
544
- const stopPedal = true;
545
- this.channels.forEach((channel, channelNumber) => {
546
- channel.scheduledNotes.forEach((scheduledNotes) => {
547
- scheduledNotes.forEach((scheduledNote) => {
548
- if (scheduledNote) {
549
- const promise = this.scheduleNoteRelease(channelNumber, scheduledNote.noteNumber, velocity, now, stopPedal);
550
- this.notePromises.push(promise);
551
- }
552
- });
553
- });
554
- channel.scheduledNotes.clear();
549
+ const channel = this.channels[channelNumber];
550
+ channel.scheduledNotes.forEach((noteList) => {
551
+ for (let i = 0; i < noteList.length; i++) {
552
+ const note = noteList[i];
553
+ if (!note)
554
+ continue;
555
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
556
+ this.notePromises.push(promise);
557
+ }
555
558
  });
559
+ channel.scheduledNotes.clear();
560
+ await Promise.all(this.notePromises);
561
+ }
562
+ stopNotes(velocity, stopPedal) {
563
+ for (let i = 0; i < this.channels.length; i++) {
564
+ this.stopChannelNotes(i, velocity, stopPedal);
565
+ }
556
566
  return Promise.all(this.notePromises);
557
567
  }
558
568
  async start() {
@@ -767,79 +777,137 @@ class MidyGM2 {
767
777
  }
768
778
  setVolumeEnvelope(note) {
769
779
  const { instrumentKey, startTime } = note;
770
- note.gainNode = new GainNode(this.audioContext, { gain: 0 });
771
780
  const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
772
781
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
773
782
  const volDelay = startTime + instrumentKey.volDelay;
774
783
  const volAttack = volDelay + instrumentKey.volAttack;
775
784
  const volHold = volAttack + instrumentKey.volHold;
776
785
  const volDecay = volHold + instrumentKey.volDecay;
777
- note.gainNode.gain
786
+ note.volumeNode.gain
787
+ .cancelScheduledValues(startTime)
788
+ .setValueAtTime(0, startTime)
778
789
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
779
790
  .exponentialRampToValueAtTime(attackVolume, volAttack)
780
791
  .setValueAtTime(attackVolume, volHold)
781
792
  .linearRampToValueAtTime(sustainVolume, volDecay);
782
793
  }
794
+ setPitch(note, semitoneOffset) {
795
+ const { instrumentKey, noteNumber, startTime } = note;
796
+ const modEnvToPitch = instrumentKey.modEnvToPitch / 100;
797
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
798
+ if (modEnvToPitch === 0)
799
+ return;
800
+ const basePitch = note.bufferSource.playbackRate.value;
801
+ const peekPitch = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset + modEnvToPitch);
802
+ const modDelay = startTime + instrumentKey.modDelay;
803
+ const modAttack = modDelay + instrumentKey.modAttack;
804
+ const modHold = modAttack + instrumentKey.modHold;
805
+ const modDecay = modHold + instrumentKey.modDecay;
806
+ note.bufferSource.playbackRate.value
807
+ .setValueAtTime(basePitch, modDelay)
808
+ .exponentialRampToValueAtTime(peekPitch, modAttack)
809
+ .setValueAtTime(peekPitch, modHold)
810
+ .linearRampToValueAtTime(basePitch, modDecay);
811
+ }
812
+ clampCutoffFrequency(frequency) {
813
+ const minFrequency = 20; // min Hz of initialFilterFc
814
+ const maxFrequency = 20000; // max Hz of initialFilterFc
815
+ return Math.max(minFrequency, Math.min(frequency, maxFrequency));
816
+ }
783
817
  setFilterEnvelope(channel, note) {
784
- const { instrumentKey, startTime, noteNumber } = note;
818
+ const { instrumentKey, noteNumber, startTime } = note;
785
819
  const softPedalFactor = 1 -
786
820
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
787
- const maxFreq = this.audioContext.sampleRate / 2;
788
821
  const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
789
822
  softPedalFactor;
790
823
  const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
791
- const sustainFreq = (baseFreq +
792
- (peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
824
+ const sustainFreq = baseFreq +
825
+ (peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
826
+ const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
827
+ const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
828
+ const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
793
829
  const modDelay = startTime + instrumentKey.modDelay;
794
830
  const modAttack = modDelay + instrumentKey.modAttack;
795
831
  const modHold = modAttack + instrumentKey.modHold;
796
832
  const modDecay = modHold + instrumentKey.modDecay;
797
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
798
- const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
799
- const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
800
- note.filterNode = new BiquadFilterNode(this.audioContext, {
801
- type: "lowpass",
802
- Q: instrumentKey.initialFilterQ / 10, // dB
803
- frequency: adjustedBaseFreq,
804
- });
805
833
  note.filterNode.frequency
834
+ .cancelScheduledValues(startTime)
835
+ .setValueAtTime(adjustedBaseFreq, startTime)
806
836
  .setValueAtTime(adjustedBaseFreq, modDelay)
807
837
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
808
838
  .setValueAtTime(adjustedPeekFreq, modHold)
809
839
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
810
- note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
811
840
  }
812
- startModulation(channel, note, time) {
841
+ startModulation(channel, note, startTime) {
813
842
  const { instrumentKey } = note;
814
- note.modLFOGain = new GainNode(this.audioContext, {
815
- gain: this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation),
816
- });
817
- note.modLFO = new OscillatorNode(this.audioContext, {
843
+ const { modLfoToPitch, modLfoToVolume } = instrumentKey;
844
+ note.modulationLFO = new OscillatorNode(this.audioContext, {
818
845
  frequency: this.centToHz(instrumentKey.freqModLFO),
819
846
  });
820
- note.modLFO.start(time);
821
- note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
822
- note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
823
- note.modLFO.connect(note.modLFOGain);
824
- note.modLFOGain.connect(note.bufferSource.detune);
847
+ note.filterDepth = new GainNode(this.audioContext, {
848
+ gain: instrumentKey.modLfoToFilterFc,
849
+ });
850
+ const modulationDepth = Math.abs(modLfoToPitch) + channel.modulationDepth;
851
+ const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
852
+ note.modulationDepth = new GainNode(this.audioContext, {
853
+ gain: modulationDepth * modulationDepthSign,
854
+ });
855
+ const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
856
+ const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
857
+ note.volumeDepth = new GainNode(this.audioContext, {
858
+ gain: volumeDepth * volumeDepthSign,
859
+ });
860
+ note.modulationLFO.start(startTime + instrumentKey.delayModLFO);
861
+ note.modulationLFO.connect(note.filterDepth);
862
+ note.filterDepth.connect(note.filterNode.frequency);
863
+ note.modulationLFO.connect(note.modulationDepth);
864
+ note.modulationDepth.connect(note.bufferSource.detune);
865
+ note.modulationLFO.connect(note.volumeDepth);
866
+ note.volumeDepth.connect(note.volumeNode.gain);
867
+ }
868
+ startVibrato(channel, note, startTime) {
869
+ const { instrumentKey } = note;
870
+ const { vibLfoToPitch } = instrumentKey;
871
+ note.vibratoLFO = new OscillatorNode(this.audioContext, {
872
+ frequency: this.centToHz(instrumentKey.freqVibLFO) *
873
+ channel.vibratoRate,
874
+ });
875
+ const vibratoDepth = Math.abs(vibLfoToPitch) * channel.vibratoDepth;
876
+ const vibratoDepthSign = 0 < vibLfoToPitch;
877
+ note.vibratoDepth = new GainNode(this.audioContext, {
878
+ gain: vibratoDepth * vibratoDepthSign,
879
+ });
880
+ note.vibratoLFO.start(startTime + instrumentKey.delayVibLFO * channel.vibratoDelay);
881
+ note.vibratoLFO.connect(note.vibratoDepth);
882
+ note.vibratoDepth.connect(note.bufferSource.detune);
825
883
  }
826
884
  async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
827
885
  const semitoneOffset = this.calcSemitoneOffset(channel);
828
886
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
829
887
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
830
- note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
888
+ note.volumeNode = new GainNode(this.audioContext);
889
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
890
+ type: "lowpass",
891
+ Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
892
+ });
831
893
  this.setVolumeEnvelope(note);
832
894
  this.setFilterEnvelope(channel, note);
833
- if (channel.modulation > 0) {
834
- const delayModLFO = startTime + instrumentKey.delayModLFO;
835
- this.startModulation(channel, note, delayModLFO);
895
+ if (0 < channel.vibratoDepth) {
896
+ this.startVibrato(channel, note, startTime);
897
+ }
898
+ if (0 < channel.modulationDepth) {
899
+ this.setPitch(note, semitoneOffset);
900
+ this.startModulation(channel, note, startTime);
901
+ }
902
+ else {
903
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
836
904
  }
837
905
  if (this.mono && channel.currentBufferSource) {
838
906
  channel.currentBufferSource.stop(startTime);
839
907
  channel.currentBufferSource = note.bufferSource;
840
908
  }
841
909
  note.bufferSource.connect(note.filterNode);
842
- note.filterNode.connect(note.gainNode);
910
+ note.filterNode.connect(note.volumeNode);
843
911
  note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
844
912
  return note;
845
913
  }
@@ -864,8 +932,8 @@ class MidyGM2 {
864
932
  if (!instrumentKey)
865
933
  return;
866
934
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
867
- note.gainNode.connect(channel.gainL);
868
- note.gainNode.connect(channel.gainR);
935
+ note.volumeNode.connect(channel.gainL);
936
+ note.volumeNode.connect(channel.gainR);
869
937
  if (channel.sostenutoPedal) {
870
938
  channel.sostenutoNotes.set(noteNumber, note);
871
939
  }
@@ -881,7 +949,7 @@ class MidyGM2 {
881
949
  const now = this.audioContext.currentTime;
882
950
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
883
951
  }
884
- scheduleNoteRelease(channelNumber, noteNumber, velocity, stopTime, stopPedal = false) {
952
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
885
953
  const channel = this.channels[channelNumber];
886
954
  if (stopPedal && channel.sustainPedal)
887
955
  return;
@@ -896,20 +964,14 @@ class MidyGM2 {
896
964
  continue;
897
965
  if (note.ending)
898
966
  continue;
899
- const velocityRate = (velocity + 127) / 127;
900
- const volEndTime = stopTime +
901
- note.instrumentKey.volRelease * velocityRate;
902
- note.gainNode.gain
967
+ const volEndTime = stopTime + note.instrumentKey.volRelease;
968
+ note.volumeNode.gain
903
969
  .cancelScheduledValues(stopTime)
904
970
  .linearRampToValueAtTime(0, volEndTime);
905
- const maxFreq = this.audioContext.sampleRate / 2;
906
- const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
907
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
908
- const modEndTime = stopTime +
909
- note.instrumentKey.modRelease * velocityRate;
971
+ const modRelease = stopTime + note.instrumentKey.modRelease;
910
972
  note.filterNode.frequency
911
973
  .cancelScheduledValues(stopTime)
912
- .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
974
+ .linearRampToValueAtTime(0, modRelease);
913
975
  note.ending = true;
914
976
  this.scheduleTask(() => {
915
977
  note.bufferSource.loop = false;
@@ -918,16 +980,18 @@ class MidyGM2 {
918
980
  note.bufferSource.onended = () => {
919
981
  scheduledNotes[i] = null;
920
982
  note.bufferSource.disconnect();
983
+ note.volumeNode.disconnect();
921
984
  note.filterNode.disconnect();
922
- note.gainNode.disconnect();
923
- if (note.modLFOGain)
924
- note.modLFOGain.disconnect();
925
- if (note.vibLFOGain)
926
- note.vibLFOGain.disconnect();
927
- if (note.modLFO)
928
- note.modLFO.stop();
929
- if (note.vibLFO)
930
- note.vibLFO.stop();
985
+ if (note.volumeDepth)
986
+ note.volumeDepth.disconnect();
987
+ if (note.modulationDepth)
988
+ note.modulationDepth.disconnect();
989
+ if (note.modulationLFO)
990
+ note.modulationLFO.stop();
991
+ if (note.vibratoDepth)
992
+ note.vibratoDepth.disconnect();
993
+ if (note.vibratoLFO)
994
+ note.vibratoLFO.stop();
931
995
  resolve();
932
996
  };
933
997
  note.bufferSource.stop(volEndTime);
@@ -943,14 +1007,15 @@ class MidyGM2 {
943
1007
  const channel = this.channels[channelNumber];
944
1008
  const promises = [];
945
1009
  channel.sustainPedal = false;
946
- channel.scheduledNotes.forEach((scheduledNotes) => {
947
- scheduledNotes.forEach((scheduledNote) => {
948
- if (scheduledNote) {
949
- const { noteNumber } = scheduledNote;
950
- const promise = this.releaseNote(channelNumber, noteNumber, velocity);
951
- promises.push(promise);
952
- }
953
- });
1010
+ channel.scheduledNotes.forEach((noteList) => {
1011
+ for (let i = 0; i < noteList.length; i++) {
1012
+ const note = noteList[i];
1013
+ if (!note)
1014
+ continue;
1015
+ const { noteNumber } = note;
1016
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
1017
+ promises.push(promise);
1018
+ }
954
1019
  });
955
1020
  return promises;
956
1021
  }
@@ -1000,8 +1065,8 @@ class MidyGM2 {
1000
1065
  const activeNotes = this.getActiveNotes(channel, now);
1001
1066
  if (channel.channelPressure.amplitudeControl !== 1) {
1002
1067
  activeNotes.forEach((activeNote) => {
1003
- const gain = activeNote.gainNode.gain.value;
1004
- activeNote.gainNode.gain
1068
+ const gain = activeNote.volumeNode.gain.value;
1069
+ activeNote.volumeNode.gain
1005
1070
  .cancelScheduledValues(now)
1006
1071
  .setValueAtTime(gain * pressure, now);
1007
1072
  });
@@ -1024,7 +1089,7 @@ class MidyGM2 {
1024
1089
  case 0:
1025
1090
  return this.setBankMSB(channelNumber, value);
1026
1091
  case 1:
1027
- return this.setModulation(channelNumber, value);
1092
+ return this.setModulationDepth(channelNumber, value);
1028
1093
  case 5:
1029
1094
  return this.setPortamentoTime(channelNumber, value);
1030
1095
  case 6:
@@ -1078,20 +1143,25 @@ class MidyGM2 {
1078
1143
  }
1079
1144
  updateModulation(channel) {
1080
1145
  const now = this.audioContext.currentTime;
1081
- const activeNotes = this.getActiveNotes(channel, now);
1082
- activeNotes.forEach((activeNote) => {
1083
- if (activeNote.modLFO) {
1084
- const { gainNode, instrumentKey } = activeNote;
1085
- gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
1086
- }
1087
- else {
1088
- this.startModulation(channel, activeNote, now);
1146
+ channel.scheduledNotes.forEach((noteList) => {
1147
+ for (let i = 0; i < noteList.length; i++) {
1148
+ const note = noteList[i];
1149
+ if (!note)
1150
+ continue;
1151
+ if (note.modulationDepth) {
1152
+ note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
1153
+ }
1154
+ else {
1155
+ const semitoneOffset = this.calcSemitoneOffset(channel);
1156
+ this.setPitch(note, semitoneOffset);
1157
+ this.startModulation(channel, note, now);
1158
+ }
1089
1159
  }
1090
1160
  });
1091
1161
  }
1092
- setModulation(channelNumber, modulation) {
1162
+ setModulationDepth(channelNumber, modulation) {
1093
1163
  const channel = this.channels[channelNumber];
1094
- channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1164
+ channel.modulationDepth = (modulation / 127) * channel.modulationDepthRange;
1095
1165
  this.updateModulation(channel);
1096
1166
  }
1097
1167
  setPortamentoTime(channelNumber, portamentoTime) {
@@ -1249,13 +1319,17 @@ class MidyGM2 {
1249
1319
  }
1250
1320
  updateDetune(channel, detuneChange) {
1251
1321
  const now = this.audioContext.currentTime;
1252
- const activeNotes = this.getActiveNotes(channel, now);
1253
- activeNotes.forEach((activeNote) => {
1254
- const { bufferSource } = activeNote;
1255
- const detune = bufferSource.detune.value + detuneChange;
1256
- bufferSource.detune
1257
- .cancelScheduledValues(now)
1258
- .setValueAtTime(detune, now);
1322
+ channel.scheduledNotes.forEach((noteList) => {
1323
+ for (let i = 0; i < noteList.length; i++) {
1324
+ const note = noteList[i];
1325
+ if (!note)
1326
+ continue;
1327
+ const { bufferSource } = note;
1328
+ const detune = bufferSource.detune.value + detuneChange;
1329
+ bufferSource.detune
1330
+ .cancelScheduledValues(now)
1331
+ .setValueAtTime(detune, now);
1332
+ }
1259
1333
  });
1260
1334
  }
1261
1335
  handlePitchBendRangeRPN(channelNumber) {
@@ -1301,47 +1375,23 @@ class MidyGM2 {
1301
1375
  handleModulationDepthRangeRPN(channelNumber) {
1302
1376
  const channel = this.channels[channelNumber];
1303
1377
  this.limitData(channel, 0, 127, 0, 127);
1304
- const modulationDepthRange = dataMSB + dataLSB / 128;
1378
+ const modulationDepthRange = (dataMSB + dataLSB / 128) * 100;
1305
1379
  this.setModulationDepthRange(channelNumber, modulationDepthRange);
1306
1380
  }
1307
1381
  setModulationDepthRange(channelNumber, modulationDepthRange) {
1308
1382
  const channel = this.channels[channelNumber];
1309
1383
  channel.modulationDepthRange = modulationDepthRange;
1310
- channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1384
+ channel.modulationDepth = (modulation / 127) * modulationDepthRange;
1311
1385
  this.updateModulation(channel);
1312
1386
  }
1313
1387
  allSoundOff(channelNumber) {
1314
- const now = this.audioContext.currentTime;
1315
- const channel = this.channels[channelNumber];
1316
- const velocity = 0;
1317
- const stopPedal = true;
1318
- const promises = [];
1319
- channel.scheduledNotes.forEach((noteList) => {
1320
- const activeNote = this.getActiveNote(noteList, now);
1321
- if (activeNote) {
1322
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
1323
- promises.push(notePromise);
1324
- }
1325
- });
1326
- return promises;
1388
+ return this.stopChannelNotes(channelNumber, 0, true);
1327
1389
  }
1328
1390
  resetAllControllers(channelNumber) {
1329
1391
  Object.assign(this.channels[channelNumber], this.effectSettings);
1330
1392
  }
1331
1393
  allNotesOff(channelNumber) {
1332
- const now = this.audioContext.currentTime;
1333
- const channel = this.channels[channelNumber];
1334
- const velocity = 0;
1335
- const stopPedal = false;
1336
- const promises = [];
1337
- channel.scheduledNotes.forEach((noteList) => {
1338
- const activeNote = this.getActiveNote(noteList, now);
1339
- if (activeNote) {
1340
- const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
1341
- promises.push(notePromise);
1342
- }
1343
- });
1344
- return promises;
1394
+ return this.stopChannelNotes(channelNumber, 0, false);
1345
1395
  }
1346
1396
  omniOff() {
1347
1397
  this.omni = false;
@@ -1702,6 +1752,9 @@ Object.defineProperty(MidyGM2, "channelSettings", {
1702
1752
  portamentoTime: 0,
1703
1753
  reverbSendLevel: 0,
1704
1754
  chorusSendLevel: 0,
1755
+ vibratoRate: 1,
1756
+ vibratoDepth: 1,
1757
+ vibratoDelay: 1,
1705
1758
  bank: 121 * 128,
1706
1759
  bankMSB: 121,
1707
1760
  bankLSB: 0,
@@ -1711,7 +1764,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
1711
1764
  pitchBend: 0,
1712
1765
  fineTuning: 0, // cb
1713
1766
  coarseTuning: 0, // cb
1714
- modulationDepthRange: 0.5, // cb
1767
+ modulationDepthRange: 50, // cent
1715
1768
  }
1716
1769
  });
1717
1770
  Object.defineProperty(MidyGM2, "effectSettings", {
@@ -1720,7 +1773,7 @@ Object.defineProperty(MidyGM2, "effectSettings", {
1720
1773
  writable: true,
1721
1774
  value: {
1722
1775
  expression: 1,
1723
- modulation: 0,
1776
+ modulationDepth: 0,
1724
1777
  sustainPedal: false,
1725
1778
  portamento: false,
1726
1779
  sostenutoPedal: false,
@@ -11,7 +11,7 @@ export class MidyGMLite {
11
11
  };
12
12
  static effectSettings: {
13
13
  expression: number;
14
- modulation: number;
14
+ modulationDepth: number;
15
15
  sustainPedal: boolean;
16
16
  rpnMSB: number;
17
17
  rpnLSB: number;
@@ -60,7 +60,8 @@ export class MidyGMLite {
60
60
  instruments: Set<any>;
61
61
  timeline: any[];
62
62
  };
63
- stopNotes(): Promise<any[]>;
63
+ stopChannelNotes(channelNumber: any, velocity: any, stopPedal: any): Promise<void>;
64
+ stopNotes(velocity: any, stopPedal: any): Promise<any[]>;
64
65
  start(): Promise<void>;
65
66
  stop(): void;
66
67
  pause(): void;
@@ -75,21 +76,23 @@ export class MidyGMLite {
75
76
  calcSemitoneOffset(channel: any): number;
76
77
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
77
78
  setVolumeEnvelope(note: any): void;
78
- setFilterEnvelope(channel: any, note: any): void;
79
- startModulation(channel: any, note: any, time: any): void;
79
+ setPitch(note: any, semitoneOffset: any): void;
80
+ clampCutoffFrequency(frequency: any): number;
81
+ setFilterEnvelope(note: any): void;
82
+ startModulation(channel: any, note: any, startTime: any): void;
80
83
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
81
84
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
82
85
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
83
- scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
86
+ scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
84
87
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
85
88
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
86
- handleMIDIMessage(statusByte: any, data1: any, data2: any): void | any[] | Promise<any>;
89
+ handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
87
90
  handleProgramChange(channelNumber: any, program: any): void;
88
91
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
89
92
  setPitchBend(channelNumber: any, pitchBend: any): void;
90
- handleControlChange(channelNumber: any, controller: any, value: any): void | any[];
93
+ handleControlChange(channelNumber: any, controller: any, value: any): void | Promise<void>;
91
94
  updateModulation(channel: any): void;
92
- setModulation(channelNumber: any, modulation: any): void;
95
+ setModulationDepth(channelNumber: any, modulation: any): void;
93
96
  setVolume(channelNumber: any, volume: any): void;
94
97
  panToGain(pan: any): {
95
98
  gainLeft: number;
@@ -107,9 +110,9 @@ export class MidyGMLite {
107
110
  updateDetune(channel: any, detuneChange: any): void;
108
111
  handlePitchBendRangeRPN(channelNumber: any): void;
109
112
  setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
110
- allSoundOff(channelNumber: any): any[];
113
+ allSoundOff(channelNumber: any): Promise<void>;
111
114
  resetAllControllers(channelNumber: any): void;
112
- allNotesOff(channelNumber: any): any[];
115
+ allNotesOff(channelNumber: any): Promise<void>;
113
116
  handleUniversalNonRealTimeExclusiveMessage(data: any): void;
114
117
  GM1SystemOn(): void;
115
118
  handleUniversalRealTimeExclusiveMessage(data: any): void;
@@ -122,10 +125,11 @@ export class MidyGMLite {
122
125
  declare class Note {
123
126
  constructor(noteNumber: any, velocity: any, startTime: any, instrumentKey: any);
124
127
  bufferSource: any;
125
- gainNode: any;
126
128
  filterNode: any;
127
- modLFO: any;
128
- modLFOGain: any;
129
+ volumeNode: any;
130
+ volumeDepth: any;
131
+ modulationLFO: any;
132
+ modulationDepth: any;
129
133
  noteNumber: any;
130
134
  velocity: any;
131
135
  startTime: any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAuBhB,kBAAgC;IAChC,gBAA4C;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,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,mCAcC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA8CC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,mFA+BC;IAED,qCAcC;IAED,yDAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA5+BD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
1
+ {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAsBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA5CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAuBhB,kBAAgC;IAChC,gBAA4C;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,yCAEC;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,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAl/BD;IAQE,gFAKC;IAZD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}