@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
package/esm/midy-GM2.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/+esm.js";
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, "gainNode", {
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, "filterNode", {
17
+ Object.defineProperty(this, "volumeNode", {
18
18
  enumerable: true,
19
19
  configurable: true,
20
20
  writable: true,
21
21
  value: void 0
22
22
  });
23
- Object.defineProperty(this, "modLFO", {
23
+ Object.defineProperty(this, "volumeDepth", {
24
24
  enumerable: true,
25
25
  configurable: true,
26
26
  writable: true,
27
27
  value: void 0
28
28
  });
29
- Object.defineProperty(this, "modLFOGain", {
29
+ Object.defineProperty(this, "modulationLFO", {
30
30
  enumerable: true,
31
31
  configurable: true,
32
32
  writable: true,
33
33
  value: void 0
34
34
  });
35
- Object.defineProperty(this, "vibLFO", {
35
+ Object.defineProperty(this, "modulationDepth", {
36
36
  enumerable: true,
37
37
  configurable: true,
38
38
  writable: true,
39
39
  value: void 0
40
40
  });
41
- Object.defineProperty(this, "vibLFOGain", {
41
+ Object.defineProperty(this, "vibratoLFO", {
42
+ enumerable: true,
43
+ configurable: true,
44
+ writable: true,
45
+ value: void 0
46
+ });
47
+ Object.defineProperty(this, "vibratoDepth", {
42
48
  enumerable: true,
43
49
  configurable: true,
44
50
  writable: true,
@@ -404,7 +410,7 @@ export class MidyGM2 {
404
410
  const t = this.audioContext.currentTime + offset;
405
411
  queueIndex = await this.scheduleTimelineEvents(t, offset, queueIndex);
406
412
  if (this.isPausing) {
407
- await this.stopNotes();
413
+ await this.stopNotes(0, true);
408
414
  this.notePromises = [];
409
415
  resolve();
410
416
  this.isPausing = false;
@@ -412,7 +418,7 @@ export class MidyGM2 {
412
418
  return;
413
419
  }
414
420
  else if (this.isStopping) {
415
- await this.stopNotes();
421
+ await this.stopNotes(0, true);
416
422
  this.notePromises = [];
417
423
  resolve();
418
424
  this.isStopping = false;
@@ -420,7 +426,7 @@ export class MidyGM2 {
420
426
  return;
421
427
  }
422
428
  else if (this.isSeeking) {
423
- this.stopNotes();
429
+ this.stopNotes(0, true);
424
430
  this.startTime = this.audioContext.currentTime;
425
431
  queueIndex = this.getQueueIndex(this.resumeTime);
426
432
  offset = this.resumeTime - this.startTime;
@@ -535,21 +541,25 @@ export class MidyGM2 {
535
541
  }
536
542
  return { instruments, timeline };
537
543
  }
538
- stopNotes() {
544
+ async stopChannelNotes(channelNumber, velocity, stopPedal) {
539
545
  const now = this.audioContext.currentTime;
540
- const velocity = 0;
541
- const stopPedal = true;
542
- this.channels.forEach((channel, channelNumber) => {
543
- channel.scheduledNotes.forEach((scheduledNotes) => {
544
- scheduledNotes.forEach((scheduledNote) => {
545
- if (scheduledNote) {
546
- const promise = this.scheduleNoteRelease(channelNumber, scheduledNote.noteNumber, velocity, now, stopPedal);
547
- this.notePromises.push(promise);
548
- }
549
- });
550
- });
551
- channel.scheduledNotes.clear();
546
+ const channel = this.channels[channelNumber];
547
+ channel.scheduledNotes.forEach((noteList) => {
548
+ for (let i = 0; i < noteList.length; i++) {
549
+ const note = noteList[i];
550
+ if (!note)
551
+ continue;
552
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
553
+ this.notePromises.push(promise);
554
+ }
552
555
  });
556
+ channel.scheduledNotes.clear();
557
+ await Promise.all(this.notePromises);
558
+ }
559
+ stopNotes(velocity, stopPedal) {
560
+ for (let i = 0; i < this.channels.length; i++) {
561
+ this.stopChannelNotes(i, velocity, stopPedal);
562
+ }
553
563
  return Promise.all(this.notePromises);
554
564
  }
555
565
  async start() {
@@ -764,79 +774,137 @@ export class MidyGM2 {
764
774
  }
765
775
  setVolumeEnvelope(note) {
766
776
  const { instrumentKey, startTime } = note;
767
- note.gainNode = new GainNode(this.audioContext, { gain: 0 });
768
777
  const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
769
778
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
770
779
  const volDelay = startTime + instrumentKey.volDelay;
771
780
  const volAttack = volDelay + instrumentKey.volAttack;
772
781
  const volHold = volAttack + instrumentKey.volHold;
773
782
  const volDecay = volHold + instrumentKey.volDecay;
774
- note.gainNode.gain
783
+ note.volumeNode.gain
784
+ .cancelScheduledValues(startTime)
785
+ .setValueAtTime(0, startTime)
775
786
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
776
787
  .exponentialRampToValueAtTime(attackVolume, volAttack)
777
788
  .setValueAtTime(attackVolume, volHold)
778
789
  .linearRampToValueAtTime(sustainVolume, volDecay);
779
790
  }
791
+ setPitch(note, semitoneOffset) {
792
+ const { instrumentKey, noteNumber, startTime } = note;
793
+ const modEnvToPitch = instrumentKey.modEnvToPitch / 100;
794
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
795
+ if (modEnvToPitch === 0)
796
+ return;
797
+ const basePitch = note.bufferSource.playbackRate.value;
798
+ const peekPitch = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset + modEnvToPitch);
799
+ const modDelay = startTime + instrumentKey.modDelay;
800
+ const modAttack = modDelay + instrumentKey.modAttack;
801
+ const modHold = modAttack + instrumentKey.modHold;
802
+ const modDecay = modHold + instrumentKey.modDecay;
803
+ note.bufferSource.playbackRate.value
804
+ .setValueAtTime(basePitch, modDelay)
805
+ .exponentialRampToValueAtTime(peekPitch, modAttack)
806
+ .setValueAtTime(peekPitch, modHold)
807
+ .linearRampToValueAtTime(basePitch, modDecay);
808
+ }
809
+ clampCutoffFrequency(frequency) {
810
+ const minFrequency = 20; // min Hz of initialFilterFc
811
+ const maxFrequency = 20000; // max Hz of initialFilterFc
812
+ return Math.max(minFrequency, Math.min(frequency, maxFrequency));
813
+ }
780
814
  setFilterEnvelope(channel, note) {
781
- const { instrumentKey, startTime, noteNumber } = note;
815
+ const { instrumentKey, noteNumber, startTime } = note;
782
816
  const softPedalFactor = 1 -
783
817
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
784
- const maxFreq = this.audioContext.sampleRate / 2;
785
818
  const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
786
819
  softPedalFactor;
787
820
  const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
788
- const sustainFreq = (baseFreq +
789
- (peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
821
+ const sustainFreq = baseFreq +
822
+ (peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
823
+ const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
824
+ const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
825
+ const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
790
826
  const modDelay = startTime + instrumentKey.modDelay;
791
827
  const modAttack = modDelay + instrumentKey.modAttack;
792
828
  const modHold = modAttack + instrumentKey.modHold;
793
829
  const modDecay = modHold + instrumentKey.modDecay;
794
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
795
- const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
796
- const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
797
- note.filterNode = new BiquadFilterNode(this.audioContext, {
798
- type: "lowpass",
799
- Q: instrumentKey.initialFilterQ / 10, // dB
800
- frequency: adjustedBaseFreq,
801
- });
802
830
  note.filterNode.frequency
831
+ .cancelScheduledValues(startTime)
832
+ .setValueAtTime(adjustedBaseFreq, startTime)
803
833
  .setValueAtTime(adjustedBaseFreq, modDelay)
804
834
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
805
835
  .setValueAtTime(adjustedPeekFreq, modHold)
806
836
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
807
- note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
808
837
  }
809
- startModulation(channel, note, time) {
838
+ startModulation(channel, note, startTime) {
810
839
  const { instrumentKey } = note;
811
- note.modLFOGain = new GainNode(this.audioContext, {
812
- gain: this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation),
813
- });
814
- note.modLFO = new OscillatorNode(this.audioContext, {
840
+ const { modLfoToPitch, modLfoToVolume } = instrumentKey;
841
+ note.modulationLFO = new OscillatorNode(this.audioContext, {
815
842
  frequency: this.centToHz(instrumentKey.freqModLFO),
816
843
  });
817
- note.modLFO.start(time);
818
- note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
819
- note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
820
- note.modLFO.connect(note.modLFOGain);
821
- note.modLFOGain.connect(note.bufferSource.detune);
844
+ note.filterDepth = new GainNode(this.audioContext, {
845
+ gain: instrumentKey.modLfoToFilterFc,
846
+ });
847
+ const modulationDepth = Math.abs(modLfoToPitch) + channel.modulationDepth;
848
+ const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
849
+ note.modulationDepth = new GainNode(this.audioContext, {
850
+ gain: modulationDepth * modulationDepthSign,
851
+ });
852
+ const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
853
+ const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
854
+ note.volumeDepth = new GainNode(this.audioContext, {
855
+ gain: volumeDepth * volumeDepthSign,
856
+ });
857
+ note.modulationLFO.start(startTime + instrumentKey.delayModLFO);
858
+ note.modulationLFO.connect(note.filterDepth);
859
+ note.filterDepth.connect(note.filterNode.frequency);
860
+ note.modulationLFO.connect(note.modulationDepth);
861
+ note.modulationDepth.connect(note.bufferSource.detune);
862
+ note.modulationLFO.connect(note.volumeDepth);
863
+ note.volumeDepth.connect(note.volumeNode.gain);
864
+ }
865
+ startVibrato(channel, note, startTime) {
866
+ const { instrumentKey } = note;
867
+ const { vibLfoToPitch } = instrumentKey;
868
+ note.vibratoLFO = new OscillatorNode(this.audioContext, {
869
+ frequency: this.centToHz(instrumentKey.freqVibLFO) *
870
+ channel.vibratoRate,
871
+ });
872
+ const vibratoDepth = Math.abs(vibLfoToPitch) * channel.vibratoDepth;
873
+ const vibratoDepthSign = 0 < vibLfoToPitch;
874
+ note.vibratoDepth = new GainNode(this.audioContext, {
875
+ gain: vibratoDepth * vibratoDepthSign,
876
+ });
877
+ note.vibratoLFO.start(startTime + instrumentKey.delayVibLFO * channel.vibratoDelay);
878
+ note.vibratoLFO.connect(note.vibratoDepth);
879
+ note.vibratoDepth.connect(note.bufferSource.detune);
822
880
  }
823
881
  async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
824
882
  const semitoneOffset = this.calcSemitoneOffset(channel);
825
883
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
826
884
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
827
- note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
885
+ note.volumeNode = new GainNode(this.audioContext);
886
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
887
+ type: "lowpass",
888
+ Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
889
+ });
828
890
  this.setVolumeEnvelope(note);
829
891
  this.setFilterEnvelope(channel, note);
830
- if (channel.modulation > 0) {
831
- const delayModLFO = startTime + instrumentKey.delayModLFO;
832
- this.startModulation(channel, note, delayModLFO);
892
+ if (0 < channel.vibratoDepth) {
893
+ this.startVibrato(channel, note, startTime);
894
+ }
895
+ if (0 < channel.modulationDepth) {
896
+ this.setPitch(note, semitoneOffset);
897
+ this.startModulation(channel, note, startTime);
898
+ }
899
+ else {
900
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
833
901
  }
834
902
  if (this.mono && channel.currentBufferSource) {
835
903
  channel.currentBufferSource.stop(startTime);
836
904
  channel.currentBufferSource = note.bufferSource;
837
905
  }
838
906
  note.bufferSource.connect(note.filterNode);
839
- note.filterNode.connect(note.gainNode);
907
+ note.filterNode.connect(note.volumeNode);
840
908
  note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
841
909
  return note;
842
910
  }
@@ -861,8 +929,8 @@ export class MidyGM2 {
861
929
  if (!instrumentKey)
862
930
  return;
863
931
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
864
- note.gainNode.connect(channel.gainL);
865
- note.gainNode.connect(channel.gainR);
932
+ note.volumeNode.connect(channel.gainL);
933
+ note.volumeNode.connect(channel.gainR);
866
934
  if (channel.sostenutoPedal) {
867
935
  channel.sostenutoNotes.set(noteNumber, note);
868
936
  }
@@ -878,7 +946,7 @@ export class MidyGM2 {
878
946
  const now = this.audioContext.currentTime;
879
947
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
880
948
  }
881
- scheduleNoteRelease(channelNumber, noteNumber, velocity, stopTime, stopPedal = false) {
949
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
882
950
  const channel = this.channels[channelNumber];
883
951
  if (stopPedal && channel.sustainPedal)
884
952
  return;
@@ -893,20 +961,14 @@ export class MidyGM2 {
893
961
  continue;
894
962
  if (note.ending)
895
963
  continue;
896
- const velocityRate = (velocity + 127) / 127;
897
- const volEndTime = stopTime +
898
- note.instrumentKey.volRelease * velocityRate;
899
- note.gainNode.gain
964
+ const volEndTime = stopTime + note.instrumentKey.volRelease;
965
+ note.volumeNode.gain
900
966
  .cancelScheduledValues(stopTime)
901
967
  .linearRampToValueAtTime(0, volEndTime);
902
- const maxFreq = this.audioContext.sampleRate / 2;
903
- const baseFreq = this.centToHz(note.instrumentKey.initialFilterFc);
904
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
905
- const modEndTime = stopTime +
906
- note.instrumentKey.modRelease * velocityRate;
968
+ const modRelease = stopTime + note.instrumentKey.modRelease;
907
969
  note.filterNode.frequency
908
970
  .cancelScheduledValues(stopTime)
909
- .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
971
+ .linearRampToValueAtTime(0, modRelease);
910
972
  note.ending = true;
911
973
  this.scheduleTask(() => {
912
974
  note.bufferSource.loop = false;
@@ -915,16 +977,18 @@ export class MidyGM2 {
915
977
  note.bufferSource.onended = () => {
916
978
  scheduledNotes[i] = null;
917
979
  note.bufferSource.disconnect();
980
+ note.volumeNode.disconnect();
918
981
  note.filterNode.disconnect();
919
- note.gainNode.disconnect();
920
- if (note.modLFOGain)
921
- note.modLFOGain.disconnect();
922
- if (note.vibLFOGain)
923
- note.vibLFOGain.disconnect();
924
- if (note.modLFO)
925
- note.modLFO.stop();
926
- if (note.vibLFO)
927
- note.vibLFO.stop();
982
+ if (note.volumeDepth)
983
+ note.volumeDepth.disconnect();
984
+ if (note.modulationDepth)
985
+ note.modulationDepth.disconnect();
986
+ if (note.modulationLFO)
987
+ note.modulationLFO.stop();
988
+ if (note.vibratoDepth)
989
+ note.vibratoDepth.disconnect();
990
+ if (note.vibratoLFO)
991
+ note.vibratoLFO.stop();
928
992
  resolve();
929
993
  };
930
994
  note.bufferSource.stop(volEndTime);
@@ -940,14 +1004,15 @@ export class MidyGM2 {
940
1004
  const channel = this.channels[channelNumber];
941
1005
  const promises = [];
942
1006
  channel.sustainPedal = false;
943
- channel.scheduledNotes.forEach((scheduledNotes) => {
944
- scheduledNotes.forEach((scheduledNote) => {
945
- if (scheduledNote) {
946
- const { noteNumber } = scheduledNote;
947
- const promise = this.releaseNote(channelNumber, noteNumber, velocity);
948
- promises.push(promise);
949
- }
950
- });
1007
+ channel.scheduledNotes.forEach((noteList) => {
1008
+ for (let i = 0; i < noteList.length; i++) {
1009
+ const note = noteList[i];
1010
+ if (!note)
1011
+ continue;
1012
+ const { noteNumber } = note;
1013
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
1014
+ promises.push(promise);
1015
+ }
951
1016
  });
952
1017
  return promises;
953
1018
  }
@@ -997,8 +1062,8 @@ export class MidyGM2 {
997
1062
  const activeNotes = this.getActiveNotes(channel, now);
998
1063
  if (channel.channelPressure.amplitudeControl !== 1) {
999
1064
  activeNotes.forEach((activeNote) => {
1000
- const gain = activeNote.gainNode.gain.value;
1001
- activeNote.gainNode.gain
1065
+ const gain = activeNote.volumeNode.gain.value;
1066
+ activeNote.volumeNode.gain
1002
1067
  .cancelScheduledValues(now)
1003
1068
  .setValueAtTime(gain * pressure, now);
1004
1069
  });
@@ -1021,7 +1086,7 @@ export class MidyGM2 {
1021
1086
  case 0:
1022
1087
  return this.setBankMSB(channelNumber, value);
1023
1088
  case 1:
1024
- return this.setModulation(channelNumber, value);
1089
+ return this.setModulationDepth(channelNumber, value);
1025
1090
  case 5:
1026
1091
  return this.setPortamentoTime(channelNumber, value);
1027
1092
  case 6:
@@ -1075,20 +1140,25 @@ export class MidyGM2 {
1075
1140
  }
1076
1141
  updateModulation(channel) {
1077
1142
  const now = this.audioContext.currentTime;
1078
- const activeNotes = this.getActiveNotes(channel, now);
1079
- activeNotes.forEach((activeNote) => {
1080
- if (activeNote.modLFO) {
1081
- const { gainNode, instrumentKey } = activeNote;
1082
- gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
1083
- }
1084
- else {
1085
- this.startModulation(channel, activeNote, now);
1143
+ channel.scheduledNotes.forEach((noteList) => {
1144
+ for (let i = 0; i < noteList.length; i++) {
1145
+ const note = noteList[i];
1146
+ if (!note)
1147
+ continue;
1148
+ if (note.modulationDepth) {
1149
+ note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
1150
+ }
1151
+ else {
1152
+ const semitoneOffset = this.calcSemitoneOffset(channel);
1153
+ this.setPitch(note, semitoneOffset);
1154
+ this.startModulation(channel, note, now);
1155
+ }
1086
1156
  }
1087
1157
  });
1088
1158
  }
1089
- setModulation(channelNumber, modulation) {
1159
+ setModulationDepth(channelNumber, modulation) {
1090
1160
  const channel = this.channels[channelNumber];
1091
- channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1161
+ channel.modulationDepth = (modulation / 127) * channel.modulationDepthRange;
1092
1162
  this.updateModulation(channel);
1093
1163
  }
1094
1164
  setPortamentoTime(channelNumber, portamentoTime) {
@@ -1246,13 +1316,17 @@ export class MidyGM2 {
1246
1316
  }
1247
1317
  updateDetune(channel, detuneChange) {
1248
1318
  const now = this.audioContext.currentTime;
1249
- const activeNotes = this.getActiveNotes(channel, now);
1250
- activeNotes.forEach((activeNote) => {
1251
- const { bufferSource } = activeNote;
1252
- const detune = bufferSource.detune.value + detuneChange;
1253
- bufferSource.detune
1254
- .cancelScheduledValues(now)
1255
- .setValueAtTime(detune, now);
1319
+ channel.scheduledNotes.forEach((noteList) => {
1320
+ for (let i = 0; i < noteList.length; i++) {
1321
+ const note = noteList[i];
1322
+ if (!note)
1323
+ continue;
1324
+ const { bufferSource } = note;
1325
+ const detune = bufferSource.detune.value + detuneChange;
1326
+ bufferSource.detune
1327
+ .cancelScheduledValues(now)
1328
+ .setValueAtTime(detune, now);
1329
+ }
1256
1330
  });
1257
1331
  }
1258
1332
  handlePitchBendRangeRPN(channelNumber) {
@@ -1298,47 +1372,23 @@ export class MidyGM2 {
1298
1372
  handleModulationDepthRangeRPN(channelNumber) {
1299
1373
  const channel = this.channels[channelNumber];
1300
1374
  this.limitData(channel, 0, 127, 0, 127);
1301
- const modulationDepthRange = dataMSB + dataLSB / 128;
1375
+ const modulationDepthRange = (dataMSB + dataLSB / 128) * 100;
1302
1376
  this.setModulationDepthRange(channelNumber, modulationDepthRange);
1303
1377
  }
1304
1378
  setModulationDepthRange(channelNumber, modulationDepthRange) {
1305
1379
  const channel = this.channels[channelNumber];
1306
1380
  channel.modulationDepthRange = modulationDepthRange;
1307
- channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1381
+ channel.modulationDepth = (modulation / 127) * modulationDepthRange;
1308
1382
  this.updateModulation(channel);
1309
1383
  }
1310
1384
  allSoundOff(channelNumber) {
1311
- const now = this.audioContext.currentTime;
1312
- const channel = this.channels[channelNumber];
1313
- const velocity = 0;
1314
- const stopPedal = true;
1315
- const promises = [];
1316
- channel.scheduledNotes.forEach((noteList) => {
1317
- const activeNote = this.getActiveNote(noteList, now);
1318
- if (activeNote) {
1319
- const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
1320
- promises.push(notePromise);
1321
- }
1322
- });
1323
- return promises;
1385
+ return this.stopChannelNotes(channelNumber, 0, true);
1324
1386
  }
1325
1387
  resetAllControllers(channelNumber) {
1326
1388
  Object.assign(this.channels[channelNumber], this.effectSettings);
1327
1389
  }
1328
1390
  allNotesOff(channelNumber) {
1329
- const now = this.audioContext.currentTime;
1330
- const channel = this.channels[channelNumber];
1331
- const velocity = 0;
1332
- const stopPedal = false;
1333
- const promises = [];
1334
- channel.scheduledNotes.forEach((noteList) => {
1335
- const activeNote = this.getActiveNote(noteList, now);
1336
- if (activeNote) {
1337
- const notePromise = this.scheduleNoteRelease(channelNumber, activeNote.noteNumber, velocity, now, stopPedal);
1338
- promises.push(notePromise);
1339
- }
1340
- });
1341
- return promises;
1391
+ return this.stopChannelNotes(channelNumber, 0, false);
1342
1392
  }
1343
1393
  omniOff() {
1344
1394
  this.omni = false;
@@ -1698,6 +1748,9 @@ Object.defineProperty(MidyGM2, "channelSettings", {
1698
1748
  portamentoTime: 0,
1699
1749
  reverbSendLevel: 0,
1700
1750
  chorusSendLevel: 0,
1751
+ vibratoRate: 1,
1752
+ vibratoDepth: 1,
1753
+ vibratoDelay: 1,
1701
1754
  bank: 121 * 128,
1702
1755
  bankMSB: 121,
1703
1756
  bankLSB: 0,
@@ -1707,7 +1760,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
1707
1760
  pitchBend: 0,
1708
1761
  fineTuning: 0, // cb
1709
1762
  coarseTuning: 0, // cb
1710
- modulationDepthRange: 0.5, // cb
1763
+ modulationDepthRange: 50, // cent
1711
1764
  }
1712
1765
  });
1713
1766
  Object.defineProperty(MidyGM2, "effectSettings", {
@@ -1716,7 +1769,7 @@ Object.defineProperty(MidyGM2, "effectSettings", {
1716
1769
  writable: true,
1717
1770
  value: {
1718
1771
  expression: 1,
1719
- modulation: 0,
1772
+ modulationDepth: 0,
1720
1773
  sustainPedal: false,
1721
1774
  portamento: false,
1722
1775
  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"}