@marmooo/midy 0.0.3 → 0.0.4

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.js CHANGED
@@ -209,6 +209,12 @@ class Midy {
209
209
  ...this.setChannelAudioNodes(audioContext),
210
210
  scheduledNotes: new Map(),
211
211
  sostenutoNotes: new Map(),
212
+ polyphonicKeyPressure: {
213
+ ...Midy.controllerDestinationSettings,
214
+ },
215
+ channelPressure: {
216
+ ...Midy.controllerDestinationSettings,
217
+ },
212
218
  };
213
219
  });
214
220
  return channels;
@@ -263,9 +269,6 @@ class Midy {
263
269
  if (event.startTime > t + this.lookAhead)
264
270
  break;
265
271
  switch (event.type) {
266
- case "controller":
267
- this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
268
- break;
269
272
  case "noteOn":
270
273
  if (event.velocity !== 0) {
271
274
  await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset);
@@ -279,9 +282,21 @@ class Midy {
279
282
  }
280
283
  break;
281
284
  }
285
+ case "noteAftertouch":
286
+ this.handlePolyphonicKeyPressure(event.channel, event.noteNumber, event.amount);
287
+ break;
288
+ case "controller":
289
+ this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
290
+ break;
282
291
  case "programChange":
283
292
  this.handleProgramChange(event.channel, event.programNumber);
284
293
  break;
294
+ case "channelAftertouch":
295
+ this.handleChannelPressure(event.channel, event.amount);
296
+ break;
297
+ case "pitchBend":
298
+ this.handlePitchBend(event.channel, event.value);
299
+ break;
285
300
  case "sysEx":
286
301
  this.handleSysEx(event.data);
287
302
  }
@@ -645,15 +660,19 @@ class Midy {
645
660
  centToHz(cent) {
646
661
  return 8.176 * Math.pow(2, cent / 1200);
647
662
  }
648
- async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
663
+ calcSemitoneOffset(channel) {
649
664
  const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
650
665
  const channelTuning = channel.coarseTuning + channel.fineTuning;
651
666
  const tuning = masterTuning + channelTuning;
652
- const semitoneOffset = channel.pitchBend * channel.pitchBendRange + tuning;
653
- const playbackRate = noteInfo.playbackRate(noteNumber) *
654
- Math.pow(2, semitoneOffset / 12);
667
+ return channel.pitchBend * channel.pitchBendRange + tuning;
668
+ }
669
+ calcPlaybackRate(noteInfo, noteNumber, semitoneOffset) {
670
+ return noteInfo.playbackRate(noteNumber) * Math.pow(2, semitoneOffset / 12);
671
+ }
672
+ async createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3) {
655
673
  const bufferSource = await this.createNoteBufferNode(noteInfo, isSF3);
656
- bufferSource.playbackRate.value = playbackRate;
674
+ const semitoneOffset = this.calcSemitoneOffset(channel);
675
+ bufferSource.playbackRate.value = this.calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
657
676
  // volume envelope
658
677
  const gainNode = new GainNode(this.audioContext, {
659
678
  gain: 0,
@@ -717,7 +736,7 @@ class Midy {
717
736
  channel.currentBufferSource = bufferSource;
718
737
  }
719
738
  bufferSource.start(startTime, noteInfo.start / noteInfo.sampleRate);
720
- return { bufferSource, gainNode, filterNode };
739
+ return { bufferSource, gainNode, filterNode, lfoGain };
721
740
  }
722
741
  calcBank(channel, channelNumber) {
723
742
  if (channel.bankMSB === 121) {
@@ -739,7 +758,7 @@ class Midy {
739
758
  const noteInfo = soundFont.getInstrumentKey(bankNumber, channel.program, noteNumber);
740
759
  if (!noteInfo)
741
760
  return;
742
- const { bufferSource, gainNode, filterNode } = await this
761
+ const { bufferSource, gainNode, filterNode, lfoGain } = await this
743
762
  .createNoteAudioChain(channel, noteInfo, noteNumber, velocity, startTime, isSF3);
744
763
  this.connectNoteEffects(channel, gainNode);
745
764
  if (channel.sostenutoPedal) {
@@ -859,7 +878,7 @@ class Midy {
859
878
  case 0x90:
860
879
  return this.noteOn(channelNumber, data1, data2);
861
880
  case 0xA0:
862
- return this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
881
+ return; // this.handlePolyphonicKeyPressure(channelNumber, data1, data2);
863
882
  case 0xB0:
864
883
  return this.handleControlChange(channelNumber, data1, data2);
865
884
  case 0xC0:
@@ -867,7 +886,7 @@ class Midy {
867
886
  case 0xD0:
868
887
  return this.handleChannelPressure(channelNumber, data1);
869
888
  case 0xE0:
870
- return this.handlePitchBend(channelNumber, data1, data2);
889
+ return this.handlePitchBendMessage(channelNumber, data1, data2);
871
890
  default:
872
891
  console.warn(`Unsupported MIDI message: ${messageType.toString(16)}`);
873
892
  }
@@ -875,17 +894,16 @@ class Midy {
875
894
  handlePolyphonicKeyPressure(channelNumber, noteNumber, pressure) {
876
895
  const now = this.audioContext.currentTime;
877
896
  const channel = this.channels[channelNumber];
878
- const scheduledNotes = channel.scheduledNotes.get(noteNumber);
879
- pressure /= 127;
880
- if (scheduledNotes) {
881
- scheduledNotes.forEach((scheduledNote) => {
882
- if (scheduledNote) {
883
- const { initialAttenuation } = scheduledNote.noteInfo;
884
- const gain = this.cbToRatio(-initialAttenuation) * pressure;
885
- scheduledNote.gainNode.gain.cancelScheduledValues(now);
886
- scheduledNote.gainNode.gain.setValueAtTime(gain, now);
887
- }
888
- });
897
+ pressure /= 64;
898
+ const activeNotes = this.getActiveNotes(channel);
899
+ if (channel.polyphonicKeyPressure.amplitudeControl !== 1) {
900
+ if (activeNotes.has(noteNumber)) {
901
+ const activeNote = activeNotes.get(noteNumber);
902
+ const gain = activeNote.gainNode.gain.value;
903
+ activeNote.gainNode.gain
904
+ .cancelScheduledValues(now)
905
+ .setValueAtTime(gain * pressure, now);
906
+ }
889
907
  }
890
908
  }
891
909
  handleProgramChange(channelNumber, program) {
@@ -894,11 +912,37 @@ class Midy {
894
912
  channel.program = program;
895
913
  }
896
914
  handleChannelPressure(channelNumber, pressure) {
897
- this.channels[channelNumber].channelPressure = pressure;
915
+ const now = this.audioContext.currentTime;
916
+ const channel = this.channels[channelNumber];
917
+ pressure /= 64;
918
+ channel.channelPressure = pressure;
919
+ const activeNotes = this.getActiveNotes(channel);
920
+ if (channel.channelPressure.amplitudeControl !== 1) {
921
+ activeNotes.forEach((activeNote) => {
922
+ const gain = activeNote.gainNode.gain.value;
923
+ activeNote.gainNode.gain
924
+ .cancelScheduledValues(now)
925
+ .setValueAtTime(gain * pressure, now);
926
+ });
927
+ }
898
928
  }
899
- handlePitchBend(channelNumber, lsb, msb) {
900
- const pitchBend = (msb * 128 + lsb - 8192) / 8192;
901
- this.channels[channelNumber].pitchBend = pitchBend;
929
+ handlePitchBendMessage(channelNumber, lsb, msb) {
930
+ const pitchBend = msb * 128 + lsb;
931
+ this.handlePitchBend(channelNumber, pitchBend);
932
+ }
933
+ handlePitchBend(channelNumber, pitchBend) {
934
+ const now = this.audioContext.currentTime;
935
+ const channel = this.channels[channelNumber];
936
+ channel.pitchBend = (pitchBend - 8192) / 8192;
937
+ const semitoneOffset = this.calcSemitoneOffset(channel);
938
+ const activeNotes = this.getActiveNotes(channel);
939
+ activeNotes.forEach((activeNote) => {
940
+ const { bufferSource, noteInfo, noteNumber } = activeNote;
941
+ const playbackRate = calcPlaybackRate(noteInfo, noteNumber, semitoneOffset);
942
+ bufferSource.playbackRate
943
+ .cancelScheduledValues(now)
944
+ .setValueAtTime(playbackRate * pressure, now);
945
+ });
902
946
  }
903
947
  handleControlChange(channelNumber, controller, value) {
904
948
  switch (controller) {
@@ -1243,10 +1287,10 @@ class Midy {
1243
1287
  switch (data[3]) {
1244
1288
  // case 1:
1245
1289
  // // TODO
1246
- // return this.handleChannelPressure();
1290
+ // return this.setChannelPressure();
1247
1291
  // case 3:
1248
1292
  // // TODO
1249
- // return this.handleControlChange();
1293
+ // return this.setControlChange();
1250
1294
  default:
1251
1295
  console.warn(`Unsupported Exclusive Message ${data}`);
1252
1296
  }
@@ -1265,20 +1309,25 @@ class Midy {
1265
1309
  }
1266
1310
  }
1267
1311
  handleMasterVolumeSysEx(data) {
1268
- const volume = (data[5] * 128 + data[4] - 8192) / 8192;
1312
+ const volume = (data[5] * 128 + data[4]) / 16383;
1269
1313
  this.handleMasterVolume(volume);
1270
1314
  }
1271
1315
  handleMasterVolume(volume) {
1272
- const now = this.audioContext.currentTime;
1273
- this.masterGain.gain.cancelScheduledValues(now);
1274
- this.masterGain.gain.setValueAtTime(volume * volume, now);
1316
+ if (volume < 0 && 1 < volume) {
1317
+ console.error("Master Volume is out of range");
1318
+ }
1319
+ else {
1320
+ const now = this.audioContext.currentTime;
1321
+ this.masterGain.gain.cancelScheduledValues(now);
1322
+ this.masterGain.gain.setValueAtTime(volume * volume, now);
1323
+ }
1275
1324
  }
1276
1325
  handleMasterFineTuningSysEx(data) {
1277
1326
  const fineTuning = (data[5] * 128 + data[4] - 8192) / 8192;
1278
1327
  this.handleMasterFineTuning(fineTuning);
1279
1328
  }
1280
1329
  handleMasterFineTuning(fineTuning) {
1281
- if (fineTuning < 0 && 1 < fineTuning) {
1330
+ if (fineTuning < -1 && 1 < fineTuning) {
1282
1331
  console.error("Master Fine Tuning value is out of range");
1283
1332
  }
1284
1333
  else {
@@ -1366,3 +1415,16 @@ Object.defineProperty(Midy, "effectSettings", {
1366
1415
  pitchBendRange: 2,
1367
1416
  }
1368
1417
  });
1418
+ Object.defineProperty(Midy, "controllerDestinationSettings", {
1419
+ enumerable: true,
1420
+ configurable: true,
1421
+ writable: true,
1422
+ value: {
1423
+ pitchControl: 0,
1424
+ filterCutoffControl: 0,
1425
+ amplitudeControl: 1,
1426
+ lfoPitchDepth: 0,
1427
+ lfoFilterDepth: 0,
1428
+ lfoAmplitudeDepth: 0,
1429
+ }
1430
+ });