@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.
package/script/midy.d.ts CHANGED
@@ -256,14 +256,15 @@ export class Midy {
256
256
  releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
257
257
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
258
258
  releaseSostenutoPedal(channelNumber: any, halfVelocity: any): any[];
259
- handleMIDIMessage(statusByte: any, data1: any, data2: any): any;
259
+ handleMIDIMessage(statusByte: any, data1: any, data2: any): void | any[] | Promise<any>;
260
260
  handlePolyphonicKeyPressure(channelNumber: any, noteNumber: any, pressure: any): void;
261
261
  handleProgramChange(channelNumber: any, program: any): void;
262
262
  handleChannelPressure(channelNumber: any, pressure: any): void;
263
263
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
264
264
  setPitchBend(channelNumber: any, pitchBend: any): void;
265
- handleControlChange(channelNumber: any, controller: any, value: any): any;
265
+ handleControlChange(channelNumber: any, controller: any, value: any): void | any[];
266
266
  setBankMSB(channelNumber: any, msb: any): void;
267
+ updateModulation(channel: any): void;
267
268
  setModulation(channelNumber: any, modulation: any): void;
268
269
  setPortamentoTime(channelNumber: any, portamentoTime: any): void;
269
270
  setVolume(channelNumber: any, volume: any): void;
@@ -274,23 +275,34 @@ export class Midy {
274
275
  setPan(channelNumber: any, pan: any): void;
275
276
  setExpression(channelNumber: any, expression: any): void;
276
277
  setBankLSB(channelNumber: any, lsb: any): void;
278
+ dataEntryLSB(channelNumber: any, value: any): void;
277
279
  updateChannelGain(channel: any): void;
278
280
  setSustainPedal(channelNumber: any, value: any): void;
279
281
  setPortamento(channelNumber: any, value: any): void;
280
- setReverb(channelNumber: any, reverb: any): void;
281
- setChorus(channelNumber: any, chorus: any): void;
282
+ setReverbSendLevel(channelNumber: any, reverb: any): void;
283
+ setChorusSendLevel(channelNumber: any, chorus: any): void;
282
284
  setSostenutoPedal(channelNumber: any, value: any): void;
283
285
  setSoftPedal(channelNumber: any, softPedal: any): void;
284
286
  setVibratoRate(channelNumber: any, vibratoRate: any): void;
285
287
  setVibratoDepth(channelNumber: any, vibratoDepth: any): void;
286
288
  setVibratoDelay(channelNumber: any, vibratoDelay: any): void;
287
- incrementRPNValue(channelNumber: any): void;
288
- decrementRPNValue(channelNumber: any): void;
289
+ limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
290
+ limitDataMSB(channel: any, minMSB: any, maxMSB: any): void;
291
+ handleRPN(channelNumber: any, value: any): void;
292
+ dataIncrement(channelNumber: any): void;
293
+ dataDecrement(channelNumber: any): void;
289
294
  setRPNMSB(channelNumber: any, value: any): void;
290
295
  setRPNLSB(channelNumber: any, value: any): void;
291
- setDataEntry(channelNumber: any, value: any, isMSB: any): void;
292
- handlePitchBendRangeMessage(channelNumber: any, dataMSB: any, dataLSB: any): void;
296
+ dataEntryMSB(channelNumber: any, value: any): void;
297
+ updateDetune(channel: any, detuneChange: any): void;
298
+ handlePitchBendRangeRPN(channelNumber: any): void;
293
299
  setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
300
+ handleFineTuningRPN(channelNumber: any): void;
301
+ setFineTuning(channelNumber: any, fineTuning: any): void;
302
+ handleCoarseTuningRPN(channelNumber: any): void;
303
+ setCoarseTuning(channelNumber: any, coarseTuning: any): void;
304
+ handleModulationDepthRangeRPN(channelNumber: any): void;
305
+ setModulationDepthRange(channelNumber: any, modulationDepthRange: any): void;
294
306
  allSoundOff(channelNumber: any): any[];
295
307
  resetAllControllers(channelNumber: any): void;
296
308
  allNotesOff(channelNumber: any): any[];
@@ -1 +1 @@
1
- {"version":3,"file":"midy.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAuBA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAzED,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;IA+ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;MAqBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiBC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAyDC;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,uDAcC;IAED,wHAqCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAwDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,gEAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,uDAeC;IAED,0EAkEC;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,2DAGC;IAED,6DAGC;IAED,6DASC;IAED,4CAkBC;IAED,4CAkBC;IAED,gDAEC;IAED,gDAEC;IAGD,+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;AAxgDD;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.d.ts","sourceRoot":"","sources":["../src/midy.js"],"names":[],"mappings":"AAuBA;IAwBE;;;;;;;;;;;;;;;;;;;;MAoBE;IAEF;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IAzED,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;IA+ChB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;;;;;;;;;;;;MAqBC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAiBC;IAED,+DAyBC;IAED,mEAWC;IAED,qDAOC;IAED,2EAyDC;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,uDAcC;IAED,wHAqCC;IAED,gDAQC;IAED,kGAgCC;IAED,0EAGC;IAED,sIAwDC;IAED,0FAGC;IAED,kEAeC;IAED,oEAYC;IAED,wFAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,uDAOC;IAED,mFAkEC;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,2DAGC;IAED,6DAGC;IAED,6DASC;IAED,kFAeC;IAED,2DAMC;IAED,gDAyBC;IAED,wCAEC;IAED,wCAEC;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;AAxjDD;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"}
package/script/midy.js CHANGED
@@ -78,13 +78,13 @@ class Midy {
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,
@@ -654,14 +654,16 @@ class Midy {
654
654
  connectNoteEffects(channel, gainNode) {
655
655
  if (channel.reverb === 0) {
656
656
  if (channel.chorus === 0) { // no effect
657
- gainNode.connect(channel.pannerNode);
657
+ gainNode.connect(channel.gainL);
658
+ gainNode.connect(channel.gainR);
658
659
  }
659
660
  else { // chorus
660
661
  channel.chorusEffect.delayNodes.forEach((delayNode) => {
661
662
  gainNode.connect(delayNode);
662
663
  });
663
664
  channel.chorusEffect.chorusGains.forEach((chorusGain) => {
664
- chorusGain.connect(channel.pannerNode);
665
+ chorusGain.connect(channel.gainL);
666
+ chorusGain.connect(channel.gainR);
665
667
  });
666
668
  }
667
669
  }
@@ -749,7 +751,7 @@ class Midy {
749
751
  startModulation(channel, note, time) {
750
752
  const { instrumentKey } = note;
751
753
  note.modLFOGain = new GainNode(this.audioContext, {
752
- gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
754
+ gain: this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation),
753
755
  });
754
756
  note.modLFO = new OscillatorNode(this.audioContext, {
755
757
  frequency: this.centToHz(instrumentKey.freqModLFO),
@@ -980,20 +982,12 @@ class Midy {
980
982
  this.setPitchBend(channelNumber, pitchBend);
981
983
  }
982
984
  setPitchBend(channelNumber, pitchBend) {
983
- const now = this.audioContext.currentTime;
984
985
  const channel = this.channels[channelNumber];
985
986
  const prevPitchBend = channel.pitchBend;
986
987
  channel.pitchBend = (pitchBend - 8192) / 8192;
987
988
  const detuneChange = (channel.pitchBend - prevPitchBend) *
988
989
  channel.pitchBendRange * 100;
989
- const activeNotes = this.getActiveNotes(channel, now);
990
- activeNotes.forEach((activeNote) => {
991
- const { bufferSource } = activeNote;
992
- const detune = bufferSource.detune.value + detuneChange;
993
- bufferSource.detune
994
- .cancelScheduledValues(now)
995
- .setValueAtTime(detune, now);
996
- });
990
+ this.updateDetune(channel, detuneChange);
997
991
  }
998
992
  handleControlChange(channelNumber, controller, value) {
999
993
  switch (controller) {
@@ -1004,7 +998,7 @@ class Midy {
1004
998
  case 5:
1005
999
  return this.setPortamentoTime(channelNumber, value);
1006
1000
  case 6:
1007
- return this.setDataEntry(channelNumber, value, true);
1001
+ return this.dataEntryMSB(channelNumber, value);
1008
1002
  case 7:
1009
1003
  return this.setVolume(channelNumber, value);
1010
1004
  case 10:
@@ -1014,7 +1008,7 @@ class Midy {
1014
1008
  case 32:
1015
1009
  return this.setBankLSB(channelNumber, value);
1016
1010
  case 38:
1017
- return this.setDataEntry(channelNumber, value, false);
1011
+ return this.dataEntryLSB(channelNumber, value);
1018
1012
  case 64:
1019
1013
  return this.setSustainPedal(channelNumber, value);
1020
1014
  case 65:
@@ -1031,17 +1025,17 @@ class Midy {
1031
1025
  case 78:
1032
1026
  return this.setVibratoDelay(channelNumber, value);
1033
1027
  case 91:
1034
- return this.setReverb(channelNumber, value);
1028
+ return this.setReverbSendLevel(channelNumber, value);
1035
1029
  case 93:
1036
- return this.setChorus(channelNumber, value);
1037
- case 96:
1038
- return incrementRPNValue(channelNumber);
1039
- case 97:
1040
- return decrementRPNValue(channelNumber);
1030
+ return this.setChorusSendLevel(channelNumber, value);
1031
+ case 96: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1032
+ return this.dataIncrement(channelNumber);
1033
+ case 97: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1034
+ return this.dataDecrement(channelNumber);
1041
1035
  case 100:
1042
- return this.setRPNMSB(channelNumber, value);
1043
- case 101:
1044
1036
  return this.setRPNLSB(channelNumber, value);
1037
+ case 101:
1038
+ return this.setRPNMSB(channelNumber, value);
1045
1039
  case 120:
1046
1040
  return this.allSoundOff(channelNumber);
1047
1041
  case 121:
@@ -1063,22 +1057,24 @@ class Midy {
1063
1057
  setBankMSB(channelNumber, msb) {
1064
1058
  this.channels[channelNumber].bankMSB = msb;
1065
1059
  }
1066
- setModulation(channelNumber, modulation) {
1060
+ updateModulation(channel) {
1067
1061
  const now = this.audioContext.currentTime;
1068
- const channel = this.channels[channelNumber];
1069
- channel.modulation = (modulation / 127) *
1070
- (channel.modulationDepthRange * 100);
1071
1062
  const activeNotes = this.getActiveNotes(channel, now);
1072
1063
  activeNotes.forEach((activeNote) => {
1073
1064
  if (activeNote.modLFO) {
1074
- activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
1075
- channel.modulation, now);
1065
+ const { gainNode, instrumentKey } = activeNote;
1066
+ gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
1076
1067
  }
1077
1068
  else {
1078
1069
  this.startModulation(channel, activeNote, now);
1079
1070
  }
1080
1071
  });
1081
1072
  }
1073
+ setModulation(channelNumber, modulation) {
1074
+ const channel = this.channels[channelNumber];
1075
+ channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1076
+ this.updateModulation(channel);
1077
+ }
1082
1078
  setPortamentoTime(channelNumber, portamentoTime) {
1083
1079
  this.channels[channelNumber].portamentoTime = portamentoTime / 127;
1084
1080
  }
@@ -1107,6 +1103,10 @@ class Midy {
1107
1103
  setBankLSB(channelNumber, lsb) {
1108
1104
  this.channels[channelNumber].bankLSB = lsb;
1109
1105
  }
1106
+ dataEntryLSB(channelNumber, value) {
1107
+ this.channels[channelNumber].dataLSB = value;
1108
+ this.handleRPN(channelNumber, 0);
1109
+ }
1110
1110
  updateChannelGain(channel) {
1111
1111
  const now = this.audioContext.currentTime;
1112
1112
  const volume = channel.volume * channel.expression;
@@ -1128,7 +1128,7 @@ class Midy {
1128
1128
  setPortamento(channelNumber, value) {
1129
1129
  this.channels[channelNumber].portamento = value >= 64;
1130
1130
  }
1131
- setReverb(channelNumber, reverb) {
1131
+ setReverbSendLevel(channelNumber, reverb) {
1132
1132
  const now = this.audioContext.currentTime;
1133
1133
  const channel = this.channels[channelNumber];
1134
1134
  const reverbEffect = channel.reverbEffect;
@@ -1138,7 +1138,7 @@ class Midy {
1138
1138
  reverbEffect.wetGain.gain.cancelScheduledValues(now);
1139
1139
  reverbEffect.wetGain.gain.setValueAtTime(channel.reverb, now);
1140
1140
  }
1141
- setChorus(channelNumber, chorus) {
1141
+ setChorusSendLevel(channelNumber, chorus) {
1142
1142
  const channel = this.channels[channelNumber];
1143
1143
  channel.chorus = chorus / 127;
1144
1144
  channel.chorusEffect.lfoGain = channel.chorus;
@@ -1178,79 +1178,74 @@ class Midy {
1178
1178
  const channel = this.channels[channelNumber];
1179
1179
  channel.vibratoDelay = vibratoDelay / 127 * 5; // 0-5sec
1180
1180
  }
1181
- incrementRPNValue(channelNumber) {
1182
- const channel = this.channels[channelNumber];
1183
- const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1184
- switch (rpn) {
1185
- case 0:
1186
- channel.pitchBendRange = Math.min(1, channel.pitchBendRange + 1);
1187
- break;
1188
- case 1:
1189
- channel.fineTuning = Math.min(1, channel.fineTuning + 1);
1190
- break;
1191
- case 2:
1192
- channel.coarseTuning = Math.min(88, channel.coarseTuning + 1);
1193
- break;
1194
- default:
1195
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1181
+ limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
1182
+ if (maxLSB < channel.dataLSB) {
1183
+ channel.dataMSB++;
1184
+ channel.dataLSB = minLSB;
1185
+ }
1186
+ else if (channel.dataLSB < 0) {
1187
+ channel.dataMSB--;
1188
+ channel.dataLSB = maxLSB;
1189
+ }
1190
+ if (maxMSB < channel.dataMSB) {
1191
+ channel.dataMSB = maxMSB;
1192
+ channel.dataLSB = maxLSB;
1193
+ }
1194
+ else if (channel.dataMSB < 0) {
1195
+ channel.dataMSB = minMSB;
1196
+ channel.dataLSB = minLSB;
1197
+ }
1198
+ }
1199
+ limitDataMSB(channel, minMSB, maxMSB) {
1200
+ if (maxMSB < channel.dataMSB) {
1201
+ channel.dataMSB = maxMSB;
1202
+ }
1203
+ else if (channel.dataMSB < 0) {
1204
+ channel.dataMSB = minMSB;
1196
1205
  }
1197
1206
  }
1198
- decrementRPNValue(channelNumber) {
1207
+ handleRPN(channelNumber, value) {
1199
1208
  const channel = this.channels[channelNumber];
1200
1209
  const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1201
1210
  switch (rpn) {
1202
1211
  case 0:
1203
- channel.pitchBendRange = Math.max(-1, channel.pitchBendRange - 1);
1212
+ channel.dataLSB += value;
1213
+ this.handlePitchBendRangeRPN(channelNumber);
1204
1214
  break;
1205
1215
  case 1:
1206
- channel.fineTuning = Math.max(-1, channel.fineTuning - 1);
1216
+ channel.dataLSB += value;
1217
+ this.handleFineTuningRPN(channelNumber);
1207
1218
  break;
1208
1219
  case 2:
1209
- channel.coarseTuning = Math.max(40, channel.coarseTuning - 1);
1220
+ channel.dataMSB += value;
1221
+ this.handleCoarseTuningRPN(channelNumber);
1222
+ break;
1223
+ case 5:
1224
+ channel.dataLSB += value;
1225
+ this.handleModulationDepthRangeRPN(channelNumber);
1210
1226
  break;
1211
1227
  default:
1212
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB}, LSB=${channel.rpnLSB}.`);
1228
+ console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1213
1229
  }
1214
1230
  }
1231
+ dataIncrement(channelNumber) {
1232
+ this.handleRPN(channelNumber, 1);
1233
+ }
1234
+ dataDecrement(channelNumber) {
1235
+ this.handleRPN(channelNumber, -1);
1236
+ }
1215
1237
  setRPNMSB(channelNumber, value) {
1216
1238
  this.channels[channelNumber].rpnMSB = value;
1217
1239
  }
1218
1240
  setRPNLSB(channelNumber, value) {
1219
1241
  this.channels[channelNumber].rpnLSB = value;
1220
1242
  }
1221
- // TODO: support 3-4?
1222
- setDataEntry(channelNumber, value, isMSB) {
1223
- const channel = this.channels[channelNumber];
1224
- const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1225
- isMSB ? channel.dataMSB = value : channel.dataLSB = value;
1226
- const { dataMSB, dataLSB } = channel;
1227
- switch (rpn) {
1228
- case 0:
1229
- return this.handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB);
1230
- case 1:
1231
- channel.fineTuning = (dataMSB * 128 + dataLSB - 8192) / 8192;
1232
- break;
1233
- case 2:
1234
- channel.coarseTuning = dataMSB - 64;
1235
- break;
1236
- case 5:
1237
- channel.modulationDepthRange = dataMSB + dataLSB / 128;
1238
- break;
1239
- default:
1240
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1241
- }
1243
+ dataEntryMSB(channelNumber, value) {
1244
+ this.channels[channelNumber].dataMSB = value;
1245
+ this.handleRPN(channelNumber, 0);
1242
1246
  }
1243
- handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB) {
1244
- const pitchBendRange = dataMSB + dataLSB / 100;
1245
- this.setPitchBendRange(channelNumber, pitchBendRange);
1246
- }
1247
- setPitchBendRange(channelNumber, pitchBendRange) {
1247
+ updateDetune(channel, detuneChange) {
1248
1248
  const now = this.audioContext.currentTime;
1249
- const channel = this.channels[channelNumber];
1250
- const prevPitchBendRange = channel.pitchBendRange;
1251
- channel.pitchBendRange = pitchBendRange;
1252
- const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
1253
- channel.pitchBend * 100;
1254
1249
  const activeNotes = this.getActiveNotes(channel, now);
1255
1250
  activeNotes.forEach((activeNote) => {
1256
1251
  const { bufferSource } = activeNote;
@@ -1260,6 +1255,58 @@ class Midy {
1260
1255
  .setValueAtTime(detune, now);
1261
1256
  });
1262
1257
  }
1258
+ handlePitchBendRangeRPN(channelNumber) {
1259
+ const channel = this.channels[channelNumber];
1260
+ this.limitData(channel, 0, 127, 0, 99);
1261
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
1262
+ this.setPitchBendRange(channelNumber, pitchBendRange);
1263
+ }
1264
+ setPitchBendRange(channelNumber, pitchBendRange) {
1265
+ const channel = this.channels[channelNumber];
1266
+ const prevPitchBendRange = channel.pitchBendRange;
1267
+ channel.pitchBendRange = pitchBendRange;
1268
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
1269
+ channel.pitchBend * 100;
1270
+ this.updateDetune(channel, detuneChange);
1271
+ }
1272
+ handleFineTuningRPN(channelNumber) {
1273
+ const channel = this.channels[channelNumber];
1274
+ this.limitData(channel, 0, 127, 0, 127);
1275
+ const fineTuning = (channel.dataMSB * 128 + channel.dataLSB - 8192) / 8192;
1276
+ this.setFineTuning(channelNumber, fineTuning);
1277
+ }
1278
+ setFineTuning(channelNumber, fineTuning) {
1279
+ const channel = this.channels[channelNumber];
1280
+ const prevFineTuning = channel.fineTuning;
1281
+ channel.fineTuning = fineTuning;
1282
+ const detuneChange = channel.fineTuning - prevFineTuning;
1283
+ this.updateDetune(channel, detuneChange);
1284
+ }
1285
+ handleCoarseTuningRPN(channelNumber) {
1286
+ const channel = this.channels[channelNumber];
1287
+ this.limitDataMSB(channel, 0, 127);
1288
+ const coarseTuning = channel.dataMSB - 64;
1289
+ this.setFineTuning(channelNumber, coarseTuning);
1290
+ }
1291
+ setCoarseTuning(channelNumber, coarseTuning) {
1292
+ const channel = this.channels[channelNumber];
1293
+ const prevCoarseTuning = channel.coarseTuning;
1294
+ channel.coarseTuning = coarseTuning;
1295
+ const detuneChange = channel.coarseTuning - prevCoarseTuning;
1296
+ this.updateDetune(channel, detuneChange);
1297
+ }
1298
+ handleModulationDepthRangeRPN(channelNumber) {
1299
+ const channel = this.channels[channelNumber];
1300
+ this.limitData(channel, 0, 127, 0, 127);
1301
+ const modulationDepthRange = dataMSB + dataLSB / 128;
1302
+ this.setModulationDepthRange(channelNumber, modulationDepthRange);
1303
+ }
1304
+ setModulationDepthRange(channelNumber, modulationDepthRange) {
1305
+ const channel = this.channels[channelNumber];
1306
+ channel.modulationDepthRange = modulationDepthRange;
1307
+ channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1308
+ this.updateModulation(channel);
1309
+ }
1263
1310
  allSoundOff(channelNumber) {
1264
1311
  const now = this.audioContext.currentTime;
1265
1312
  const channel = this.channels[channelNumber];
@@ -1331,8 +1378,8 @@ class Midy {
1331
1378
  channel.bankLSB = 0;
1332
1379
  channel.bank = 0;
1333
1380
  });
1334
- this.channels[9].bankMSB = 120;
1335
- this.channels[9].bank = 120 * 128;
1381
+ this.channels[9].bankMSB = 1;
1382
+ this.channels[9].bank = 128;
1336
1383
  }
1337
1384
  GM2SystemOn() {
1338
1385
  this.channels.forEach((channel) => {
@@ -1349,9 +1396,9 @@ class Midy {
1349
1396
  switch (data[3]) {
1350
1397
  case 1:
1351
1398
  return this.handleMasterVolumeSysEx(data);
1352
- case 3:
1399
+ case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
1353
1400
  return this.handleMasterFineTuningSysEx(data);
1354
- case 4:
1401
+ case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
1355
1402
  return this.handleMasterCoarseTuningSysEx(data);
1356
1403
  // case 5: // TODO: Global Parameter Control
1357
1404
  default:
@@ -1477,9 +1524,9 @@ Object.defineProperty(Midy, "channelSettings", {
1477
1524
  dataLSB: 0,
1478
1525
  program: 0,
1479
1526
  pitchBend: 0,
1480
- fineTuning: 0,
1481
- coarseTuning: 0,
1482
- modulationDepthRange: 0.5,
1527
+ fineTuning: 0, // cb
1528
+ coarseTuning: 0, // cb
1529
+ modulationDepthRange: 0.5, // cb
1483
1530
  }
1484
1531
  });
1485
1532
  Object.defineProperty(Midy, "effectSettings", {