@marmooo/midy 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -228,13 +228,13 @@ class MidyGM2 {
228
228
  configurable: true,
229
229
  writable: true,
230
230
  value: 0
231
- }); // cb
231
+ }); // cent
232
232
  Object.defineProperty(this, "masterCoarseTuning", {
233
233
  enumerable: true,
234
234
  configurable: true,
235
235
  writable: true,
236
236
  value: 0
237
- }); // cb
237
+ }); // cent
238
238
  Object.defineProperty(this, "reverb", {
239
239
  enumerable: true,
240
240
  configurable: true,
@@ -359,6 +359,12 @@ class MidyGM2 {
359
359
  writable: true,
360
360
  value: false
361
361
  });
362
+ Object.defineProperty(this, "playPromise", {
363
+ enumerable: true,
364
+ configurable: true,
365
+ writable: true,
366
+ value: void 0
367
+ });
362
368
  Object.defineProperty(this, "timeline", {
363
369
  enumerable: true,
364
370
  configurable: true,
@@ -527,7 +533,7 @@ class MidyGM2 {
527
533
  channel.controlTable.fill(-1);
528
534
  channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
529
535
  channel.channelPressureTable.fill(-1);
530
- channel.keyBasedInstrumentControlTable.fill(-1);
536
+ channel.keyBasedTable.fill(-1);
531
537
  }
532
538
  createChannels(audioContext) {
533
539
  const channels = Array.from({ length: this.numChannels }, () => {
@@ -543,7 +549,7 @@ class MidyGM2 {
543
549
  controlTable: this.initControlTable(),
544
550
  scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
545
551
  channelPressureTable: new Int8Array(6).fill(-1),
546
- keyBasedInstrumentControlTable: new Int8Array(128 * 128).fill(-1),
552
+ keyBasedTable: new Int8Array(128 * 128).fill(-1),
547
553
  keyBasedGainLs: new Array(128),
548
554
  keyBasedGainRs: new Array(128),
549
555
  };
@@ -618,72 +624,80 @@ class MidyGM2 {
618
624
  }
619
625
  return 0;
620
626
  }
621
- playNotes() {
622
- return new Promise((resolve) => {
623
- this.isPlaying = true;
624
- this.isPaused = false;
625
- this.startTime = this.audioContext.currentTime;
626
- let queueIndex = this.getQueueIndex(this.resumeTime);
627
- let resumeTime = this.resumeTime - this.startTime;
627
+ resetAllStates() {
628
+ this.exclusiveClassNotes.fill(undefined);
629
+ this.drumExclusiveClassNotes.fill(undefined);
630
+ this.voiceCache.clear();
631
+ for (let i = 0; i < this.channels.length; i++) {
632
+ this.channels[i].scheduledNotes = [];
633
+ this.resetChannelStates(i);
634
+ }
635
+ }
636
+ updateStates(queueIndex, nextQueueIndex) {
637
+ if (nextQueueIndex < queueIndex)
638
+ queueIndex = 0;
639
+ for (let i = queueIndex; i < nextQueueIndex; i++) {
640
+ const event = this.timeline[i];
641
+ switch (event.type) {
642
+ case "controller":
643
+ this.setControlChange(event.channel, event.controllerType, event.value, 0);
644
+ break;
645
+ case "programChange":
646
+ this.setProgramChange(event.channel, event.programNumber, 0);
647
+ break;
648
+ case "pitchBend":
649
+ this.setPitchBend(event.channel, event.value + 8192, 0);
650
+ break;
651
+ case "sysEx":
652
+ this.handleSysEx(event.data, 0);
653
+ }
654
+ }
655
+ }
656
+ async playNotes() {
657
+ if (this.audioContext.state === "suspended") {
658
+ await this.audioContext.resume();
659
+ }
660
+ this.isPlaying = true;
661
+ this.isPaused = false;
662
+ this.startTime = this.audioContext.currentTime;
663
+ let queueIndex = this.getQueueIndex(this.resumeTime);
664
+ let resumeTime = this.resumeTime - this.startTime;
665
+ let finished = false;
666
+ this.notePromises = [];
667
+ while (queueIndex < this.timeline.length) {
668
+ const now = this.audioContext.currentTime;
669
+ const t = now + resumeTime;
670
+ queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
671
+ if (this.isPausing) {
672
+ await this.stopNotes(0, true, now);
673
+ await this.audioContext.suspend();
674
+ this.notePromises = [];
675
+ break;
676
+ }
677
+ else if (this.isStopping) {
678
+ await this.stopNotes(0, true, now);
679
+ await this.audioContext.suspend();
680
+ finished = true;
681
+ break;
682
+ }
683
+ else if (this.isSeeking) {
684
+ await this.stopNotes(0, true, now);
685
+ this.startTime = this.audioContext.currentTime;
686
+ const nextQueueIndex = this.getQueueIndex(this.resumeTime);
687
+ this.updateStates(queueIndex, nextQueueIndex);
688
+ queueIndex = nextQueueIndex;
689
+ resumeTime = this.resumeTime - this.startTime;
690
+ this.isSeeking = false;
691
+ continue;
692
+ }
693
+ const waitTime = now + this.noteCheckInterval;
694
+ await this.scheduleTask(() => { }, waitTime);
695
+ }
696
+ if (finished) {
628
697
  this.notePromises = [];
629
- const schedulePlayback = async () => {
630
- if (queueIndex >= this.timeline.length) {
631
- await Promise.all(this.notePromises);
632
- this.notePromises = [];
633
- this.exclusiveClassNotes.fill(undefined);
634
- this.drumExclusiveClassNotes.fill(undefined);
635
- this.voiceCache.clear();
636
- for (let i = 0; i < this.channels.length; i++) {
637
- this.channels[i].scheduledNotes = [];
638
- this.resetAllStates(i);
639
- }
640
- resolve();
641
- return;
642
- }
643
- const now = this.audioContext.currentTime;
644
- const t = now + resumeTime;
645
- queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
646
- if (this.isPausing) {
647
- await this.stopNotes(0, true, now);
648
- this.notePromises = [];
649
- this.isPausing = false;
650
- this.isPaused = true;
651
- resolve();
652
- return;
653
- }
654
- else if (this.isStopping) {
655
- await this.stopNotes(0, true, now);
656
- this.notePromises = [];
657
- this.exclusiveClassNotes.fill(undefined);
658
- this.drumExclusiveClassNotes.fill(undefined);
659
- this.voiceCache.clear();
660
- for (let i = 0; i < this.channels.length; i++) {
661
- this.channels[i].scheduledNotes = [];
662
- this.resetAllStates(i);
663
- }
664
- this.isStopping = false;
665
- this.isPaused = false;
666
- resolve();
667
- return;
668
- }
669
- else if (this.isSeeking) {
670
- this.stopNotes(0, true, now);
671
- this.exclusiveClassNotes.fill(undefined);
672
- this.drumExclusiveClassNotes.fill(undefined);
673
- this.startTime = this.audioContext.currentTime;
674
- queueIndex = this.getQueueIndex(this.resumeTime);
675
- resumeTime = this.resumeTime - this.startTime;
676
- this.isSeeking = false;
677
- await schedulePlayback();
678
- }
679
- else {
680
- const waitTime = now + this.noteCheckInterval;
681
- await this.scheduleTask(() => { }, waitTime);
682
- await schedulePlayback();
683
- }
684
- };
685
- schedulePlayback();
686
- });
698
+ this.resetAllStates();
699
+ }
700
+ this.isPlaying = false;
687
701
  }
688
702
  ticksToSecond(ticks, secondsPerBeat) {
689
703
  return ticks * secondsPerBeat / this.ticksPerBeat;
@@ -820,26 +834,32 @@ class MidyGM2 {
820
834
  this.resumeTime = 0;
821
835
  if (this.voiceCounter.size === 0)
822
836
  this.cacheVoiceIds();
823
- await this.playNotes();
824
- this.isPlaying = false;
837
+ this.playPromise = this.playNotes();
838
+ await this.playPromise;
825
839
  }
826
- stop() {
840
+ async stop() {
827
841
  if (!this.isPlaying)
828
842
  return;
829
843
  this.isStopping = true;
844
+ await this.playPromise;
845
+ this.isStopping = false;
830
846
  }
831
- pause() {
847
+ async pause() {
832
848
  if (!this.isPlaying || this.isPaused)
833
849
  return;
834
850
  const now = this.audioContext.currentTime;
835
851
  this.resumeTime += now - this.startTime - this.startDelay;
836
852
  this.isPausing = true;
853
+ await this.playPromise;
854
+ this.isPausing = false;
855
+ this.isPaused = true;
837
856
  }
838
857
  async resume() {
839
858
  if (!this.isPaused)
840
859
  return;
841
- await this.playNotes();
842
- this.isPlaying = false;
860
+ this.playPromise = this.playNotes();
861
+ await this.playPromise;
862
+ this.isPaused = false;
843
863
  }
844
864
  seekTo(second) {
845
865
  this.resumeTime = second;
@@ -1274,10 +1294,12 @@ class MidyGM2 {
1274
1294
  startVibrato(channel, note, scheduleTime) {
1275
1295
  const { voiceParams } = note;
1276
1296
  const state = channel.state;
1297
+ const vibratoRate = state.vibratoRate * 2;
1298
+ const vibratoDelay = state.vibratoDelay * 2;
1277
1299
  note.vibratoLFO = new OscillatorNode(this.audioContext, {
1278
- frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
1300
+ frequency: this.centToHz(voiceParams.freqVibLFO) * vibratoRate,
1279
1301
  });
1280
- note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
1302
+ note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO * vibratoDelay);
1281
1303
  note.vibratoDepth = new GainNode(this.audioContext);
1282
1304
  this.setVibLfoToPitch(channel, note, scheduleTime);
1283
1305
  note.vibratoLFO.connect(note.vibratoDepth);
@@ -1589,6 +1611,7 @@ class MidyGM2 {
1589
1611
  break;
1590
1612
  }
1591
1613
  }
1614
+ channel.keyBasedTable.fill(-1);
1592
1615
  }
1593
1616
  setChannelPressure(channelNumber, value, scheduleTime) {
1594
1617
  const channel = this.channels[channelNumber];
@@ -1604,7 +1627,7 @@ class MidyGM2 {
1604
1627
  }
1605
1628
  const table = channel.channelPressureTable;
1606
1629
  this.processActiveNotes(channel, scheduleTime, (note) => {
1607
- this.setEffects(channel, note, table);
1630
+ this.setEffects(channel, note, table, scheduleTime);
1608
1631
  });
1609
1632
  this.applyVoiceParams(channel, 13);
1610
1633
  }
@@ -1630,23 +1653,28 @@ class MidyGM2 {
1630
1653
  const modLfoToPitch = note.voiceParams.modLfoToPitch +
1631
1654
  this.getLFOPitchDepth(channel, note);
1632
1655
  const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1633
- const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1656
+ const depth = baseDepth * Math.sign(modLfoToPitch);
1634
1657
  note.modulationDepth.gain
1635
1658
  .cancelScheduledValues(scheduleTime)
1636
- .setValueAtTime(modulationDepth, scheduleTime);
1659
+ .setValueAtTime(depth, scheduleTime);
1637
1660
  }
1638
1661
  else {
1639
1662
  this.startModulation(channel, note, scheduleTime);
1640
1663
  }
1641
1664
  }
1642
1665
  setVibLfoToPitch(channel, note, scheduleTime) {
1643
- const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
1644
- const vibratoDepth = Math.abs(vibLfoToPitch) * channel.state.vibratoDepth *
1645
- 2;
1646
- const vibratoDepthSign = 0 < vibLfoToPitch;
1647
- note.vibratoDepth.gain
1648
- .cancelScheduledValues(scheduleTime)
1649
- .setValueAtTime(vibratoDepth * vibratoDepthSign, scheduleTime);
1666
+ if (note.vibratoDepth) {
1667
+ const vibratoDepth = channel.state.vibratoDepth * 2;
1668
+ const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
1669
+ const baseDepth = Math.abs(vibLfoToPitch) * vibratoDepth;
1670
+ const depth = baseDepth * Math.sign(vibLfoToPitch);
1671
+ note.vibratoDepth.gain
1672
+ .cancelScheduledValues(scheduleTime)
1673
+ .setValueAtTime(depth, scheduleTime);
1674
+ }
1675
+ else {
1676
+ this.startVibrato(channel, note, scheduleTime);
1677
+ }
1650
1678
  }
1651
1679
  setModLfoToFilterFc(channel, note, scheduleTime) {
1652
1680
  const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc +
@@ -1724,13 +1752,12 @@ class MidyGM2 {
1724
1752
  }
1725
1753
  }
1726
1754
  }
1727
- setDelayModLFO(note, scheduleTime) {
1728
- const startTime = note.startTime;
1729
- if (startTime < scheduleTime)
1730
- return;
1731
- note.modulationLFO.stop(scheduleTime);
1732
- note.modulationLFO.start(startTime + note.voiceParams.delayModLFO);
1733
- note.modulationLFO.connect(note.filterDepth);
1755
+ setDelayModLFO(note) {
1756
+ const startTime = note.startTime + note.voiceParams.delayModLFO;
1757
+ try {
1758
+ note.modulationLFO.start(startTime);
1759
+ }
1760
+ catch { /* empty */ }
1734
1761
  }
1735
1762
  setFreqModLFO(note, scheduleTime) {
1736
1763
  const freqModLFO = note.voiceParams.freqModLFO;
@@ -1739,54 +1766,65 @@ class MidyGM2 {
1739
1766
  .setValueAtTime(freqModLFO, scheduleTime);
1740
1767
  }
1741
1768
  setFreqVibLFO(channel, note, scheduleTime) {
1769
+ const vibratoRate = channel.state.vibratoRate * 2;
1742
1770
  const freqVibLFO = note.voiceParams.freqVibLFO;
1743
1771
  note.vibratoLFO.frequency
1744
1772
  .cancelScheduledValues(scheduleTime)
1745
- .setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, scheduleTime);
1773
+ .setValueAtTime(freqVibLFO * vibratoRate, scheduleTime);
1774
+ }
1775
+ setDelayVibLFO(channel, note) {
1776
+ const vibratoDelay = channel.state.vibratoDelay * 2;
1777
+ const value = note.voiceParams.delayVibLFO;
1778
+ const startTime = note.startTime + value * vibratoDelay;
1779
+ try {
1780
+ note.vibratoLFO.start(startTime);
1781
+ }
1782
+ catch { /* empty */ }
1746
1783
  }
1747
1784
  createVoiceParamsHandlers() {
1748
1785
  return {
1749
- modLfoToPitch: (channel, note, _prevValue, scheduleTime) => {
1786
+ modLfoToPitch: (channel, note, scheduleTime) => {
1750
1787
  if (0 < channel.state.modulationDepth) {
1751
1788
  this.setModLfoToPitch(channel, note, scheduleTime);
1752
1789
  }
1753
1790
  },
1754
- vibLfoToPitch: (channel, note, _prevValue, scheduleTime) => {
1791
+ vibLfoToPitch: (channel, note, scheduleTime) => {
1755
1792
  if (0 < channel.state.vibratoDepth) {
1756
1793
  this.setVibLfoToPitch(channel, note, scheduleTime);
1757
1794
  }
1758
1795
  },
1759
- modLfoToFilterFc: (channel, note, _prevValue, scheduleTime) => {
1796
+ modLfoToFilterFc: (channel, note, scheduleTime) => {
1760
1797
  if (0 < channel.state.modulationDepth) {
1761
1798
  this.setModLfoToFilterFc(channel, note, scheduleTime);
1762
1799
  }
1763
1800
  },
1764
- modLfoToVolume: (channel, note, _prevValue, scheduleTime) => {
1801
+ modLfoToVolume: (channel, note, scheduleTime) => {
1765
1802
  if (0 < channel.state.modulationDepth) {
1766
1803
  this.setModLfoToVolume(channel, note, scheduleTime);
1767
1804
  }
1768
1805
  },
1769
- chorusEffectsSend: (channel, note, prevValue, scheduleTime) => {
1770
- this.setChorusEffectsSend(channel, note, prevValue, scheduleTime);
1806
+ chorusEffectsSend: (channel, note, scheduleTime) => {
1807
+ this.setChorusSend(channel, note, scheduleTime);
1808
+ },
1809
+ reverbEffectsSend: (channel, note, scheduleTime) => {
1810
+ this.setReverbSend(channel, note, scheduleTime);
1811
+ },
1812
+ delayModLFO: (_channel, note, _scheduleTime) => {
1813
+ if (0 < channel.state.modulationDepth) {
1814
+ this.setDelayModLFO(note);
1815
+ }
1771
1816
  },
1772
- reverbEffectsSend: (channel, note, prevValue, scheduleTime) => {
1773
- this.setReverbEffectsSend(channel, note, prevValue, scheduleTime);
1817
+ freqModLFO: (_channel, note, scheduleTime) => {
1818
+ if (0 < channel.state.modulationDepth) {
1819
+ this.setFreqModLFO(note, scheduleTime);
1820
+ }
1774
1821
  },
1775
- delayModLFO: (_channel, note, _prevValue, scheduleTime) => this.setDelayModLFO(note, scheduleTime),
1776
- freqModLFO: (_channel, note, _prevValue, scheduleTime) => this.setFreqModLFO(note, scheduleTime),
1777
- delayVibLFO: (channel, note, prevValue, scheduleTime) => {
1822
+ delayVibLFO: (channel, note, _scheduleTime) => {
1778
1823
  if (0 < channel.state.vibratoDepth) {
1779
- const vibratoDelay = channel.state.vibratoDelay * 2;
1780
- const prevStartTime = note.startTime + prevValue * vibratoDelay;
1781
- if (scheduleTime < prevStartTime)
1782
- return;
1783
- const value = note.voiceParams.delayVibLFO;
1784
- const startTime = note.startTime + value * vibratoDelay;
1785
- note.vibratoLFO.stop(scheduleTime);
1786
- note.vibratoLFO.start(startTime);
1824
+ setDelayVibLFO(channel, note);
1787
1825
  }
1788
1826
  },
1789
- freqVibLFO: (channel, note, _prevValue, scheduleTime) => {
1827
+ freqVibLFO: (channel, note, scheduleTime) => {
1790
1828
  if (0 < channel.state.vibratoDepth) {
1791
1829
  this.setFreqVibLFO(channel, note, scheduleTime);
1792
1830
  }
@@ -1814,7 +1852,7 @@ class MidyGM2 {
1814
1852
  continue;
1815
1853
  note.voiceParams[key] = value;
1816
1854
  if (key in this.voiceParamsHandlers) {
1817
- this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
1855
+ this.voiceParamsHandlers[key](channel, note, scheduleTime);
1818
1856
  }
1819
1857
  else {
1820
1858
  if (volumeEnvelopeKeySet.has(key))
@@ -1983,13 +2021,13 @@ class MidyGM2 {
1983
2021
  .setValueAtTime(volume * gainRight, scheduleTime);
1984
2022
  }
1985
2023
  updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
1986
- const state = channel.state;
1987
- const defaultVolume = state.volume * state.expression;
1988
- const defaultPan = state.pan;
1989
2024
  const gainL = channel.keyBasedGainLs[keyNumber];
1990
- const gainR = channel.keyBasedGainRs[keyNumber];
1991
2025
  if (!gainL)
1992
2026
  return;
2027
+ const gainR = channel.keyBasedGainRs[keyNumber];
2028
+ const state = channel.state;
2029
+ const defaultVolume = state.volume * state.expression;
2030
+ const defaultPan = state.pan;
1993
2031
  const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
1994
2032
  const volume = (0 <= keyBasedVolume)
1995
2033
  ? defaultVolume * keyBasedVolume / 64
@@ -2141,8 +2179,8 @@ class MidyGM2 {
2141
2179
  }
2142
2180
  handlePitchBendRangeRPN(channelNumber, scheduleTime) {
2143
2181
  const channel = this.channels[channelNumber];
2144
- this.limitData(channel, 0, 127, 0, 99);
2145
- const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
2182
+ this.limitData(channel, 0, 127, 0, 127);
2183
+ const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
2146
2184
  this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
2147
2185
  }
2148
2186
  setPitchBendRange(channelNumber, value, scheduleTime) {
@@ -2152,7 +2190,7 @@ class MidyGM2 {
2152
2190
  scheduleTime ??= this.audioContext.currentTime;
2153
2191
  const state = channel.state;
2154
2192
  const prev = state.pitchWheelSensitivity;
2155
- const next = value / 128;
2193
+ const next = value / 12800;
2156
2194
  state.pitchWheelSensitivity = next;
2157
2195
  channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
2158
2196
  this.updateChannelDetune(channel, scheduleTime);
@@ -2161,7 +2199,8 @@ class MidyGM2 {
2161
2199
  handleFineTuningRPN(channelNumber, scheduleTime) {
2162
2200
  const channel = this.channels[channelNumber];
2163
2201
  this.limitData(channel, 0, 127, 0, 127);
2164
- const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
2202
+ const value = channel.dataMSB * 128 + channel.dataLSB;
2203
+ const fineTuning = (value - 8192) / 8192 * 100;
2165
2204
  this.setFineTuning(channelNumber, fineTuning, scheduleTime);
2166
2205
  }
2167
2206
  setFineTuning(channelNumber, value, scheduleTime) {
@@ -2170,7 +2209,7 @@ class MidyGM2 {
2170
2209
  return;
2171
2210
  scheduleTime ??= this.audioContext.currentTime;
2172
2211
  const prev = channel.fineTuning;
2173
- const next = (value - 8192) / 8.192; // cent
2212
+ const next = value;
2174
2213
  channel.fineTuning = next;
2175
2214
  channel.detune += next - prev;
2176
2215
  this.updateChannelDetune(channel, scheduleTime);
@@ -2178,7 +2217,7 @@ class MidyGM2 {
2178
2217
  handleCoarseTuningRPN(channelNumber, scheduleTime) {
2179
2218
  const channel = this.channels[channelNumber];
2180
2219
  this.limitDataMSB(channel, 0, 127);
2181
- const coarseTuning = channel.dataMSB;
2220
+ const coarseTuning = (channel.dataMSB - 64) * 100;
2182
2221
  this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
2183
2222
  }
2184
2223
  setCoarseTuning(channelNumber, value, scheduleTime) {
@@ -2187,7 +2226,7 @@ class MidyGM2 {
2187
2226
  return;
2188
2227
  scheduleTime ??= this.audioContext.currentTime;
2189
2228
  const prev = channel.coarseTuning;
2190
- const next = (value - 64) * 100; // cent
2229
+ const next = value;
2191
2230
  channel.coarseTuning = next;
2192
2231
  channel.detune += next - prev;
2193
2232
  this.updateChannelDetune(channel, scheduleTime);
@@ -2195,22 +2234,22 @@ class MidyGM2 {
2195
2234
  handleModulationDepthRangeRPN(channelNumber, scheduleTime) {
2196
2235
  const channel = this.channels[channelNumber];
2197
2236
  this.limitData(channel, 0, 127, 0, 127);
2198
- const modulationDepthRange = (dataMSB + dataLSB / 128) * 100;
2199
- this.setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime);
2237
+ const value = (channel.dataMSB + channel.dataLSB / 128) * 100;
2238
+ this.setModulationDepthRange(channelNumber, value, scheduleTime);
2200
2239
  }
2201
- setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime) {
2240
+ setModulationDepthRange(channelNumber, value, scheduleTime) {
2202
2241
  const channel = this.channels[channelNumber];
2203
2242
  if (channel.isDrum)
2204
2243
  return;
2205
2244
  scheduleTime ??= this.audioContext.currentTime;
2206
- channel.modulationDepthRange = modulationDepthRange;
2245
+ channel.modulationDepthRange = value;
2207
2246
  this.updateModulation(channel, scheduleTime);
2208
2247
  }
2209
2248
  allSoundOff(channelNumber, _value, scheduleTime) {
2210
2249
  scheduleTime ??= this.audioContext.currentTime;
2211
2250
  return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
2212
2251
  }
2213
- resetAllStates(channelNumber) {
2252
+ resetChannelStates(channelNumber) {
2214
2253
  const scheduleTime = this.audioContext.currentTime;
2215
2254
  const channel = this.channels[channelNumber];
2216
2255
  const state = channel.state;
@@ -2228,8 +2267,8 @@ class MidyGM2 {
2228
2267
  }
2229
2268
  this.resetChannelTable(channel);
2230
2269
  this.mode = "GM2";
2231
- this.masterFineTuning = 0; // cb
2232
- this.masterCoarseTuning = 0; // cb
2270
+ this.masterFineTuning = 0; // cent
2271
+ this.masterCoarseTuning = 0; // cent
2233
2272
  }
2234
2273
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
2235
2274
  resetAllControllers(channelNumber, _value, scheduleTime) {
@@ -2398,12 +2437,13 @@ class MidyGM2 {
2398
2437
  }
2399
2438
  }
2400
2439
  handleMasterFineTuningSysEx(data, scheduleTime) {
2401
- const fineTuning = data[5] * 128 + data[4];
2440
+ const value = (data[5] * 128 + data[4]) / 16383;
2441
+ const fineTuning = (value - 8192) / 8192 * 100;
2402
2442
  this.setMasterFineTuning(fineTuning, scheduleTime);
2403
2443
  }
2404
2444
  setMasterFineTuning(value, scheduleTime) {
2405
2445
  const prev = this.masterFineTuning;
2406
- const next = (value - 8192) / 8.192; // cent
2446
+ const next = value;
2407
2447
  this.masterFineTuning = next;
2408
2448
  const detuneChange = next - prev;
2409
2449
  for (let i = 0; i < this.channels.length; i++) {
@@ -2415,12 +2455,12 @@ class MidyGM2 {
2415
2455
  }
2416
2456
  }
2417
2457
  handleMasterCoarseTuningSysEx(data, scheduleTime) {
2418
- const coarseTuning = data[4];
2458
+ const coarseTuning = (data[4] - 64) * 100;
2419
2459
  this.setMasterCoarseTuning(coarseTuning, scheduleTime);
2420
2460
  }
2421
2461
  setMasterCoarseTuning(value, scheduleTime) {
2422
2462
  const prev = this.masterCoarseTuning;
2423
- const next = (value - 64) * 100; // cent
2463
+ const next = value;
2424
2464
  this.masterCoarseTuning = next;
2425
2465
  const detuneChange = next - prev;
2426
2466
  for (let i = 0; i < this.channels.length; i++) {
@@ -2754,7 +2794,7 @@ class MidyGM2 {
2754
2794
  }
2755
2795
  getKeyBasedValue(channel, keyNumber, controllerType) {
2756
2796
  const index = keyNumber * 128 + controllerType;
2757
- const controlValue = channel.keyBasedInstrumentControlTable[index];
2797
+ const controlValue = channel.keyBasedTable[index];
2758
2798
  return controlValue;
2759
2799
  }
2760
2800
  handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
@@ -2763,7 +2803,7 @@ class MidyGM2 {
2763
2803
  if (!channel.isDrum)
2764
2804
  return;
2765
2805
  const keyNumber = data[5];
2766
- const table = channel.keyBasedInstrumentControlTable;
2806
+ const table = channel.keyBasedTable;
2767
2807
  for (let i = 6; i < data.length; i += 2) {
2768
2808
  const controllerType = data[i];
2769
2809
  const value = data[i + 1];
@@ -2774,8 +2814,20 @@ class MidyGM2 {
2774
2814
  case 10:
2775
2815
  this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
2776
2816
  break;
2777
- default: // TODO
2778
- this.setControlChange(channelNumber, controllerType, value, scheduleTime);
2817
+ case 91:
2818
+ this.processScheduledNotes(channel, (note) => {
2819
+ if (note.noteNumber === keyNumber) {
2820
+ this.setReverbSend(channel, note, scheduleTime);
2821
+ }
2822
+ });
2823
+ break;
2824
+ case 93:
2825
+ this.processScheduledNotes(channel, (note) => {
2826
+ if (note.noteNumber === keyNumber) {
2827
+ this.setChorusSend(channel, note, scheduleTime);
2828
+ }
2829
+ });
2830
+ break;
2779
2831
  }
2780
2832
  }
2781
2833
  }
@@ -2827,7 +2879,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2827
2879
  rpnLSB: 127,
2828
2880
  mono: false, // CC#124, CC#125
2829
2881
  modulationDepthRange: 50, // cent
2830
- fineTuning: 0, // cb
2831
- coarseTuning: 0, // cb
2882
+ fineTuning: 0, // cent
2883
+ coarseTuning: 0, // cent
2832
2884
  }
2833
2885
  });