@marmooo/midy 0.0.6 → 0.0.7

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.
@@ -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) {
@@ -983,9 +978,9 @@ class MidyGM2 {
983
978
  case 93:
984
979
  return this.setChorus(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:
@@ -1104,44 +1099,65 @@ class MidyGM2 {
1104
1099
  const channel = this.channels[channelNumber];
1105
1100
  channel.softPedal = softPedal / 127;
1106
1101
  }
1107
- setRPNMSB(channelNumber, value) {
1108
- this.channels[channelNumber].rpnMSB = value;
1102
+ limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
1103
+ if (maxLSB < channel.dataLSB) {
1104
+ channel.dataMSB++;
1105
+ channel.dataLSB = minLSB;
1106
+ }
1107
+ else if (channel.dataLSB < 0) {
1108
+ channel.dataMSB--;
1109
+ channel.dataLSB = maxLSB;
1110
+ }
1111
+ if (maxMSB < channel.dataMSB) {
1112
+ channel.dataMSB = maxMSB;
1113
+ channel.dataLSB = maxLSB;
1114
+ }
1115
+ else if (channel.dataMSB < 0) {
1116
+ channel.dataMSB = minMSB;
1117
+ channel.dataLSB = minLSB;
1118
+ }
1109
1119
  }
1110
- setRPNLSB(channelNumber, value) {
1111
- this.channels[channelNumber].rpnLSB = value;
1120
+ limitDataMSB(channel, minMSB, maxMSB) {
1121
+ if (maxMSB < channel.dataMSB) {
1122
+ channel.dataMSB = maxMSB;
1123
+ }
1124
+ else if (channel.dataMSB < 0) {
1125
+ channel.dataMSB = minMSB;
1126
+ }
1112
1127
  }
1113
- setDataEntry(channelNumber, value, isMSB) {
1128
+ handleRPN(channelNumber, value) {
1114
1129
  const channel = this.channels[channelNumber];
1115
1130
  const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1116
- isMSB ? channel.dataMSB = value : channel.dataLSB = value;
1117
- const { dataMSB, dataLSB } = channel;
1118
1131
  switch (rpn) {
1119
1132
  case 0:
1120
- return this.handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB);
1133
+ channel.dataLSB += value;
1134
+ this.handlePitchBendRangeMessage(channelNumber);
1135
+ break;
1121
1136
  case 1:
1122
- channel.fineTuning = (dataMSB * 128 + dataLSB - 8192) / 8192;
1137
+ channel.dataLSB += value;
1138
+ this.handleFineTuningMessage(channelNumber);
1123
1139
  break;
1124
1140
  case 2:
1125
- channel.coarseTuning = dataMSB - 64;
1126
- break;
1127
- case 5:
1128
- channel.modulationDepthRange = dataMSB + dataLSB / 128;
1141
+ channel.dataMSB += value;
1142
+ this.handleCoarseTuningMessage(channelNumber);
1129
1143
  break;
1130
1144
  default:
1131
1145
  console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1132
1146
  }
1133
1147
  }
1134
- handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB) {
1135
- const pitchBendRange = dataMSB + dataLSB / 100;
1136
- this.setPitchBendRange(channelNumber, pitchBendRange);
1148
+ setRPNMSB(channelNumber, value) {
1149
+ this.channels[channelNumber].rpnMSB = value;
1137
1150
  }
1138
- setPitchBendRange(channelNumber, pitchBendRange) {
1139
- const now = this.audioContext.currentTime;
1151
+ setRPNLSB(channelNumber, value) {
1152
+ this.channels[channelNumber].rpnLSB = value;
1153
+ }
1154
+ setDataEntry(channelNumber, value, isMSB) {
1140
1155
  const channel = this.channels[channelNumber];
1141
- const prevPitchBendRange = channel.pitchBendRange;
1142
- channel.pitchBendRange = pitchBendRange;
1143
- const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
1144
- channel.pitchBend * 100;
1156
+ isMSB ? channel.dataMSB = value : channel.dataLSB = value;
1157
+ this.handleRPN(channelNumber);
1158
+ }
1159
+ updateDetune(channel, detuneChange) {
1160
+ const now = this.audioContext.currentTime;
1145
1161
  const activeNotes = this.getActiveNotes(channel, now);
1146
1162
  activeNotes.forEach((activeNote) => {
1147
1163
  const { bufferSource } = activeNote;
@@ -1151,6 +1167,40 @@ class MidyGM2 {
1151
1167
  .setValueAtTime(detune, now);
1152
1168
  });
1153
1169
  }
1170
+ handlePitchBendRangeMessage(channelNumber) {
1171
+ const channel = this.channels[channelNumber];
1172
+ this.limitData(channel, 0, 127, 0, 99);
1173
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
1174
+ this.setPitchBendRange(channelNumber, pitchBendRange);
1175
+ }
1176
+ setPitchBendRange(channelNumber, pitchBendRange) {
1177
+ const channel = this.channels[channelNumber];
1178
+ const prevPitchBendRange = channel.pitchBendRange;
1179
+ channel.pitchBendRange = pitchBendRange;
1180
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
1181
+ channel.pitchBend * 100;
1182
+ this.updateDetune(channel, detuneChange);
1183
+ }
1184
+ handleFineTuningMessage(channelNumber) {
1185
+ const channel = this.channels[channelNumber];
1186
+ this.limitData(channel, 0, 127, 0, 127);
1187
+ const fineTuning = (channel.dataMSB * 128 + channel.dataLSB - 8192) / 8192;
1188
+ this.setFineTuning(channelNumber, fineTuning);
1189
+ }
1190
+ setFineTuning(channelNumber, fineTuning) {
1191
+ const channel = this.channels[channelNumber];
1192
+ channel.fineTuning = fineTuning;
1193
+ }
1194
+ handleCoarseTuningMessage(channelNumber) {
1195
+ const channel = this.channels[channelNumber];
1196
+ this.limitDataMSB(channel, 0, 127);
1197
+ const coarseTuning = channel.dataMSB - 64;
1198
+ this.setFineTuning(channelNumber, coarseTuning);
1199
+ }
1200
+ setCoarseTuning(channelNumber, coarseTuning) {
1201
+ const channel = this.channels[channelNumber];
1202
+ channel.fineTuning = coarseTuning;
1203
+ }
1154
1204
  allSoundOff(channelNumber) {
1155
1205
  const now = this.audioContext.currentTime;
1156
1206
  const channel = this.channels[channelNumber];
@@ -134,10 +134,12 @@ export class MidyGMLite {
134
134
  setExpression(channelNumber: any, expression: any): void;
135
135
  updateChannelGain(channel: any): void;
136
136
  setSustainPedal(channelNumber: any, value: any): void;
137
+ handleRPN(channelNumber: any): void;
137
138
  setRPNMSB(channelNumber: any, value: any): void;
138
139
  setRPNLSB(channelNumber: any, value: any): void;
139
140
  setDataEntry(channelNumber: any, value: any, isMSB: any): void;
140
- handlePitchBendRangeMessage(channelNumber: any, dataMSB: any, dataLSB: any): void;
141
+ updateDetune(channel: any, detuneChange: any): void;
142
+ handlePitchBendRangeMessage(channelNumber: any): void;
141
143
  setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
142
144
  allSoundOff(channelNumber: any): any[];
143
145
  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,yDAiBC;IAED,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,sCAUC;IAED,sDAMC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,+DAIC;IAED,oDAUC;IAED,sDAKC;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;AAl/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,20 +706,12 @@ 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) {
@@ -737,9 +730,9 @@ class MidyGMLite {
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:
@@ -806,6 +799,17 @@ class MidyGMLite {
806
799
  this.releaseSustainPedal(channelNumber, value);
807
800
  }
808
801
  }
802
+ handleRPN(channelNumber) {
803
+ const channel = this.channels[channelNumber];
804
+ const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
805
+ switch (rpn) {
806
+ case 0:
807
+ this.handlePitchBendRangeMessage(channelNumber);
808
+ break;
809
+ default:
810
+ console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
811
+ }
812
+ }
809
813
  setRPNMSB(channelNumber, value) {
810
814
  this.channels[channelNumber].rpnMSB = value;
811
815
  }
@@ -814,27 +818,11 @@ class MidyGMLite {
814
818
  }
815
819
  setDataEntry(channelNumber, value, isMSB) {
816
820
  const channel = this.channels[channelNumber];
817
- const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
818
821
  isMSB ? channel.dataMSB = value : channel.dataLSB = value;
819
- const { dataMSB, dataLSB } = channel;
820
- switch (rpn) {
821
- case 0:
822
- return this.handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB);
823
- default:
824
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
825
- }
826
- }
827
- handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB) {
828
- const pitchBendRange = dataMSB + dataLSB / 100;
829
- this.setPitchBendRange(channelNumber, pitchBendRange);
822
+ this.handleRPN(channelNumber);
830
823
  }
831
- setPitchBendRange(channelNumber, pitchBendRange) {
824
+ updateDetune(channel, detuneChange) {
832
825
  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
826
  const activeNotes = this.getActiveNotes(channel, now);
839
827
  activeNotes.forEach((activeNote) => {
840
828
  const { bufferSource } = activeNote;
@@ -844,6 +832,20 @@ class MidyGMLite {
844
832
  .setValueAtTime(detune, now);
845
833
  });
846
834
  }
835
+ handlePitchBendRangeMessage(channelNumber) {
836
+ const channel = this.channels[channelNumber];
837
+ this.limitData(channel, 0, 127, 0, 99);
838
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
839
+ this.setPitchBendRange(channelNumber, pitchBendRange);
840
+ }
841
+ setPitchBendRange(channelNumber, pitchBendRange) {
842
+ const channel = this.channels[channelNumber];
843
+ const prevPitchBendRange = channel.pitchBendRange;
844
+ channel.pitchBendRange = pitchBendRange;
845
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
846
+ channel.pitchBend * 100;
847
+ this.updateDetune(channel, detuneChange);
848
+ }
847
849
  allSoundOff(channelNumber) {
848
850
  const now = this.audioContext.currentTime;
849
851
  const channel = this.channels[channelNumber];
package/script/midy.d.ts CHANGED
@@ -284,13 +284,21 @@ export class Midy {
284
284
  setVibratoRate(channelNumber: any, vibratoRate: any): void;
285
285
  setVibratoDepth(channelNumber: any, vibratoDepth: any): void;
286
286
  setVibratoDelay(channelNumber: any, vibratoDelay: any): void;
287
+ limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
288
+ limitDataMSB(channel: any, minMSB: any, maxMSB: any): void;
289
+ handleRPN(channelNumber: any, value: any): void;
287
290
  incrementRPNValue(channelNumber: any): void;
288
291
  decrementRPNValue(channelNumber: any): void;
289
292
  setRPNMSB(channelNumber: any, value: any): void;
290
293
  setRPNLSB(channelNumber: any, value: any): void;
291
294
  setDataEntry(channelNumber: any, value: any, isMSB: any): void;
292
- handlePitchBendRangeMessage(channelNumber: any, dataMSB: any, dataLSB: any): void;
295
+ updateDetune(channel: any, detuneChange: any): void;
296
+ handlePitchBendRangeMessage(channelNumber: any): void;
293
297
  setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
298
+ handleFineTuningMessage(channelNumber: any): void;
299
+ setFineTuning(channelNumber: any, fineTuning: any): void;
300
+ handleCoarseTuningMessage(channelNumber: any): void;
301
+ setCoarseTuning(channelNumber: any, coarseTuning: any): void;
294
302
  allSoundOff(channelNumber: any): any[];
295
303
  resetAllControllers(channelNumber: any): void;
296
304
  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,gEAqBC;IAED,sFAcC;IAED,4DAIC;IAED,+DAcC;IAED,qEAGC;IAED,uDAOC;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,kFAeC;IAED,2DAMC;IAGD,gDAqBC;IAED,4CAEC;IAED,4CAEC;IAED,gDAEC;IAED,gDAEC;IAED,+DAIC;IAED,oDAUC;IAED,sDAKC;IAED,iEAOC;IAED,kDAKC;IAED,yDAGC;IAED,oDAKC;IAED,6DAGC;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;AA1hDD;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
@@ -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
  }
@@ -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) {
@@ -1034,14 +1028,14 @@ class Midy {
1034
1028
  return this.setReverb(channelNumber, value);
1035
1029
  case 93:
1036
1030
  return this.setChorus(channelNumber, value);
1037
- case 96:
1031
+ case 96: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1038
1032
  return incrementRPNValue(channelNumber);
1039
- case 97:
1033
+ case 97: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1040
1034
  return decrementRPNValue(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:
@@ -1178,39 +1172,58 @@ class Midy {
1178
1172
  const channel = this.channels[channelNumber];
1179
1173
  channel.vibratoDelay = vibratoDelay / 127 * 5; // 0-5sec
1180
1174
  }
1181
- incrementRPNValue(channelNumber) {
1175
+ limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
1176
+ if (maxLSB < channel.dataLSB) {
1177
+ channel.dataMSB++;
1178
+ channel.dataLSB = minLSB;
1179
+ }
1180
+ else if (channel.dataLSB < 0) {
1181
+ channel.dataMSB--;
1182
+ channel.dataLSB = maxLSB;
1183
+ }
1184
+ if (maxMSB < channel.dataMSB) {
1185
+ channel.dataMSB = maxMSB;
1186
+ channel.dataLSB = maxLSB;
1187
+ }
1188
+ else if (channel.dataMSB < 0) {
1189
+ channel.dataMSB = minMSB;
1190
+ channel.dataLSB = minLSB;
1191
+ }
1192
+ }
1193
+ limitDataMSB(channel, minMSB, maxMSB) {
1194
+ if (maxMSB < channel.dataMSB) {
1195
+ channel.dataMSB = maxMSB;
1196
+ }
1197
+ else if (channel.dataMSB < 0) {
1198
+ channel.dataMSB = minMSB;
1199
+ }
1200
+ }
1201
+ // TODO: support 3-4?
1202
+ handleRPN(channelNumber, value) {
1182
1203
  const channel = this.channels[channelNumber];
1183
1204
  const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1184
1205
  switch (rpn) {
1185
1206
  case 0:
1186
- channel.pitchBendRange = Math.min(1, channel.pitchBendRange + 1);
1207
+ channel.dataLSB += value;
1208
+ this.handlePitchBendRangeMessage(channelNumber);
1187
1209
  break;
1188
1210
  case 1:
1189
- channel.fineTuning = Math.min(1, channel.fineTuning + 1);
1211
+ channel.dataLSB += value;
1212
+ this.handleFineTuningMessage(channelNumber);
1190
1213
  break;
1191
1214
  case 2:
1192
- channel.coarseTuning = Math.min(88, channel.coarseTuning + 1);
1215
+ channel.dataMSB += value;
1216
+ this.handleCoarseTuningMessage(channelNumber);
1193
1217
  break;
1194
1218
  default:
1195
1219
  console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1196
1220
  }
1197
1221
  }
1222
+ incrementRPNValue(channelNumber) {
1223
+ this.handleRPN(channelNumber, 1);
1224
+ }
1198
1225
  decrementRPNValue(channelNumber) {
1199
- const channel = this.channels[channelNumber];
1200
- const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1201
- switch (rpn) {
1202
- case 0:
1203
- channel.pitchBendRange = Math.max(-1, channel.pitchBendRange - 1);
1204
- break;
1205
- case 1:
1206
- channel.fineTuning = Math.max(-1, channel.fineTuning - 1);
1207
- break;
1208
- case 2:
1209
- channel.coarseTuning = Math.max(40, channel.coarseTuning - 1);
1210
- break;
1211
- default:
1212
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB}, LSB=${channel.rpnLSB}.`);
1213
- }
1226
+ this.handleRPN(channelNumber, -1);
1214
1227
  }
1215
1228
  setRPNMSB(channelNumber, value) {
1216
1229
  this.channels[channelNumber].rpnMSB = value;
@@ -1218,39 +1231,13 @@ class Midy {
1218
1231
  setRPNLSB(channelNumber, value) {
1219
1232
  this.channels[channelNumber].rpnLSB = value;
1220
1233
  }
1221
- // TODO: support 3-4?
1222
1234
  setDataEntry(channelNumber, value, isMSB) {
1223
1235
  const channel = this.channels[channelNumber];
1224
- const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1225
1236
  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
- }
1237
+ this.handleRPN(channelNumber, 0);
1242
1238
  }
1243
- handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB) {
1244
- const pitchBendRange = dataMSB + dataLSB / 100;
1245
- this.setPitchBendRange(channelNumber, pitchBendRange);
1246
- }
1247
- setPitchBendRange(channelNumber, pitchBendRange) {
1239
+ updateDetune(channel, detuneChange) {
1248
1240
  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
1241
  const activeNotes = this.getActiveNotes(channel, now);
1255
1242
  activeNotes.forEach((activeNote) => {
1256
1243
  const { bufferSource } = activeNote;
@@ -1260,6 +1247,40 @@ class Midy {
1260
1247
  .setValueAtTime(detune, now);
1261
1248
  });
1262
1249
  }
1250
+ handlePitchBendRangeMessage(channelNumber) {
1251
+ const channel = this.channels[channelNumber];
1252
+ this.limitData(channel, 0, 127, 0, 99);
1253
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
1254
+ this.setPitchBendRange(channelNumber, pitchBendRange);
1255
+ }
1256
+ setPitchBendRange(channelNumber, pitchBendRange) {
1257
+ const channel = this.channels[channelNumber];
1258
+ const prevPitchBendRange = channel.pitchBendRange;
1259
+ channel.pitchBendRange = pitchBendRange;
1260
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
1261
+ channel.pitchBend * 100;
1262
+ this.updateDetune(channel, detuneChange);
1263
+ }
1264
+ handleFineTuningMessage(channelNumber) {
1265
+ const channel = this.channels[channelNumber];
1266
+ this.limitData(channel, 0, 127, 0, 127);
1267
+ const fineTuning = (channel.dataMSB * 128 + channel.dataLSB - 8192) / 8192;
1268
+ this.setFineTuning(channelNumber, fineTuning);
1269
+ }
1270
+ setFineTuning(channelNumber, fineTuning) {
1271
+ const channel = this.channels[channelNumber];
1272
+ channel.fineTuning = fineTuning;
1273
+ }
1274
+ handleCoarseTuningMessage(channelNumber) {
1275
+ const channel = this.channels[channelNumber];
1276
+ this.limitDataMSB(channel, 0, 127);
1277
+ const coarseTuning = channel.dataMSB - 64;
1278
+ this.setFineTuning(channelNumber, coarseTuning);
1279
+ }
1280
+ setCoarseTuning(channelNumber, coarseTuning) {
1281
+ const channel = this.channels[channelNumber];
1282
+ channel.fineTuning = coarseTuning;
1283
+ }
1263
1284
  allSoundOff(channelNumber) {
1264
1285
  const now = this.audioContext.currentTime;
1265
1286
  const channel = this.channels[channelNumber];
@@ -1331,8 +1352,8 @@ class Midy {
1331
1352
  channel.bankLSB = 0;
1332
1353
  channel.bank = 0;
1333
1354
  });
1334
- this.channels[9].bankMSB = 120;
1335
- this.channels[9].bank = 120 * 128;
1355
+ this.channels[9].bankMSB = 1;
1356
+ this.channels[9].bank = 128;
1336
1357
  }
1337
1358
  GM2SystemOn() {
1338
1359
  this.channels.forEach((channel) => {