@marmooo/midy 0.3.5 → 0.3.6

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.
@@ -71,13 +71,13 @@ class Note {
71
71
  writable: true,
72
72
  value: void 0
73
73
  });
74
- Object.defineProperty(this, "reverbEffectsSend", {
74
+ Object.defineProperty(this, "reverbSend", {
75
75
  enumerable: true,
76
76
  configurable: true,
77
77
  writable: true,
78
78
  value: void 0
79
79
  });
80
- Object.defineProperty(this, "chorusEffectsSend", {
80
+ Object.defineProperty(this, "chorusSend", {
81
81
  enumerable: true,
82
82
  configurable: true,
83
83
  writable: true,
@@ -365,17 +365,17 @@ class MidyGM2 {
365
365
  writable: true,
366
366
  value: []
367
367
  });
368
- Object.defineProperty(this, "instruments", {
368
+ Object.defineProperty(this, "notePromises", {
369
369
  enumerable: true,
370
370
  configurable: true,
371
371
  writable: true,
372
372
  value: []
373
373
  });
374
- Object.defineProperty(this, "notePromises", {
374
+ Object.defineProperty(this, "instruments", {
375
375
  enumerable: true,
376
376
  configurable: true,
377
377
  writable: true,
378
- value: []
378
+ value: new Set()
379
379
  });
380
380
  Object.defineProperty(this, "exclusiveClassNotes", {
381
381
  enumerable: true,
@@ -507,7 +507,7 @@ class MidyGM2 {
507
507
  const soundFont = this.soundFonts[soundFontIndex];
508
508
  const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
509
509
  const { instrument, sampleID } = voice.generators;
510
- return `${soundFontIndex}:${instrument}:${sampleID}`;
510
+ return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
511
511
  }
512
512
  createChannelAudioNodes(audioContext) {
513
513
  const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
@@ -907,13 +907,11 @@ class MidyGM2 {
907
907
  return impulse;
908
908
  }
909
909
  createConvolutionReverb(audioContext, impulse) {
910
- const input = new GainNode(audioContext);
911
910
  const convolverNode = new ConvolverNode(audioContext, {
912
911
  buffer: impulse,
913
912
  });
914
- input.connect(convolverNode);
915
913
  return {
916
- input,
914
+ input: convolverNode,
917
915
  output: convolverNode,
918
916
  convolverNode,
919
917
  };
@@ -1343,12 +1341,8 @@ class MidyGM2 {
1343
1341
  }
1344
1342
  note.bufferSource.connect(note.filterNode);
1345
1343
  note.filterNode.connect(note.volumeEnvelopeNode);
1346
- if (0 < state.chorusSendLevel) {
1347
- this.setChorusEffectsSend(channel, note, 0, now);
1348
- }
1349
- if (0 < state.reverbSendLevel) {
1350
- this.setReverbEffectsSend(channel, note, 0, now);
1351
- }
1344
+ this.setChorusSend(channel, note, now);
1345
+ this.setReverbSend(channel, note, now);
1352
1346
  note.bufferSource.start(startTime);
1353
1347
  return note;
1354
1348
  }
@@ -1414,10 +1408,14 @@ class MidyGM2 {
1414
1408
  return;
1415
1409
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
1416
1410
  if (channel.isDrum) {
1417
- const audioContext = this.audioContext;
1418
- const { gainL, gainR } = this.createChannelAudioNodes(audioContext);
1419
- channel.keyBasedGainLs[noteNumber] = gainL;
1420
- channel.keyBasedGainRs[noteNumber] = gainR;
1411
+ const { keyBasedGainLs, keyBasedGainRs } = channel;
1412
+ let gainL = keyBasedGainLs[noteNumber];
1413
+ let gainR = keyBasedGainRs[noteNumber];
1414
+ if (!gainL) {
1415
+ const audioNodes = this.createChannelAudioNodes(this.audioContext);
1416
+ gainL = keyBasedGainLs[noteNumber] = audioNodes.gainL;
1417
+ gainR = keyBasedGainRs[noteNumber] = audioNodes.gainR;
1418
+ }
1421
1419
  note.volumeEnvelopeNode.connect(gainL);
1422
1420
  note.volumeEnvelopeNode.connect(gainR);
1423
1421
  }
@@ -1451,11 +1449,11 @@ class MidyGM2 {
1451
1449
  note.vibratoDepth.disconnect();
1452
1450
  note.vibratoLFO.stop();
1453
1451
  }
1454
- if (note.reverbEffectsSend) {
1455
- note.reverbEffectsSend.disconnect();
1452
+ if (note.reverbSend) {
1453
+ note.reverbSend.disconnect();
1456
1454
  }
1457
- if (note.chorusEffectsSend) {
1458
- note.chorusEffectsSend.disconnect();
1455
+ if (note.chorusSend) {
1456
+ note.chorusSend.disconnect();
1459
1457
  }
1460
1458
  }
1461
1459
  releaseNote(channel, note, endTime) {
@@ -1606,7 +1604,7 @@ class MidyGM2 {
1606
1604
  }
1607
1605
  const table = channel.channelPressureTable;
1608
1606
  this.processActiveNotes(channel, scheduleTime, (note) => {
1609
- this.setControllerParameters(channel, note, table);
1607
+ this.setEffects(channel, note, table);
1610
1608
  });
1611
1609
  this.applyVoiceParams(channel, 13);
1612
1610
  }
@@ -1628,13 +1626,18 @@ class MidyGM2 {
1628
1626
  this.applyVoiceParams(channel, 14, scheduleTime);
1629
1627
  }
1630
1628
  setModLfoToPitch(channel, note, scheduleTime) {
1631
- const modLfoToPitch = note.voiceParams.modLfoToPitch +
1632
- this.getLFOPitchDepth(channel);
1633
- const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1634
- const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1635
- note.modulationDepth.gain
1636
- .cancelScheduledValues(scheduleTime)
1637
- .setValueAtTime(modulationDepth, scheduleTime);
1629
+ if (note.modulationDepth) {
1630
+ const modLfoToPitch = note.voiceParams.modLfoToPitch +
1631
+ this.getLFOPitchDepth(channel, note);
1632
+ const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1633
+ const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1634
+ note.modulationDepth.gain
1635
+ .cancelScheduledValues(scheduleTime)
1636
+ .setValueAtTime(modulationDepth, scheduleTime);
1637
+ }
1638
+ else {
1639
+ this.startModulation(channel, note, scheduleTime);
1640
+ }
1638
1641
  }
1639
1642
  setVibLfoToPitch(channel, note, scheduleTime) {
1640
1643
  const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
@@ -1661,63 +1664,63 @@ class MidyGM2 {
1661
1664
  .cancelScheduledValues(scheduleTime)
1662
1665
  .setValueAtTime(volumeDepth, scheduleTime);
1663
1666
  }
1664
- setReverbEffectsSend(channel, note, prevValue, scheduleTime) {
1665
- let value = note.voiceParams.reverbEffectsSend;
1667
+ setReverbSend(channel, note, scheduleTime) {
1668
+ let value = note.voiceParams.reverbEffectsSend *
1669
+ channel.state.reverbSendLevel;
1666
1670
  if (channel.isDrum) {
1667
1671
  const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 91);
1668
- if (0 <= keyBasedValue) {
1669
- value *= keyBasedValue / 127 / channel.state.reverbSendLevel;
1670
- }
1672
+ if (0 <= keyBasedValue)
1673
+ value = keyBasedValue / 127;
1671
1674
  }
1672
- if (0 < prevValue) {
1675
+ if (!note.reverbSend) {
1673
1676
  if (0 < value) {
1674
- note.reverbEffectsSend.gain
1675
- .cancelScheduledValues(scheduleTime)
1676
- .setValueAtTime(value, scheduleTime);
1677
- }
1678
- else {
1679
- note.reverbEffectsSend.disconnect();
1677
+ note.reverbSend = new GainNode(this.audioContext, { gain: value });
1678
+ note.volumeEnvelopeNode.connect(note.reverbSend);
1679
+ note.reverbSend.connect(this.reverbEffect.input);
1680
1680
  }
1681
1681
  }
1682
1682
  else {
1683
+ note.reverbSend.gain
1684
+ .cancelScheduledValues(scheduleTime)
1685
+ .setValueAtTime(value, scheduleTime);
1683
1686
  if (0 < value) {
1684
- if (!note.reverbEffectsSend) {
1685
- note.reverbEffectsSend = new GainNode(this.audioContext, {
1686
- gain: value,
1687
- });
1688
- note.volumeNode.connect(note.reverbEffectsSend);
1687
+ note.volumeEnvelopeNode.connect(note.reverbSend);
1688
+ }
1689
+ else {
1690
+ try {
1691
+ note.volumeEnvelopeNode.disconnect(note.reverbSend);
1689
1692
  }
1690
- note.reverbEffectsSend.connect(this.reverbEffect.input);
1693
+ catch { /* empty */ }
1691
1694
  }
1692
1695
  }
1693
1696
  }
1694
- setChorusEffectsSend(channel, note, prevValue, scheduleTime) {
1695
- let value = note.voiceParams.chorusEffectsSend;
1697
+ setChorusSend(channel, note, scheduleTime) {
1698
+ let value = note.voiceParams.chorusEffectsSend *
1699
+ channel.state.chorusSendLevel;
1696
1700
  if (channel.isDrum) {
1697
1701
  const keyBasedValue = this.getKeyBasedValue(channel, note.noteNumber, 93);
1698
- if (0 <= keyBasedValue) {
1699
- value *= keyBasedValue / 127 / channel.state.chorusSendLevel;
1700
- }
1702
+ if (0 <= keyBasedValue)
1703
+ value = keyBasedValue / 127;
1701
1704
  }
1702
- if (0 < prevValue) {
1705
+ if (!note.chorusSend) {
1703
1706
  if (0 < value) {
1704
- note.chorusEffectsSend.gain
1705
- .cancelScheduledValues(scheduleTime)
1706
- .setValueAtTime(value, scheduleTime);
1707
- }
1708
- else {
1709
- note.chorusEffectsSend.disconnect();
1707
+ note.chorusSend = new GainNode(this.audioContext, { gain: value });
1708
+ note.volumeEnvelopeNode.connect(note.chorusSend);
1709
+ note.chorusSend.connect(this.chorusEffect.input);
1710
1710
  }
1711
1711
  }
1712
1712
  else {
1713
+ note.chorusSend.gain
1714
+ .cancelScheduledValues(scheduleTime)
1715
+ .setValueAtTime(value, scheduleTime);
1713
1716
  if (0 < value) {
1714
- if (!note.chorusEffectsSend) {
1715
- note.chorusEffectsSend = new GainNode(this.audioContext, {
1716
- gain: value,
1717
- });
1718
- note.volumeEnvelopeNode.connect(note.chorusEffectsSend);
1717
+ note.volumeEnvelopeNode.connect(note.chorusSend);
1718
+ }
1719
+ else {
1720
+ try {
1721
+ note.volumeEnvelopeNode.disconnect(note.chorusSend);
1719
1722
  }
1720
- note.chorusEffectsSend.connect(this.chorusEffect.input);
1723
+ catch { /* empty */ }
1721
1724
  }
1722
1725
  }
1723
1726
  }
@@ -1866,7 +1869,7 @@ class MidyGM2 {
1866
1869
  handler.call(this, channelNumber, value, scheduleTime);
1867
1870
  const channel = this.channels[channelNumber];
1868
1871
  this.applyVoiceParams(channel, controllerType + 128, scheduleTime);
1869
- this.applyControlTable(channel, controllerType, scheduleTime);
1872
+ this.setControlChangeEffects(channel, controllerType, scheduleTime);
1870
1873
  }
1871
1874
  else {
1872
1875
  console.warn(`Unsupported Control change: controllerType=${controllerType} value=${value}`);
@@ -1882,7 +1885,6 @@ class MidyGM2 {
1882
1885
  note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
1883
1886
  }
1884
1887
  else {
1885
- this.setPitchEnvelope(note, scheduleTime);
1886
1888
  this.startModulation(channel, note, scheduleTime);
1887
1889
  }
1888
1890
  });
@@ -1927,8 +1929,14 @@ class MidyGM2 {
1927
1929
  scheduleTime ??= this.audioContext.currentTime;
1928
1930
  const channel = this.channels[channelNumber];
1929
1931
  channel.state.volume = volume / 127;
1930
- this.updateChannelVolume(channel, scheduleTime);
1931
- this.updateKeyBasedVolume(channel, scheduleTime);
1932
+ if (channel.isDrum) {
1933
+ for (let i = 0; i < 128; i++) {
1934
+ this.updateKeyBasedVolume(channel, i, scheduleTime);
1935
+ }
1936
+ }
1937
+ else {
1938
+ this.updateChannelVolume(channel, scheduleTime);
1939
+ }
1932
1940
  }
1933
1941
  panToGain(pan) {
1934
1942
  const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
@@ -1941,8 +1949,14 @@ class MidyGM2 {
1941
1949
  scheduleTime ??= this.audioContext.currentTime;
1942
1950
  const channel = this.channels[channelNumber];
1943
1951
  channel.state.pan = pan / 127;
1944
- this.updateChannelVolume(channel, scheduleTime);
1945
- this.updateKeyBasedVolume(channel, scheduleTime);
1952
+ if (channel.isDrum) {
1953
+ for (let i = 0; i < 128; i++) {
1954
+ this.updateKeyBasedVolume(channel, i, scheduleTime);
1955
+ }
1956
+ }
1957
+ else {
1958
+ this.updateChannelVolume(channel, scheduleTime);
1959
+ }
1946
1960
  }
1947
1961
  setExpression(channelNumber, expression, scheduleTime) {
1948
1962
  scheduleTime ??= this.audioContext.currentTime;
@@ -1968,33 +1982,27 @@ class MidyGM2 {
1968
1982
  .cancelScheduledValues(scheduleTime)
1969
1983
  .setValueAtTime(volume * gainRight, scheduleTime);
1970
1984
  }
1971
- updateKeyBasedVolume(channel, scheduleTime) {
1972
- if (!channel.isDrum)
1973
- return;
1985
+ updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
1974
1986
  const state = channel.state;
1975
1987
  const defaultVolume = state.volume * state.expression;
1976
1988
  const defaultPan = state.pan;
1977
- for (let i = 0; i < 128; i++) {
1978
- const gainL = channel.keyBasedGainLs[i];
1979
- const gainR = channel.keyBasedGainLs[i];
1980
- if (!gainL)
1981
- continue;
1982
- if (!gainR)
1983
- continue;
1984
- const keyBasedVolume = this.getKeyBasedValue(channel, i, 7);
1985
- const volume = (0 <= keyBasedVolume)
1986
- ? defaultVolume * keyBasedVolume / 64
1987
- : defaultVolume;
1988
- const keyBasedPan = this.getKeyBasedValue(channel, i, 10);
1989
- const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
1990
- const { gainLeft, gainRight } = this.panToGain(pan);
1991
- gainL.gain
1992
- .cancelScheduledValues(scheduleTime)
1993
- .setValueAtTime(volume * gainLeft, scheduleTime);
1994
- gainR.gain
1995
- .cancelScheduledValues(scheduleTime)
1996
- .setValueAtTime(volume * gainRight, scheduleTime);
1997
- }
1989
+ const gainL = channel.keyBasedGainLs[keyNumber];
1990
+ const gainR = channel.keyBasedGainRs[keyNumber];
1991
+ if (!gainL)
1992
+ return;
1993
+ const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
1994
+ const volume = (0 <= keyBasedVolume)
1995
+ ? defaultVolume * keyBasedVolume / 64
1996
+ : defaultVolume;
1997
+ const keyBasedPan = this.getKeyBasedValue(channel, keyNumber, 10);
1998
+ const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
1999
+ const { gainLeft, gainRight } = this.panToGain(pan);
2000
+ gainL.gain
2001
+ .cancelScheduledValues(scheduleTime)
2002
+ .setValueAtTime(volume * gainLeft, scheduleTime);
2003
+ gainR.gain
2004
+ .cancelScheduledValues(scheduleTime)
2005
+ .setValueAtTime(volume * gainRight, scheduleTime);
1998
2006
  }
1999
2007
  setSustainPedal(channelNumber, value, scheduleTime) {
2000
2008
  const channel = this.channels[channelNumber];
@@ -2061,67 +2069,19 @@ class MidyGM2 {
2061
2069
  scheduleTime ??= this.audioContext.currentTime;
2062
2070
  const channel = this.channels[channelNumber];
2063
2071
  const state = channel.state;
2064
- const reverbEffect = this.reverbEffect;
2065
- if (0 < state.reverbSendLevel) {
2066
- if (0 < reverbSendLevel) {
2067
- state.reverbSendLevel = reverbSendLevel / 127;
2068
- reverbEffect.input.gain
2069
- .cancelScheduledValues(scheduleTime)
2070
- .setValueAtTime(state.reverbSendLevel, scheduleTime);
2071
- }
2072
- else {
2073
- this.processScheduledNotes(channel, (note) => {
2074
- if (note.voiceParams.reverbEffectsSend <= 0)
2075
- return false;
2076
- if (note.reverbEffectsSend)
2077
- note.reverbEffectsSend.disconnect();
2078
- });
2079
- }
2080
- }
2081
- else {
2082
- if (0 < reverbSendLevel) {
2083
- this.processScheduledNotes(channel, (note) => {
2084
- this.setReverbEffectsSend(channel, note, 0, scheduleTime);
2085
- });
2086
- state.reverbSendLevel = reverbSendLevel / 127;
2087
- reverbEffect.input.gain
2088
- .cancelScheduledValues(scheduleTime)
2089
- .setValueAtTime(state.reverbSendLevel, scheduleTime);
2090
- }
2091
- }
2072
+ state.reverbSendLevel = reverbSendLevel / 127;
2073
+ this.processScheduledNotes(channel, (note) => {
2074
+ this.setReverbSend(channel, note, scheduleTime);
2075
+ });
2092
2076
  }
2093
2077
  setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
2094
2078
  scheduleTime ??= this.audioContext.currentTime;
2095
2079
  const channel = this.channels[channelNumber];
2096
2080
  const state = channel.state;
2097
- const chorusEffect = this.chorusEffect;
2098
- if (0 < state.chorusSendLevel) {
2099
- if (0 < chorusSendLevel) {
2100
- state.chorusSendLevel = chorusSendLevel / 127;
2101
- chorusEffect.input.gain
2102
- .cancelScheduledValues(scheduleTime)
2103
- .setValueAtTime(state.chorusSendLevel, scheduleTime);
2104
- }
2105
- else {
2106
- this.processScheduledNotes(channel, (note) => {
2107
- if (note.voiceParams.chorusEffectsSend <= 0)
2108
- return false;
2109
- if (note.chorusEffectsSend)
2110
- note.chorusEffectsSend.disconnect();
2111
- });
2112
- }
2113
- }
2114
- else {
2115
- if (0 < chorusSendLevel) {
2116
- this.processScheduledNotes(channel, (note) => {
2117
- this.setChorusEffectsSend(channel, note, 0, scheduleTime);
2118
- });
2119
- state.chorusSendLevel = chorusSendLevel / 127;
2120
- chorusEffect.input.gain
2121
- .cancelScheduledValues(scheduleTime)
2122
- .setValueAtTime(state.chorusSendLevel, scheduleTime);
2123
- }
2124
- }
2081
+ state.chorusSendLevel = chorusSendLevel / 127;
2082
+ this.processScheduledNotes(channel, (note) => {
2083
+ this.setChorusSend(channel, note, scheduleTime);
2084
+ });
2125
2085
  }
2126
2086
  limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
2127
2087
  if (maxLSB < channel.dataLSB) {
@@ -2403,9 +2363,9 @@ class MidyGM2 {
2403
2363
  case 9:
2404
2364
  switch (data[3]) {
2405
2365
  case 1: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2406
- return this.handlePressureSysEx(data, "channelPressureTable");
2366
+ return this.handlePressureSysEx(data, "channelPressureTable", scheduleTime);
2407
2367
  case 3: // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca22.pdf
2408
- return this.handleControlChangeSysEx(data);
2368
+ return this.handleControlChangeSysEx(data, scheduleTime);
2409
2369
  default:
2410
2370
  console.warn(`Unsupported Exclusive Message: ${data}`);
2411
2371
  }
@@ -2724,9 +2684,9 @@ class MidyGM2 {
2724
2684
  : 0;
2725
2685
  return channelPressure / 127;
2726
2686
  }
2727
- setControllerParameters(channel, note, table, scheduleTime) {
2687
+ setEffects(channel, note, table, scheduleTime) {
2728
2688
  if (0 <= table[0])
2729
- this.updateDetune(channel, note, scueduleTime);
2689
+ this.updateDetune(channel, note, scheduleTime);
2730
2690
  if (0.5 <= channel.state.portamemento && 0 <= note.portamentoNoteNumber) {
2731
2691
  if (0 <= table[1]) {
2732
2692
  this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
@@ -2748,7 +2708,7 @@ class MidyGM2 {
2748
2708
  if (0 <= table[5])
2749
2709
  this.setModLfoToVolume(channel, note, scheduleTime);
2750
2710
  }
2751
- handlePressureSysEx(data, tableName) {
2711
+ handlePressureSysEx(data, tableName, scheduleTime) {
2752
2712
  const channelNumber = data[4];
2753
2713
  const channel = this.channels[channelNumber];
2754
2714
  if (channel.isDrum)
@@ -2759,32 +2719,38 @@ class MidyGM2 {
2759
2719
  const rr = data[i + 1];
2760
2720
  table[pp] = rr;
2761
2721
  }
2722
+ this.processActiveNotes(channel, scheduleTime, (note) => {
2723
+ this.setEffects(channel, note, table, scheduleTime);
2724
+ });
2762
2725
  }
2763
2726
  initControlTable() {
2764
2727
  const ccCount = 128;
2765
2728
  const slotSize = 6;
2766
2729
  return new Int8Array(ccCount * slotSize).fill(-1);
2767
2730
  }
2768
- applyControlTable(channel, controllerType, scheduleTime) {
2731
+ setControlChangeEffects(channel, controllerType, scheduleTime) {
2769
2732
  const slotSize = 6;
2770
2733
  const offset = controllerType * slotSize;
2771
2734
  const table = channel.controlTable.subarray(offset, offset + slotSize);
2772
2735
  this.processScheduledNotes(channel, (note) => {
2773
- this.setControllerParameters(channel, note, table, scheduleTime);
2736
+ this.setEffects(channel, note, table, scheduleTime);
2774
2737
  });
2775
2738
  }
2776
- handleControlChangeSysEx(data) {
2739
+ handleControlChangeSysEx(data, scheduleTime) {
2777
2740
  const channelNumber = data[4];
2778
2741
  const channel = this.channels[channelNumber];
2779
2742
  if (channel.isDrum)
2780
2743
  return;
2744
+ const slotSize = 6;
2781
2745
  const controllerType = data[5];
2782
- const table = channel.controlTable[controllerType];
2783
- for (let i = 6; i < data.length - 1; i += 2) {
2746
+ const offset = controllerType * slotSize;
2747
+ const table = channel.controlTable;
2748
+ for (let i = 6; i < data.length; i += 2) {
2784
2749
  const pp = data[i];
2785
2750
  const rr = data[i + 1];
2786
- table[pp] = rr;
2751
+ table[offset + pp] = rr;
2787
2752
  }
2753
+ this.setControlChangeEffects(channel, controllerType, scheduleTime);
2788
2754
  }
2789
2755
  getKeyBasedValue(channel, keyNumber, controllerType) {
2790
2756
  const index = keyNumber * 128 + controllerType;
@@ -2798,13 +2764,20 @@ class MidyGM2 {
2798
2764
  return;
2799
2765
  const keyNumber = data[5];
2800
2766
  const table = channel.keyBasedInstrumentControlTable;
2801
- for (let i = 6; i < data.length - 1; i += 2) {
2767
+ for (let i = 6; i < data.length; i += 2) {
2802
2768
  const controllerType = data[i];
2803
2769
  const value = data[i + 1];
2804
2770
  const index = keyNumber * 128 + controllerType;
2805
2771
  table[index] = value;
2772
+ switch (controllerType) {
2773
+ case 7:
2774
+ case 10:
2775
+ this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
2776
+ break;
2777
+ default: // TODO
2778
+ this.setControlChange(channelNumber, controllerType, value, scheduleTime);
2779
+ }
2806
2780
  }
2807
- this.setChannelPressure(channelNumber, channel.state.channelPressure * 127, scheduleTime);
2808
2781
  }
2809
2782
  handleSysEx(data, scheduleTime) {
2810
2783
  switch (data[0]) {
@@ -30,8 +30,8 @@ export class MidyGMLite {
30
30
  isStopping: boolean;
31
31
  isSeeking: boolean;
32
32
  timeline: any[];
33
- instruments: any[];
34
33
  notePromises: any[];
34
+ instruments: Set<any>;
35
35
  exclusiveClassNotes: any[];
36
36
  drumExclusiveClassNotes: any[];
37
37
  audioContext: any;
@@ -58,7 +58,7 @@ export class MidyGMLite {
58
58
  loadSoundFont(input: any): Promise<void>;
59
59
  loadMIDI(input: any): Promise<void>;
60
60
  cacheVoiceIds(): void;
61
- getVoiceId(channel: any, noteNumber: any, velocity: any): string | undefined;
61
+ getVoiceId(channel: any, noteNumber: any, velocity: any): any;
62
62
  createChannelAudioNodes(audioContext: any): {
63
63
  gainL: any;
64
64
  gainR: any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AA0GA;IA2BE;;;;;;;;;;MAUE;IAEF,+BAcC;IApDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,4BAAyB;IACzB,0BAAuB;IACvB,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,2BAAqC;IACrC,+BAEE;IAeA,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,6EAcC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDAUC;IAED,0EAUC;IAED,+EAkDC;IAED,mCAOC;IAED,0BAiEC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,kGAeC;IAED,mGAeC;IAED,wEAMC;IAED,uBAMC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,yEASC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,6FAyBC;IAED,oGAuCC;IAED,0EAiBC;IAED,8EAiBC;IAED,kGAoCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAkBC;IAED,6CAUC;IAED,qDAUC;IAED,qFASC;IAED,sFAeC;IAED,oGA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EAgCC;IAED,qCAeC;IAED,+FAWC;IAED,wDAUC;IAED,iFAKC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAWC;IAED,kFAeC;IAED,uDAYC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,gFAGC;IAED,yCAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AAt/CD;IAWE,0FAMC;IAhBD,cAAW;IACX,gBAAe;IACf,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
1
+ {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AA0GA;IA2BE;;;;;;;;;;MAUE;IAEF,+BAcC;IApDD,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,4BAAyB;IACzB,0BAAuB;IACvB,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,oBAAkB;IAClB,sBAAwB;IACxB,2BAAqC;IACrC,+BAEE;IAeA,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,8DAcC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDAUC;IAED,0EAUC;IAED,+EAkDC;IAED,mCAOC;IAED,0BAiEC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,kGAeC;IAED,mGAeC;IAED,wEAMC;IAED,uBAMC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,yEASC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,6FAyBC;IAED,oGAuCC;IAED,0EAiBC;IAED,8EAiBC;IAED,kGAoCC;IAED,6FASC;IAED,gCASC;IAED,iEAoBC;IAED,qGAkBC;IAED,6CAUC;IAED,qDAUC;IAED,qFASC;IAED,sFAeC;IAED,oGA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAWC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EAgCC;IAED,qCAeC;IAED,+FAWC;IAED,wDASC;IAED,iFAKC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAWC;IAED,kFAeC;IAED,uDAYC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,gFAGC;IAED,yCAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAGD,6DAgBC;CACF;AAx/CD;IAWE,0FAMC;IAhBD,cAAW;IACX,gBAAe;IACf,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
@@ -266,17 +266,17 @@ class MidyGMLite {
266
266
  writable: true,
267
267
  value: []
268
268
  });
269
- Object.defineProperty(this, "instruments", {
269
+ Object.defineProperty(this, "notePromises", {
270
270
  enumerable: true,
271
271
  configurable: true,
272
272
  writable: true,
273
273
  value: []
274
274
  });
275
- Object.defineProperty(this, "notePromises", {
275
+ Object.defineProperty(this, "instruments", {
276
276
  enumerable: true,
277
277
  configurable: true,
278
278
  writable: true,
279
- value: []
279
+ value: new Set()
280
280
  });
281
281
  Object.defineProperty(this, "exclusiveClassNotes", {
282
282
  enumerable: true,
@@ -404,7 +404,7 @@ class MidyGMLite {
404
404
  const soundFont = this.soundFonts[soundFontIndex];
405
405
  const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
406
406
  const { instrument, sampleID } = voice.generators;
407
- return `${soundFontIndex}:${instrument}:${sampleID}`;
407
+ return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
408
408
  }
409
409
  createChannelAudioNodes(audioContext) {
410
410
  const { gainLeft, gainRight } = this.panToGain(defaultControllerState.pan.defaultValue);
@@ -1058,13 +1058,17 @@ class MidyGMLite {
1058
1058
  this.applyVoiceParams(channel, 14, scheduleTime);
1059
1059
  }
1060
1060
  setModLfoToPitch(channel, note, scheduleTime) {
1061
- const modLfoToPitch = note.voiceParams.modLfoToPitch;
1062
- const baseDepth = Math.abs(modLfoToPitch) +
1063
- channel.state.modulationDepth;
1064
- const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1065
- note.modulationDepth.gain
1066
- .cancelScheduledValues(scheduleTime)
1067
- .setValueAtTime(modulationDepth, scheduleTime);
1061
+ if (note.modulationDepth) {
1062
+ const modLfoToPitch = note.voiceParams.modLfoToPitch;
1063
+ const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1064
+ const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1065
+ note.modulationDepth.gain
1066
+ .cancelScheduledValues(scheduleTime)
1067
+ .setValueAtTime(modulationDepth, scheduleTime);
1068
+ }
1069
+ else {
1070
+ this.startModulation(channel, note, scheduleTime);
1071
+ }
1068
1072
  }
1069
1073
  setModLfoToFilterFc(note, scheduleTime) {
1070
1074
  const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
@@ -1193,7 +1197,6 @@ class MidyGMLite {
1193
1197
  note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
1194
1198
  }
1195
1199
  else {
1196
- this.setPitchEnvelope(note, scheduleTime);
1197
1200
  this.startModulation(channel, note, scheduleTime);
1198
1201
  }
1199
1202
  });