@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 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
- setFilterNode(channel: any, note: any): void;
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, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
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>;
@@ -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,mCAcC;IAED,+CAwBC;IAED,6CA6BC;IAED,+DA0BC;IAED,wHA8BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA4CC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,2FA+BC;IAED,qCAeC;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,oDAUC;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;AA5iCD;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"}
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"}
package/esm/midy-GM1.js CHANGED
@@ -432,12 +432,13 @@ export class MidyGM1 {
432
432
  const now = this.audioContext.currentTime;
433
433
  const channel = this.channels[channelNumber];
434
434
  channel.scheduledNotes.forEach((noteList) => {
435
- noteList.forEach((note) => {
436
- if (note) {
437
- const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
438
- this.notePromises.push(promise);
439
- }
440
- });
435
+ for (let i = 0; i < noteList.length; i++) {
436
+ const note = noteList[i];
437
+ if (!note)
438
+ continue;
439
+ const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
440
+ this.notePromises.push(promise);
441
+ }
441
442
  });
442
443
  channel.scheduledNotes.clear();
443
444
  await Promise.all(this.notePromises);
@@ -529,7 +530,6 @@ export class MidyGM1 {
529
530
  }
530
531
  setVolumeEnvelope(note) {
531
532
  const { instrumentKey, startTime } = note;
532
- note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
533
533
  const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
534
534
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
535
535
  const volDelay = startTime + instrumentKey.volDelay;
@@ -537,6 +537,8 @@ export class MidyGM1 {
537
537
  const volHold = volAttack + instrumentKey.volHold;
538
538
  const volDecay = volHold + instrumentKey.volDecay;
539
539
  note.volumeNode.gain
540
+ .cancelScheduledValues(startTime)
541
+ .setValueAtTime(0, startTime)
540
542
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
541
543
  .exponentialRampToValueAtTime(attackVolume, volAttack)
542
544
  .setValueAtTime(attackVolume, volHold)
@@ -560,29 +562,27 @@ export class MidyGM1 {
560
562
  .setValueAtTime(peekPitch, modHold)
561
563
  .linearRampToValueAtTime(basePitch, modDecay);
562
564
  }
563
- setFilterNode(channel, note) {
564
- const { instrumentKey, noteNumber, startTime } = note;
565
- const softPedalFactor = 1 -
566
- (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
567
- const maxFreq = this.audioContext.sampleRate / 2;
568
- const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
569
- softPedalFactor;
570
- const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
571
- const sustainFreq = (baseFreq +
572
- (peekFreq - baseFreq) * (1 - instrumentKey.modSustain)) * softPedalFactor;
573
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
574
- const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
575
- const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
565
+ clampCutoffFrequency(frequency) {
566
+ const minFrequency = 20; // min Hz of initialFilterFc
567
+ const maxFrequency = 20000; // max Hz of initialFilterFc
568
+ return Math.max(minFrequency, Math.min(frequency, maxFrequency));
569
+ }
570
+ setFilterEnvelope(note) {
571
+ const { instrumentKey, startTime } = note;
572
+ const baseFreq = this.centToHz(instrumentKey.initialFilterFc);
573
+ const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc);
574
+ const sustainFreq = baseFreq +
575
+ (peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
576
+ const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
577
+ const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
578
+ const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
576
579
  const modDelay = startTime + instrumentKey.modDelay;
577
580
  const modAttack = modDelay + instrumentKey.modAttack;
578
581
  const modHold = modAttack + instrumentKey.modHold;
579
582
  const modDecay = modHold + instrumentKey.modDecay;
580
- note.filterNode = new BiquadFilterNode(this.audioContext, {
581
- type: "lowpass",
582
- Q: instrumentKey.initialFilterQ / 10, // dB
583
- frequency: adjustedBaseFreq,
584
- });
585
583
  note.filterNode.frequency
584
+ .cancelScheduledValues(startTime)
585
+ .setValueAtTime(adjustedBaseFreq, startTime)
586
586
  .setValueAtTime(adjustedBaseFreq, modDelay)
587
587
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
588
588
  .setValueAtTime(adjustedPeekFreq, modHold)
@@ -619,8 +619,13 @@ export class MidyGM1 {
619
619
  const semitoneOffset = this.calcSemitoneOffset(channel);
620
620
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
621
621
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
622
- this.setFilterNode(channel, note);
622
+ note.volumeNode = new GainNode(this.audioContext);
623
+ note.filterNode = new BiquadFilterNode(this.audioContext, {
624
+ type: "lowpass",
625
+ Q: instrumentKey.initialFilterQ / 10 * channel.filterResonance, // dB
626
+ });
623
627
  this.setVolumeEnvelope(note);
628
+ this.setFilterEnvelope(note);
624
629
  if (0 < channel.modulationDepth) {
625
630
  this.setPitch(note, semitoneOffset);
626
631
  this.startModulation(channel, note, startTime);
@@ -659,7 +664,7 @@ export class MidyGM1 {
659
664
  const now = this.audioContext.currentTime;
660
665
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
661
666
  }
662
- scheduleNoteRelease(channelNumber, noteNumber, velocity, stopTime, stopPedal = false) {
667
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
663
668
  const channel = this.channels[channelNumber];
664
669
  if (stopPedal && channel.sustainPedal)
665
670
  return;
@@ -672,14 +677,11 @@ export class MidyGM1 {
672
677
  continue;
673
678
  if (note.ending)
674
679
  continue;
675
- const velocityRate = (velocity + 127) / 127;
676
- const volEndTime = stopTime +
677
- note.instrumentKey.volRelease * velocityRate;
680
+ const volEndTime = stopTime + note.instrumentKey.volRelease;
678
681
  note.volumeNode.gain
679
682
  .cancelScheduledValues(stopTime)
680
683
  .linearRampToValueAtTime(0, volEndTime);
681
- const modRelease = stopTime +
682
- note.instrumentKey.modRelease * velocityRate;
684
+ const modRelease = stopTime + note.instrumentKey.modRelease;
683
685
  note.filterNode.frequency
684
686
  .cancelScheduledValues(stopTime)
685
687
  .linearRampToValueAtTime(0, modRelease);
@@ -715,13 +717,14 @@ export class MidyGM1 {
715
717
  const promises = [];
716
718
  channel.sustainPedal = false;
717
719
  channel.scheduledNotes.forEach((noteList) => {
718
- noteList.forEach((note) => {
719
- if (note) {
720
- const { noteNumber } = note;
721
- const promise = this.releaseNote(channelNumber, noteNumber, velocity);
722
- promises.push(promise);
723
- }
724
- });
720
+ for (let i = 0; i < noteList.length; i++) {
721
+ const note = noteList[i];
722
+ if (!note)
723
+ continue;
724
+ const { noteNumber } = note;
725
+ const promise = this.releaseNote(channelNumber, noteNumber, velocity);
726
+ promises.push(promise);
727
+ }
725
728
  });
726
729
  return promises;
727
730
  }
@@ -791,15 +794,19 @@ export class MidyGM1 {
791
794
  }
792
795
  updateModulation(channel) {
793
796
  const now = this.audioContext.currentTime;
794
- const activeNotes = this.getActiveNotes(channel, now);
795
- activeNotes.forEach((activeNote) => {
796
- if (activeNote.modulationDepth) {
797
- activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
798
- }
799
- else {
800
- const semitoneOffset = this.calcSemitoneOffset(channel);
801
- this.setPitch(activeNote, semitoneOffset);
802
- this.startModulation(channel, activeNote, now);
797
+ channel.scheduledNotes.forEach((noteList) => {
798
+ for (let i = 0; i < noteList.length; i++) {
799
+ const note = noteList[i];
800
+ if (!note)
801
+ continue;
802
+ if (note.modulationDepth) {
803
+ note.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
804
+ }
805
+ else {
806
+ const semitoneOffset = this.calcSemitoneOffset(channel);
807
+ this.setPitch(note, semitoneOffset);
808
+ this.startModulation(channel, note, now);
809
+ }
803
810
  }
804
811
  });
805
812
  }
@@ -907,13 +914,17 @@ export class MidyGM1 {
907
914
  }
908
915
  updateDetune(channel, detuneChange) {
909
916
  const now = this.audioContext.currentTime;
910
- const activeNotes = this.getActiveNotes(channel, now);
911
- activeNotes.forEach((activeNote) => {
912
- const { bufferSource } = activeNote;
913
- const detune = bufferSource.detune.value + detuneChange;
914
- bufferSource.detune
915
- .cancelScheduledValues(now)
916
- .setValueAtTime(detune, now);
917
+ channel.scheduledNotes.forEach((noteList) => {
918
+ for (let i = 0; i < noteList.length; i++) {
919
+ const note = noteList[i];
920
+ if (!note)
921
+ continue;
922
+ const { bufferSource } = note;
923
+ const detune = bufferSource.detune.value + detuneChange;
924
+ bufferSource.detune
925
+ .cancelScheduledValues(now)
926
+ .setValueAtTime(detune, now);
927
+ }
917
928
  });
918
929
  }
919
930
  handlePitchBendRangeRPN(channelNumber) {
package/esm/midy-GM2.d.ts CHANGED
@@ -167,14 +167,15 @@ export class MidyGM2 {
167
167
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
168
168
  setVolumeEnvelope(note: any): void;
169
169
  setPitch(note: any, semitoneOffset: any): void;
170
- setFilterNode(channel: any, note: any): void;
170
+ clampCutoffFrequency(frequency: any): number;
171
+ setFilterEnvelope(channel: any, note: any): void;
171
172
  startModulation(channel: any, note: any, startTime: any): void;
172
173
  startVibrato(channel: any, note: any, startTime: any): void;
173
174
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
174
175
  calcBank(channel: any, channelNumber: any): any;
175
176
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
176
177
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
177
- scheduleNoteRelease(channelNumber: any, noteNumber: any, velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
178
+ scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, stopPedal?: boolean): Promise<any> | undefined;
178
179
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
179
180
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
180
181
  releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
package/esm/midy-GM2.js CHANGED
@@ -545,12 +545,13 @@ export class MidyGM2 {
545
545
  const now = this.audioContext.currentTime;
546
546
  const channel = this.channels[channelNumber];
547
547
  channel.scheduledNotes.forEach((noteList) => {
548
- noteList.forEach((note) => {
549
- if (note) {
550
- const promise = this.scheduleNoteRelease(channelNumber, note.noteNumber, velocity, now, stopPedal);
551
- this.notePromises.push(promise);
552
- }
553
- });
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
+ }
554
555
  });
555
556
  channel.scheduledNotes.clear();
556
557
  await Promise.all(this.notePromises);
@@ -773,7 +774,6 @@ export class MidyGM2 {
773
774
  }
774
775
  setVolumeEnvelope(note) {
775
776
  const { instrumentKey, startTime } = note;
776
- note.volumeNode = new GainNode(this.audioContext, { gain: 0 });
777
777
  const attackVolume = this.cbToRatio(-instrumentKey.initialAttenuation);
778
778
  const sustainVolume = attackVolume * (1 - instrumentKey.volSustain);
779
779
  const volDelay = startTime + instrumentKey.volDelay;
@@ -781,6 +781,8 @@ export class MidyGM2 {
781
781
  const volHold = volAttack + instrumentKey.volHold;
782
782
  const volDecay = volHold + instrumentKey.volDecay;
783
783
  note.volumeNode.gain
784
+ .cancelScheduledValues(startTime)
785
+ .setValueAtTime(0, startTime)
784
786
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
785
787
  .exponentialRampToValueAtTime(attackVolume, volAttack)
786
788
  .setValueAtTime(attackVolume, volHold)
@@ -804,29 +806,30 @@ export class MidyGM2 {
804
806
  .setValueAtTime(peekPitch, modHold)
805
807
  .linearRampToValueAtTime(basePitch, modDecay);
806
808
  }
807
- setFilterNode(channel, note) {
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
+ }
814
+ setFilterEnvelope(channel, note) {
808
815
  const { instrumentKey, noteNumber, startTime } = note;
809
816
  const softPedalFactor = 1 -
810
817
  (0.1 + (noteNumber / 127) * 0.2) * channel.softPedal;
811
- const maxFreq = this.audioContext.sampleRate / 2;
812
818
  const baseFreq = this.centToHz(instrumentKey.initialFilterFc) *
813
819
  softPedalFactor;
814
820
  const peekFreq = this.centToHz(instrumentKey.initialFilterFc + instrumentKey.modEnvToFilterFc) * softPedalFactor;
815
821
  const sustainFreq = baseFreq +
816
822
  (peekFreq - baseFreq) * (1 - instrumentKey.modSustain);
817
- const adjustedBaseFreq = Math.min(maxFreq, baseFreq);
818
- const adjustedPeekFreq = Math.min(maxFreq, peekFreq);
819
- const adjustedSustainFreq = Math.min(maxFreq, sustainFreq);
823
+ const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
824
+ const adjustedPeekFreq = this.clampCutoffFrequency(peekFreq);
825
+ const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
820
826
  const modDelay = startTime + instrumentKey.modDelay;
821
827
  const modAttack = modDelay + instrumentKey.modAttack;
822
828
  const modHold = modAttack + instrumentKey.modHold;
823
829
  const modDecay = modHold + instrumentKey.modDecay;
824
- note.filterNode = new BiquadFilterNode(this.audioContext, {
825
- type: "lowpass",
826
- Q: instrumentKey.initialFilterQ / 10, // dB
827
- frequency: adjustedBaseFreq,
828
- });
829
830
  note.filterNode.frequency
831
+ .cancelScheduledValues(startTime)
832
+ .setValueAtTime(adjustedBaseFreq, startTime)
830
833
  .setValueAtTime(adjustedBaseFreq, modDelay)
831
834
  .exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
832
835
  .setValueAtTime(adjustedPeekFreq, modHold)
@@ -879,8 +882,13 @@ export class MidyGM2 {
879
882
  const semitoneOffset = this.calcSemitoneOffset(channel);
880
883
  const note = new Note(noteNumber, velocity, startTime, instrumentKey);
881
884
  note.bufferSource = await this.createNoteBufferNode(instrumentKey, isSF3);
882
- this.setFilterNode(channel, note);
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
+ });
883
890
  this.setVolumeEnvelope(note);
891
+ this.setFilterEnvelope(channel, note);
884
892
  if (0 < channel.vibratoDepth) {
885
893
  this.startVibrato(channel, note, startTime);
886
894
  }
@@ -938,7 +946,7 @@ export class MidyGM2 {
938
946
  const now = this.audioContext.currentTime;
939
947
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
940
948
  }
941
- scheduleNoteRelease(channelNumber, noteNumber, velocity, stopTime, stopPedal = false) {
949
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, stopPedal = false) {
942
950
  const channel = this.channels[channelNumber];
943
951
  if (stopPedal && channel.sustainPedal)
944
952
  return;
@@ -953,14 +961,11 @@ export class MidyGM2 {
953
961
  continue;
954
962
  if (note.ending)
955
963
  continue;
956
- const velocityRate = (velocity + 127) / 127;
957
- const volEndTime = stopTime +
958
- note.instrumentKey.volRelease * velocityRate;
964
+ const volEndTime = stopTime + note.instrumentKey.volRelease;
959
965
  note.volumeNode.gain
960
966
  .cancelScheduledValues(stopTime)
961
967
  .linearRampToValueAtTime(0, volEndTime);
962
- const modRelease = stopTime +
963
- note.instrumentKey.modRelease * velocityRate;
968
+ const modRelease = stopTime + note.instrumentKey.modRelease;
964
969
  note.filterNode.frequency
965
970
  .cancelScheduledValues(stopTime)
966
971
  .linearRampToValueAtTime(0, modRelease);
@@ -1000,13 +1005,14 @@ export class MidyGM2 {
1000
1005
  const promises = [];
1001
1006
  channel.sustainPedal = false;
1002
1007
  channel.scheduledNotes.forEach((noteList) => {
1003
- noteList.forEach((note) => {
1004
- if (note) {
1005
- const { noteNumber } = note;
1006
- const promise = this.releaseNote(channelNumber, noteNumber, velocity);
1007
- promises.push(promise);
1008
- }
1009
- });
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
+ }
1010
1016
  });
1011
1017
  return promises;
1012
1018
  }
@@ -1134,15 +1140,19 @@ export class MidyGM2 {
1134
1140
  }
1135
1141
  updateModulation(channel) {
1136
1142
  const now = this.audioContext.currentTime;
1137
- const activeNotes = this.getActiveNotes(channel, now);
1138
- activeNotes.forEach((activeNote) => {
1139
- if (activeNote.modulationDepth) {
1140
- activeNote.modulationDepth.gain.setValueAtTime(channel.modulationDepth, now);
1141
- }
1142
- else {
1143
- const semitoneOffset = this.calcSemitoneOffset(channel);
1144
- this.setPitch(activeNote, semitoneOffset);
1145
- 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
+ }
1146
1156
  }
1147
1157
  });
1148
1158
  }
@@ -1306,13 +1316,17 @@ export class MidyGM2 {
1306
1316
  }
1307
1317
  updateDetune(channel, detuneChange) {
1308
1318
  const now = this.audioContext.currentTime;
1309
- const activeNotes = this.getActiveNotes(channel, now);
1310
- activeNotes.forEach((activeNote) => {
1311
- const { bufferSource } = activeNote;
1312
- const detune = bufferSource.detune.value + detuneChange;
1313
- bufferSource.detune
1314
- .cancelScheduledValues(now)
1315
- .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
+ }
1316
1330
  });
1317
1331
  }
1318
1332
  handlePitchBendRangeRPN(channelNumber) {
@@ -77,12 +77,13 @@ export class MidyGMLite {
77
77
  calcPlaybackRate(instrumentKey: any, noteNumber: any, semitoneOffset: any): number;
78
78
  setVolumeEnvelope(note: any): void;
79
79
  setPitch(note: any, semitoneOffset: any): void;
80
- setFilterNode(channel: any, note: any): void;
80
+ clampCutoffFrequency(frequency: any): number;
81
+ setFilterEnvelope(note: any): void;
81
82
  startModulation(channel: any, note: any, startTime: any): void;
82
83
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
83
84
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
84
85
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
85
- 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;
86
87
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
87
88
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
88
89
  handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
@@ -1 +1 @@
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,mCAcC;IAED,+CAwBC;IAED,6CA6BC;IAED,+DA0BC;IAED,wHA8BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIA4CC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED,2FA+BC;IAED,qCAeC;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,oDAUC;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;AA1+BD;IAQE,gFAKC;IAZD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,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"}