@marmooo/midy 0.2.0 → 0.2.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marmooo/midy",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "A MIDI player/synthesizer written in JavaScript that supports GM-Lite/GM1 and SF2/SF3.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,6 +1,7 @@
1
1
  export class MidyGM1 {
2
2
  static channelSettings: {
3
3
  currentBufferSource: null;
4
+ detune: number;
4
5
  program: number;
5
6
  bank: number;
6
7
  dataMSB: number;
@@ -92,11 +93,13 @@ export class MidyGM1 {
92
93
  getActiveNotes(channel: any, time: any): Map<any, any>;
93
94
  getActiveNote(noteList: any, time: any): any;
94
95
  cbToRatio(cb: any): number;
96
+ rateToCent(rate: any): number;
97
+ centToRate(cent: any): number;
95
98
  centToHz(cent: any): number;
96
- calcSemitoneOffset(channel: any): any;
99
+ calcChannelDetune(channel: any): any;
100
+ updateDetune(channel: any): void;
97
101
  setVolumeEnvelope(note: any): void;
98
- setPlaybackRate(note: any): void;
99
- setPitch(channel: any, note: any): void;
102
+ setPitchEnvelope(note: any): void;
100
103
  clampCutoffFrequency(frequency: any): number;
101
104
  setFilterEnvelope(note: any): void;
102
105
  startModulation(channel: any, note: any, startTime: any): void;
@@ -112,9 +115,9 @@ export class MidyGM1 {
112
115
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
113
116
  setPitchBend(channelNumber: any, value: any): void;
114
117
  setModLfoToPitch(channel: any, note: any): void;
115
- setModLfoToVolume(note: any): void;
116
118
  setVibLfoToPitch(channel: any, note: any): void;
117
119
  setModLfoToFilterFc(note: any): void;
120
+ setModLfoToVolume(note: any): void;
118
121
  setDelayModLFO(note: any): void;
119
122
  setFreqModLFO(note: any): void;
120
123
  createVoiceParamsHandlers(): {
@@ -129,7 +132,7 @@ export class MidyGM1 {
129
132
  delayVibLFO: (channel: any, note: any, prevValue: any) => void;
130
133
  freqVibLFO: (channel: any, note: any, _prevValue: any) => void;
131
134
  };
132
- getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array;
135
+ getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array<any>;
133
136
  applyVoiceParams(channel: any, controllerType: any): void;
134
137
  createControlChangeHandlers(): {
135
138
  1: (channelNumber: any, modulation: any) => void;
@@ -164,13 +167,12 @@ export class MidyGM1 {
164
167
  setRPNMSB(channelNumber: any, value: any): void;
165
168
  setRPNLSB(channelNumber: any, value: any): void;
166
169
  dataEntryMSB(channelNumber: any, value: any): void;
167
- updateDetune(channel: any, detune: any): void;
168
170
  handlePitchBendRangeRPN(channelNumber: any): void;
169
- setPitchBendRange(channelNumber: any, pitchWheelSensitivity: any): void;
171
+ setPitchBendRange(channelNumber: any, value: any): void;
170
172
  handleFineTuningRPN(channelNumber: any): void;
171
- setFineTuning(channelNumber: any, fineTuning: any): void;
173
+ setFineTuning(channelNumber: any, value: any): void;
172
174
  handleCoarseTuningRPN(channelNumber: any): void;
173
- setCoarseTuning(channelNumber: any, coarseTuning: any): void;
175
+ setCoarseTuning(channelNumber: any, value: any): void;
174
176
  allSoundOff(channelNumber: any): Promise<void>;
175
177
  resetAllControllers(channelNumber: any): void;
176
178
  allNotesOff(channelNumber: any): Promise<void>;
@@ -187,7 +189,7 @@ declare class Note {
187
189
  constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
188
190
  bufferSource: any;
189
191
  filterNode: any;
190
- volumeNode: any;
192
+ volumeEnvelopeNode: any;
191
193
  volumeDepth: any;
192
194
  modulationLFO: any;
193
195
  modulationDepth: any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqFA;IAoBE;;;;;;;;;;;MAWE;IAEF,+BAQC;IAxCD,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;IAClB,iCAA8B;IAgB5B,kBAAgC;IAChC,gBAA4C;IAC5C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,iEAUC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,sCAMC;IAED,mCAgBC;IAED,iCAKC;IAED,wCAmBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA4BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFA4BC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDAOC;IAED,gDASC;IAED,mCAQC;IAED,gDASC;IAED,qCAMC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MA2CC;IAED,+EAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EASC;IAED,qCAiBC;IAED,8DAKC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,8CAYC;IAED,kDAKC;IAED,wEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAzyCD;IAUE,0FAMC;IAfD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
1
+ {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqFA;IAoBE;;;;;;;;;;;;MAYE;IAEF,+BAQC;IAzCD,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;IAClB,iCAA8B;IAiB5B,kBAAgC;IAChC,gBAA4C;IAC5C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,iEAUC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,qCAMC;IAED,iCAWC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA2BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFA4BC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MA2CC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EASC;IAED,qCAiBC;IAED,8DAKC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,8CAKC;IAED,oDAOC;IAED,gDAKC;IAED,sDAOC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAjzCD;IAUE,0FAMC;IAfD,kBAAa;IACb,gBAAW;IACX,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
@@ -17,7 +17,7 @@ class Note {
17
17
  writable: true,
18
18
  value: void 0
19
19
  });
20
- Object.defineProperty(this, "volumeNode", {
20
+ Object.defineProperty(this, "volumeEnvelopeNode", {
21
21
  enumerable: true,
22
22
  configurable: true,
23
23
  writable: true,
@@ -596,16 +596,35 @@ class MidyGM1 {
596
596
  cbToRatio(cb) {
597
597
  return Math.pow(10, cb / 200);
598
598
  }
599
+ rateToCent(rate) {
600
+ return 1200 * Math.log2(rate);
601
+ }
602
+ centToRate(cent) {
603
+ return Math.pow(2, cent / 1200);
604
+ }
599
605
  centToHz(cent) {
600
- return 8.176 * Math.pow(2, cent / 1200);
606
+ return 8.176 * this.centToRate(cent);
601
607
  }
602
- calcSemitoneOffset(channel) {
608
+ calcChannelDetune(channel) {
603
609
  const tuning = channel.coarseTuning + channel.fineTuning;
604
610
  const pitchWheel = channel.state.pitchWheel * 2 - 1;
605
- const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 128;
611
+ const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
606
612
  const pitch = pitchWheel * pitchWheelSensitivity;
607
613
  return tuning + pitch;
608
614
  }
615
+ updateDetune(channel) {
616
+ const now = this.audioContext.currentTime;
617
+ channel.scheduledNotes.forEach((noteList) => {
618
+ for (let i = 0; i < noteList.length; i++) {
619
+ const note = noteList[i];
620
+ if (!note)
621
+ continue;
622
+ note.bufferSource.detune
623
+ .cancelScheduledValues(now)
624
+ .setValueAtTime(channel.detune, now);
625
+ }
626
+ });
627
+ }
609
628
  setVolumeEnvelope(note) {
610
629
  const now = this.audioContext.currentTime;
611
630
  const { voiceParams, startTime } = note;
@@ -615,7 +634,7 @@ class MidyGM1 {
615
634
  const volAttack = volDelay + voiceParams.volAttack;
616
635
  const volHold = volAttack + voiceParams.volHold;
617
636
  const volDecay = volHold + voiceParams.volDecay;
618
- note.volumeNode.gain
637
+ note.volumeEnvelopeNode.gain
619
638
  .cancelScheduledValues(now)
620
639
  .setValueAtTime(0, startTime)
621
640
  .setValueAtTime(1e-6, volDelay) // exponentialRampToValueAtTime() requires a non-zero value
@@ -623,32 +642,28 @@ class MidyGM1 {
623
642
  .setValueAtTime(attackVolume, volHold)
624
643
  .linearRampToValueAtTime(sustainVolume, volDecay);
625
644
  }
626
- setPlaybackRate(note) {
645
+ setPitchEnvelope(note) {
627
646
  const now = this.audioContext.currentTime;
647
+ const { voiceParams } = note;
648
+ const baseRate = voiceParams.playbackRate;
628
649
  note.bufferSource.playbackRate
629
650
  .cancelScheduledValues(now)
630
- .setValueAtTime(note.voiceParams.playbackRate, now);
631
- }
632
- setPitch(channel, note) {
633
- const now = this.audioContext.currentTime;
634
- const { startTime } = note;
635
- const basePitch = this.calcSemitoneOffset(channel) * 100;
636
- note.bufferSource.detune
637
- .cancelScheduledValues(now)
638
- .setValueAtTime(basePitch, startTime);
639
- const modEnvToPitch = note.voiceParams.modEnvToPitch;
651
+ .setValueAtTime(baseRate, now);
652
+ const modEnvToPitch = voiceParams.modEnvToPitch;
640
653
  if (modEnvToPitch === 0)
641
654
  return;
655
+ const basePitch = this.rateToCent(baseRate);
642
656
  const peekPitch = basePitch + modEnvToPitch;
657
+ const peekRate = this.centToRate(peekPitch);
643
658
  const modDelay = startTime + voiceParams.modDelay;
644
659
  const modAttack = modDelay + voiceParams.modAttack;
645
660
  const modHold = modAttack + voiceParams.modHold;
646
661
  const modDecay = modHold + voiceParams.modDecay;
647
- note.bufferSource.detune
648
- .setValueAtTime(basePitch, modDelay)
649
- .exponentialRampToValueAtTime(peekPitch, modAttack)
650
- .setValueAtTime(peekPitch, modHold)
651
- .linearRampToValueAtTime(basePitch, modDecay);
662
+ note.bufferSource.playbackRate
663
+ .setValueAtTime(baseRate, modDelay)
664
+ .exponentialRampToValueAtTime(peekRate, modAttack)
665
+ .setValueAtTime(peekRate, modHold)
666
+ .linearRampToValueAtTime(baseRate, modDecay);
652
667
  }
653
668
  clampCutoffFrequency(frequency) {
654
669
  const minFrequency = 20; // min Hz of initialFilterFc
@@ -695,27 +710,26 @@ class MidyGM1 {
695
710
  note.modulationLFO.connect(note.modulationDepth);
696
711
  note.modulationDepth.connect(note.bufferSource.detune);
697
712
  note.modulationLFO.connect(note.volumeDepth);
698
- note.volumeDepth.connect(note.volumeNode.gain);
713
+ note.volumeDepth.connect(note.volumeEnvelopeNode.gain);
699
714
  }
700
715
  async createNote(channel, voice, noteNumber, velocity, startTime, isSF3) {
701
716
  const state = channel.state;
702
717
  const voiceParams = voice.getAllParams(state.array);
703
718
  const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
704
719
  note.bufferSource = await this.createNoteBufferNode(voiceParams, isSF3);
705
- note.volumeNode = new GainNode(this.audioContext);
720
+ note.volumeEnvelopeNode = new GainNode(this.audioContext);
706
721
  note.filterNode = new BiquadFilterNode(this.audioContext, {
707
722
  type: "lowpass",
708
723
  Q: voiceParams.initialFilterQ / 10, // dB
709
724
  });
710
725
  this.setVolumeEnvelope(note);
711
726
  this.setFilterEnvelope(note);
712
- this.setPlaybackRate(note);
727
+ this.setPitchEnvelope(note);
713
728
  if (0 < state.modulationDepth) {
714
- this.setPitch(channel, note);
715
729
  this.startModulation(channel, note, startTime);
716
730
  }
717
731
  note.bufferSource.connect(note.filterNode);
718
- note.filterNode.connect(note.volumeNode);
732
+ note.filterNode.connect(note.volumeEnvelopeNode);
719
733
  note.bufferSource.start(startTime);
720
734
  return note;
721
735
  }
@@ -731,8 +745,8 @@ class MidyGM1 {
731
745
  if (!voice)
732
746
  return;
733
747
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, isSF3);
734
- note.volumeNode.connect(channel.gainL);
735
- note.volumeNode.connect(channel.gainR);
748
+ note.volumeEnvelopeNode.connect(channel.gainL);
749
+ note.volumeEnvelopeNode.connect(channel.gainR);
736
750
  const exclusiveClass = note.voiceParams.exclusiveClass;
737
751
  if (exclusiveClass !== 0) {
738
752
  if (this.exclusiveClassMap.has(exclusiveClass)) {
@@ -760,7 +774,7 @@ class MidyGM1 {
760
774
  }
761
775
  stopNote(endTime, stopTime, scheduledNotes, index) {
762
776
  const note = scheduledNotes[index];
763
- note.volumeNode.gain
777
+ note.volumeEnvelopeNode.gain
764
778
  .cancelScheduledValues(endTime)
765
779
  .linearRampToValueAtTime(0, stopTime);
766
780
  note.ending = true;
@@ -771,8 +785,8 @@ class MidyGM1 {
771
785
  note.bufferSource.onended = () => {
772
786
  scheduledNotes[index] = null;
773
787
  note.bufferSource.disconnect();
774
- note.volumeNode.disconnect();
775
788
  note.filterNode.disconnect();
789
+ note.volumeEnvelopeNode.disconnect();
776
790
  if (note.modulationDepth) {
777
791
  note.volumeDepth.disconnect();
778
792
  note.modulationDepth.disconnect();
@@ -859,29 +873,22 @@ class MidyGM1 {
859
873
  setPitchBend(channelNumber, value) {
860
874
  const channel = this.channels[channelNumber];
861
875
  const state = channel.state;
876
+ const prev = state.pitchWheel * 2 - 1;
877
+ const next = (value - 8192) / 8192;
862
878
  state.pitchWheel = value / 16383;
863
- const pitchWheel = (value - 8192) / 8192;
864
- const detuneChange = pitchWheel * state.pitchWheelSensitivity * 12800;
865
- this.updateDetune(channel, detuneChange);
879
+ channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
880
+ this.updateDetune(channel);
881
+ this.applyVoiceParams(channel, 14);
866
882
  }
867
883
  setModLfoToPitch(channel, note) {
868
884
  const now = this.audioContext.currentTime;
869
885
  const modLfoToPitch = note.voiceParams.modLfoToPitch;
870
- const modulationDepth = Math.abs(modLfoToPitch) +
886
+ const baseDepth = Math.abs(modLfoToPitch) +
871
887
  channel.state.modulationDepth;
872
- const modulationDepthSign = (0 < modLfoToPitch) ? 1 : -1;
888
+ const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
873
889
  note.modulationDepth.gain
874
890
  .cancelScheduledValues(now)
875
- .setValueAtTime(modulationDepth * modulationDepthSign, now);
876
- }
877
- setModLfoToVolume(note) {
878
- const now = this.audioContext.currentTime;
879
- const modLfoToVolume = note.voiceParams.modLfoToVolume;
880
- const volumeDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
881
- const volumeDepthSign = (0 < modLfoToVolume) ? 1 : -1;
882
- note.volumeDepth.gain
883
- .cancelScheduledValues(now)
884
- .setValueAtTime(volumeDepth * volumeDepthSign, now);
891
+ .setValueAtTime(modulationDepth, now);
885
892
  }
886
893
  setVibLfoToPitch(channel, note) {
887
894
  const now = this.audioContext.currentTime;
@@ -900,6 +907,15 @@ class MidyGM1 {
900
907
  .cancelScheduledValues(now)
901
908
  .setValueAtTime(modLfoToFilterFc, now);
902
909
  }
910
+ setModLfoToVolume(note) {
911
+ const now = this.audioContext.currentTime;
912
+ const modLfoToVolume = note.voiceParams.modLfoToVolume;
913
+ const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
914
+ const volumeDepth = baseDepth * Math.sign(modLfoToVolume);
915
+ note.volumeDepth.gain
916
+ .cancelScheduledValues(now)
917
+ .setValueAtTime(volumeDepth, now);
918
+ }
903
919
  setDelayModLFO(note) {
904
920
  const now = this.audioContext.currentTime;
905
921
  const startTime = note.startTime;
@@ -999,7 +1015,7 @@ class MidyGM1 {
999
1015
  noteVoiceParams[key] = voiceParams[key];
1000
1016
  }
1001
1017
  this.setFilterEnvelope(channel, note);
1002
- this.setPitch(channel, note);
1018
+ this.setPitchEnvelope(note);
1003
1019
  }
1004
1020
  else if (volumeEnvelopeKeySet.has(key)) {
1005
1021
  if (appliedVolumeEnvelope)
@@ -1053,7 +1069,7 @@ class MidyGM1 {
1053
1069
  note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
1054
1070
  }
1055
1071
  else {
1056
- this.setPitch(channel, note);
1072
+ this.setPitchEnvelope(note);
1057
1073
  this.startModulation(channel, note, now);
1058
1074
  }
1059
1075
  }
@@ -1162,59 +1178,49 @@ class MidyGM1 {
1162
1178
  this.channels[channelNumber].dataMSB = value;
1163
1179
  this.handleRPN(channelNumber);
1164
1180
  }
1165
- updateDetune(channel, detune) {
1166
- const now = this.audioContext.currentTime;
1167
- channel.scheduledNotes.forEach((noteList) => {
1168
- for (let i = 0; i < noteList.length; i++) {
1169
- const note = noteList[i];
1170
- if (!note)
1171
- continue;
1172
- const { bufferSource } = note;
1173
- bufferSource.detune
1174
- .cancelScheduledValues(now)
1175
- .setValueAtTime(detune, now);
1176
- }
1177
- });
1178
- }
1179
1181
  handlePitchBendRangeRPN(channelNumber) {
1180
1182
  const channel = this.channels[channelNumber];
1181
1183
  this.limitData(channel, 0, 127, 0, 99);
1182
1184
  const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
1183
1185
  this.setPitchBendRange(channelNumber, pitchBendRange);
1184
1186
  }
1185
- setPitchBendRange(channelNumber, pitchWheelSensitivity) {
1187
+ setPitchBendRange(channelNumber, value) {
1186
1188
  const channel = this.channels[channelNumber];
1187
1189
  const state = channel.state;
1188
- state.pitchWheelSensitivity = pitchWheelSensitivity / 128;
1189
- const detune = (state.pitchWheel * 2 - 1) * pitchWheelSensitivity * 100;
1190
- this.updateDetune(channel, detune);
1190
+ const prev = state.pitchWheelSensitivity;
1191
+ const next = value / 128;
1192
+ state.pitchWheelSensitivity = next;
1193
+ channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
1194
+ this.updateDetune(channel);
1191
1195
  this.applyVoiceParams(channel, 16);
1192
1196
  }
1193
1197
  handleFineTuningRPN(channelNumber) {
1194
1198
  const channel = this.channels[channelNumber];
1195
1199
  this.limitData(channel, 0, 127, 0, 127);
1196
- const fineTuning = (channel.dataMSB * 128 + channel.dataLSB - 8192) / 8192;
1200
+ const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
1197
1201
  this.setFineTuning(channelNumber, fineTuning);
1198
1202
  }
1199
- setFineTuning(channelNumber, fineTuning) {
1203
+ setFineTuning(channelNumber, value) {
1200
1204
  const channel = this.channels[channelNumber];
1201
- const prevFineTuning = channel.fineTuning;
1202
- channel.fineTuning = fineTuning;
1203
- const detuneChange = channel.fineTuning - prevFineTuning;
1204
- this.updateDetune(channel, detuneChange);
1205
+ const prev = channel.fineTuning;
1206
+ const next = (value - 8192) / 8.192; // cent
1207
+ channel.fineTuning = next;
1208
+ channel.detune += next - prev;
1209
+ this.updateDetune(channel);
1205
1210
  }
1206
1211
  handleCoarseTuningRPN(channelNumber) {
1207
1212
  const channel = this.channels[channelNumber];
1208
1213
  this.limitDataMSB(channel, 0, 127);
1209
- const coarseTuning = channel.dataMSB - 64;
1210
- this.setFineTuning(channelNumber, coarseTuning);
1214
+ const coarseTuning = channel.dataMSB;
1215
+ this.setCoarseTuning(channelNumber, coarseTuning);
1211
1216
  }
1212
- setCoarseTuning(channelNumber, coarseTuning) {
1217
+ setCoarseTuning(channelNumber, value) {
1213
1218
  const channel = this.channels[channelNumber];
1214
- const prevCoarseTuning = channel.coarseTuning;
1215
- channel.coarseTuning = coarseTuning;
1216
- const detuneChange = channel.coarseTuning - prevCoarseTuning;
1217
- this.updateDetune(channel, detuneChange);
1219
+ const prev = channel.coarseTuning;
1220
+ const next = (value - 64) * 100; // cent
1221
+ channel.coarseTuning = next;
1222
+ channel.detune += next - prev;
1223
+ this.updateDetune(channel);
1218
1224
  }
1219
1225
  allSoundOff(channelNumber) {
1220
1226
  return this.stopChannelNotes(channelNumber, 0, true);
@@ -1328,6 +1334,7 @@ Object.defineProperty(MidyGM1, "channelSettings", {
1328
1334
  writable: true,
1329
1335
  value: {
1330
1336
  currentBufferSource: null,
1337
+ detune: 0,
1331
1338
  program: 0,
1332
1339
  bank: 0,
1333
1340
  dataMSB: 0,
@@ -1,6 +1,10 @@
1
1
  export class MidyGM2 {
2
2
  static channelSettings: {
3
3
  currentBufferSource: null;
4
+ detune: number;
5
+ scaleOctaveTuningTable: any[];
6
+ pressureTable: Uint8Array<ArrayBuffer>;
7
+ keyBasedInstrumentControlTable: Int8Array<ArrayBuffer>;
4
8
  program: number;
5
9
  bank: number;
6
10
  bankMSB: number;
@@ -79,8 +83,8 @@ export class MidyGM2 {
79
83
  vibLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
80
84
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
81
85
  modLfoToVolume: (channel: any, note: any) => void;
82
- chorusEffectsSend: (_channel: any, note: any, prevValue: any) => void;
83
- reverbEffectsSend: (_channel: any, note: any, prevValue: any) => void;
86
+ chorusEffectsSend: (channel: any, note: any, prevValue: any) => void;
87
+ reverbEffectsSend: (channel: any, note: any, prevValue: any) => void;
84
88
  delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
85
89
  freqModLFO: (_channel: any, note: any, _prevValue: any) => void;
86
90
  delayVibLFO: (channel: any, note: any, prevValue: any) => void;
@@ -182,12 +186,16 @@ export class MidyGM2 {
182
186
  feedbackGains: any[];
183
187
  };
184
188
  cbToRatio(cb: any): number;
189
+ rateToCent(rate: any): number;
190
+ centToRate(cent: any): number;
185
191
  centToHz(cent: any): number;
186
- calcSemitoneOffset(channel: any): any;
192
+ calcChannelDetune(channel: any): any;
193
+ calcNoteDetune(channel: any, note: any): any;
194
+ updateDetune(channel: any): void;
195
+ getPortamentoTime(channel: any): number;
187
196
  setPortamentoStartVolumeEnvelope(channel: any, note: any): void;
188
- setVolumeEnvelope(note: any): void;
189
- setPlaybackRate(note: any): void;
190
- setPitch(channel: any, note: any): void;
197
+ setVolumeEnvelope(channel: any, note: any): void;
198
+ setPitchEnvelope(note: any): void;
191
199
  clampCutoffFrequency(frequency: any): number;
192
200
  setPortamentoStartFilterEnvelope(channel: any, note: any): void;
193
201
  setFilterEnvelope(channel: any, note: any): void;
@@ -204,15 +212,16 @@ export class MidyGM2 {
204
212
  releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
205
213
  handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
206
214
  handleProgramChange(channelNumber: any, program: any): void;
207
- handleChannelPressure(channelNumber: any, pressure: any): void;
215
+ handleChannelPressure(channelNumber: any, value: any): void;
216
+ setChannelPressure(channel: any, note: any): void;
208
217
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
209
218
  setPitchBend(channelNumber: any, value: any): void;
210
219
  setModLfoToPitch(channel: any, note: any): void;
211
- setModLfoToVolume(note: any): void;
212
- setChorusEffectsSend(note: any, prevValue: any): void;
213
- setReverbEffectsSend(note: any, prevValue: any): void;
214
220
  setVibLfoToPitch(channel: any, note: any): void;
215
- setModLfoToFilterFc(note: any): void;
221
+ setModLfoToFilterFc(channel: any, note: any): void;
222
+ setModLfoToVolume(channel: any, note: any): void;
223
+ setReverbEffectsSend(channel: any, note: any, prevValue: any): void;
224
+ setChorusEffectsSend(channel: any, note: any, prevValue: any): void;
216
225
  setDelayModLFO(note: any): void;
217
226
  setFreqModLFO(note: any): void;
218
227
  createVoiceParamsHandlers(): {
@@ -220,14 +229,14 @@ export class MidyGM2 {
220
229
  vibLfoToPitch: (channel: any, note: any, _prevValue: any) => void;
221
230
  modLfoToFilterFc: (channel: any, note: any, _prevValue: any) => void;
222
231
  modLfoToVolume: (channel: any, note: any) => void;
223
- chorusEffectsSend: (_channel: any, note: any, prevValue: any) => void;
224
- reverbEffectsSend: (_channel: any, note: any, prevValue: any) => void;
232
+ chorusEffectsSend: (channel: any, note: any, prevValue: any) => void;
233
+ reverbEffectsSend: (channel: any, note: any, prevValue: any) => void;
225
234
  delayModLFO: (_channel: any, note: any, _prevValue: any) => void;
226
235
  freqModLFO: (_channel: any, note: any, _prevValue: any) => void;
227
236
  delayVibLFO: (channel: any, note: any, prevValue: any) => void;
228
237
  freqVibLFO: (channel: any, note: any, _prevValue: any) => void;
229
238
  };
230
- getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array;
239
+ getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array<any>;
231
240
  applyVoiceParams(channel: any, controllerType: any): void;
232
241
  createControlChangeHandlers(): {
233
242
  0: (channelNumber: any, msb: any) => void;
@@ -260,11 +269,13 @@ export class MidyGM2 {
260
269
  updateModulation(channel: any): void;
261
270
  setModulationDepth(channelNumber: any, modulation: any): void;
262
271
  setPortamentoTime(channelNumber: any, portamentoTime: any): void;
272
+ setKeyBasedVolume(channel: any): void;
263
273
  setVolume(channelNumber: any, volume: any): void;
264
274
  panToGain(pan: any): {
265
275
  gainLeft: number;
266
276
  gainRight: number;
267
277
  };
278
+ setKeyBasedPan(channel: any): void;
268
279
  setPan(channelNumber: any, pan: any): void;
269
280
  setExpression(channelNumber: any, expression: any): void;
270
281
  setBankLSB(channelNumber: any, lsb: any): void;
@@ -272,23 +283,22 @@ export class MidyGM2 {
272
283
  updateChannelVolume(channel: any): void;
273
284
  setSustainPedal(channelNumber: any, value: any): void;
274
285
  setPortamento(channelNumber: any, value: any): void;
275
- setReverbSendLevel(channelNumber: any, reverbSendLevel: any): void;
276
- setChorusSendLevel(channelNumber: any, chorusSendLevel: any): void;
277
286
  setSostenutoPedal(channelNumber: any, value: any): void;
278
287
  setSoftPedal(channelNumber: any, softPedal: any): void;
288
+ setReverbSendLevel(channelNumber: any, reverbSendLevel: any): void;
289
+ setChorusSendLevel(channelNumber: any, chorusSendLevel: any): void;
279
290
  limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
280
291
  limitDataMSB(channel: any, minMSB: any, maxMSB: any): void;
281
292
  handleRPN(channelNumber: any): void;
282
293
  setRPNMSB(channelNumber: any, value: any): void;
283
294
  setRPNLSB(channelNumber: any, value: any): void;
284
295
  dataEntryMSB(channelNumber: any, value: any): void;
285
- updateDetune(channel: any, detune: any): void;
286
296
  handlePitchBendRangeRPN(channelNumber: any): void;
287
- setPitchBendRange(channelNumber: any, pitchWheelSensitivity: any): void;
297
+ setPitchBendRange(channelNumber: any, value: any): void;
288
298
  handleFineTuningRPN(channelNumber: any): void;
289
- setFineTuning(channelNumber: any, fineTuning: any): void;
299
+ setFineTuning(channelNumber: any, value: any): void;
290
300
  handleCoarseTuningRPN(channelNumber: any): void;
291
- setCoarseTuning(channelNumber: any, coarseTuning: any): void;
301
+ setCoarseTuning(channelNumber: any, value: any): void;
292
302
  handleModulationDepthRangeRPN(channelNumber: any): void;
293
303
  setModulationDepthRange(channelNumber: any, modulationDepthRange: any): void;
294
304
  allSoundOff(channelNumber: any): Promise<void>;
@@ -305,9 +315,9 @@ export class MidyGM2 {
305
315
  handleMasterVolumeSysEx(data: any): void;
306
316
  setMasterVolume(volume: any): void;
307
317
  handleMasterFineTuningSysEx(data: any): void;
308
- setMasterFineTuning(fineTuning: any): void;
318
+ setMasterFineTuning(value: any): void;
309
319
  handleMasterCoarseTuningSysEx(data: any): void;
310
- setMasterCoarseTuning(coarseTuning: any): void;
320
+ setMasterCoarseTuning(value: any): void;
311
321
  handleGlobalParameterControlSysEx(data: any): void;
312
322
  handleReverbParameterSysEx(data: any): void;
313
323
  setReverbType(type: any): void;
@@ -326,6 +336,11 @@ export class MidyGM2 {
326
336
  getChorusFeedback(value: any): number;
327
337
  setChorusSendToReverb(value: any): void;
328
338
  getChorusSendToReverb(value: any): number;
339
+ getChannelBitmap(data: any): any[];
340
+ handleScaleOctaveTuning1ByteFormatSysEx(data: any): void;
341
+ handleChannelPressureSysEx(data: any): void;
342
+ getKeyBasedInstrumentControlValue(channel: any, keyNumber: any, controllerType: any): number;
343
+ handleKeyBasedInstrumentControlSysEx(data: any): void;
329
344
  handleExclusiveMessage(data: any): void;
330
345
  handleSysEx(data: any): void;
331
346
  scheduleTask(callback: any, startTime: any): Promise<any>;
@@ -334,7 +349,10 @@ declare class Note {
334
349
  constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
335
350
  bufferSource: any;
336
351
  filterNode: any;
352
+ volumeEnvelopeNode: any;
337
353
  volumeNode: any;
354
+ gainL: any;
355
+ gainR: any;
338
356
  volumeDepth: any;
339
357
  modulationLFO: any;
340
358
  modulationDepth: any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"AA8GA;IAmCE;;;;;;;;;;;;;MAaE;IAEF;;;;;;;MAOE;IAgCF;;;;;OAaC;IArGD,qBAAmB;IACnB,kBAAc;IACd,yBAAqB;IACrB,2BAAuB;IACvB;;;MAGE;IACF;;;;;;MAME;IACF,cAAa;IACb,cAAa;IACb,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;IAClB,iCAA8B;IA0B9B;;;;;MA4BE;IAGA,kBAAgC;IAChC;;;;;MAAqD;IACrD,gBAA4C;IAC5C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IACjD;;;MAA8D;IAC9D;;;;;;;;MAAyD;IAO3D,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAcC;IAED,6DA2BC;IAED,iEAUC;IAED,2CAcC;IAED,2EAuDC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAoGC;IAED,+EAoBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,kFAuBC;IAED;;;;MAWC;IAED,gFAUC;IAED,mFAYC;IAED,sGAcC;IAID;;;MA8BC;IAED;;;;;;;;MA0CC;IAED,2BAEC;IAED,4BAEC;IAED,sCAOC;IAED,gEAWC;IAED,mCAgBC;IAED,iCAKC;IAED,wCAmBC;IAED,6CAIC;IAED,gEAsBC;IAED,iDA2BC;IAED,+DAoBC;IAED,4DAcC;IAED,iIAwDC;IAED,gDAQC;IAED,mHA0DC;IAED,2FASC;IAED,qFAkCC;IAED,wJAsCC;IAED,qHAUC;IAED,kEAeC;IAED,oEAYC;IAED,gFAmBC;IAED,4DAIC;IAED,+DAeC;IAED,qEAGC;IAED,mDAQC;IAED,gDASC;IAED,mCAQC;IAED,sDAsBC;IAED,sDAsBC;IAED,gDASC;IAED,qCAMC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MA+CC;IAED,+EAMC;IAED,0DAiDC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;MA2BC;IAED,+EAYC;IAED,+CAEC;IAED,qCAiBC;IAED,8DAKC;IAED,iEAIC;IAED,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,+CAEC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,oDAEC;IAED,mEAmCC;IAED,mEAmCC;IAED,wDAUC;IAED,uDAGC;IAED,kFAeC;IAED,2DAMC;IAED,oCAqBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,8CAYC;IAED,kDAKC;IAED,wEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,wDAKC;IAED,6EAKC;IAED,+CAEC;IAED,8CAyBC;IAED,+CAEC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBASC;IAED,oBASC;IAED,yDAiDC;IAED,yCAGC;IAED,mCAQC;IAED,6CAGC;IAED,2CAMC;IAED,+CAGC;IAED,+CAMC;IAED,mDAeC;IAED,4CAOC;IAED,+BAKC;IAED,qDAiBC;IAED,gCAIC;IAED,kCAEC;IA6BD,4CAEC;IAED,4CAaC;IAED,+BAiBC;IAED,wFAKC;IAED,mCAKC;IAED,qCAEC;IAED,oCAOC;IAED,sCAEC;IAED,oCAUC;IAED,sCAEC;IAED,wCAuBC;IAED,0CAEC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAjwED;IAaE,0FAMC;IAlBD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IACb,uBAAkB;IAClB,uBAAkB;IAClB,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
1
+ {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}