@marmooo/midy 0.0.6 → 0.0.8

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.
@@ -73,8 +73,8 @@ export class MidyGM2 {
73
73
  lfoFilterDepth: number;
74
74
  lfoAmplitudeDepth: number;
75
75
  };
76
- gainNode: any;
77
- pannerNode: any;
76
+ gainL: any;
77
+ gainR: any;
78
78
  reverbEffect: {
79
79
  convolverNode: any;
80
80
  dryGain: any;
@@ -117,8 +117,8 @@ export class MidyGM2 {
117
117
  loadSoundFont(soundFontUrl: any): Promise<void>;
118
118
  loadMIDI(midiUrl: any): Promise<void>;
119
119
  setChannelAudioNodes(audioContext: any): {
120
- gainNode: any;
121
- pannerNode: any;
120
+ gainL: any;
121
+ gainR: any;
122
122
  reverbEffect: {
123
123
  convolverNode: any;
124
124
  dryGain: any;
@@ -142,8 +142,8 @@ export class MidyGM2 {
142
142
  lfoFilterDepth: number;
143
143
  lfoAmplitudeDepth: number;
144
144
  };
145
- gainNode: any;
146
- pannerNode: any;
145
+ gainL: any;
146
+ gainR: any;
147
147
  reverbEffect: {
148
148
  convolverNode: any;
149
149
  dryGain: any;
@@ -237,6 +237,7 @@ export class MidyGM2 {
237
237
  setPitchBend(channelNumber: any, pitchBend: any): void;
238
238
  handleControlChange(channelNumber: any, controller: any, value: any): void | any[];
239
239
  setBankMSB(channelNumber: any, msb: any): void;
240
+ updateModulation(channel: any): void;
240
241
  setModulation(channelNumber: any, modulation: any): void;
241
242
  setPortamentoTime(channelNumber: any, portamentoTime: any): void;
242
243
  setVolume(channelNumber: any, volume: any): void;
@@ -247,18 +248,29 @@ export class MidyGM2 {
247
248
  setPan(channelNumber: any, pan: any): void;
248
249
  setExpression(channelNumber: any, expression: any): void;
249
250
  setBankLSB(channelNumber: any, lsb: any): void;
251
+ dataEntryLSB(channelNumber: any, value: any): void;
250
252
  updateChannelGain(channel: any): void;
251
253
  setSustainPedal(channelNumber: any, value: any): void;
252
254
  setPortamento(channelNumber: any, value: any): void;
253
- setReverb(channelNumber: any, reverb: any): void;
254
- setChorus(channelNumber: any, chorus: any): void;
255
+ setReverbSendLevel(channelNumber: any, reverb: any): void;
256
+ setChorusSendLevel(channelNumber: any, chorus: any): void;
255
257
  setSostenutoPedal(channelNumber: any, value: any): void;
256
258
  setSoftPedal(channelNumber: any, softPedal: any): void;
259
+ limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
260
+ limitDataMSB(channel: any, minMSB: any, maxMSB: any): void;
261
+ handleRPN(channelNumber: any): void;
257
262
  setRPNMSB(channelNumber: any, value: any): void;
258
263
  setRPNLSB(channelNumber: any, value: any): void;
259
- setDataEntry(channelNumber: any, value: any, isMSB: any): void;
260
- handlePitchBendRangeMessage(channelNumber: any, dataMSB: any, dataLSB: any): void;
264
+ dataEntryMSB(channelNumber: any, value: any): void;
265
+ updateDetune(channel: any, detuneChange: any): void;
266
+ handlePitchBendRangeRPN(channelNumber: any): void;
261
267
  setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
268
+ handleFineTuningRPN(channelNumber: any): void;
269
+ setFineTuning(channelNumber: any, fineTuning: any): void;
270
+ handleCoarseTuningRPN(channelNumber: any): void;
271
+ setCoarseTuning(channelNumber: any, coarseTuning: any): void;
272
+ handleModulationDepthRangeRPN(channelNumber: any): void;
273
+ setModulationDepthRange(channelNumber: any, modulationDepthRange: any): void;
262
274
  allSoundOff(channelNumber: any): any[];
263
275
  resetAllControllers(channelNumber: any): void;
264
276
  allNotesOff(channelNumber: any): any[];
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"AAuBA;IAwBE;;;;;;;;;;;;;;;;;MAiBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAtED,qBAAmB;IACnB,kBAAc;IACd,qBAAmB;IACnB,yBAAqB;IACrB,2BAAuB;IACvB,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;IA4ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;MAoBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAcC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAkDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgGC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED;;;;MAoCC;IAED;;;;;MAqCC;IAED,sDA2BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHAiCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAoDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,wFAmBC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,uDAeC;IAED,mFAuDC;IAED,+CAEC;IAED,yDAiBC;IAED,iEAEC;IAED,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,+CAEC;IAED,sCAUC;IAED,sDAMC;IAED,oDAEC;IAED,iDASC;IAED,iDAIC;IAED,wDAWC;IAED,uDAGC;IAED,gDAEC;IAED,gDAEC;IAED,+DA0BC;IAED,kFAGC;IAED,iEAeC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBAQC;IAED,oBAQC;IAED,yDAgDC;IAED,yCAGC;IAED,mCAQC;IAED,6CAGC;IAED,2CAMC;IAED,+CAGC;IAED,+CAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAv4CD;IASE,gFAKC;IAbD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
1
+ {"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"AAuBA;IAwBE;;;;;;;;;;;;;;;;;MAiBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAtED,qBAAmB;IACnB,kBAAc;IACd,qBAAmB;IACnB,yBAAqB;IACrB,2BAAuB;IACvB,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;IA4ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;MAqBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAcC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAkDC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgGC;IAED,4BAsBC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED;;;;MAoCC;IAED;;;;;MAqCC;IAED,sDA6BC;IAED,2BAEC;IAED,4BAEC;IAED,sCAKC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHAiCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAoDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,wFAmBC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,uDAOC;IAED,mFAuDC;IAED,+CAEC;IAED,qCAcC;IAED,yDAIC;IAED,iEAEC;IAED,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,+CAEC;IAED,mDAGC;IAED,sCAUC;IAED,sDAMC;IAED,oDAEC;IAED,0DASC;IAED,0DAIC;IAED,wDAWC;IAED,uDAGC;IAED,kFAeC;IAED,2DAMC;IAED,oCAqBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,wDAKC;IAED,6EAKC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,gBAEC;IAED,eAEC;IAED,eAEC;IAED,eAEC;IAED,4DAmBC;IAED,oBAQC;IAED,oBAQC;IAED,yDAgDC;IAED,yCAGC;IAED,mCAQC;IAED,6CAGC;IAED,2CAMC;IAED,+CAGC;IAED,+CAMC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAr9CD;IASE,gFAKC;IAbD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -78,13 +78,13 @@ class MidyGM2 {
78
78
  configurable: true,
79
79
  writable: true,
80
80
  value: 0
81
- });
81
+ }); // cb
82
82
  Object.defineProperty(this, "masterCoarseTuning", {
83
83
  enumerable: true,
84
84
  configurable: true,
85
85
  writable: true,
86
86
  value: 0
87
- });
87
+ }); // cb
88
88
  Object.defineProperty(this, "mono", {
89
89
  enumerable: true,
90
90
  configurable: true,
@@ -228,22 +228,23 @@ class MidyGM2 {
228
228
  this.totalTime = this.calcTotalTime();
229
229
  }
230
230
  setChannelAudioNodes(audioContext) {
231
- const gainNode = new GainNode(audioContext, {
232
- gain: MidyGM2.channelSettings.volume,
233
- });
234
- const pannerNode = new StereoPannerNode(audioContext, {
235
- pan: MidyGM2.channelSettings.pan,
236
- });
231
+ const { gainLeft, gainRight } = this.panToGain(MidyGM2.channelSettings.pan);
232
+ const gainL = new GainNode(audioContext, { gain: gainLeft });
233
+ const gainR = new GainNode(audioContext, { gain: gainRight });
234
+ const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
235
+ gainL.connect(merger, 0, 0);
236
+ gainR.connect(merger, 0, 1);
237
+ merger.connect(this.masterGain);
237
238
  const reverbEffect = this.createReverbEffect(audioContext);
238
239
  const chorusEffect = this.createChorusEffect(audioContext);
239
240
  chorusEffect.lfo.start();
240
- reverbEffect.dryGain.connect(pannerNode);
241
- reverbEffect.wetGain.connect(pannerNode);
242
- pannerNode.connect(gainNode);
243
- gainNode.connect(this.masterGain);
241
+ reverbEffect.dryGain.connect(gainL);
242
+ reverbEffect.dryGain.connect(gainR);
243
+ reverbEffect.wetGain.connect(gainL);
244
+ reverbEffect.wetGain.connect(gainR);
244
245
  return {
245
- gainNode,
246
- pannerNode,
246
+ gainL,
247
+ gainR,
247
248
  reverbEffect,
248
249
  chorusEffect,
249
250
  };
@@ -647,14 +648,16 @@ class MidyGM2 {
647
648
  connectNoteEffects(channel, gainNode) {
648
649
  if (channel.reverb === 0) {
649
650
  if (channel.chorus === 0) { // no effect
650
- gainNode.connect(channel.pannerNode);
651
+ gainNode.connect(channel.gainL);
652
+ gainNode.connect(channel.gainR);
651
653
  }
652
654
  else { // chorus
653
655
  channel.chorusEffect.delayNodes.forEach((delayNode) => {
654
656
  gainNode.connect(delayNode);
655
657
  });
656
658
  channel.chorusEffect.chorusGains.forEach((chorusGain) => {
657
- chorusGain.connect(channel.pannerNode);
659
+ chorusGain.connect(channel.gainL);
660
+ chorusGain.connect(channel.gainR);
658
661
  });
659
662
  }
660
663
  }
@@ -935,20 +938,12 @@ class MidyGM2 {
935
938
  this.setPitchBend(channelNumber, pitchBend);
936
939
  }
937
940
  setPitchBend(channelNumber, pitchBend) {
938
- const now = this.audioContext.currentTime;
939
941
  const channel = this.channels[channelNumber];
940
942
  const prevPitchBend = channel.pitchBend;
941
943
  channel.pitchBend = (pitchBend - 8192) / 8192;
942
944
  const detuneChange = (channel.pitchBend - prevPitchBend) *
943
945
  channel.pitchBendRange * 100;
944
- const activeNotes = this.getActiveNotes(channel, now);
945
- activeNotes.forEach((activeNote) => {
946
- const { bufferSource } = activeNote;
947
- const detune = bufferSource.detune.value + detuneChange;
948
- bufferSource.detune
949
- .cancelScheduledValues(now)
950
- .setValueAtTime(detune, now);
951
- });
946
+ this.updateDetune(channel, detuneChange);
952
947
  }
953
948
  handleControlChange(channelNumber, controller, value) {
954
949
  switch (controller) {
@@ -959,7 +954,7 @@ class MidyGM2 {
959
954
  case 5:
960
955
  return this.setPortamentoTime(channelNumber, value);
961
956
  case 6:
962
- return this.setDataEntry(channelNumber, value, true);
957
+ return this.dataEntryMSB(channelNumber, value);
963
958
  case 7:
964
959
  return this.setVolume(channelNumber, value);
965
960
  case 10:
@@ -969,7 +964,7 @@ class MidyGM2 {
969
964
  case 32:
970
965
  return this.setBankLSB(channelNumber, value);
971
966
  case 38:
972
- return this.setDataEntry(channelNumber, value, false);
967
+ return this.dataEntryLSB(channelNumber, value);
973
968
  case 64:
974
969
  return this.setSustainPedal(channelNumber, value);
975
970
  case 65:
@@ -979,13 +974,13 @@ class MidyGM2 {
979
974
  case 67:
980
975
  return this.setSoftPedal(channelNumber, value);
981
976
  case 91:
982
- return this.setReverb(channelNumber, value);
977
+ return this.setReverbSendLevel(channelNumber, value);
983
978
  case 93:
984
- return this.setChorus(channelNumber, value);
979
+ return this.setChorusSendLevel(channelNumber, value);
985
980
  case 100:
986
- return this.setRPNMSB(channelNumber, value);
987
- case 101:
988
981
  return this.setRPNLSB(channelNumber, value);
982
+ case 101:
983
+ return this.setRPNMSB(channelNumber, value);
989
984
  case 120:
990
985
  return this.allSoundOff(channelNumber);
991
986
  case 121:
@@ -1007,22 +1002,24 @@ class MidyGM2 {
1007
1002
  setBankMSB(channelNumber, msb) {
1008
1003
  this.channels[channelNumber].bankMSB = msb;
1009
1004
  }
1010
- setModulation(channelNumber, modulation) {
1005
+ updateModulation(channel) {
1011
1006
  const now = this.audioContext.currentTime;
1012
- const channel = this.channels[channelNumber];
1013
- channel.modulation = (modulation / 127) *
1014
- (channel.modulationDepthRange * 100);
1015
1007
  const activeNotes = this.getActiveNotes(channel, now);
1016
1008
  activeNotes.forEach((activeNote) => {
1017
1009
  if (activeNote.modLFO) {
1018
- activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
1019
- channel.modulation, now);
1010
+ const { gainNode, instrumentKey } = activeNote;
1011
+ gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
1020
1012
  }
1021
1013
  else {
1022
1014
  this.startModulation(channel, activeNote, now);
1023
1015
  }
1024
1016
  });
1025
1017
  }
1018
+ setModulation(channelNumber, modulation) {
1019
+ const channel = this.channels[channelNumber];
1020
+ channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1021
+ this.updateModulation(channel);
1022
+ }
1026
1023
  setPortamentoTime(channelNumber, portamentoTime) {
1027
1024
  this.channels[channelNumber].portamentoTime = portamentoTime / 127;
1028
1025
  }
@@ -1051,6 +1048,10 @@ class MidyGM2 {
1051
1048
  setBankLSB(channelNumber, lsb) {
1052
1049
  this.channels[channelNumber].bankLSB = lsb;
1053
1050
  }
1051
+ dataEntryLSB(channelNumber, value) {
1052
+ this.channels[channelNumber].dataLSB = value;
1053
+ this.handleRPN(channelNumber);
1054
+ }
1054
1055
  updateChannelGain(channel) {
1055
1056
  const now = this.audioContext.currentTime;
1056
1057
  const volume = channel.volume * channel.expression;
@@ -1072,7 +1073,7 @@ class MidyGM2 {
1072
1073
  setPortamento(channelNumber, value) {
1073
1074
  this.channels[channelNumber].portamento = value >= 64;
1074
1075
  }
1075
- setReverb(channelNumber, reverb) {
1076
+ setReverbSendLevel(channelNumber, reverb) {
1076
1077
  const now = this.audioContext.currentTime;
1077
1078
  const channel = this.channels[channelNumber];
1078
1079
  const reverbEffect = channel.reverbEffect;
@@ -1082,7 +1083,7 @@ class MidyGM2 {
1082
1083
  reverbEffect.wetGain.gain.cancelScheduledValues(now);
1083
1084
  reverbEffect.wetGain.gain.setValueAtTime(channel.reverb, now);
1084
1085
  }
1085
- setChorus(channelNumber, chorus) {
1086
+ setChorusSendLevel(channelNumber, chorus) {
1086
1087
  const channel = this.channels[channelNumber];
1087
1088
  channel.chorus = chorus / 127;
1088
1089
  channel.chorusEffect.lfoGain = channel.chorus;
@@ -1104,44 +1105,64 @@ class MidyGM2 {
1104
1105
  const channel = this.channels[channelNumber];
1105
1106
  channel.softPedal = softPedal / 127;
1106
1107
  }
1107
- setRPNMSB(channelNumber, value) {
1108
- this.channels[channelNumber].rpnMSB = value;
1108
+ limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
1109
+ if (maxLSB < channel.dataLSB) {
1110
+ channel.dataMSB++;
1111
+ channel.dataLSB = minLSB;
1112
+ }
1113
+ else if (channel.dataLSB < 0) {
1114
+ channel.dataMSB--;
1115
+ channel.dataLSB = maxLSB;
1116
+ }
1117
+ if (maxMSB < channel.dataMSB) {
1118
+ channel.dataMSB = maxMSB;
1119
+ channel.dataLSB = maxLSB;
1120
+ }
1121
+ else if (channel.dataMSB < 0) {
1122
+ channel.dataMSB = minMSB;
1123
+ channel.dataLSB = minLSB;
1124
+ }
1109
1125
  }
1110
- setRPNLSB(channelNumber, value) {
1111
- this.channels[channelNumber].rpnLSB = value;
1126
+ limitDataMSB(channel, minMSB, maxMSB) {
1127
+ if (maxMSB < channel.dataMSB) {
1128
+ channel.dataMSB = maxMSB;
1129
+ }
1130
+ else if (channel.dataMSB < 0) {
1131
+ channel.dataMSB = minMSB;
1132
+ }
1112
1133
  }
1113
- setDataEntry(channelNumber, value, isMSB) {
1134
+ handleRPN(channelNumber) {
1114
1135
  const channel = this.channels[channelNumber];
1115
1136
  const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1116
- isMSB ? channel.dataMSB = value : channel.dataLSB = value;
1117
- const { dataMSB, dataLSB } = channel;
1118
1137
  switch (rpn) {
1119
1138
  case 0:
1120
- return this.handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB);
1139
+ this.handlePitchBendRangeRPN(channelNumber);
1140
+ break;
1121
1141
  case 1:
1122
- channel.fineTuning = (dataMSB * 128 + dataLSB - 8192) / 8192;
1142
+ this.handleFineTuningRPN(channelNumber);
1123
1143
  break;
1124
1144
  case 2:
1125
- channel.coarseTuning = dataMSB - 64;
1145
+ this.handleCoarseTuningRPN(channelNumber);
1126
1146
  break;
1127
1147
  case 5:
1128
- channel.modulationDepthRange = dataMSB + dataLSB / 128;
1148
+ this.handleModulationDepthRangeRPN(channelNumber);
1129
1149
  break;
1130
1150
  default:
1131
1151
  console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1132
1152
  }
1133
1153
  }
1134
- handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB) {
1135
- const pitchBendRange = dataMSB + dataLSB / 100;
1136
- this.setPitchBendRange(channelNumber, pitchBendRange);
1154
+ setRPNMSB(channelNumber, value) {
1155
+ this.channels[channelNumber].rpnMSB = value;
1137
1156
  }
1138
- setPitchBendRange(channelNumber, pitchBendRange) {
1157
+ setRPNLSB(channelNumber, value) {
1158
+ this.channels[channelNumber].rpnLSB = value;
1159
+ }
1160
+ dataEntryMSB(channelNumber, value) {
1161
+ this.channels[channelNumber].dataMSB = value;
1162
+ this.handleRPN(channelNumber);
1163
+ }
1164
+ updateDetune(channel, detuneChange) {
1139
1165
  const now = this.audioContext.currentTime;
1140
- const channel = this.channels[channelNumber];
1141
- const prevPitchBendRange = channel.pitchBendRange;
1142
- channel.pitchBendRange = pitchBendRange;
1143
- const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
1144
- channel.pitchBend * 100;
1145
1166
  const activeNotes = this.getActiveNotes(channel, now);
1146
1167
  activeNotes.forEach((activeNote) => {
1147
1168
  const { bufferSource } = activeNote;
@@ -1151,6 +1172,58 @@ class MidyGM2 {
1151
1172
  .setValueAtTime(detune, now);
1152
1173
  });
1153
1174
  }
1175
+ handlePitchBendRangeRPN(channelNumber) {
1176
+ const channel = this.channels[channelNumber];
1177
+ this.limitData(channel, 0, 127, 0, 99);
1178
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
1179
+ this.setPitchBendRange(channelNumber, pitchBendRange);
1180
+ }
1181
+ setPitchBendRange(channelNumber, pitchBendRange) {
1182
+ const channel = this.channels[channelNumber];
1183
+ const prevPitchBendRange = channel.pitchBendRange;
1184
+ channel.pitchBendRange = pitchBendRange;
1185
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
1186
+ channel.pitchBend * 100;
1187
+ this.updateDetune(channel, detuneChange);
1188
+ }
1189
+ handleFineTuningRPN(channelNumber) {
1190
+ const channel = this.channels[channelNumber];
1191
+ this.limitData(channel, 0, 127, 0, 127);
1192
+ const fineTuning = (channel.dataMSB * 128 + channel.dataLSB - 8192) / 8192;
1193
+ this.setFineTuning(channelNumber, fineTuning);
1194
+ }
1195
+ setFineTuning(channelNumber, fineTuning) {
1196
+ const channel = this.channels[channelNumber];
1197
+ const prevFineTuning = channel.fineTuning;
1198
+ channel.fineTuning = fineTuning;
1199
+ const detuneChange = channel.fineTuning - prevFineTuning;
1200
+ this.updateDetune(channel, detuneChange);
1201
+ }
1202
+ handleCoarseTuningRPN(channelNumber) {
1203
+ const channel = this.channels[channelNumber];
1204
+ this.limitDataMSB(channel, 0, 127);
1205
+ const coarseTuning = channel.dataMSB - 64;
1206
+ this.setFineTuning(channelNumber, coarseTuning);
1207
+ }
1208
+ setCoarseTuning(channelNumber, coarseTuning) {
1209
+ const channel = this.channels[channelNumber];
1210
+ const prevCoarseTuning = channel.coarseTuning;
1211
+ channel.coarseTuning = coarseTuning;
1212
+ const detuneChange = channel.coarseTuning - prevCoarseTuning;
1213
+ this.updateDetune(channel, detuneChange);
1214
+ }
1215
+ handleModulationDepthRangeRPN(channelNumber) {
1216
+ const channel = this.channels[channelNumber];
1217
+ this.limitData(channel, 0, 127, 0, 127);
1218
+ const modulationDepthRange = dataMSB + dataLSB / 128;
1219
+ this.setModulationDepthRange(channelNumber, modulationDepthRange);
1220
+ }
1221
+ setModulationDepthRange(channelNumber, modulationDepthRange) {
1222
+ const channel = this.channels[channelNumber];
1223
+ channel.modulationDepthRange = modulationDepthRange;
1224
+ channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1225
+ this.updateModulation(channel);
1226
+ }
1154
1227
  allSoundOff(channelNumber) {
1155
1228
  const now = this.audioContext.currentTime;
1156
1229
  const channel = this.channels[channelNumber];
@@ -1240,9 +1313,9 @@ class MidyGM2 {
1240
1313
  switch (data[3]) {
1241
1314
  case 1:
1242
1315
  return this.handleMasterVolumeSysEx(data);
1243
- case 3:
1316
+ case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
1244
1317
  return this.handleMasterFineTuningSysEx(data);
1245
- case 4:
1318
+ case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
1246
1319
  return this.handleMasterCoarseTuningSysEx(data);
1247
1320
  // case 5: // TODO: Global Parameter Control
1248
1321
  default:
@@ -1354,7 +1427,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
1354
1427
  value: {
1355
1428
  currentBufferSource: null,
1356
1429
  volume: 100 / 127,
1357
- pan: 0,
1430
+ pan: 64,
1358
1431
  portamentoTime: 0,
1359
1432
  reverb: 0,
1360
1433
  chorus: 0,
@@ -1365,9 +1438,9 @@ Object.defineProperty(MidyGM2, "channelSettings", {
1365
1438
  dataLSB: 0,
1366
1439
  program: 0,
1367
1440
  pitchBend: 0,
1368
- fineTuning: 0,
1369
- coarseTuning: 0,
1370
- modulationDepthRange: 0.5,
1441
+ fineTuning: 0, // cb
1442
+ coarseTuning: 0, // cb
1443
+ modulationDepthRange: 0.5, // cb
1371
1444
  }
1372
1445
  });
1373
1446
  Object.defineProperty(MidyGM2, "effectSettings", {
@@ -124,6 +124,7 @@ export class MidyGMLite {
124
124
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
125
125
  setPitchBend(channelNumber: any, pitchBend: any): void;
126
126
  handleControlChange(channelNumber: any, controller: any, value: any): void | any[];
127
+ updateModulation(channel: any): void;
127
128
  setModulation(channelNumber: any, modulation: any): void;
128
129
  setVolume(channelNumber: any, volume: any): void;
129
130
  panToGain(pan: any): {
@@ -132,12 +133,15 @@ export class MidyGMLite {
132
133
  };
133
134
  setPan(channelNumber: any, pan: any): void;
134
135
  setExpression(channelNumber: any, expression: any): void;
136
+ dataEntryLSB(channelNumber: any, value: any): void;
135
137
  updateChannelGain(channel: any): void;
136
138
  setSustainPedal(channelNumber: any, value: any): void;
139
+ handleRPN(channelNumber: any): void;
137
140
  setRPNMSB(channelNumber: any, value: any): void;
138
141
  setRPNLSB(channelNumber: any, value: any): void;
139
- setDataEntry(channelNumber: any, value: any, isMSB: any): void;
140
- handlePitchBendRangeMessage(channelNumber: any, dataMSB: any, dataLSB: any): void;
142
+ dataEntryMSB(channelNumber: any, value: any): void;
143
+ updateDetune(channel: any, detuneChange: any): void;
144
+ handlePitchBendRangeRPN(channelNumber: any): void;
141
145
  setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
142
146
  allSoundOff(channelNumber: any): any[];
143
147
  resetAllControllers(channelNumber: any): void;
@@ -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;IAE5C;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;MAcC;IAED;;;;;;;;;;;;;;;;;;QAUC;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,sDAEC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIAmDC;IAED,0FAGC;IAED,kEAeC;IAED,wFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAeC;IAED,mFA+BC;IAED,yDAiBC;IAED,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,sCAUC;IAED,sDAMC;IAED,gDAEC;IAED,gDAEC;IAED,+DAiBC;IAED,kFAGC;IAED,iEAeC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAl/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":"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;IAE5C;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;MAcC;IAED;;;;;;;;;;;;;;;;;;QAUC;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,sDAGC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,iDAiBC;IAED,iDAiCC;IAED,0DAmBC;IAED,wHA6BC;IAED,kGA6BC;IAED,0EAGC;IAED,sIAmDC;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;AAx/BD;IAOE,gFAKC;IAXD,kBAAa;IACb,cAAS;IACT,gBAAW;IACX,YAAO;IACP,gBAAW;IAGT,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -495,7 +495,8 @@ class MidyGMLite {
495
495
  return noteList[0];
496
496
  }
497
497
  connectNoteEffects(channel, gainNode) {
498
- gainNode.connect(channel.pannerNode);
498
+ gainNode.connect(channel.gainL);
499
+ gainNode.connect(channel.gainR);
499
500
  }
500
501
  cbToRatio(cb) {
501
502
  return Math.pow(10, cb / 200);
@@ -705,27 +706,19 @@ class MidyGMLite {
705
706
  this.setPitchBend(channelNumber, pitchBend);
706
707
  }
707
708
  setPitchBend(channelNumber, pitchBend) {
708
- const now = this.audioContext.currentTime;
709
709
  const channel = this.channels[channelNumber];
710
710
  const prevPitchBend = channel.pitchBend;
711
711
  channel.pitchBend = (pitchBend - 8192) / 8192;
712
712
  const detuneChange = (channel.pitchBend - prevPitchBend) *
713
713
  channel.pitchBendRange * 100;
714
- const activeNotes = this.getActiveNotes(channel, now);
715
- activeNotes.forEach((activeNote) => {
716
- const { bufferSource } = activeNote;
717
- const detune = bufferSource.detune.value + detuneChange;
718
- bufferSource.detune
719
- .cancelScheduledValues(now)
720
- .setValueAtTime(detune, now);
721
- });
714
+ this.updateDetune(channel, detuneChange);
722
715
  }
723
716
  handleControlChange(channelNumber, controller, value) {
724
717
  switch (controller) {
725
718
  case 1:
726
719
  return this.setModulation(channelNumber, value);
727
720
  case 6:
728
- return this.setDataEntry(channelNumber, value, true);
721
+ return this.dataEntryMSB(channelNumber, value);
729
722
  case 7:
730
723
  return this.setVolume(channelNumber, value);
731
724
  case 10:
@@ -733,13 +726,13 @@ class MidyGMLite {
733
726
  case 11:
734
727
  return this.setExpression(channelNumber, value);
735
728
  case 38:
736
- return this.setDataEntry(channelNumber, value, false);
729
+ return this.dataEntryLSB(channelNumber, value);
737
730
  case 64:
738
731
  return this.setSustainPedal(channelNumber, value);
739
732
  case 100:
740
- return this.setRPNMSB(channelNumber, value);
741
- case 101:
742
733
  return this.setRPNLSB(channelNumber, value);
734
+ case 101:
735
+ return this.setRPNMSB(channelNumber, value);
743
736
  case 120:
744
737
  return this.allSoundOff(channelNumber);
745
738
  case 121:
@@ -750,22 +743,24 @@ class MidyGMLite {
750
743
  console.warn(`Unsupported Control change: controller=${controller} value=${value}`);
751
744
  }
752
745
  }
753
- setModulation(channelNumber, modulation) {
746
+ updateModulation(channel) {
754
747
  const now = this.audioContext.currentTime;
755
- const channel = this.channels[channelNumber];
756
- channel.modulation = (modulation / 127) *
757
- (channel.modulationDepthRange * 100);
758
748
  const activeNotes = this.getActiveNotes(channel, now);
759
749
  activeNotes.forEach((activeNote) => {
760
750
  if (activeNote.modLFO) {
761
- activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
762
- channel.modulation, now);
751
+ const { gainNode, instrumentKey } = activeNote;
752
+ gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
763
753
  }
764
754
  else {
765
755
  this.startModulation(channel, activeNote, now);
766
756
  }
767
757
  });
768
758
  }
759
+ setModulation(channelNumber, modulation) {
760
+ const channel = this.channels[channelNumber];
761
+ channel.modulation = (modulation / 127) * channel.modulationDepthRange;
762
+ this.updateModulation(channel);
763
+ }
769
764
  setVolume(channelNumber, volume) {
770
765
  const channel = this.channels[channelNumber];
771
766
  channel.volume = volume / 127;
@@ -788,6 +783,10 @@ class MidyGMLite {
788
783
  channel.expression = expression / 127;
789
784
  this.updateChannelGain(channel);
790
785
  }
786
+ dataEntryLSB(channelNumber, value) {
787
+ this.channels[channelNumber].dataLSB = value;
788
+ this.handleRPN(channelNumber);
789
+ }
791
790
  updateChannelGain(channel) {
792
791
  const now = this.audioContext.currentTime;
793
792
  const volume = channel.volume * channel.expression;
@@ -806,35 +805,29 @@ class MidyGMLite {
806
805
  this.releaseSustainPedal(channelNumber, value);
807
806
  }
808
807
  }
809
- setRPNMSB(channelNumber, value) {
810
- this.channels[channelNumber].rpnMSB = value;
811
- }
812
- setRPNLSB(channelNumber, value) {
813
- this.channels[channelNumber].rpnLSB = value;
814
- }
815
- setDataEntry(channelNumber, value, isMSB) {
808
+ handleRPN(channelNumber) {
816
809
  const channel = this.channels[channelNumber];
817
810
  const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
818
- isMSB ? channel.dataMSB = value : channel.dataLSB = value;
819
- const { dataMSB, dataLSB } = channel;
820
811
  switch (rpn) {
821
812
  case 0:
822
- return this.handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB);
813
+ this.handlePitchBendRangeRPN(channelNumber);
814
+ break;
823
815
  default:
824
816
  console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
825
817
  }
826
818
  }
827
- handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB) {
828
- const pitchBendRange = dataMSB + dataLSB / 100;
829
- this.setPitchBendRange(channelNumber, pitchBendRange);
819
+ setRPNMSB(channelNumber, value) {
820
+ this.channels[channelNumber].rpnMSB = value;
830
821
  }
831
- setPitchBendRange(channelNumber, pitchBendRange) {
822
+ setRPNLSB(channelNumber, value) {
823
+ this.channels[channelNumber].rpnLSB = value;
824
+ }
825
+ dataEntryMSB(channelNumber, value) {
826
+ this.channels[channelNumber].dataMSB = value;
827
+ this.handleRPN(channelNumber);
828
+ }
829
+ updateDetune(channel, detuneChange) {
832
830
  const now = this.audioContext.currentTime;
833
- const channel = this.channels[channelNumber];
834
- const prevPitchBendRange = channel.pitchBendRange;
835
- channel.pitchBendRange = pitchBendRange;
836
- const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
837
- channel.pitchBend * 100;
838
831
  const activeNotes = this.getActiveNotes(channel, now);
839
832
  activeNotes.forEach((activeNote) => {
840
833
  const { bufferSource } = activeNote;
@@ -844,6 +837,20 @@ class MidyGMLite {
844
837
  .setValueAtTime(detune, now);
845
838
  });
846
839
  }
840
+ handlePitchBendRangeRPN(channelNumber) {
841
+ const channel = this.channels[channelNumber];
842
+ this.limitData(channel, 0, 127, 0, 99);
843
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
844
+ this.setPitchBendRange(channelNumber, pitchBendRange);
845
+ }
846
+ setPitchBendRange(channelNumber, pitchBendRange) {
847
+ const channel = this.channels[channelNumber];
848
+ const prevPitchBendRange = channel.pitchBendRange;
849
+ channel.pitchBendRange = pitchBendRange;
850
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
851
+ channel.pitchBend * 100;
852
+ this.updateDetune(channel, detuneChange);
853
+ }
847
854
  allSoundOff(channelNumber) {
848
855
  const now = this.audioContext.currentTime;
849
856
  const channel = this.channels[channelNumber];
@@ -969,7 +976,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
969
976
  dataLSB: 0,
970
977
  program: 0,
971
978
  pitchBend: 0,
972
- modulationDepthRange: 0.5,
979
+ modulationDepthRange: 0.5, // cb
973
980
  }
974
981
  });
975
982
  Object.defineProperty(MidyGMLite, "effectSettings", {