@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.
package/esm/midy-GM2.js CHANGED
@@ -225,13 +225,13 @@ export class MidyGM2 {
225
225
  configurable: true,
226
226
  writable: true,
227
227
  value: 0
228
- }); // cb
228
+ }); // cent
229
229
  Object.defineProperty(this, "masterCoarseTuning", {
230
230
  enumerable: true,
231
231
  configurable: true,
232
232
  writable: true,
233
233
  value: 0
234
- }); // cb
234
+ }); // cent
235
235
  Object.defineProperty(this, "reverb", {
236
236
  enumerable: true,
237
237
  configurable: true,
@@ -356,6 +356,12 @@ export class MidyGM2 {
356
356
  writable: true,
357
357
  value: false
358
358
  });
359
+ Object.defineProperty(this, "playPromise", {
360
+ enumerable: true,
361
+ configurable: true,
362
+ writable: true,
363
+ value: void 0
364
+ });
359
365
  Object.defineProperty(this, "timeline", {
360
366
  enumerable: true,
361
367
  configurable: true,
@@ -524,7 +530,7 @@ export class MidyGM2 {
524
530
  channel.controlTable.fill(-1);
525
531
  channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
526
532
  channel.channelPressureTable.fill(-1);
527
- channel.keyBasedInstrumentControlTable.fill(-1);
533
+ channel.keyBasedTable.fill(-1);
528
534
  }
529
535
  createChannels(audioContext) {
530
536
  const channels = Array.from({ length: this.numChannels }, () => {
@@ -540,7 +546,7 @@ export class MidyGM2 {
540
546
  controlTable: this.initControlTable(),
541
547
  scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
542
548
  channelPressureTable: new Int8Array(6).fill(-1),
543
- keyBasedInstrumentControlTable: new Int8Array(128 * 128).fill(-1),
549
+ keyBasedTable: new Int8Array(128 * 128).fill(-1),
544
550
  keyBasedGainLs: new Array(128),
545
551
  keyBasedGainRs: new Array(128),
546
552
  };
@@ -615,72 +621,80 @@ export class MidyGM2 {
615
621
  }
616
622
  return 0;
617
623
  }
618
- playNotes() {
619
- return new Promise((resolve) => {
620
- this.isPlaying = true;
621
- this.isPaused = false;
622
- this.startTime = this.audioContext.currentTime;
623
- let queueIndex = this.getQueueIndex(this.resumeTime);
624
- let resumeTime = this.resumeTime - this.startTime;
624
+ resetAllStates() {
625
+ this.exclusiveClassNotes.fill(undefined);
626
+ this.drumExclusiveClassNotes.fill(undefined);
627
+ this.voiceCache.clear();
628
+ for (let i = 0; i < this.channels.length; i++) {
629
+ this.channels[i].scheduledNotes = [];
630
+ this.resetChannelStates(i);
631
+ }
632
+ }
633
+ updateStates(queueIndex, nextQueueIndex) {
634
+ if (nextQueueIndex < queueIndex)
635
+ queueIndex = 0;
636
+ for (let i = queueIndex; i < nextQueueIndex; i++) {
637
+ const event = this.timeline[i];
638
+ switch (event.type) {
639
+ case "controller":
640
+ this.setControlChange(event.channel, event.controllerType, event.value, 0);
641
+ break;
642
+ case "programChange":
643
+ this.setProgramChange(event.channel, event.programNumber, 0);
644
+ break;
645
+ case "pitchBend":
646
+ this.setPitchBend(event.channel, event.value + 8192, 0);
647
+ break;
648
+ case "sysEx":
649
+ this.handleSysEx(event.data, 0);
650
+ }
651
+ }
652
+ }
653
+ async playNotes() {
654
+ if (this.audioContext.state === "suspended") {
655
+ await this.audioContext.resume();
656
+ }
657
+ this.isPlaying = true;
658
+ this.isPaused = false;
659
+ this.startTime = this.audioContext.currentTime;
660
+ let queueIndex = this.getQueueIndex(this.resumeTime);
661
+ let resumeTime = this.resumeTime - this.startTime;
662
+ let finished = false;
663
+ this.notePromises = [];
664
+ while (queueIndex < this.timeline.length) {
665
+ const now = this.audioContext.currentTime;
666
+ const t = now + resumeTime;
667
+ queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
668
+ if (this.isPausing) {
669
+ await this.stopNotes(0, true, now);
670
+ await this.audioContext.suspend();
671
+ this.notePromises = [];
672
+ break;
673
+ }
674
+ else if (this.isStopping) {
675
+ await this.stopNotes(0, true, now);
676
+ await this.audioContext.suspend();
677
+ finished = true;
678
+ break;
679
+ }
680
+ else if (this.isSeeking) {
681
+ await this.stopNotes(0, true, now);
682
+ this.startTime = this.audioContext.currentTime;
683
+ const nextQueueIndex = this.getQueueIndex(this.resumeTime);
684
+ this.updateStates(queueIndex, nextQueueIndex);
685
+ queueIndex = nextQueueIndex;
686
+ resumeTime = this.resumeTime - this.startTime;
687
+ this.isSeeking = false;
688
+ continue;
689
+ }
690
+ const waitTime = now + this.noteCheckInterval;
691
+ await this.scheduleTask(() => { }, waitTime);
692
+ }
693
+ if (finished) {
625
694
  this.notePromises = [];
626
- const schedulePlayback = async () => {
627
- if (queueIndex >= this.timeline.length) {
628
- await Promise.all(this.notePromises);
629
- this.notePromises = [];
630
- this.exclusiveClassNotes.fill(undefined);
631
- this.drumExclusiveClassNotes.fill(undefined);
632
- this.voiceCache.clear();
633
- for (let i = 0; i < this.channels.length; i++) {
634
- this.channels[i].scheduledNotes = [];
635
- this.resetAllStates(i);
636
- }
637
- resolve();
638
- return;
639
- }
640
- const now = this.audioContext.currentTime;
641
- const t = now + resumeTime;
642
- queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
643
- if (this.isPausing) {
644
- await this.stopNotes(0, true, now);
645
- this.notePromises = [];
646
- this.isPausing = false;
647
- this.isPaused = true;
648
- resolve();
649
- return;
650
- }
651
- else if (this.isStopping) {
652
- await this.stopNotes(0, true, now);
653
- this.notePromises = [];
654
- this.exclusiveClassNotes.fill(undefined);
655
- this.drumExclusiveClassNotes.fill(undefined);
656
- this.voiceCache.clear();
657
- for (let i = 0; i < this.channels.length; i++) {
658
- this.channels[i].scheduledNotes = [];
659
- this.resetAllStates(i);
660
- }
661
- this.isStopping = false;
662
- this.isPaused = false;
663
- resolve();
664
- return;
665
- }
666
- else if (this.isSeeking) {
667
- this.stopNotes(0, true, now);
668
- this.exclusiveClassNotes.fill(undefined);
669
- this.drumExclusiveClassNotes.fill(undefined);
670
- this.startTime = this.audioContext.currentTime;
671
- queueIndex = this.getQueueIndex(this.resumeTime);
672
- resumeTime = this.resumeTime - this.startTime;
673
- this.isSeeking = false;
674
- await schedulePlayback();
675
- }
676
- else {
677
- const waitTime = now + this.noteCheckInterval;
678
- await this.scheduleTask(() => { }, waitTime);
679
- await schedulePlayback();
680
- }
681
- };
682
- schedulePlayback();
683
- });
695
+ this.resetAllStates();
696
+ }
697
+ this.isPlaying = false;
684
698
  }
685
699
  ticksToSecond(ticks, secondsPerBeat) {
686
700
  return ticks * secondsPerBeat / this.ticksPerBeat;
@@ -817,26 +831,32 @@ export class MidyGM2 {
817
831
  this.resumeTime = 0;
818
832
  if (this.voiceCounter.size === 0)
819
833
  this.cacheVoiceIds();
820
- await this.playNotes();
821
- this.isPlaying = false;
834
+ this.playPromise = this.playNotes();
835
+ await this.playPromise;
822
836
  }
823
- stop() {
837
+ async stop() {
824
838
  if (!this.isPlaying)
825
839
  return;
826
840
  this.isStopping = true;
841
+ await this.playPromise;
842
+ this.isStopping = false;
827
843
  }
828
- pause() {
844
+ async pause() {
829
845
  if (!this.isPlaying || this.isPaused)
830
846
  return;
831
847
  const now = this.audioContext.currentTime;
832
848
  this.resumeTime += now - this.startTime - this.startDelay;
833
849
  this.isPausing = true;
850
+ await this.playPromise;
851
+ this.isPausing = false;
852
+ this.isPaused = true;
834
853
  }
835
854
  async resume() {
836
855
  if (!this.isPaused)
837
856
  return;
838
- await this.playNotes();
839
- this.isPlaying = false;
857
+ this.playPromise = this.playNotes();
858
+ await this.playPromise;
859
+ this.isPaused = false;
840
860
  }
841
861
  seekTo(second) {
842
862
  this.resumeTime = second;
@@ -1271,10 +1291,12 @@ export class MidyGM2 {
1271
1291
  startVibrato(channel, note, scheduleTime) {
1272
1292
  const { voiceParams } = note;
1273
1293
  const state = channel.state;
1294
+ const vibratoRate = state.vibratoRate * 2;
1295
+ const vibratoDelay = state.vibratoDelay * 2;
1274
1296
  note.vibratoLFO = new OscillatorNode(this.audioContext, {
1275
- frequency: this.centToHz(voiceParams.freqVibLFO) * state.vibratoRate * 2,
1297
+ frequency: this.centToHz(voiceParams.freqVibLFO) * vibratoRate,
1276
1298
  });
1277
- note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO * state.vibratoDelay * 2);
1299
+ note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO * vibratoDelay);
1278
1300
  note.vibratoDepth = new GainNode(this.audioContext);
1279
1301
  this.setVibLfoToPitch(channel, note, scheduleTime);
1280
1302
  note.vibratoLFO.connect(note.vibratoDepth);
@@ -1586,6 +1608,7 @@ export class MidyGM2 {
1586
1608
  break;
1587
1609
  }
1588
1610
  }
1611
+ channel.keyBasedTable.fill(-1);
1589
1612
  }
1590
1613
  setChannelPressure(channelNumber, value, scheduleTime) {
1591
1614
  const channel = this.channels[channelNumber];
@@ -1601,7 +1624,7 @@ export class MidyGM2 {
1601
1624
  }
1602
1625
  const table = channel.channelPressureTable;
1603
1626
  this.processActiveNotes(channel, scheduleTime, (note) => {
1604
- this.setEffects(channel, note, table);
1627
+ this.setEffects(channel, note, table, scheduleTime);
1605
1628
  });
1606
1629
  this.applyVoiceParams(channel, 13);
1607
1630
  }
@@ -1627,23 +1650,28 @@ export class MidyGM2 {
1627
1650
  const modLfoToPitch = note.voiceParams.modLfoToPitch +
1628
1651
  this.getLFOPitchDepth(channel, note);
1629
1652
  const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1630
- const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1653
+ const depth = baseDepth * Math.sign(modLfoToPitch);
1631
1654
  note.modulationDepth.gain
1632
1655
  .cancelScheduledValues(scheduleTime)
1633
- .setValueAtTime(modulationDepth, scheduleTime);
1656
+ .setValueAtTime(depth, scheduleTime);
1634
1657
  }
1635
1658
  else {
1636
1659
  this.startModulation(channel, note, scheduleTime);
1637
1660
  }
1638
1661
  }
1639
1662
  setVibLfoToPitch(channel, note, scheduleTime) {
1640
- const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
1641
- const vibratoDepth = Math.abs(vibLfoToPitch) * channel.state.vibratoDepth *
1642
- 2;
1643
- const vibratoDepthSign = 0 < vibLfoToPitch;
1644
- note.vibratoDepth.gain
1645
- .cancelScheduledValues(scheduleTime)
1646
- .setValueAtTime(vibratoDepth * vibratoDepthSign, scheduleTime);
1663
+ if (note.vibratoDepth) {
1664
+ const vibratoDepth = channel.state.vibratoDepth * 2;
1665
+ const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
1666
+ const baseDepth = Math.abs(vibLfoToPitch) * vibratoDepth;
1667
+ const depth = baseDepth * Math.sign(vibLfoToPitch);
1668
+ note.vibratoDepth.gain
1669
+ .cancelScheduledValues(scheduleTime)
1670
+ .setValueAtTime(depth, scheduleTime);
1671
+ }
1672
+ else {
1673
+ this.startVibrato(channel, note, scheduleTime);
1674
+ }
1647
1675
  }
1648
1676
  setModLfoToFilterFc(channel, note, scheduleTime) {
1649
1677
  const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc +
@@ -1721,13 +1749,12 @@ export class MidyGM2 {
1721
1749
  }
1722
1750
  }
1723
1751
  }
1724
- setDelayModLFO(note, scheduleTime) {
1725
- const startTime = note.startTime;
1726
- if (startTime < scheduleTime)
1727
- return;
1728
- note.modulationLFO.stop(scheduleTime);
1729
- note.modulationLFO.start(startTime + note.voiceParams.delayModLFO);
1730
- note.modulationLFO.connect(note.filterDepth);
1752
+ setDelayModLFO(note) {
1753
+ const startTime = note.startTime + note.voiceParams.delayModLFO;
1754
+ try {
1755
+ note.modulationLFO.start(startTime);
1756
+ }
1757
+ catch { /* empty */ }
1731
1758
  }
1732
1759
  setFreqModLFO(note, scheduleTime) {
1733
1760
  const freqModLFO = note.voiceParams.freqModLFO;
@@ -1736,54 +1763,65 @@ export class MidyGM2 {
1736
1763
  .setValueAtTime(freqModLFO, scheduleTime);
1737
1764
  }
1738
1765
  setFreqVibLFO(channel, note, scheduleTime) {
1766
+ const vibratoRate = channel.state.vibratoRate * 2;
1739
1767
  const freqVibLFO = note.voiceParams.freqVibLFO;
1740
1768
  note.vibratoLFO.frequency
1741
1769
  .cancelScheduledValues(scheduleTime)
1742
- .setValueAtTime(freqVibLFO * channel.state.vibratoRate * 2, scheduleTime);
1770
+ .setValueAtTime(freqVibLFO * vibratoRate, scheduleTime);
1771
+ }
1772
+ setDelayVibLFO(channel, note) {
1773
+ const vibratoDelay = channel.state.vibratoDelay * 2;
1774
+ const value = note.voiceParams.delayVibLFO;
1775
+ const startTime = note.startTime + value * vibratoDelay;
1776
+ try {
1777
+ note.vibratoLFO.start(startTime);
1778
+ }
1779
+ catch { /* empty */ }
1743
1780
  }
1744
1781
  createVoiceParamsHandlers() {
1745
1782
  return {
1746
- modLfoToPitch: (channel, note, _prevValue, scheduleTime) => {
1783
+ modLfoToPitch: (channel, note, scheduleTime) => {
1747
1784
  if (0 < channel.state.modulationDepth) {
1748
1785
  this.setModLfoToPitch(channel, note, scheduleTime);
1749
1786
  }
1750
1787
  },
1751
- vibLfoToPitch: (channel, note, _prevValue, scheduleTime) => {
1788
+ vibLfoToPitch: (channel, note, scheduleTime) => {
1752
1789
  if (0 < channel.state.vibratoDepth) {
1753
1790
  this.setVibLfoToPitch(channel, note, scheduleTime);
1754
1791
  }
1755
1792
  },
1756
- modLfoToFilterFc: (channel, note, _prevValue, scheduleTime) => {
1793
+ modLfoToFilterFc: (channel, note, scheduleTime) => {
1757
1794
  if (0 < channel.state.modulationDepth) {
1758
1795
  this.setModLfoToFilterFc(channel, note, scheduleTime);
1759
1796
  }
1760
1797
  },
1761
- modLfoToVolume: (channel, note, _prevValue, scheduleTime) => {
1798
+ modLfoToVolume: (channel, note, scheduleTime) => {
1762
1799
  if (0 < channel.state.modulationDepth) {
1763
1800
  this.setModLfoToVolume(channel, note, scheduleTime);
1764
1801
  }
1765
1802
  },
1766
- chorusEffectsSend: (channel, note, prevValue, scheduleTime) => {
1767
- this.setChorusEffectsSend(channel, note, prevValue, scheduleTime);
1803
+ chorusEffectsSend: (channel, note, scheduleTime) => {
1804
+ this.setChorusSend(channel, note, scheduleTime);
1805
+ },
1806
+ reverbEffectsSend: (channel, note, scheduleTime) => {
1807
+ this.setReverbSend(channel, note, scheduleTime);
1808
+ },
1809
+ delayModLFO: (_channel, note, _scheduleTime) => {
1810
+ if (0 < channel.state.modulationDepth) {
1811
+ this.setDelayModLFO(note);
1812
+ }
1768
1813
  },
1769
- reverbEffectsSend: (channel, note, prevValue, scheduleTime) => {
1770
- this.setReverbEffectsSend(channel, note, prevValue, scheduleTime);
1814
+ freqModLFO: (_channel, note, scheduleTime) => {
1815
+ if (0 < channel.state.modulationDepth) {
1816
+ this.setFreqModLFO(note, scheduleTime);
1817
+ }
1771
1818
  },
1772
- delayModLFO: (_channel, note, _prevValue, scheduleTime) => this.setDelayModLFO(note, scheduleTime),
1773
- freqModLFO: (_channel, note, _prevValue, scheduleTime) => this.setFreqModLFO(note, scheduleTime),
1774
- delayVibLFO: (channel, note, prevValue, scheduleTime) => {
1819
+ delayVibLFO: (channel, note, _scheduleTime) => {
1775
1820
  if (0 < channel.state.vibratoDepth) {
1776
- const vibratoDelay = channel.state.vibratoDelay * 2;
1777
- const prevStartTime = note.startTime + prevValue * vibratoDelay;
1778
- if (scheduleTime < prevStartTime)
1779
- return;
1780
- const value = note.voiceParams.delayVibLFO;
1781
- const startTime = note.startTime + value * vibratoDelay;
1782
- note.vibratoLFO.stop(scheduleTime);
1783
- note.vibratoLFO.start(startTime);
1821
+ setDelayVibLFO(channel, note);
1784
1822
  }
1785
1823
  },
1786
- freqVibLFO: (channel, note, _prevValue, scheduleTime) => {
1824
+ freqVibLFO: (channel, note, scheduleTime) => {
1787
1825
  if (0 < channel.state.vibratoDepth) {
1788
1826
  this.setFreqVibLFO(channel, note, scheduleTime);
1789
1827
  }
@@ -1811,7 +1849,7 @@ export class MidyGM2 {
1811
1849
  continue;
1812
1850
  note.voiceParams[key] = value;
1813
1851
  if (key in this.voiceParamsHandlers) {
1814
- this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
1852
+ this.voiceParamsHandlers[key](channel, note, scheduleTime);
1815
1853
  }
1816
1854
  else {
1817
1855
  if (volumeEnvelopeKeySet.has(key))
@@ -1980,13 +2018,13 @@ export class MidyGM2 {
1980
2018
  .setValueAtTime(volume * gainRight, scheduleTime);
1981
2019
  }
1982
2020
  updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
1983
- const state = channel.state;
1984
- const defaultVolume = state.volume * state.expression;
1985
- const defaultPan = state.pan;
1986
2021
  const gainL = channel.keyBasedGainLs[keyNumber];
1987
- const gainR = channel.keyBasedGainRs[keyNumber];
1988
2022
  if (!gainL)
1989
2023
  return;
2024
+ const gainR = channel.keyBasedGainRs[keyNumber];
2025
+ const state = channel.state;
2026
+ const defaultVolume = state.volume * state.expression;
2027
+ const defaultPan = state.pan;
1990
2028
  const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
1991
2029
  const volume = (0 <= keyBasedVolume)
1992
2030
  ? defaultVolume * keyBasedVolume / 64
@@ -2138,8 +2176,8 @@ export class MidyGM2 {
2138
2176
  }
2139
2177
  handlePitchBendRangeRPN(channelNumber, scheduleTime) {
2140
2178
  const channel = this.channels[channelNumber];
2141
- this.limitData(channel, 0, 127, 0, 99);
2142
- const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
2179
+ this.limitData(channel, 0, 127, 0, 127);
2180
+ const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
2143
2181
  this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
2144
2182
  }
2145
2183
  setPitchBendRange(channelNumber, value, scheduleTime) {
@@ -2149,7 +2187,7 @@ export class MidyGM2 {
2149
2187
  scheduleTime ??= this.audioContext.currentTime;
2150
2188
  const state = channel.state;
2151
2189
  const prev = state.pitchWheelSensitivity;
2152
- const next = value / 128;
2190
+ const next = value / 12800;
2153
2191
  state.pitchWheelSensitivity = next;
2154
2192
  channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
2155
2193
  this.updateChannelDetune(channel, scheduleTime);
@@ -2158,7 +2196,8 @@ export class MidyGM2 {
2158
2196
  handleFineTuningRPN(channelNumber, scheduleTime) {
2159
2197
  const channel = this.channels[channelNumber];
2160
2198
  this.limitData(channel, 0, 127, 0, 127);
2161
- const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
2199
+ const value = channel.dataMSB * 128 + channel.dataLSB;
2200
+ const fineTuning = (value - 8192) / 8192 * 100;
2162
2201
  this.setFineTuning(channelNumber, fineTuning, scheduleTime);
2163
2202
  }
2164
2203
  setFineTuning(channelNumber, value, scheduleTime) {
@@ -2167,7 +2206,7 @@ export class MidyGM2 {
2167
2206
  return;
2168
2207
  scheduleTime ??= this.audioContext.currentTime;
2169
2208
  const prev = channel.fineTuning;
2170
- const next = (value - 8192) / 8.192; // cent
2209
+ const next = value;
2171
2210
  channel.fineTuning = next;
2172
2211
  channel.detune += next - prev;
2173
2212
  this.updateChannelDetune(channel, scheduleTime);
@@ -2175,7 +2214,7 @@ export class MidyGM2 {
2175
2214
  handleCoarseTuningRPN(channelNumber, scheduleTime) {
2176
2215
  const channel = this.channels[channelNumber];
2177
2216
  this.limitDataMSB(channel, 0, 127);
2178
- const coarseTuning = channel.dataMSB;
2217
+ const coarseTuning = (channel.dataMSB - 64) * 100;
2179
2218
  this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
2180
2219
  }
2181
2220
  setCoarseTuning(channelNumber, value, scheduleTime) {
@@ -2184,7 +2223,7 @@ export class MidyGM2 {
2184
2223
  return;
2185
2224
  scheduleTime ??= this.audioContext.currentTime;
2186
2225
  const prev = channel.coarseTuning;
2187
- const next = (value - 64) * 100; // cent
2226
+ const next = value;
2188
2227
  channel.coarseTuning = next;
2189
2228
  channel.detune += next - prev;
2190
2229
  this.updateChannelDetune(channel, scheduleTime);
@@ -2192,22 +2231,22 @@ export class MidyGM2 {
2192
2231
  handleModulationDepthRangeRPN(channelNumber, scheduleTime) {
2193
2232
  const channel = this.channels[channelNumber];
2194
2233
  this.limitData(channel, 0, 127, 0, 127);
2195
- const modulationDepthRange = (dataMSB + dataLSB / 128) * 100;
2196
- this.setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime);
2234
+ const value = (channel.dataMSB + channel.dataLSB / 128) * 100;
2235
+ this.setModulationDepthRange(channelNumber, value, scheduleTime);
2197
2236
  }
2198
- setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime) {
2237
+ setModulationDepthRange(channelNumber, value, scheduleTime) {
2199
2238
  const channel = this.channels[channelNumber];
2200
2239
  if (channel.isDrum)
2201
2240
  return;
2202
2241
  scheduleTime ??= this.audioContext.currentTime;
2203
- channel.modulationDepthRange = modulationDepthRange;
2242
+ channel.modulationDepthRange = value;
2204
2243
  this.updateModulation(channel, scheduleTime);
2205
2244
  }
2206
2245
  allSoundOff(channelNumber, _value, scheduleTime) {
2207
2246
  scheduleTime ??= this.audioContext.currentTime;
2208
2247
  return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
2209
2248
  }
2210
- resetAllStates(channelNumber) {
2249
+ resetChannelStates(channelNumber) {
2211
2250
  const scheduleTime = this.audioContext.currentTime;
2212
2251
  const channel = this.channels[channelNumber];
2213
2252
  const state = channel.state;
@@ -2225,8 +2264,8 @@ export class MidyGM2 {
2225
2264
  }
2226
2265
  this.resetChannelTable(channel);
2227
2266
  this.mode = "GM2";
2228
- this.masterFineTuning = 0; // cb
2229
- this.masterCoarseTuning = 0; // cb
2267
+ this.masterFineTuning = 0; // cent
2268
+ this.masterCoarseTuning = 0; // cent
2230
2269
  }
2231
2270
  // https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
2232
2271
  resetAllControllers(channelNumber, _value, scheduleTime) {
@@ -2395,12 +2434,13 @@ export class MidyGM2 {
2395
2434
  }
2396
2435
  }
2397
2436
  handleMasterFineTuningSysEx(data, scheduleTime) {
2398
- const fineTuning = data[5] * 128 + data[4];
2437
+ const value = (data[5] * 128 + data[4]) / 16383;
2438
+ const fineTuning = (value - 8192) / 8192 * 100;
2399
2439
  this.setMasterFineTuning(fineTuning, scheduleTime);
2400
2440
  }
2401
2441
  setMasterFineTuning(value, scheduleTime) {
2402
2442
  const prev = this.masterFineTuning;
2403
- const next = (value - 8192) / 8.192; // cent
2443
+ const next = value;
2404
2444
  this.masterFineTuning = next;
2405
2445
  const detuneChange = next - prev;
2406
2446
  for (let i = 0; i < this.channels.length; i++) {
@@ -2412,12 +2452,12 @@ export class MidyGM2 {
2412
2452
  }
2413
2453
  }
2414
2454
  handleMasterCoarseTuningSysEx(data, scheduleTime) {
2415
- const coarseTuning = data[4];
2455
+ const coarseTuning = (data[4] - 64) * 100;
2416
2456
  this.setMasterCoarseTuning(coarseTuning, scheduleTime);
2417
2457
  }
2418
2458
  setMasterCoarseTuning(value, scheduleTime) {
2419
2459
  const prev = this.masterCoarseTuning;
2420
- const next = (value - 64) * 100; // cent
2460
+ const next = value;
2421
2461
  this.masterCoarseTuning = next;
2422
2462
  const detuneChange = next - prev;
2423
2463
  for (let i = 0; i < this.channels.length; i++) {
@@ -2751,7 +2791,7 @@ export class MidyGM2 {
2751
2791
  }
2752
2792
  getKeyBasedValue(channel, keyNumber, controllerType) {
2753
2793
  const index = keyNumber * 128 + controllerType;
2754
- const controlValue = channel.keyBasedInstrumentControlTable[index];
2794
+ const controlValue = channel.keyBasedTable[index];
2755
2795
  return controlValue;
2756
2796
  }
2757
2797
  handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
@@ -2760,7 +2800,7 @@ export class MidyGM2 {
2760
2800
  if (!channel.isDrum)
2761
2801
  return;
2762
2802
  const keyNumber = data[5];
2763
- const table = channel.keyBasedInstrumentControlTable;
2803
+ const table = channel.keyBasedTable;
2764
2804
  for (let i = 6; i < data.length; i += 2) {
2765
2805
  const controllerType = data[i];
2766
2806
  const value = data[i + 1];
@@ -2771,8 +2811,20 @@ export class MidyGM2 {
2771
2811
  case 10:
2772
2812
  this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
2773
2813
  break;
2774
- default: // TODO
2775
- this.setControlChange(channelNumber, controllerType, value, scheduleTime);
2814
+ case 91:
2815
+ this.processScheduledNotes(channel, (note) => {
2816
+ if (note.noteNumber === keyNumber) {
2817
+ this.setReverbSend(channel, note, scheduleTime);
2818
+ }
2819
+ });
2820
+ break;
2821
+ case 93:
2822
+ this.processScheduledNotes(channel, (note) => {
2823
+ if (note.noteNumber === keyNumber) {
2824
+ this.setChorusSend(channel, note, scheduleTime);
2825
+ }
2826
+ });
2827
+ break;
2776
2828
  }
2777
2829
  }
2778
2830
  }
@@ -2823,7 +2875,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2823
2875
  rpnLSB: 127,
2824
2876
  mono: false, // CC#124, CC#125
2825
2877
  modulationDepthRange: 50, // cent
2826
- fineTuning: 0, // cb
2827
- coarseTuning: 0, // cb
2878
+ fineTuning: 0, // cent
2879
+ coarseTuning: 0, // cent
2828
2880
  }
2829
2881
  });