@marmooo/midy 0.1.6 → 0.1.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.
Files changed (37) hide show
  1. package/esm/midy-GM1.d.ts +5 -4
  2. package/esm/midy-GM1.d.ts.map +1 -1
  3. package/esm/midy-GM1.js +45 -16
  4. package/esm/midy-GM2.d.ts +5 -2
  5. package/esm/midy-GM2.d.ts.map +1 -1
  6. package/esm/midy-GM2.js +176 -66
  7. package/esm/midy-GMLite.d.ts +5 -4
  8. package/esm/midy-GMLite.d.ts.map +1 -1
  9. package/esm/midy-GMLite.js +45 -16
  10. package/esm/midy.d.ts +5 -2
  11. package/esm/midy.d.ts.map +1 -1
  12. package/esm/midy.js +177 -67
  13. package/package.json +5 -1
  14. package/script/midy-GM1.d.ts +5 -4
  15. package/script/midy-GM1.d.ts.map +1 -1
  16. package/script/midy-GM1.js +48 -19
  17. package/script/midy-GM2.d.ts +5 -2
  18. package/script/midy-GM2.d.ts.map +1 -1
  19. package/script/midy-GM2.js +179 -69
  20. package/script/midy-GMLite.d.ts +5 -4
  21. package/script/midy-GMLite.d.ts.map +1 -1
  22. package/script/midy-GMLite.js +48 -19
  23. package/script/midy.d.ts +5 -2
  24. package/script/midy.d.ts.map +1 -1
  25. package/script/midy.js +180 -70
  26. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts +0 -149
  27. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +0 -1
  28. package/esm/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +0 -180
  29. package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts +0 -84
  30. package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts.map +0 -1
  31. package/esm/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js +0 -216
  32. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts +0 -149
  33. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.d.ts.map +0 -1
  34. package/script/deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js +0 -190
  35. package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts +0 -84
  36. package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.d.ts.map +0 -1
  37. package/script/deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js +0 -221
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MidyGM2 = void 0;
4
- const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
5
- const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js");
4
+ const midi_file_1 = require("midi-file");
5
+ const soundfont_parser_1 = require("@marmooo/soundfont-parser");
6
6
  class Note {
7
7
  constructor(noteNumber, velocity, startTime, instrumentKey) {
8
8
  Object.defineProperty(this, "bufferSource", {
@@ -53,6 +53,18 @@ class Note {
53
53
  writable: true,
54
54
  value: void 0
55
55
  });
56
+ Object.defineProperty(this, "reverbEffectsSend", {
57
+ enumerable: true,
58
+ configurable: true,
59
+ writable: true,
60
+ value: void 0
61
+ });
62
+ Object.defineProperty(this, "chorusEffectsSend", {
63
+ enumerable: true,
64
+ configurable: true,
65
+ writable: true,
66
+ value: void 0
67
+ });
56
68
  this.noteNumber = noteNumber;
57
69
  this.velocity = velocity;
58
70
  this.startTime = startTime;
@@ -208,6 +220,12 @@ class MidyGM2 {
208
220
  writable: true,
209
221
  value: []
210
222
  });
223
+ Object.defineProperty(this, "exclusiveClassMap", {
224
+ enumerable: true,
225
+ configurable: true,
226
+ writable: true,
227
+ value: new Map()
228
+ });
211
229
  Object.defineProperty(this, "defaultOptions", {
212
230
  enumerable: true,
213
231
  configurable: true,
@@ -264,14 +282,14 @@ class MidyGM2 {
264
282
  async loadSoundFont(soundFontUrl) {
265
283
  const response = await fetch(soundFontUrl);
266
284
  const arrayBuffer = await response.arrayBuffer();
267
- const parsed = (0, _esm_js_2.parse)(new Uint8Array(arrayBuffer));
268
- const soundFont = new _esm_js_2.SoundFont(parsed);
285
+ const parsed = (0, soundfont_parser_1.parse)(new Uint8Array(arrayBuffer));
286
+ const soundFont = new soundfont_parser_1.SoundFont(parsed);
269
287
  this.addSoundFont(soundFont);
270
288
  }
271
289
  async loadMIDI(midiUrl) {
272
290
  const response = await fetch(midiUrl);
273
291
  const arrayBuffer = await response.arrayBuffer();
274
- const midi = (0, _esm_js_1.parseMidi)(new Uint8Array(arrayBuffer));
292
+ const midi = (0, midi_file_1.parseMidi)(new Uint8Array(arrayBuffer));
275
293
  this.ticksPerBeat = midi.header.ticksPerBeat;
276
294
  const midiData = this.extractMidiData(midi);
277
295
  this.instruments = midiData.instruments;
@@ -311,19 +329,25 @@ class MidyGM2 {
311
329
  const sampleStart = instrumentKey.start;
312
330
  const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
313
331
  if (isSF3) {
314
- const sample = instrumentKey.sample.slice(sampleStart, sampleEnd);
315
- const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
332
+ const sample = instrumentKey.sample;
333
+ const start = sample.byteOffset + sampleStart;
334
+ const end = sample.byteOffset + sampleEnd;
335
+ const buffer = sample.buffer.slice(start, end);
336
+ const audioBuffer = await this.audioContext.decodeAudioData(buffer);
316
337
  return audioBuffer;
317
338
  }
318
339
  else {
319
- const sample = instrumentKey.sample.subarray(sampleStart, sampleEnd);
340
+ const sample = instrumentKey.sample;
341
+ const start = sample.byteOffset + sampleStart;
342
+ const end = sample.byteOffset + sampleEnd;
343
+ const buffer = sample.buffer.slice(start, end);
320
344
  const audioBuffer = new AudioBuffer({
321
345
  numberOfChannels: 1,
322
346
  length: sample.length,
323
347
  sampleRate: instrumentKey.sampleRate,
324
348
  });
325
349
  const channelData = audioBuffer.getChannelData(0);
326
- const int16Array = new Int16Array(sample.buffer);
350
+ const int16Array = new Int16Array(buffer);
327
351
  for (let i = 0; i < int16Array.length; i++) {
328
352
  channelData[i] = int16Array[i] / 32768;
329
353
  }
@@ -421,6 +445,7 @@ class MidyGM2 {
421
445
  if (queueIndex >= this.timeline.length) {
422
446
  await Promise.all(this.notePromises);
423
447
  this.notePromises = [];
448
+ this.exclusiveClassMap.clear();
424
449
  resolve();
425
450
  return;
426
451
  }
@@ -436,6 +461,7 @@ class MidyGM2 {
436
461
  }
437
462
  else if (this.isStopping) {
438
463
  await this.stopNotes(0, true);
464
+ this.exclusiveClassMap.clear();
439
465
  this.notePromises = [];
440
466
  resolve();
441
467
  this.isStopping = false;
@@ -444,6 +470,7 @@ class MidyGM2 {
444
470
  }
445
471
  else if (this.isSeeking) {
446
472
  this.stopNotes(0, true);
473
+ this.exclusiveClassMap.clear();
447
474
  this.startTime = this.audioContext.currentTime;
448
475
  queueIndex = this.getQueueIndex(this.resumeTime);
449
476
  offset = this.resumeTime - this.startTime;
@@ -672,14 +699,14 @@ class MidyGM2 {
672
699
  return impulse;
673
700
  }
674
701
  createConvolutionReverb(audioContext, impulse) {
675
- const output = new GainNode(audioContext);
702
+ const input = new GainNode(audioContext);
676
703
  const convolverNode = new ConvolverNode(audioContext, {
677
704
  buffer: impulse,
678
705
  });
679
- convolverNode.connect(output);
706
+ input.connect(convolverNode);
680
707
  return {
681
- input: convolverNode,
682
- output,
708
+ input,
709
+ output: convolverNode,
683
710
  convolverNode,
684
711
  };
685
712
  }
@@ -721,7 +748,6 @@ class MidyGM2 {
721
748
  // M.R.Schroeder, "Natural Sounding Artificial Reverberation", J.Audio Eng. Soc., vol.10, p.219, 1962
722
749
  createSchroederReverb(audioContext, combFeedbacks, combDelays, allpassFeedbacks, allpassDelays) {
723
750
  const input = new GainNode(audioContext);
724
- const output = new GainNode(audioContext);
725
751
  const mergerGain = new GainNode(audioContext);
726
752
  for (let i = 0; i < combDelays.length; i++) {
727
753
  const comb = this.createCombFilter(audioContext, input, combDelays[i], combFeedbacks[i]);
@@ -732,7 +758,7 @@ class MidyGM2 {
732
758
  const allpass = this.createAllpassFilter(audioContext, (i === 0) ? mergerGain : allpasses.at(-1), allpassDelays[i], allpassFeedbacks[i]);
733
759
  allpasses.push(allpass);
734
760
  }
735
- allpasses.at(-1).connect(output);
761
+ const output = allpasses.at(-1);
736
762
  return { input, output };
737
763
  }
738
764
  createChorusEffect(audioContext) {
@@ -963,6 +989,20 @@ class MidyGM2 {
963
989
  }
964
990
  note.bufferSource.connect(note.filterNode);
965
991
  note.filterNode.connect(note.volumeNode);
992
+ if (0 < channel.reverbSendLevel && 0 < instrumentKey.reverbEffectsSend) {
993
+ note.reverbEffectsSend = new GainNode(this.audioContext, {
994
+ gain: instrumentKey.reverbEffectsSend,
995
+ });
996
+ note.volumeNode.connect(note.reverbEffectsSend);
997
+ note.reverbEffectsSend.connect(this.reverbEffect.input);
998
+ }
999
+ if (0 < channel.chorusSendLevel && 0 < instrumentKey.chorusEffectsSend) {
1000
+ note.chorusEffectsSend = new GainNode(this.audioContext, {
1001
+ gain: instrumentKey.chorusEffectsSend,
1002
+ });
1003
+ note.volumeNode.connect(note.chorusEffectsSend);
1004
+ note.chorusEffectsSend.connect(this.chorusEffect.input);
1005
+ }
966
1006
  note.bufferSource.start(startTime);
967
1007
  return note;
968
1008
  }
@@ -992,6 +1032,19 @@ class MidyGM2 {
992
1032
  if (channel.sostenutoPedal) {
993
1033
  channel.sostenutoNotes.set(noteNumber, note);
994
1034
  }
1035
+ const exclusiveClass = instrumentKey.exclusiveClass;
1036
+ if (exclusiveClass !== 0) {
1037
+ if (this.exclusiveClassMap.has(exclusiveClass)) {
1038
+ const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
1039
+ const [prevNote, prevChannelNumber] = prevEntry;
1040
+ if (!prevNote.ending) {
1041
+ this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
1042
+ startTime, undefined, // portamentoNoteNumber
1043
+ true);
1044
+ }
1045
+ }
1046
+ this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
1047
+ }
995
1048
  const scheduledNotes = channel.scheduledNotes;
996
1049
  if (scheduledNotes.has(noteNumber)) {
997
1050
  scheduledNotes.get(noteNumber).push(note);
@@ -1004,15 +1057,15 @@ class MidyGM2 {
1004
1057
  const now = this.audioContext.currentTime;
1005
1058
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now, portamento);
1006
1059
  }
1007
- stopNote(stopTime, endTime, scheduledNotes, index) {
1060
+ stopNote(endTime, stopTime, scheduledNotes, index) {
1008
1061
  const note = scheduledNotes[index];
1009
1062
  note.volumeNode.gain
1010
- .cancelScheduledValues(stopTime)
1011
- .linearRampToValueAtTime(0, endTime);
1063
+ .cancelScheduledValues(endTime)
1064
+ .linearRampToValueAtTime(0, stopTime);
1012
1065
  note.ending = true;
1013
1066
  this.scheduleTask(() => {
1014
1067
  note.bufferSource.loop = false;
1015
- }, endTime);
1068
+ }, stopTime);
1016
1069
  return new Promise((resolve) => {
1017
1070
  note.bufferSource.onended = () => {
1018
1071
  scheduledNotes[index] = null;
@@ -1028,12 +1081,18 @@ class MidyGM2 {
1028
1081
  note.vibratoDepth.disconnect();
1029
1082
  note.vibratoLFO.stop();
1030
1083
  }
1084
+ if (note.reverbEffectsSend) {
1085
+ note.reverbEffectsSend.disconnect();
1086
+ }
1087
+ if (note.chorusEffectsSend) {
1088
+ note.chorusEffectsSend.disconnect();
1089
+ }
1031
1090
  resolve();
1032
1091
  };
1033
- note.bufferSource.stop(endTime);
1092
+ note.bufferSource.stop(stopTime);
1034
1093
  });
1035
1094
  }
1036
- scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, portamentoNoteNumber, force) {
1095
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, portamentoNoteNumber, force) {
1037
1096
  const channel = this.channels[channelNumber];
1038
1097
  if (!force) {
1039
1098
  if (channel.sustainPedal)
@@ -1051,21 +1110,22 @@ class MidyGM2 {
1051
1110
  if (note.ending)
1052
1111
  continue;
1053
1112
  if (portamentoNoteNumber === undefined) {
1054
- const volEndTime = stopTime + note.instrumentKey.volRelease;
1055
- const modRelease = stopTime + note.instrumentKey.modRelease;
1113
+ const volRelease = endTime + note.instrumentKey.volRelease;
1114
+ const modRelease = endTime + note.instrumentKey.modRelease;
1056
1115
  note.filterNode.frequency
1057
- .cancelScheduledValues(stopTime)
1116
+ .cancelScheduledValues(endTime)
1058
1117
  .linearRampToValueAtTime(0, modRelease);
1059
- return this.stopNote(stopTime, volEndTime, scheduledNotes, i);
1118
+ const stopTime = Math.min(volRelease, modRelease);
1119
+ return this.stopNote(endTime, stopTime, scheduledNotes, i);
1060
1120
  }
1061
1121
  else {
1062
- const portamentoTime = stopTime + channel.portamentoTime;
1122
+ const portamentoTime = endTime + channel.portamentoTime;
1063
1123
  const detuneChange = (portamentoNoteNumber - noteNumber) * 100;
1064
1124
  const detune = note.bufferSource.detune.value + detuneChange;
1065
1125
  note.bufferSource.detune
1066
- .cancelScheduledValues(stopTime)
1126
+ .cancelScheduledValues(endTime)
1067
1127
  .linearRampToValueAtTime(detune, portamentoTime);
1068
- return this.stopNote(stopTime, portamentoTime, scheduledNotes, i);
1128
+ return this.stopNote(endTime, portamentoTime, scheduledNotes, i);
1069
1129
  }
1070
1130
  }
1071
1131
  }
@@ -1280,20 +1340,44 @@ class MidyGM2 {
1280
1340
  if (0 < reverbSendLevel) {
1281
1341
  const now = this.audioContext.currentTime;
1282
1342
  channel.reverbSendLevel = reverbSendLevel / 127;
1283
- reverbEffect.output.gain.cancelScheduledValues(now);
1284
- reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1343
+ reverbEffect.input.gain.cancelScheduledValues(now);
1344
+ reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
1285
1345
  }
1286
1346
  else {
1287
- channel.merger.disconnect(reverbEffect.input);
1347
+ channel.scheduledNotes.forEach((noteList) => {
1348
+ for (let i = 0; i < noteList.length; i++) {
1349
+ const note = noteList[i];
1350
+ if (!note)
1351
+ continue;
1352
+ if (note.instrumentKey.reverbEffectsSend <= 0)
1353
+ continue;
1354
+ note.reverbEffectsSend.disconnect();
1355
+ }
1356
+ });
1288
1357
  }
1289
1358
  }
1290
1359
  else {
1291
1360
  if (0 < reverbSendLevel) {
1292
- channel.merger.connect(reverbEffect.input);
1293
1361
  const now = this.audioContext.currentTime;
1362
+ channel.scheduledNotes.forEach((noteList) => {
1363
+ for (let i = 0; i < noteList.length; i++) {
1364
+ const note = noteList[i];
1365
+ if (!note)
1366
+ continue;
1367
+ if (note.instrumentKey.reverbEffectsSend <= 0)
1368
+ continue;
1369
+ if (!note.reverbEffectsSend) {
1370
+ note.reverbEffectsSend = new GainNode(this.audioContext, {
1371
+ gain: note.instrumentKey.reverbEffectsSend,
1372
+ });
1373
+ note.volumeNode.connect(note.reverbEffectsSend);
1374
+ }
1375
+ note.reverbEffectsSend.connect(reverbEffect.input);
1376
+ }
1377
+ });
1294
1378
  channel.reverbSendLevel = reverbSendLevel / 127;
1295
- reverbEffect.output.gain.cancelScheduledValues(now);
1296
- reverbEffect.output.gain.setValueAtTime(channel.reverbSendLevel, now);
1379
+ reverbEffect.input.gain.cancelScheduledValues(now);
1380
+ reverbEffect.input.gain.setValueAtTime(channel.reverbSendLevel, now);
1297
1381
  }
1298
1382
  }
1299
1383
  }
@@ -1304,20 +1388,44 @@ class MidyGM2 {
1304
1388
  if (0 < chorusSendLevel) {
1305
1389
  const now = this.audioContext.currentTime;
1306
1390
  channel.chorusSendLevel = chorusSendLevel / 127;
1307
- chorusEffect.output.gain.cancelScheduledValues(now);
1308
- chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
1391
+ chorusEffect.input.gain.cancelScheduledValues(now);
1392
+ chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
1309
1393
  }
1310
1394
  else {
1311
- channel.merger.disconnect(chorusEffect.input);
1395
+ channel.scheduledNotes.forEach((noteList) => {
1396
+ for (let i = 0; i < noteList.length; i++) {
1397
+ const note = noteList[i];
1398
+ if (!note)
1399
+ continue;
1400
+ if (note.instrumentKey.chorusEffectsSend <= 0)
1401
+ continue;
1402
+ note.chorusEffectsSend.disconnect();
1403
+ }
1404
+ });
1312
1405
  }
1313
1406
  }
1314
1407
  else {
1315
1408
  if (0 < chorusSendLevel) {
1316
- channel.merger.connect(chorusEffect.input);
1317
1409
  const now = this.audioContext.currentTime;
1410
+ channel.scheduledNotes.forEach((noteList) => {
1411
+ for (let i = 0; i < noteList.length; i++) {
1412
+ const note = noteList[i];
1413
+ if (!note)
1414
+ continue;
1415
+ if (note.instrumentKey.chorusEffectsSend <= 0)
1416
+ continue;
1417
+ if (!note.chorusEffectsSend) {
1418
+ note.chorusEffectsSend = new GainNode(this.audioContext, {
1419
+ gain: note.instrumentKey.chorusEffectsSend,
1420
+ });
1421
+ note.volumeNode.connect(note.chorusEffectsSend);
1422
+ }
1423
+ note.chorusEffectsSend.connect(chorusEffect.input);
1424
+ }
1425
+ });
1318
1426
  channel.chorusSendLevel = chorusSendLevel / 127;
1319
- chorusEffect.output.gain.cancelScheduledValues(now);
1320
- chorusEffect.output.gain.setValueAtTime(channel.chorusSendLevel, now);
1427
+ chorusEffect.input.gain.cancelScheduledValues(now);
1428
+ chorusEffect.input.gain.setValueAtTime(channel.chorusSendLevel, now);
1321
1429
  }
1322
1430
  }
1323
1431
  }
@@ -1659,10 +1767,8 @@ class MidyGM2 {
1659
1767
  }
1660
1768
  setReverbTime(value) {
1661
1769
  this.reverb.time = this.getReverbTime(value);
1662
- const { audioContext, channels, options } = this;
1663
- for (let i = 0; i < channels.length; i++) {
1664
- channels[i].reverbEffect = options.reverbAlgorithm(audioContext);
1665
- }
1770
+ const { audioContext, options } = this;
1771
+ this.reverbEffect = options.reverbAlgorithm(audioContext);
1666
1772
  }
1667
1773
  getReverbTime(value) {
1668
1774
  return Math.pow(Math.E, (value - 40) * 0.025);
@@ -1739,10 +1845,7 @@ class MidyGM2 {
1739
1845
  const now = this.audioContext.currentTime;
1740
1846
  const modRate = this.getChorusModRate(value);
1741
1847
  this.chorus.modRate = modRate;
1742
- for (let i = 0; i < this.channels.length; i++) {
1743
- const lfo = this.channels[i].chorusEffect.lfo;
1744
- lfo.frequency.setValueAtTime(modRate, now);
1745
- }
1848
+ this.chorusEffect.lfo.frequency.setValueAtTime(modRate, now);
1746
1849
  }
1747
1850
  getChorusModRate(value) {
1748
1851
  return value * 0.122; // Hz
@@ -1751,12 +1854,9 @@ class MidyGM2 {
1751
1854
  const now = this.audioContext.currentTime;
1752
1855
  const modDepth = this.getChorusModDepth(value);
1753
1856
  this.chorus.modDepth = modDepth;
1754
- for (let i = 0; i < this.channels.length; i++) {
1755
- const chorusEffect = this.channels[i].chorusEffect;
1756
- chorusEffect.lfoGain.gain
1757
- .cancelScheduledValues(now)
1758
- .setValueAtTime(modDepth / 2, now);
1759
- }
1857
+ this.chorusEffect.lfoGain.gain
1858
+ .cancelScheduledValues(now)
1859
+ .setValueAtTime(modDepth / 2, now);
1760
1860
  }
1761
1861
  getChorusModDepth(value) {
1762
1862
  return (value + 1) / 3200; // second
@@ -1765,14 +1865,11 @@ class MidyGM2 {
1765
1865
  const now = this.audioContext.currentTime;
1766
1866
  const feedback = this.getChorusFeedback(value);
1767
1867
  this.chorus.feedback = feedback;
1768
- for (let i = 0; i < this.channels.length; i++) {
1769
- const chorusEffect = this.channels[i].chorusEffect;
1770
- for (let j = 0; j < chorusEffect.feedbackGains.length; j++) {
1771
- const feedbackGain = chorusEffect.feedbackGains[j];
1772
- feedbackGain.gain
1773
- .cancelScheduledValues(now)
1774
- .setValueAtTime(feedback, now);
1775
- }
1868
+ const chorusEffect = this.chorusEffect;
1869
+ for (let i = 0; i < chorusEffect.feedbackGains.length; i++) {
1870
+ chorusEffect.feedbackGains[i].gain
1871
+ .cancelScheduledValues(now)
1872
+ .setValueAtTime(feedback, now);
1776
1873
  }
1777
1874
  }
1778
1875
  getChorusFeedback(value) {
@@ -1780,15 +1877,28 @@ class MidyGM2 {
1780
1877
  }
1781
1878
  setChorusSendToReverb(value) {
1782
1879
  const sendToReverb = this.getChorusSendToReverb(value);
1783
- if (0 < sendToReverb) {
1784
- const now = this.audioContext.currentTime;
1880
+ const sendGain = this.chorusEffect.sendGain;
1881
+ if (0 < this.chorus.sendToReverb) {
1785
1882
  this.chorus.sendToReverb = sendToReverb;
1786
- this.chorusEffect.sendGain.gain
1787
- .cancelScheduledValues(now)
1788
- .setValueAtTime(sendToReverb, now);
1883
+ if (0 < sendToReverb) {
1884
+ const now = this.audioContext.currentTime;
1885
+ sendGain.gain
1886
+ .cancelScheduledValues(now)
1887
+ .setValueAtTime(sendToReverb, now);
1888
+ }
1889
+ else {
1890
+ sendGain.disconnect();
1891
+ }
1789
1892
  }
1790
- else if (this.chorus.sendToReverb !== 0) {
1791
- this.chorusEffect.sendGain.disconnect(this.reverbEffect.input);
1893
+ else {
1894
+ this.chorus.sendToReverb = sendToReverb;
1895
+ if (0 < sendToReverb) {
1896
+ const now = this.audioContext.currentTime;
1897
+ sendGain.connect(this.reverbEffect.input);
1898
+ sendGain.gain
1899
+ .cancelScheduledValues(now)
1900
+ .setValueAtTime(sendToReverb, now);
1901
+ }
1792
1902
  }
1793
1903
  }
1794
1904
  getChorusSendToReverb(value) {
@@ -35,6 +35,7 @@ export class MidyGMLite {
35
35
  timeline: any[];
36
36
  instruments: any[];
37
37
  notePromises: any[];
38
+ exclusiveClassMap: Map<any, any>;
38
39
  audioContext: any;
39
40
  masterGain: any;
40
41
  controlChangeHandlers: {
@@ -96,11 +97,11 @@ export class MidyGMLite {
96
97
  createNote(channel: any, instrumentKey: any, noteNumber: any, velocity: any, startTime: any, isSF3: any): Promise<Note>;
97
98
  scheduleNoteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
98
99
  noteOn(channelNumber: any, noteNumber: any, velocity: any): Promise<void>;
99
- stopNote(stopTime: any, endTime: any, scheduledNotes: any, index: any): Promise<any>;
100
- scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, stopTime: any, force: any): void;
101
- releaseNote(channelNumber: any, noteNumber: any, velocity: any): void;
100
+ stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
101
+ scheduleNoteRelease(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
102
+ releaseNote(channelNumber: any, noteNumber: any, velocity: any): Promise<any> | undefined;
102
103
  releaseSustainPedal(channelNumber: any, halfVelocity: any): any[];
103
- handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<void>;
104
+ handleMIDIMessage(statusByte: any, data1: any, data2: any): void | Promise<any>;
104
105
  handleProgramChange(channelNumber: any, program: any): void;
105
106
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
106
107
  setPitchBend(channelNumber: any, pitchBend: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAsBA;IAmBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAOC;IA7CD,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;IAC5C;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DAuBC;IAED,mEAWC;IAED,2EA+CC;IAED,mCAOC;IAED,0BA+CC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,mCAeC;IAED,+CAwBC;IAED,6CAIC;IAED,mCAsBC;IAED,+DA0BC;IAED,wHAgCC;IAED,kGA8BC;IAED,0EAGC;IAED,qFA4BC;IAED,0GAsBC;IAED,sEAGC;IAED,kEAeC;IAED,iFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED;;;;;;;;;;;;;MAeC;IAED,2EASC;IAED,qCAkBC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAUC;IAED,sDAMC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBASC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAj/BD;IAQE,gFAKC;IAZD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
1
+ {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAmBA;IAoBE;;;;;;;;;MASE;IAEF;;;;;;;MAOE;IAEF,+BAOC;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;IAClB,iCAA8B;IAuB5B,kBAAgC;IAChC,gBAA4C;IAC5C;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,+DA2BC;IAED,mEAWC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,4BAEC;IAED,yCAEC;IAED,mFAGC;IAED,mCAeC;IAED,+CAwBC;IAED,6CAIC;IAED,mCAsBC;IAED,+DA0BC;IAED,wHAgCC;IAED,kGAgDC;IAED,0EAGC;IAED,qFA4BC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,uDAOC;IAED;;;;;;;;;;;;;MAeC;IAED,2EASC;IAED,qCAkBC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAUC;IAED,sDAMC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,oDAaC;IAED,kDAKC;IAED,iEAOC;IAED,+CAEC;IAED,8CAEC;IAED,+CAEC;IAED,4DAgBC;IAED,oBASC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AA5gCD;IAQE,gFAKC;IAZD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,mBAAkC;CAErC"}
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MidyGMLite = void 0;
4
- const _esm_js_1 = require("./deps/cdn.jsdelivr.net/npm/midi-file@1.2.4/+esm.js");
5
- const _esm_js_2 = require("./deps/cdn.jsdelivr.net/npm/@marmooo/soundfont-parser@0.0.6/+esm.js");
4
+ const midi_file_1 = require("midi-file");
5
+ const soundfont_parser_1 = require("@marmooo/soundfont-parser");
6
6
  class Note {
7
7
  constructor(noteNumber, velocity, startTime, instrumentKey) {
8
8
  Object.defineProperty(this, "bufferSource", {
@@ -151,6 +151,12 @@ class MidyGMLite {
151
151
  writable: true,
152
152
  value: []
153
153
  });
154
+ Object.defineProperty(this, "exclusiveClassMap", {
155
+ enumerable: true,
156
+ configurable: true,
157
+ writable: true,
158
+ value: new Map()
159
+ });
154
160
  this.audioContext = audioContext;
155
161
  this.masterGain = new GainNode(audioContext);
156
162
  this.controlChangeHandlers = this.createControlChangeHandlers();
@@ -180,14 +186,14 @@ class MidyGMLite {
180
186
  async loadSoundFont(soundFontUrl) {
181
187
  const response = await fetch(soundFontUrl);
182
188
  const arrayBuffer = await response.arrayBuffer();
183
- const parsed = (0, _esm_js_2.parse)(new Uint8Array(arrayBuffer));
184
- const soundFont = new _esm_js_2.SoundFont(parsed);
189
+ const parsed = (0, soundfont_parser_1.parse)(new Uint8Array(arrayBuffer));
190
+ const soundFont = new soundfont_parser_1.SoundFont(parsed);
185
191
  this.addSoundFont(soundFont);
186
192
  }
187
193
  async loadMIDI(midiUrl) {
188
194
  const response = await fetch(midiUrl);
189
195
  const arrayBuffer = await response.arrayBuffer();
190
- const midi = (0, _esm_js_1.parseMidi)(new Uint8Array(arrayBuffer));
196
+ const midi = (0, midi_file_1.parseMidi)(new Uint8Array(arrayBuffer));
191
197
  this.ticksPerBeat = midi.header.ticksPerBeat;
192
198
  const midiData = this.extractMidiData(midi);
193
199
  this.instruments = midiData.instruments;
@@ -223,19 +229,25 @@ class MidyGMLite {
223
229
  const sampleStart = instrumentKey.start;
224
230
  const sampleEnd = instrumentKey.sample.length + instrumentKey.end;
225
231
  if (isSF3) {
226
- const sample = instrumentKey.sample.slice(sampleStart, sampleEnd);
227
- const audioBuffer = await this.audioContext.decodeAudioData(sample.buffer);
232
+ const sample = instrumentKey.sample;
233
+ const start = sample.byteOffset + sampleStart;
234
+ const end = sample.byteOffset + sampleEnd;
235
+ const buffer = sample.buffer.slice(start, end);
236
+ const audioBuffer = await this.audioContext.decodeAudioData(buffer);
228
237
  return audioBuffer;
229
238
  }
230
239
  else {
231
- const sample = instrumentKey.sample.subarray(sampleStart, sampleEnd);
240
+ const sample = instrumentKey.sample;
241
+ const start = sample.byteOffset + sampleStart;
242
+ const end = sample.byteOffset + sampleEnd;
243
+ const buffer = sample.buffer.slice(start, end);
232
244
  const audioBuffer = new AudioBuffer({
233
245
  numberOfChannels: 1,
234
246
  length: sample.length,
235
247
  sampleRate: instrumentKey.sampleRate,
236
248
  });
237
249
  const channelData = audioBuffer.getChannelData(0);
238
- const int16Array = new Int16Array(sample.buffer);
250
+ const int16Array = new Int16Array(buffer);
239
251
  for (let i = 0; i < int16Array.length; i++) {
240
252
  channelData[i] = int16Array[i] / 32768;
241
253
  }
@@ -309,6 +321,7 @@ class MidyGMLite {
309
321
  if (queueIndex >= this.timeline.length) {
310
322
  await Promise.all(this.notePromises);
311
323
  this.notePromises = [];
324
+ this.exclusiveClassMap.clear();
312
325
  resolve();
313
326
  return;
314
327
  }
@@ -324,6 +337,7 @@ class MidyGMLite {
324
337
  }
325
338
  else if (this.isStopping) {
326
339
  await this.stopNotes(0, true);
340
+ this.exclusiveClassMap.clear();
327
341
  this.notePromises = [];
328
342
  resolve();
329
343
  this.isStopping = false;
@@ -332,6 +346,7 @@ class MidyGMLite {
332
346
  }
333
347
  else if (this.isSeeking) {
334
348
  this.stopNotes(0, true);
349
+ this.exclusiveClassMap.clear();
335
350
  this.startTime = this.audioContext.currentTime;
336
351
  queueIndex = this.getQueueIndex(this.resumeTime);
337
352
  offset = this.resumeTime - this.startTime;
@@ -637,6 +652,19 @@ class MidyGMLite {
637
652
  const note = await this.createNote(channel, instrumentKey, noteNumber, velocity, startTime, isSF3);
638
653
  note.volumeNode.connect(channel.gainL);
639
654
  note.volumeNode.connect(channel.gainR);
655
+ const exclusiveClass = instrumentKey.exclusiveClass;
656
+ if (exclusiveClass !== 0) {
657
+ if (this.exclusiveClassMap.has(exclusiveClass)) {
658
+ const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
659
+ const [prevNote, prevChannelNumber] = prevEntry;
660
+ if (!prevNote.ending) {
661
+ this.scheduleNoteRelease(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
662
+ startTime, undefined, // portamentoNoteNumber
663
+ true);
664
+ }
665
+ }
666
+ this.exclusiveClassMap.set(exclusiveClass, [note, channelNumber]);
667
+ }
640
668
  const scheduledNotes = channel.scheduledNotes;
641
669
  if (scheduledNotes.has(noteNumber)) {
642
670
  scheduledNotes.get(noteNumber).push(note);
@@ -649,15 +677,15 @@ class MidyGMLite {
649
677
  const now = this.audioContext.currentTime;
650
678
  return this.scheduleNoteOn(channelNumber, noteNumber, velocity, now);
651
679
  }
652
- stopNote(stopTime, endTime, scheduledNotes, index) {
680
+ stopNote(endTime, stopTime, scheduledNotes, index) {
653
681
  const note = scheduledNotes[index];
654
682
  note.volumeNode.gain
655
- .cancelScheduledValues(stopTime)
656
- .linearRampToValueAtTime(0, endTime);
683
+ .cancelScheduledValues(endTime)
684
+ .linearRampToValueAtTime(0, stopTime);
657
685
  note.ending = true;
658
686
  this.scheduleTask(() => {
659
687
  note.bufferSource.loop = false;
660
- }, endTime);
688
+ }, stopTime);
661
689
  return new Promise((resolve) => {
662
690
  note.bufferSource.onended = () => {
663
691
  scheduledNotes[index] = null;
@@ -675,10 +703,10 @@ class MidyGMLite {
675
703
  }
676
704
  resolve();
677
705
  };
678
- note.bufferSource.stop(endTime);
706
+ note.bufferSource.stop(stopTime);
679
707
  });
680
708
  }
681
- scheduleNoteRelease(channelNumber, noteNumber, _velocity, stopTime, force) {
709
+ scheduleNoteRelease(channelNumber, noteNumber, _velocity, endTime, force) {
682
710
  const channel = this.channels[channelNumber];
683
711
  if (!force && channel.sustainPedal)
684
712
  return;
@@ -691,12 +719,13 @@ class MidyGMLite {
691
719
  continue;
692
720
  if (note.ending)
693
721
  continue;
694
- const volEndTime = stopTime + note.instrumentKey.volRelease;
695
- const modRelease = stopTime + note.instrumentKey.modRelease;
722
+ const volRelease = endTime + note.instrumentKey.volRelease;
723
+ const modRelease = endTime + note.instrumentKey.modRelease;
696
724
  note.filterNode.frequency
697
- .cancelScheduledValues(stopTime)
725
+ .cancelScheduledValues(endTime)
698
726
  .linearRampToValueAtTime(0, modRelease);
699
- this.stopNote(stopTime, volEndTime, scheduledNotes, i);
727
+ const stopTime = Math.min(volRelease, modRelease);
728
+ return this.stopNote(endTime, stopTime, scheduledNotes, i);
700
729
  }
701
730
  }
702
731
  releaseNote(channelNumber, noteNumber, velocity) {