@marmooo/midy 0.0.4 → 0.0.5

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 (33) hide show
  1. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
  2. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
  3. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
  4. package/esm/midy-GM1.d.ts +24 -34
  5. package/esm/midy-GM1.d.ts.map +1 -1
  6. package/esm/midy-GM1.js +167 -106
  7. package/esm/midy-GM2.d.ts +123 -21
  8. package/esm/midy-GM2.d.ts.map +1 -1
  9. package/esm/midy-GM2.js +170 -116
  10. package/esm/midy-GMLite.d.ts +23 -35
  11. package/esm/midy-GMLite.d.ts.map +1 -1
  12. package/esm/midy-GMLite.js +156 -107
  13. package/esm/midy.d.ts +25 -23
  14. package/esm/midy.d.ts.map +1 -1
  15. package/esm/midy.js +191 -120
  16. package/package.json +1 -1
  17. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.d.ts +13 -6
  18. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.d.ts.map +1 -0
  19. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/{soundfont-parser@0.0.1 → soundfont-parser@0.0.2}/+esm.js +5 -5
  20. package/script/midy-GM1.d.ts +24 -34
  21. package/script/midy-GM1.d.ts.map +1 -1
  22. package/script/midy-GM1.js +167 -106
  23. package/script/midy-GM2.d.ts +123 -21
  24. package/script/midy-GM2.d.ts.map +1 -1
  25. package/script/midy-GM2.js +170 -116
  26. package/script/midy-GMLite.d.ts +23 -35
  27. package/script/midy-GMLite.d.ts.map +1 -1
  28. package/script/midy-GMLite.js +156 -107
  29. package/script/midy.d.ts +25 -23
  30. package/script/midy.d.ts.map +1 -1
  31. package/script/midy.js +191 -120
  32. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.d.ts.map +0 -1
  33. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.d.ts.map +0 -1
package/script/midy.js CHANGED
@@ -2,7 +2,57 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Midy = void 0;
4
4
  const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
5
- const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.js");
5
+ const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.2/+esm.js");
6
+ class Note {
7
+ constructor(noteNumber, velocity, startTime, instrumentKey) {
8
+ Object.defineProperty(this, "bufferSource", {
9
+ enumerable: true,
10
+ configurable: true,
11
+ writable: true,
12
+ value: void 0
13
+ });
14
+ Object.defineProperty(this, "gainNode", {
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true,
18
+ value: void 0
19
+ });
20
+ Object.defineProperty(this, "filterNode", {
21
+ enumerable: true,
22
+ configurable: true,
23
+ writable: true,
24
+ value: void 0
25
+ });
26
+ Object.defineProperty(this, "modLFO", {
27
+ enumerable: true,
28
+ configurable: true,
29
+ writable: true,
30
+ value: void 0
31
+ });
32
+ Object.defineProperty(this, "modLFOGain", {
33
+ enumerable: true,
34
+ configurable: true,
35
+ writable: true,
36
+ value: void 0
37
+ });
38
+ Object.defineProperty(this, "vibLFO", {
39
+ enumerable: true,
40
+ configurable: true,
41
+ writable: true,
42
+ value: void 0
43
+ });
44
+ Object.defineProperty(this, "vibLFOGain", {
45
+ enumerable: true,
46
+ configurable: true,
47
+ writable: true,
48
+ value: void 0
49
+ });
50
+ this.noteNumber = noteNumber;
51
+ this.velocity = velocity;
52
+ this.startTime = startTime;
53
+ this.instrumentKey = instrumentKey;
54
+ }
55
+ }
6
56
  class Midy {
7
57
  constructor(audioContext) {
8
58
  Object.defineProperty(this, "ticksPerBeat", {
@@ -184,10 +234,8 @@ class Midy {
184
234
  const pannerNode = new StereoPannerNode(audioContext, {
185
235
  pan: Midy.channelSettings.pan,
186
236
  });
187
- const modulationEffect = this.createModulationEffect(audioContext);
188
237
  const reverbEffect = this.createReverbEffect(audioContext);
189
238
  const chorusEffect = this.createChorusEffect(audioContext);
190
- modulationEffect.lfo.start();
191
239
  chorusEffect.lfo.start();
192
240
  reverbEffect.dryGain.connect(pannerNode);
193
241
  reverbEffect.wetGain.connect(pannerNode);
@@ -196,7 +244,6 @@ class Midy {
196
244
  return {
197
245
  gainNode,
198
246
  pannerNode,
199
- modulationEffect,
200
247
  reverbEffect,
201
248
  chorusEffect,
202
249
  };
@@ -219,11 +266,11 @@ class Midy {
219
266
  });
220
267
  return channels;
221
268
  }
222
- async createNoteBuffer(noteInfo, isSF3) {
223
- const sampleEnd = noteInfo.sample.length + noteInfo.end;
269
+ async createNoteBuffer(instrumentKey, isSF3) {
270
+ const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
224
271
  if (isSF3) {
225
- const sample = new Uint8Array(noteInfo.sample.length);
226
- sample.set(noteInfo.sample);
272
+ const sample = new Uint8Array(instrumentKey.sample.length);
273
+ sample.set(instrumentKey.sample);
227
274
  const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
228
275
  for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
229
276
  const channelData = audioBuffer.getChannelData(channel);
@@ -232,26 +279,27 @@ class Midy {
232
279
  return audioBuffer;
233
280
  }
234
281
  else {
235
- const sample = noteInfo.sample.subarray(0, sampleEnd);
282
+ const sample = instrumentKey.sample.subarray(0, sampleEnd);
236
283
  const floatSample = this.convertToFloat32Array(sample);
237
284
  const audioBuffer = new AudioBuffer({
238
285
  numberOfChannels: 1,
239
286
  length: sample.length,
240
- sampleRate: noteInfo.sampleRate,
287
+ sampleRate: instrumentKey.sampleRate,
241
288
  });
242
289
  const channelData = audioBuffer.getChannelData(0);
243
290
  channelData.set(floatSample);
244
291
  return audioBuffer;
245
292
  }
246
293
  }
247
- async createNoteBufferNode(noteInfo, isSF3) {
294
+ async createNoteBufferNode(instrumentKey, isSF3) {
248
295
  const bufferSource = new AudioBufferSourceNode(this.audioContext);
249
- const audioBuffer = await this.createNoteBuffer(noteInfo, isSF3);
296
+ const audioBuffer = await this.createNoteBuffer(instrumentKey, isSF3);
250
297
  bufferSource.buffer = audioBuffer;
251
- bufferSource.loop = noteInfo.sampleModes % 2 !== 0;
298
+ bufferSource.loop = instrumentKey.sampleModes % 2 !== 0;
252
299
  if (bufferSource.loop) {
253
- bufferSource.loopStart = noteInfo.loopStart / noteInfo.sampleRate;
254
- bufferSource.loopEnd = noteInfo.loopEnd / noteInfo.sampleRate;
300
+ bufferSource.loopStart = instrumentKey.loopStart /
301
+ instrumentKey.sampleRate;
302
+ bufferSource.loopEnd = instrumentKey.loopEnd / instrumentKey.sampleRate;
255
303
  }
256
304
  return bufferSource;
257
305
  }
@@ -533,30 +581,26 @@ class Midy {
533
581
  const now = this.audioContext.currentTime;
534
582
  return this.resumeTime + now - this.startTime - this.startDelay;
535
583
  }
536
- getActiveNotes(channel) {
584
+ getActiveNotes(channel, time) {
537
585
  const activeNotes = new Map();
538
- channel.scheduledNotes.forEach((scheduledNotes) => {
539
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
586
+ channel.scheduledNotes.forEach((noteList) => {
587
+ const activeNote = this.getActiveNote(noteList, time);
540
588
  if (activeNote) {
541
589
  activeNotes.set(activeNote.noteNumber, activeNote);
542
590
  }
543
591
  });
544
592
  return activeNotes;
545
593
  }
546
- getActiveChannelNotes(scheduledNotes) {
547
- for (let i = 0; i < scheduledNotes; i++) {
548
- const scheduledNote = scheduledNotes[i];
549
- if (scheduledNote)
550
- return scheduledNote;
594
+ getActiveNote(noteList, time) {
595
+ for (let i = noteList.length - 1; i >= 0; i--) {
596
+ const note = noteList[i];
597
+ if (!note)
598
+ return;
599
+ if (time < note.startTime)
600
+ continue;
601
+ return (note.ending) ? null : note;
551
602
  }
552
- }
553
- createModulationEffect(audioContext) {
554
- const lfo = new OscillatorNode(audioContext, {
555
- frequency: 5,
556
- });
557
- return {
558
- lfo,
559
- };
603
+ return noteList[0];
560
604
  }
561
605
  createReverbEffect(audioContext, options = {}) {
562
606
  const { decay = 0.8, preDecay = 0, } = options;
@@ -666,77 +710,110 @@ class Midy {
666
710
  const tuning = masterTuning + channelTuning;
667
711
  return channel.pitchBend * channel.pitchBendRange + tuning;
668
712
  }
669
- calcPlaybackRate(noteInfo, noteNumber, semitoneOffset) {
670
- return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
713
+ calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset) {
714
+ return instrumentKey.playbackRate(noteNumber) *
715
+ Math.pow(2, semitoneOffset / 12);
671
716
  }
672
- async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
673
- const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
674
- const semitoneOffset = this.calcSemitoneOffset(channel);
675
- bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
676
- // volume envelope
677
- const gainNode = new GainNode(this.audioContext, {
717
+ setVolumeEnvelope(channel, note) {
718
+ const { instrumentKey, startTime, velocity } = note;
719
+ note.gainNode = new GainNode(this.audioContext, {
678
720
  gain: 0,
679
721
  });
680
722
  let volume = (velocity / 127) * channel.volume * channel.expression;
681
723
  if (volume === 0)
682
724
  volume = 1e-6; // exponentialRampToValueAtTime() requires a non-zero value
683
- const attackVolume = this.cbToRatio(-noteInfo.initialAttenuation) * volume;
684
- const sustainVolume = attackVolume * (1 - noteInfo.volSustain);
685
- const volDelay = startTime + noteInfo.volDelay;
686
- const volAttack = volDelay + noteInfo.volAttack;
687
- const volHold = volAttack + noteInfo.volHold;
688
- const volDecay = volHold + noteInfo.volDecay;
689
- gainNode.gain
725
+ const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation) *
726
+ volume;
727
+ const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
728
+ const volDelay = startTime + instrumentKey.volDelay;
729
+ const volAttack = volDelay + instrumentKey.volAttack;
730
+ const volHold = volAttack + instrumentKey.volHold;
731
+ const volDecay = volHold + instrumentKey.volDecay;
732
+ note.gainNode.gain
690
733
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
691
734
  .exponentialRampToValueAtTime(attackVolume, volAttack)
692
735
  .setValueAtTime(attackVolume, volHold)
693
736
  .linearRampToValueAtTime(sustainVolume, volDecay);
694
- // filter envelope
737
+ }
738
+ setFilterEnvelope(channel, note) {
739
+ const { instrumentKey, startTime, noteNumber } = note;
695
740
  const softPedalFactor = 1 -
696
741
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
697
742
  const maxFreq = this.audioContext.sampleRate / 2;
698
- const baseFreq = this.centToHz(noteInfo.initialFilterFc) * softPedalFactor;
699
- const peekFreq = this.centToHz(noteInfo.initialFilterFc + noteInfo.modEnvToFilterFc) * softPedalFactor;
743
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
744
+ softPedalFactor;
745
+ const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
700
746
  const sustainFreq = (baseFreq +
701
- (peekFreq - baseFreq) * (1 - noteInfo.modSustain)) * softPedalFactor;
747
+ (peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
748
+ const modDelay = startTime + instrumentKey.modDelay;
749
+ const modAttack = modDelay + instrumentKey.modAttack;
750
+ const modHold = modAttack + instrumentKey.modHold;
751
+ const modDecay = modHold + instrumentKey.modDecay;
702
752
  const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
703
753
  const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
704
754
  const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
705
- const filterNode = new BiquadFilterNode(this.audioContext, {
755
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
706
756
  type: "lowpass",
707
- Q: noteInfo.initialFilterQ / 10, // dB
757
+ Q: instrumentKey.initialFilterQ / 10, // dB
708
758
  frequency: adjustedBaseFreq,
709
759
  });
710
- const modDelay = startTime + noteInfo.modDelay;
711
- const modAttack = modDelay + noteInfo.modAttack;
712
- const modHold = modAttack + noteInfo.modHold;
713
- const modDecay = modHold + noteInfo.modDecay;
714
- filterNode.frequency
760
+ note.filterNode.frequency
715
761
  .setValueAtTime(adjustedBaseFreq, modDelay)
716
762
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
717
763
  .setValueAtTime(adjustedPeekFreq, modHold)
718
764
  .linearRampToValueAtTime(adjustedSustainFreq, modDecay);
719
- let lfoGain;
765
+ note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modEnvToPitch, modDelay);
766
+ }
767
+ startModulation(channel, note, time) {
768
+ const { instrumentKey } = note;
769
+ note.modLFOGain = new GainNode(this.audioContext, {
770
+ gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
771
+ });
772
+ note.modLFO = new OscillatorNode(this.audioContext, {
773
+ frequency: this.centToHz(instrumentKey.freqModLFO),
774
+ });
775
+ note.modLFO.start(time);
776
+ note.filterNode.frequency.setValueAtTime(note.filterNode.frequency.value + instrumentKey.modLfoToFilterFc, time);
777
+ note.bufferSource.detune.setValueAtTime(note.bufferSource.detune.value + instrumentKey.modLfoToPitch, time);
778
+ note.modLFO.connect(note.modLFOGain);
779
+ note.modLFOGain.connect(note.bufferSource.detune);
780
+ }
781
+ startVibrato(channel, note, time) {
782
+ const { instrumentKey } = note;
783
+ note.vibLFOGain = new GainNode(this.audioContext, {
784
+ gain: channel.vibratoDepth,
785
+ });
786
+ note.vibLFO = new OscillatorNode(this.audioContext, {
787
+ frequency: this.centToHz(instrumentKey.freqModLFO) +
788
+ channel.vibratoRate,
789
+ });
790
+ note.vibLFO.start(time + channel.vibratoDelay);
791
+ note.vibLFO.connect(note.vibLFOGain);
792
+ note.vibLFOGain.connect(note.bufferSource.detune);
793
+ }
794
+ async createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3) {
795
+ const semitoneOffset = this.calcSemitoneOffset(channel);
796
+ const note = new Note(noteNumber, velocity, startTime, instrumentKey);
797
+ note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
798
+ note.bufferSource.playbackRate.value = this.calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
799
+ this.setVolumeEnvelope(channel, note);
800
+ this.setFilterEnvelope(channel, note);
720
801
  if (channel.modulation > 0) {
721
- const vibratoDelay = startTime + channel.vibratoDelay;
722
- const vibratoAttack = vibratoDelay + 0.1;
723
- lfoGain = new GainNode(this.audioContext, {
724
- gain: 0,
725
- });
726
- lfoGain.gain
727
- .setValueAtTime(1e-6, vibratoDelay) // exponentialRampToValueAtTime() requires a non-zero value
728
- .exponentialRampToValueAtTime(channel.modulation, vibratoAttack);
729
- channel.modulationEffect.lfo.connect(lfoGain);
730
- lfoGain.connect(bufferSource.detune);
802
+ const delayModLFO = startTime + instrumentKey.delayModLFO;
803
+ this.startModulation(channel, note, delayModLFO);
804
+ }
805
+ if (channel.vibratoDepth > 0) {
806
+ const delayVibLFO = startTime + instrumentKey.delayVibLFO;
807
+ this.startVibrato(channel, note, delayVibLFO);
731
808
  }
732
- bufferSource.connect(filterNode);
733
- filterNode.connect(gainNode);
734
809
  if (this.mono && channel.currentBufferSource) {
735
810
  channel.currentBufferSource.stop(startTime);
736
- channel.currentBufferSource = bufferSource;
811
+ channel.currentBufferSource = note.bufferSource;
737
812
  }
738
- bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
739
- return { bufferSource, gainNode, filterNode, lfoGain };
813
+ note.bufferSource.connect(note.filterNode);
814
+ note.filterNode.connect(note.gainNode);
815
+ note.bufferSource.start(startTime, instrumentKey.start / instrumentKey.sampleRate);
816
+ return note;
740
817
  }
741
818
  calcBank(channel, channelNumber) {
742
819
  if (channel.bankMSB === 121) {
@@ -755,36 +832,20 @@ class Midy {
755
832
  return;
756
833
  const soundFont = this.soundFonts[soundFontIndex];
757
834
  const isSF3 = soundFont.parsed.info.version.major === 3;
758
- const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
759
- if (!noteInfo)
835
+ const instrumentKey = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
836
+ if (!instrumentKey)
760
837
  return;
761
- const { bufferSource, gainNode, filterNode, lfoGain } = await this
762
- .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
763
- this.connectNoteEffects(channel, gainNode);
838
+ const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
839
+ this.connectNoteEffects(channel, note.gainNode);
764
840
  if (channel.sostenutoPedal) {
765
- channel.sostenutoNotes.set(noteNumber, {
766
- gainNode,
767
- filterNode,
768
- bufferSource,
769
- noteNumber,
770
- noteInfo,
771
- });
841
+ channel.sostenutoNotes.set(noteNumber, note);
772
842
  }
773
843
  const scheduledNotes = channel.scheduledNotes;
774
- const scheduledNote = {
775
- bufferSource,
776
- filterNode,
777
- gainNode,
778
- lfoGain,
779
- noteInfo,
780
- noteNumber,
781
- startTime,
782
- };
783
844
  if (scheduledNotes.has(noteNumber)) {
784
- scheduledNotes.get(noteNumber).push(scheduledNote);
845
+ scheduledNotes.get(noteNumber).push(note);
785
846
  }
786
847
  else {
787
- scheduledNotes.set(noteNumber, [scheduledNote]);
848
+ scheduledNotes.set(noteNumber, [note]);
788
849
  }
789
850
  }
790
851
  noteOn(channelNumber, noteNumber, velocity) {
@@ -806,15 +867,15 @@ class Midy {
806
867
  continue;
807
868
  if (targetNote.ending)
808
869
  continue;
809
- const { bufferSource, filterNode, gainNode, lfoGain, noteInfo } = targetNote;
870
+ const { bufferSource, filterNode, gainNode, modLFO, modLFOGain, vibLFO, vibLFOGain, instrumentKey, } = targetNote;
810
871
  const velocityRate = (velocity + 127) / 127;
811
- const volEndTime = stopTime + noteInfo.volRelease * velocityRate;
872
+ const volEndTime = stopTime + instrumentKey.volRelease * velocityRate;
812
873
  gainNode.gain.cancelScheduledValues(stopTime);
813
874
  gainNode.gain.linearRampToValueAtTime(0, volEndTime);
814
875
  const maxFreq = this.audioContext.sampleRate / 2;
815
- const baseFreq = this.centToHz(noteInfo.initialFilterFc);
876
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
816
877
  const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
817
- const modEndTime = stopTime + noteInfo.modRelease * velocityRate;
878
+ const modEndTime = stopTime + instrumentKey.modRelease * velocityRate;
818
879
  filterNode.frequency
819
880
  .cancelScheduledValues(stopTime)
820
881
  .linearRampToValueAtTime(adjustedBaseFreq, modEndTime);
@@ -828,8 +889,14 @@ class Midy {
828
889
  bufferSource.disconnect(0);
829
890
  filterNode.disconnect(0);
830
891
  gainNode.disconnect(0);
831
- if (lfoGain)
832
- lfoGain.disconnect(0);
892
+ if (modLFOGain)
893
+ modLFOGain.disconnect(0);
894
+ if (vibLFOGain)
895
+ vibLFOGain.disconnect(0);
896
+ if (modLFO)
897
+ modLFO.stop();
898
+ if (vibLFO)
899
+ vibLFO.stop();
833
900
  resolve();
834
901
  };
835
902
  bufferSource.stop(volEndTime);
@@ -895,7 +962,7 @@ class Midy {
895
962
  const now = this.audioContext.currentTime;
896
963
  const channel = this.channels[channelNumber];
897
964
  pressure /= 64;
898
- const activeNotes = this.getActiveNotes(channel);
965
+ const activeNotes = this.getActiveNotes(channel, now);
899
966
  if (channel.polyphonicKeyPressure.amplitudeControl !== 1) {
900
967
  if (activeNotes.has(noteNumber)) {
901
968
  const activeNote = activeNotes.get(noteNumber);
@@ -916,7 +983,7 @@ class Midy {
916
983
  const channel = this.channels[channelNumber];
917
984
  pressure /= 64;
918
985
  channel.channelPressure = pressure;
919
- const activeNotes = this.getActiveNotes(channel);
986
+ const activeNotes = this.getActiveNotes(channel, now);
920
987
  if (channel.channelPressure.amplitudeControl !== 1) {
921
988
  activeNotes.forEach((activeNote) => {
922
989
  const gain = activeNote.gainNode.gain.value;
@@ -935,10 +1002,10 @@ class Midy {
935
1002
  const channel = this.channels[channelNumber];
936
1003
  channel.pitchBend = (pitchBend - 8192) / 8192;
937
1004
  const semitoneOffset = this.calcSemitoneOffset(channel);
938
- const activeNotes = this.getActiveNotes(channel);
1005
+ const activeNotes = this.getActiveNotes(channel, now);
939
1006
  activeNotes.forEach((activeNote) => {
940
- const { bufferSource, noteInfo, noteNumber } = activeNote;
941
- const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
1007
+ const { bufferSource, instrumentKey, noteNumber } = activeNote;
1008
+ const playbackRate = calcPlaybackRate(instrumentKey, noteNumber, semitoneOffset);
942
1009
  bufferSource.playbackRate
943
1010
  .cancelScheduledValues(now)
944
1011
  .setValueAtTime(playbackRate * pressure, now);
@@ -1013,9 +1080,20 @@ class Midy {
1013
1080
  this.channels[channelNumber].bankMSB = msb;
1014
1081
  }
1015
1082
  setModulation(channelNumber, modulation) {
1083
+ const now = this.audioContext.currentTime;
1016
1084
  const channel = this.channels[channelNumber];
1017
1085
  channel.modulation = (modulation / 127) *
1018
1086
  (channel.modulationDepthRange * 100);
1087
+ const activeNotes = this.getActiveNotes(channel, now);
1088
+ activeNotes.forEach((activeNote) => {
1089
+ if (activeNote.modLFO) {
1090
+ activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
1091
+ channel.modulation, now);
1092
+ }
1093
+ else {
1094
+ this.startModulation(channel, activeNote, now);
1095
+ }
1096
+ });
1019
1097
  }
1020
1098
  setPortamentoTime(channelNumber, portamentoTime) {
1021
1099
  this.channels[channelNumber].portamentoTime = portamentoTime / 127;
@@ -1076,7 +1154,8 @@ class Midy {
1076
1154
  const channel = this.channels[channelNumber];
1077
1155
  channel.sostenutoPedal = isOn;
1078
1156
  if (isOn) {
1079
- const activeNotes = this.getActiveNotes(channel);
1157
+ const now = this.audioContext.currentTime;
1158
+ const activeNotes = this.getActiveNotes(channel, now);
1080
1159
  channel.sostenutoNotes = new Map(activeNotes);
1081
1160
  }
1082
1161
  else {
@@ -1088,20 +1167,12 @@ class Midy {
1088
1167
  channel.softPedal = softPedal / 127;
1089
1168
  }
1090
1169
  setVibratoRate(channelNumber, vibratoRate) {
1091
- const now = this.audioContext.currentTime;
1092
1170
  const channel = this.channels[channelNumber];
1093
1171
  channel.vibratoRate = vibratoRate / 127 * 4 + 3; // 3-7Hz
1094
- channel.modulationEffect.lfo.frequency
1095
- .cancelScheduledValues(now)
1096
- .setValueAtTime(channel.vibratoRate, now);
1097
1172
  }
1098
1173
  setVibratoDepth(channelNumber, vibratoDepth) {
1099
- const now = this.audioContext.currentTime;
1100
1174
  const channel = this.channels[channelNumber];
1101
1175
  channel.vibratoDepth = vibratoDepth / 127;
1102
- channel.modulationEffect.lfoGain.gain
1103
- .cancelScheduledValues(now)
1104
- .setValueAtTime(channel.vibratoDepth, now);
1105
1176
  }
1106
1177
  setVibratoDelay(channelNumber, vibratoDelay) {
1107
1178
  // Access Virus: 0-10sec
@@ -1182,8 +1253,8 @@ class Midy {
1182
1253
  const velocity = 0;
1183
1254
  const stopPedal = true;
1184
1255
  const promises = [];
1185
- channel.scheduledNotes.forEach((scheduledNotes) => {
1186
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
1256
+ channel.scheduledNotes.forEach((noteList) => {
1257
+ const activeNote = this.getActiveNote(noteList, now);
1187
1258
  if (activeNote) {
1188
1259
  const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
1189
1260
  promises.push(notePromise);
@@ -1200,8 +1271,8 @@ class Midy {
1200
1271
  const velocity = 0;
1201
1272
  const stopPedal = false;
1202
1273
  const promises = [];
1203
- channel.scheduledNotes.forEach((scheduledNotes) => {
1204
- const activeNote = this.getActiveChannelNotes(scheduledNotes);
1274
+ channel.scheduledNotes.forEach((noteList) => {
1275
+ const activeNote = this.getActiveNote(noteList, now);
1205
1276
  if (activeNote) {
1206
1277
  const notePromise = this.scheduleNoteRelease(channelNumber, noteNumber, velocity, now, stopPedal);
1207
1278
  promises.push(notePromise);
@@ -1 +0,0 @@
1
- {"version":3,"file":"+esm.d.ts","sourceRoot":"","sources":["../../../../../../src/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.js"],"names":[],"mappings":";;AAMszX;IAAQ,oBAA2H;IAAd,YAAa;IAAC,qDAAoJ;IAAA,mCAAoP;IAAA,uCAAwQ;IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAAi/D;IAAA,qBAA0I;CAAC;AAAA,mCAAwC;AAAl+H,+BAA8F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAA98D;;;;;;;;;;;;;;;;EAA27B;AAAr4I;IAAqQ,wBAAwD;IAArT,4BAAyN;IAApB,QAAS;IAAC,QAAS;IAAC,oBAAoC;CAAyD;AAAllG;IAAwjC,gCAA8d;CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"+esm.d.ts","sourceRoot":"","sources":["../../../../../../src/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.1/+esm.js"],"names":[],"mappings":";;AAMszX;IAAQ,oBAA2H;IAAd,YAAa;IAAC,qDAAoJ;IAAA,mCAAoP;IAAA,uCAAwQ;IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAAi/D;IAAA,qBAA0I;CAAC;AAAA,mCAAwC;AAAl+H,+BAA8F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAA98D;;;;;;;;;;;;;;;;EAA27B;AAAr4I;IAAqQ,wBAAwD;IAArT,4BAAyN;IAApB,QAAS;IAAC,QAAS;IAAC,oBAAoC;CAAyD;AAAllG;IAAwjC,gCAA8d;CAAC"}