@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/esm/midy.js CHANGED
@@ -75,13 +75,13 @@ export class Midy {
75
75
  configurable: true,
76
76
  writable: true,
77
77
  value: 0
78
- });
78
+ }); // cb
79
79
  Object.defineProperty(this, "masterCoarseTuning", {
80
80
  enumerable: true,
81
81
  configurable: true,
82
82
  writable: true,
83
83
  value: 0
84
- });
84
+ }); // cb
85
85
  Object.defineProperty(this, "mono", {
86
86
  enumerable: true,
87
87
  configurable: true,
@@ -651,14 +651,16 @@ export class Midy {
651
651
  connectNoteEffects(channel, gainNode) {
652
652
  if (channel.reverb === 0) {
653
653
  if (channel.chorus === 0) { // no effect
654
- gainNode.connect(channel.pannerNode);
654
+ gainNode.connect(channel.gainL);
655
+ gainNode.connect(channel.gainR);
655
656
  }
656
657
  else { // chorus
657
658
  channel.chorusEffect.delayNodes.forEach((delayNode) => {
658
659
  gainNode.connect(delayNode);
659
660
  });
660
661
  channel.chorusEffect.chorusGains.forEach((chorusGain) => {
661
- chorusGain.connect(channel.pannerNode);
662
+ chorusGain.connect(channel.gainL);
663
+ chorusGain.connect(channel.gainR);
662
664
  });
663
665
  }
664
666
  }
@@ -746,7 +748,7 @@ export class Midy {
746
748
  startModulation(channel, note, time) {
747
749
  const { instrumentKey } = note;
748
750
  note.modLFOGain = new GainNode(this.audioContext, {
749
- gain: this.cbToRatio(instrumentKey.modLfoToVolume) * channel.modulation,
751
+ gain: this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation),
750
752
  });
751
753
  note.modLFO = new OscillatorNode(this.audioContext, {
752
754
  frequency: this.centToHz(instrumentKey.freqModLFO),
@@ -977,20 +979,12 @@ export class Midy {
977
979
  this.setPitchBend(channelNumber, pitchBend);
978
980
  }
979
981
  setPitchBend(channelNumber, pitchBend) {
980
- const now = this.audioContext.currentTime;
981
982
  const channel = this.channels[channelNumber];
982
983
  const prevPitchBend = channel.pitchBend;
983
984
  channel.pitchBend = (pitchBend - 8192) / 8192;
984
985
  const detuneChange = (channel.pitchBend - prevPitchBend) *
985
986
  channel.pitchBendRange * 100;
986
- const activeNotes = this.getActiveNotes(channel, now);
987
- activeNotes.forEach((activeNote) => {
988
- const { bufferSource } = activeNote;
989
- const detune = bufferSource.detune.value + detuneChange;
990
- bufferSource.detune
991
- .cancelScheduledValues(now)
992
- .setValueAtTime(detune, now);
993
- });
987
+ this.updateDetune(channel, detuneChange);
994
988
  }
995
989
  handleControlChange(channelNumber, controller, value) {
996
990
  switch (controller) {
@@ -1001,7 +995,7 @@ export class Midy {
1001
995
  case 5:
1002
996
  return this.setPortamentoTime(channelNumber, value);
1003
997
  case 6:
1004
- return this.setDataEntry(channelNumber, value, true);
998
+ return this.dataEntryMSB(channelNumber, value);
1005
999
  case 7:
1006
1000
  return this.setVolume(channelNumber, value);
1007
1001
  case 10:
@@ -1011,7 +1005,7 @@ export class Midy {
1011
1005
  case 32:
1012
1006
  return this.setBankLSB(channelNumber, value);
1013
1007
  case 38:
1014
- return this.setDataEntry(channelNumber, value, false);
1008
+ return this.dataEntryLSB(channelNumber, value);
1015
1009
  case 64:
1016
1010
  return this.setSustainPedal(channelNumber, value);
1017
1011
  case 65:
@@ -1028,17 +1022,17 @@ export class Midy {
1028
1022
  case 78:
1029
1023
  return this.setVibratoDelay(channelNumber, value);
1030
1024
  case 91:
1031
- return this.setReverb(channelNumber, value);
1025
+ return this.setReverbSendLevel(channelNumber, value);
1032
1026
  case 93:
1033
- return this.setChorus(channelNumber, value);
1034
- case 96:
1035
- return incrementRPNValue(channelNumber);
1036
- case 97:
1037
- return decrementRPNValue(channelNumber);
1027
+ return this.setChorusSendLevel(channelNumber, value);
1028
+ case 96: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1029
+ return this.dataIncrement(channelNumber);
1030
+ case 97: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp18.pdf
1031
+ return this.dataDecrement(channelNumber);
1038
1032
  case 100:
1039
- return this.setRPNMSB(channelNumber, value);
1040
- case 101:
1041
1033
  return this.setRPNLSB(channelNumber, value);
1034
+ case 101:
1035
+ return this.setRPNMSB(channelNumber, value);
1042
1036
  case 120:
1043
1037
  return this.allSoundOff(channelNumber);
1044
1038
  case 121:
@@ -1060,22 +1054,24 @@ export class Midy {
1060
1054
  setBankMSB(channelNumber, msb) {
1061
1055
  this.channels[channelNumber].bankMSB = msb;
1062
1056
  }
1063
- setModulation(channelNumber, modulation) {
1057
+ updateModulation(channel) {
1064
1058
  const now = this.audioContext.currentTime;
1065
- const channel = this.channels[channelNumber];
1066
- channel.modulation = (modulation / 127) *
1067
- (channel.modulationDepthRange * 100);
1068
1059
  const activeNotes = this.getActiveNotes(channel, now);
1069
1060
  activeNotes.forEach((activeNote) => {
1070
1061
  if (activeNote.modLFO) {
1071
- activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
1072
- channel.modulation, now);
1062
+ const { gainNode, instrumentKey } = activeNote;
1063
+ gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
1073
1064
  }
1074
1065
  else {
1075
1066
  this.startModulation(channel, activeNote, now);
1076
1067
  }
1077
1068
  });
1078
1069
  }
1070
+ setModulation(channelNumber, modulation) {
1071
+ const channel = this.channels[channelNumber];
1072
+ channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1073
+ this.updateModulation(channel);
1074
+ }
1079
1075
  setPortamentoTime(channelNumber, portamentoTime) {
1080
1076
  this.channels[channelNumber].portamentoTime = portamentoTime / 127;
1081
1077
  }
@@ -1104,6 +1100,10 @@ export class Midy {
1104
1100
  setBankLSB(channelNumber, lsb) {
1105
1101
  this.channels[channelNumber].bankLSB = lsb;
1106
1102
  }
1103
+ dataEntryLSB(channelNumber, value) {
1104
+ this.channels[channelNumber].dataLSB = value;
1105
+ this.handleRPN(channelNumber, 0);
1106
+ }
1107
1107
  updateChannelGain(channel) {
1108
1108
  const now = this.audioContext.currentTime;
1109
1109
  const volume = channel.volume * channel.expression;
@@ -1125,7 +1125,7 @@ export class Midy {
1125
1125
  setPortamento(channelNumber, value) {
1126
1126
  this.channels[channelNumber].portamento = value >= 64;
1127
1127
  }
1128
- setReverb(channelNumber, reverb) {
1128
+ setReverbSendLevel(channelNumber, reverb) {
1129
1129
  const now = this.audioContext.currentTime;
1130
1130
  const channel = this.channels[channelNumber];
1131
1131
  const reverbEffect = channel.reverbEffect;
@@ -1135,7 +1135,7 @@ export class Midy {
1135
1135
  reverbEffect.wetGain.gain.cancelScheduledValues(now);
1136
1136
  reverbEffect.wetGain.gain.setValueAtTime(channel.reverb, now);
1137
1137
  }
1138
- setChorus(channelNumber, chorus) {
1138
+ setChorusSendLevel(channelNumber, chorus) {
1139
1139
  const channel = this.channels[channelNumber];
1140
1140
  channel.chorus = chorus / 127;
1141
1141
  channel.chorusEffect.lfoGain = channel.chorus;
@@ -1175,79 +1175,74 @@ export class Midy {
1175
1175
  const channel = this.channels[channelNumber];
1176
1176
  channel.vibratoDelay = vibratoDelay / 127 * 5; // 0-5sec
1177
1177
  }
1178
- incrementRPNValue(channelNumber) {
1179
- const channel = this.channels[channelNumber];
1180
- const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1181
- switch (rpn) {
1182
- case 0:
1183
- channel.pitchBendRange = Math.min(1, channel.pitchBendRange + 1);
1184
- break;
1185
- case 1:
1186
- channel.fineTuning = Math.min(1, channel.fineTuning + 1);
1187
- break;
1188
- case 2:
1189
- channel.coarseTuning = Math.min(88, channel.coarseTuning + 1);
1190
- break;
1191
- default:
1192
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1178
+ limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
1179
+ if (maxLSB < channel.dataLSB) {
1180
+ channel.dataMSB++;
1181
+ channel.dataLSB = minLSB;
1182
+ }
1183
+ else if (channel.dataLSB < 0) {
1184
+ channel.dataMSB--;
1185
+ channel.dataLSB = maxLSB;
1186
+ }
1187
+ if (maxMSB < channel.dataMSB) {
1188
+ channel.dataMSB = maxMSB;
1189
+ channel.dataLSB = maxLSB;
1190
+ }
1191
+ else if (channel.dataMSB < 0) {
1192
+ channel.dataMSB = minMSB;
1193
+ channel.dataLSB = minLSB;
1194
+ }
1195
+ }
1196
+ limitDataMSB(channel, minMSB, maxMSB) {
1197
+ if (maxMSB < channel.dataMSB) {
1198
+ channel.dataMSB = maxMSB;
1199
+ }
1200
+ else if (channel.dataMSB < 0) {
1201
+ channel.dataMSB = minMSB;
1193
1202
  }
1194
1203
  }
1195
- decrementRPNValue(channelNumber) {
1204
+ handleRPN(channelNumber, value) {
1196
1205
  const channel = this.channels[channelNumber];
1197
1206
  const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1198
1207
  switch (rpn) {
1199
1208
  case 0:
1200
- channel.pitchBendRange = Math.max(-1, channel.pitchBendRange - 1);
1209
+ channel.dataLSB += value;
1210
+ this.handlePitchBendRangeRPN(channelNumber);
1201
1211
  break;
1202
1212
  case 1:
1203
- channel.fineTuning = Math.max(-1, channel.fineTuning - 1);
1213
+ channel.dataLSB += value;
1214
+ this.handleFineTuningRPN(channelNumber);
1204
1215
  break;
1205
1216
  case 2:
1206
- channel.coarseTuning = Math.max(40, channel.coarseTuning - 1);
1217
+ channel.dataMSB += value;
1218
+ this.handleCoarseTuningRPN(channelNumber);
1219
+ break;
1220
+ case 5:
1221
+ channel.dataLSB += value;
1222
+ this.handleModulationDepthRangeRPN(channelNumber);
1207
1223
  break;
1208
1224
  default:
1209
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB}, LSB=${channel.rpnLSB}.`);
1225
+ console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1210
1226
  }
1211
1227
  }
1228
+ dataIncrement(channelNumber) {
1229
+ this.handleRPN(channelNumber, 1);
1230
+ }
1231
+ dataDecrement(channelNumber) {
1232
+ this.handleRPN(channelNumber, -1);
1233
+ }
1212
1234
  setRPNMSB(channelNumber, value) {
1213
1235
  this.channels[channelNumber].rpnMSB = value;
1214
1236
  }
1215
1237
  setRPNLSB(channelNumber, value) {
1216
1238
  this.channels[channelNumber].rpnLSB = value;
1217
1239
  }
1218
- // TODO: support 3-4?
1219
- setDataEntry(channelNumber, value, isMSB) {
1220
- const channel = this.channels[channelNumber];
1221
- const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
1222
- isMSB ? channel.dataMSB = value : channel.dataLSB = value;
1223
- const { dataMSB, dataLSB } = channel;
1224
- switch (rpn) {
1225
- case 0:
1226
- return this.handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB);
1227
- case 1:
1228
- channel.fineTuning = (dataMSB * 128 + dataLSB - 8192) / 8192;
1229
- break;
1230
- case 2:
1231
- channel.coarseTuning = dataMSB - 64;
1232
- break;
1233
- case 5:
1234
- channel.modulationDepthRange = dataMSB + dataLSB / 128;
1235
- break;
1236
- default:
1237
- console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
1238
- }
1240
+ dataEntryMSB(channelNumber, value) {
1241
+ this.channels[channelNumber].dataMSB = value;
1242
+ this.handleRPN(channelNumber, 0);
1239
1243
  }
1240
- handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB) {
1241
- const pitchBendRange = dataMSB + dataLSB / 100;
1242
- this.setPitchBendRange(channelNumber, pitchBendRange);
1243
- }
1244
- setPitchBendRange(channelNumber, pitchBendRange) {
1244
+ updateDetune(channel, detuneChange) {
1245
1245
  const now = this.audioContext.currentTime;
1246
- const channel = this.channels[channelNumber];
1247
- const prevPitchBendRange = channel.pitchBendRange;
1248
- channel.pitchBendRange = pitchBendRange;
1249
- const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
1250
- channel.pitchBend * 100;
1251
1246
  const activeNotes = this.getActiveNotes(channel, now);
1252
1247
  activeNotes.forEach((activeNote) => {
1253
1248
  const { bufferSource } = activeNote;
@@ -1257,6 +1252,58 @@ export class Midy {
1257
1252
  .setValueAtTime(detune, now);
1258
1253
  });
1259
1254
  }
1255
+ handlePitchBendRangeRPN(channelNumber) {
1256
+ const channel = this.channels[channelNumber];
1257
+ this.limitData(channel, 0, 127, 0, 99);
1258
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
1259
+ this.setPitchBendRange(channelNumber, pitchBendRange);
1260
+ }
1261
+ setPitchBendRange(channelNumber, pitchBendRange) {
1262
+ const channel = this.channels[channelNumber];
1263
+ const prevPitchBendRange = channel.pitchBendRange;
1264
+ channel.pitchBendRange = pitchBendRange;
1265
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
1266
+ channel.pitchBend * 100;
1267
+ this.updateDetune(channel, detuneChange);
1268
+ }
1269
+ handleFineTuningRPN(channelNumber) {
1270
+ const channel = this.channels[channelNumber];
1271
+ this.limitData(channel, 0, 127, 0, 127);
1272
+ const fineTuning = (channel.dataMSB * 128 + channel.dataLSB - 8192) / 8192;
1273
+ this.setFineTuning(channelNumber, fineTuning);
1274
+ }
1275
+ setFineTuning(channelNumber, fineTuning) {
1276
+ const channel = this.channels[channelNumber];
1277
+ const prevFineTuning = channel.fineTuning;
1278
+ channel.fineTuning = fineTuning;
1279
+ const detuneChange = channel.fineTuning - prevFineTuning;
1280
+ this.updateDetune(channel, detuneChange);
1281
+ }
1282
+ handleCoarseTuningRPN(channelNumber) {
1283
+ const channel = this.channels[channelNumber];
1284
+ this.limitDataMSB(channel, 0, 127);
1285
+ const coarseTuning = channel.dataMSB - 64;
1286
+ this.setFineTuning(channelNumber, coarseTuning);
1287
+ }
1288
+ setCoarseTuning(channelNumber, coarseTuning) {
1289
+ const channel = this.channels[channelNumber];
1290
+ const prevCoarseTuning = channel.coarseTuning;
1291
+ channel.coarseTuning = coarseTuning;
1292
+ const detuneChange = channel.coarseTuning - prevCoarseTuning;
1293
+ this.updateDetune(channel, detuneChange);
1294
+ }
1295
+ handleModulationDepthRangeRPN(channelNumber) {
1296
+ const channel = this.channels[channelNumber];
1297
+ this.limitData(channel, 0, 127, 0, 127);
1298
+ const modulationDepthRange = dataMSB + dataLSB / 128;
1299
+ this.setModulationDepthRange(channelNumber, modulationDepthRange);
1300
+ }
1301
+ setModulationDepthRange(channelNumber, modulationDepthRange) {
1302
+ const channel = this.channels[channelNumber];
1303
+ channel.modulationDepthRange = modulationDepthRange;
1304
+ channel.modulation = (modulation / 127) * channel.modulationDepthRange;
1305
+ this.updateModulation(channel);
1306
+ }
1260
1307
  allSoundOff(channelNumber) {
1261
1308
  const now = this.audioContext.currentTime;
1262
1309
  const channel = this.channels[channelNumber];
@@ -1328,8 +1375,8 @@ export class Midy {
1328
1375
  channel.bankLSB = 0;
1329
1376
  channel.bank = 0;
1330
1377
  });
1331
- this.channels[9].bankMSB = 120;
1332
- this.channels[9].bank = 120 * 128;
1378
+ this.channels[9].bankMSB = 1;
1379
+ this.channels[9].bank = 128;
1333
1380
  }
1334
1381
  GM2SystemOn() {
1335
1382
  this.channels.forEach((channel) => {
@@ -1346,9 +1393,9 @@ export class Midy {
1346
1393
  switch (data[3]) {
1347
1394
  case 1:
1348
1395
  return this.handleMasterVolumeSysEx(data);
1349
- case 3:
1396
+ case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
1350
1397
  return this.handleMasterFineTuningSysEx(data);
1351
- case 4:
1398
+ case 4: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca25.pdf
1352
1399
  return this.handleMasterCoarseTuningSysEx(data);
1353
1400
  // case 5: // TODO: Global Parameter Control
1354
1401
  default:
@@ -1473,9 +1520,9 @@ Object.defineProperty(Midy, "channelSettings", {
1473
1520
  dataLSB: 0,
1474
1521
  program: 0,
1475
1522
  pitchBend: 0,
1476
- fineTuning: 0,
1477
- coarseTuning: 0,
1478
- modulationDepthRange: 0.5,
1523
+ fineTuning: 0, // cb
1524
+ coarseTuning: 0, // cb
1525
+ modulationDepthRange: 0.5, // cb
1479
1526
  }
1480
1527
  });
1481
1528
  Object.defineProperty(Midy, "effectSettings", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marmooo/midy",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
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",
@@ -130,6 +130,7 @@ export class MidyGM1 {
130
130
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
131
131
  setPitchBend(channelNumber: any, pitchBend: any): void;
132
132
  handleControlChange(channelNumber: any, controller: any, value: any): void | any[];
133
+ updateModulation(channel: any): void;
133
134
  setModulation(channelNumber: any, modulation: any): void;
134
135
  setVolume(channelNumber: any, volume: any): void;
135
136
  panToGain(pan: any): {
@@ -138,13 +139,22 @@ export class MidyGM1 {
138
139
  };
139
140
  setPan(channelNumber: any, pan: any): void;
140
141
  setExpression(channelNumber: any, expression: any): void;
142
+ dataEntryLSB(channelNumber: any, value: any): void;
141
143
  updateChannelGain(channel: any): void;
142
144
  setSustainPedal(channelNumber: any, value: any): void;
145
+ limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
146
+ limitDataMSB(channel: any, minMSB: any, maxMSB: any): void;
147
+ handleRPN(channelNumber: any): void;
143
148
  setRPNMSB(channelNumber: any, value: any): void;
144
149
  setRPNLSB(channelNumber: any, value: any): void;
145
- setDataEntry(channelNumber: any, value: any, isMSB: any): void;
146
- handlePitchBendRangeMessage(channelNumber: any, dataMSB: any, dataLSB: any): void;
150
+ dataEntryMSB(channelNumber: any, value: any): void;
151
+ updateDetune(channel: any, detuneChange: any): void;
152
+ handlePitchBendRangeRPN(channelNumber: any): void;
147
153
  setPitchBendRange(channelNumber: any, pitchBendRange: any): void;
154
+ handleFineTuningRPN(channelNumber: any): void;
155
+ setFineTuning(channelNumber: any, fineTuning: any): void;
156
+ handleCoarseTuningRPN(channelNumber: any): void;
157
+ setCoarseTuning(channelNumber: any, coarseTuning: any): void;
148
158
  allSoundOff(channelNumber: any): any[];
149
159
  resetAllControllers(channelNumber: any): void;
150
160
  allNotesOff(channelNumber: any): any[];
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;MAYC;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,sCAGC;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,+DAuBC;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;AAz/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-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqBA;IAmBE;;;;;;;;;;;MAWE;IAEF;;;;;;;MAOE;IAEF,+BAMC;IA9CD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAyBhB,kBAAgC;IAChC,gBAA4C;IAE5C;;;;;;;;;;;;;;;;;;;;QAAiD;IAInD,4BAMC;IAED,mCASC;IAED,gDAMC;IAED,sCASC;IAED;;;MAYC;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,sCAGC;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,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAUC;IAED,kDAKC;IAED,iEAOC;IAED,8CAKC;IAED,yDAMC;IAED,gDAKC;IAED,6DAMC;IAED,uCAoBC;IAED,8CAEC;IAED,uCAoBC;IAED,4DAgBC;IAED,oBAQC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAtjCD;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 MidyGM1 {
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);
@@ -706,27 +707,19 @@ class MidyGM1 {
706
707
  this.setPitchBend(channelNumber, pitchBend);
707
708
  }
708
709
  setPitchBend(channelNumber, pitchBend) {
709
- const now = this.audioContext.currentTime;
710
710
  const channel = this.channels[channelNumber];
711
711
  const prevPitchBend = channel.pitchBend;
712
712
  channel.pitchBend = (pitchBend - 8192) / 8192;
713
713
  const detuneChange = (channel.pitchBend - prevPitchBend) *
714
714
  channel.pitchBendRange * 100;
715
- const activeNotes = this.getActiveNotes(channel, now);
716
- activeNotes.forEach((activeNote) => {
717
- const { bufferSource } = activeNote;
718
- const detune = bufferSource.detune.value + detuneChange;
719
- bufferSource.detune
720
- .cancelScheduledValues(now)
721
- .setValueAtTime(detune, now);
722
- });
715
+ this.updateDetune(channel, detuneChange);
723
716
  }
724
717
  handleControlChange(channelNumber, controller, value) {
725
718
  switch (controller) {
726
719
  case 1:
727
720
  return this.setModulation(channelNumber, value);
728
721
  case 6:
729
- return this.setDataEntry(channelNumber, value, true);
722
+ return this.dataEntryMSB(channelNumber, value);
730
723
  case 7:
731
724
  return this.setVolume(channelNumber, value);
732
725
  case 10:
@@ -734,13 +727,13 @@ class MidyGM1 {
734
727
  case 11:
735
728
  return this.setExpression(channelNumber, value);
736
729
  case 38:
737
- return this.setDataEntry(channelNumber, value, false);
730
+ return this.dataEntryLSB(channelNumber, value);
738
731
  case 64:
739
732
  return this.setSustainPedal(channelNumber, value);
740
733
  case 100:
741
- return this.setRPNMSB(channelNumber, value);
742
- case 101:
743
734
  return this.setRPNLSB(channelNumber, value);
735
+ case 101:
736
+ return this.setRPNMSB(channelNumber, value);
744
737
  case 120:
745
738
  return this.allSoundOff(channelNumber);
746
739
  case 121:
@@ -751,22 +744,24 @@ class MidyGM1 {
751
744
  console.warn(`Unsupported Control change: controller=${controller} value=${value}`);
752
745
  }
753
746
  }
754
- setModulation(channelNumber, modulation) {
747
+ updateModulation(channel) {
755
748
  const now = this.audioContext.currentTime;
756
- const channel = this.channels[channelNumber];
757
- channel.modulation = (modulation / 127) *
758
- (channel.modulationDepthRange * 100);
759
749
  const activeNotes = this.getActiveNotes(channel, now);
760
750
  activeNotes.forEach((activeNote) => {
761
751
  if (activeNote.modLFO) {
762
- activeNote.gainNode.gain.setValueAtTime(this.cbToRatio(activeNote.instrumentKey.modLfoToVolume) *
763
- channel.modulation, now);
752
+ const { gainNode, instrumentKey } = activeNote;
753
+ gainNode.gain.setValueAtTime(this.cbToRatio(instrumentKey.modLfoToVolume + channel.modulation), now);
764
754
  }
765
755
  else {
766
756
  this.startModulation(channel, activeNote, now);
767
757
  }
768
758
  });
769
759
  }
760
+ setModulation(channelNumber, modulation) {
761
+ const channel = this.channels[channelNumber];
762
+ channel.modulation = (modulation / 127) * channel.modulationDepthRange;
763
+ this.updateModulation(channel);
764
+ }
770
765
  setVolume(channelNumber, volume) {
771
766
  const channel = this.channels[channelNumber];
772
767
  channel.volume = volume / 127;
@@ -789,6 +784,10 @@ class MidyGM1 {
789
784
  channel.expression = expression / 127;
790
785
  this.updateChannelGain(channel);
791
786
  }
787
+ dataEntryLSB(channelNumber, value) {
788
+ this.channels[channelNumber].dataLSB = value;
789
+ this.handleRPN(channelNumber, 0);
790
+ }
792
791
  updateChannelGain(channel) {
793
792
  const now = this.audioContext.currentTime;
794
793
  const volume = channel.volume * channel.expression;
@@ -807,41 +806,61 @@ class MidyGM1 {
807
806
  this.releaseSustainPedal(channelNumber, value);
808
807
  }
809
808
  }
810
- setRPNMSB(channelNumber, value) {
811
- this.channels[channelNumber].rpnMSB = value;
809
+ limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
810
+ if (maxLSB < channel.dataLSB) {
811
+ channel.dataMSB++;
812
+ channel.dataLSB = minLSB;
813
+ }
814
+ else if (channel.dataLSB < 0) {
815
+ channel.dataMSB--;
816
+ channel.dataLSB = maxLSB;
817
+ }
818
+ if (maxMSB < channel.dataMSB) {
819
+ channel.dataMSB = maxMSB;
820
+ channel.dataLSB = maxLSB;
821
+ }
822
+ else if (channel.dataMSB < 0) {
823
+ channel.dataMSB = minMSB;
824
+ channel.dataLSB = minLSB;
825
+ }
812
826
  }
813
- setRPNLSB(channelNumber, value) {
814
- this.channels[channelNumber].rpnLSB = value;
827
+ limitDataMSB(channel, minMSB, maxMSB) {
828
+ if (maxMSB < channel.dataMSB) {
829
+ channel.dataMSB = maxMSB;
830
+ }
831
+ else if (channel.dataMSB < 0) {
832
+ channel.dataMSB = minMSB;
833
+ }
815
834
  }
816
- setDataEntry(channelNumber, value, isMSB) {
835
+ handleRPN(channelNumber) {
817
836
  const channel = this.channels[channelNumber];
818
837
  const rpn = channel.rpnMSB * 128 + channel.rpnLSB;
819
- isMSB ? channel.dataMSB = value : channel.dataLSB = value;
820
- const { dataMSB, dataLSB } = channel;
821
838
  switch (rpn) {
822
839
  case 0:
823
- return this.handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB);
840
+ this.handlePitchBendRangeRPN(channelNumber);
841
+ break;
824
842
  case 1:
825
- channel.fineTuning = (dataMSB * 128 + dataLSB - 8192) / 8192;
843
+ this.handleFineTuningRPN(channelNumber);
826
844
  break;
827
845
  case 2:
828
- channel.coarseTuning = dataMSB - 64;
846
+ this.handleCoarseTuningRPN(channelNumber);
829
847
  break;
830
848
  default:
831
849
  console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
832
850
  }
833
851
  }
834
- handlePitchBendRangeMessage(channelNumber, dataMSB, dataLSB) {
835
- const pitchBendRange = dataMSB + dataLSB / 100;
836
- this.setPitchBendRange(channelNumber, pitchBendRange);
852
+ setRPNMSB(channelNumber, value) {
853
+ this.channels[channelNumber].rpnMSB = value;
837
854
  }
838
- setPitchBendRange(channelNumber, pitchBendRange) {
855
+ setRPNLSB(channelNumber, value) {
856
+ this.channels[channelNumber].rpnLSB = value;
857
+ }
858
+ dataEntryMSB(channelNumber, value) {
859
+ this.channels[channelNumber].dataMSB = value;
860
+ this.handleRPN(channelNumber);
861
+ }
862
+ updateDetune(channel, detuneChange) {
839
863
  const now = this.audioContext.currentTime;
840
- const channel = this.channels[channelNumber];
841
- const prevPitchBendRange = channel.pitchBendRange;
842
- channel.pitchBendRange = pitchBendRange;
843
- const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
844
- channel.pitchBend * 100;
845
864
  const activeNotes = this.getActiveNotes(channel, now);
846
865
  activeNotes.forEach((activeNote) => {
847
866
  const { bufferSource } = activeNote;
@@ -851,6 +870,46 @@ class MidyGM1 {
851
870
  .setValueAtTime(detune, now);
852
871
  });
853
872
  }
873
+ handlePitchBendRangeRPN(channelNumber) {
874
+ const channel = this.channels[channelNumber];
875
+ this.limitData(channel, 0, 127, 0, 99);
876
+ const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
877
+ this.setPitchBendRange(channelNumber, pitchBendRange);
878
+ }
879
+ setPitchBendRange(channelNumber, pitchBendRange) {
880
+ const channel = this.channels[channelNumber];
881
+ const prevPitchBendRange = channel.pitchBendRange;
882
+ channel.pitchBendRange = pitchBendRange;
883
+ const detuneChange = (channel.pitchBendRange - prevPitchBendRange) *
884
+ channel.pitchBend * 100;
885
+ this.updateDetune(channel, detuneChange);
886
+ }
887
+ handleFineTuningRPN(channelNumber) {
888
+ const channel = this.channels[channelNumber];
889
+ this.limitData(channel, 0, 127, 0, 127);
890
+ const fineTuning = (channel.dataMSB * 128 + channel.dataLSB - 8192) / 8192;
891
+ this.setFineTuning(channelNumber, fineTuning);
892
+ }
893
+ setFineTuning(channelNumber, fineTuning) {
894
+ const channel = this.channels[channelNumber];
895
+ const prevFineTuning = channel.fineTuning;
896
+ channel.fineTuning = fineTuning;
897
+ const detuneChange = channel.fineTuning - prevFineTuning;
898
+ this.updateDetune(channel, detuneChange);
899
+ }
900
+ handleCoarseTuningRPN(channelNumber) {
901
+ const channel = this.channels[channelNumber];
902
+ this.limitDataMSB(channel, 0, 127);
903
+ const coarseTuning = channel.dataMSB - 64;
904
+ this.setFineTuning(channelNumber, coarseTuning);
905
+ }
906
+ setCoarseTuning(channelNumber, coarseTuning) {
907
+ const channel = this.channels[channelNumber];
908
+ const prevCoarseTuning = channel.coarseTuning;
909
+ channel.coarseTuning = coarseTuning;
910
+ const detuneChange = channel.coarseTuning - prevCoarseTuning;
911
+ this.updateDetune(channel, detuneChange);
912
+ }
854
913
  allSoundOff(channelNumber) {
855
914
  const now = this.audioContext.currentTime;
856
915
  const channel = this.channels[channelNumber];
@@ -976,9 +1035,9 @@ Object.defineProperty(MidyGM1, "channelSettings", {
976
1035
  dataLSB: 0,
977
1036
  program: 0,
978
1037
  pitchBend: 0,
979
- fineTuning: 0,
980
- coarseTuning: 0,
981
- modulationDepthRange: 0.5,
1038
+ fineTuning: 0, // cb
1039
+ coarseTuning: 0, // cb
1040
+ modulationDepthRange: 0.5, // cb
982
1041
  }
983
1042
  });
984
1043
  Object.defineProperty(MidyGM1, "effectSettings", {