@marmooo/midy 0.2.7 → 0.2.8

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.
@@ -287,18 +287,6 @@ class MidyGM2 {
287
287
  delayTimes: this.generateDistributedArray(0.02, 2, 0.5),
288
288
  }
289
289
  });
290
- Object.defineProperty(this, "mono", {
291
- enumerable: true,
292
- configurable: true,
293
- writable: true,
294
- value: false
295
- }); // CC#124, CC#125
296
- Object.defineProperty(this, "omni", {
297
- enumerable: true,
298
- configurable: true,
299
- writable: true,
300
- value: false
301
- }); // CC#126, CC#127
302
290
  Object.defineProperty(this, "noteCheckInterval", {
303
291
  enumerable: true,
304
292
  configurable: true,
@@ -500,6 +488,7 @@ class MidyGM2 {
500
488
  controlTable: this.initControlTable(),
501
489
  ...this.setChannelAudioNodes(audioContext),
502
490
  scheduledNotes: new SparseMap(128),
491
+ sustainNotes: [],
503
492
  sostenutoNotes: new SparseMap(128),
504
493
  scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
505
494
  channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
@@ -582,7 +571,7 @@ class MidyGM2 {
582
571
  const portamentoTarget = this.findPortamentoTarget(queueIndex);
583
572
  if (portamentoTarget)
584
573
  portamentoTarget.portamento = true;
585
- const notePromise = this.scheduleNoteOff(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, startTime, false, // force
574
+ const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false, // force
586
575
  portamentoTarget?.noteNumber);
587
576
  if (notePromise) {
588
577
  this.notePromises.push(notePromise);
@@ -590,7 +579,7 @@ class MidyGM2 {
590
579
  break;
591
580
  }
592
581
  case "controller":
593
- this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value, startTime);
582
+ this.handleControlChange(event.channel, event.controllerType, event.value, startTime);
594
583
  break;
595
584
  case "programChange":
596
585
  this.handleProgramChange(event.channel, event.programNumber, startTime);
@@ -786,15 +775,10 @@ class MidyGM2 {
786
775
  stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
787
776
  const channel = this.channels[channelNumber];
788
777
  const promises = [];
789
- channel.scheduledNotes.forEach((noteList) => {
790
- for (let i = 0; i < noteList.length; i++) {
791
- const note = noteList[i];
792
- if (!note)
793
- continue;
794
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
795
- this.notePromises.push(promise);
796
- promises.push(promise);
797
- }
778
+ this.processScheduledNotes(channel, (note) => {
779
+ const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
780
+ this.notePromises.push(promise);
781
+ promises.push(promise);
798
782
  });
799
783
  channel.scheduledNotes.clear();
800
784
  return Promise.all(promises);
@@ -850,14 +834,12 @@ class MidyGM2 {
850
834
  const now = this.audioContext.currentTime;
851
835
  return this.resumeTime + now - this.startTime - this.startDelay;
852
836
  }
853
- processScheduledNotes(channel, scheduleTime, callback) {
837
+ processScheduledNotes(channel, callback) {
854
838
  channel.scheduledNotes.forEach((noteList) => {
855
839
  for (let i = 0; i < noteList.length; i++) {
856
840
  const note = noteList[i];
857
841
  if (!note)
858
842
  continue;
859
- if (scheduleTime < note.startTime)
860
- continue;
861
843
  callback(note);
862
844
  }
863
845
  });
@@ -1038,7 +1020,7 @@ class MidyGM2 {
1038
1020
  return channel.scaleOctaveTuningTable[note.noteNumber % 12];
1039
1021
  }
1040
1022
  updateChannelDetune(channel, scheduleTime) {
1041
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1023
+ this.processScheduledNotes(channel, (note) => {
1042
1024
  this.updateDetune(channel, note, scheduleTime);
1043
1025
  });
1044
1026
  }
@@ -1239,7 +1221,7 @@ class MidyGM2 {
1239
1221
  if (0 < state.modulationDepth) {
1240
1222
  this.startModulation(channel, note, now);
1241
1223
  }
1242
- if (this.mono && channel.currentBufferSource) {
1224
+ if (channel.mono && channel.currentBufferSource) {
1243
1225
  channel.currentBufferSource.stop(startTime);
1244
1226
  channel.currentBufferSource = note.bufferSource;
1245
1227
  }
@@ -1280,8 +1262,8 @@ class MidyGM2 {
1280
1262
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3);
1281
1263
  note.gainL.connect(channel.gainL);
1282
1264
  note.gainR.connect(channel.gainR);
1283
- if (channel.state.sostenutoPedal) {
1284
- channel.sostenutoNotes.set(noteNumber, note);
1265
+ if (0.5 <= channel.state.sustainPedal) {
1266
+ channel.sustainNotes.push(note);
1285
1267
  }
1286
1268
  const exclusiveClass = note.voiceParams.exclusiveClass;
1287
1269
  if (exclusiveClass !== 0) {
@@ -1350,7 +1332,7 @@ class MidyGM2 {
1350
1332
  const channel = this.channels[channelNumber];
1351
1333
  const state = channel.state;
1352
1334
  if (!force) {
1353
- if (0.5 < state.sustainPedal)
1335
+ if (0.5 <= state.sustainPedal)
1354
1336
  return;
1355
1337
  if (channel.sostenutoNotes.has(noteNumber))
1356
1338
  return;
@@ -1394,28 +1376,27 @@ class MidyGM2 {
1394
1376
  const velocity = halfVelocity * 2;
1395
1377
  const channel = this.channels[channelNumber];
1396
1378
  const promises = [];
1397
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1398
- const { noteNumber } = note;
1399
- const promise = this.noteOff(channelNumber, noteNumber, velocity);
1379
+ for (let i = 0; i < channel.sustainNotes.length; i++) {
1380
+ const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
1400
1381
  promises.push(promise);
1401
- });
1382
+ }
1383
+ channel.sustainNotes = [];
1402
1384
  return promises;
1403
1385
  }
1404
- releaseSostenutoPedal(channelNumber, halfVelocity) {
1386
+ releaseSostenutoPedal(channelNumber, halfVelocity, scheduleTime) {
1405
1387
  const velocity = halfVelocity * 2;
1406
1388
  const channel = this.channels[channelNumber];
1407
1389
  const promises = [];
1408
1390
  channel.state.sostenutoPedal = 0;
1409
- channel.sostenutoNotes.forEach((activeNote) => {
1410
- const { noteNumber } = activeNote;
1411
- const promise = this.noteOff(channelNumber, noteNumber, velocity);
1391
+ channel.sostenutoNotes.forEach((note) => {
1392
+ const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
1412
1393
  promises.push(promise);
1413
1394
  });
1414
1395
  channel.sostenutoNotes.clear();
1415
1396
  return promises;
1416
1397
  }
1417
1398
  handleMIDIMessage(statusByte, data1, data2, scheduleTime) {
1418
- const channelNumber = omni ? 0 : statusByte & 0x0F;
1399
+ const channelNumber = statusByte & 0x0F;
1419
1400
  const messageType = statusByte & 0xF0;
1420
1401
  switch (messageType) {
1421
1402
  case 0x80:
@@ -1630,53 +1611,48 @@ class MidyGM2 {
1630
1611
  return state;
1631
1612
  }
1632
1613
  applyVoiceParams(channel, controllerType, scheduleTime) {
1633
- channel.scheduledNotes.forEach((noteList) => {
1634
- for (let i = 0; i < noteList.length; i++) {
1635
- const note = noteList[i];
1636
- if (!note)
1614
+ this.processScheduledNotes(channel, (note) => {
1615
+ const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
1616
+ const voiceParams = note.voice.getParams(controllerType, controllerState);
1617
+ let appliedFilterEnvelope = false;
1618
+ let appliedVolumeEnvelope = false;
1619
+ for (const [key, value] of Object.entries(voiceParams)) {
1620
+ const prevValue = note.voiceParams[key];
1621
+ if (value === prevValue)
1637
1622
  continue;
1638
- const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
1639
- const voiceParams = note.voice.getParams(controllerType, controllerState);
1640
- let appliedFilterEnvelope = false;
1641
- let appliedVolumeEnvelope = false;
1642
- for (const [key, value] of Object.entries(voiceParams)) {
1643
- const prevValue = note.voiceParams[key];
1644
- if (value === prevValue)
1623
+ note.voiceParams[key] = value;
1624
+ if (key in this.voiceParamsHandlers) {
1625
+ this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
1626
+ }
1627
+ else if (filterEnvelopeKeySet.has(key)) {
1628
+ if (appliedFilterEnvelope)
1645
1629
  continue;
1646
- note.voiceParams[key] = value;
1647
- if (key in this.voiceParamsHandlers) {
1648
- this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
1630
+ appliedFilterEnvelope = true;
1631
+ const noteVoiceParams = note.voiceParams;
1632
+ for (let i = 0; i < filterEnvelopeKeys.length; i++) {
1633
+ const key = filterEnvelopeKeys[i];
1634
+ if (key in voiceParams)
1635
+ noteVoiceParams[key] = voiceParams[key];
1649
1636
  }
1650
- else if (filterEnvelopeKeySet.has(key)) {
1651
- if (appliedFilterEnvelope)
1652
- continue;
1653
- appliedFilterEnvelope = true;
1654
- const noteVoiceParams = note.voiceParams;
1655
- for (let i = 0; i < filterEnvelopeKeys.length; i++) {
1656
- const key = filterEnvelopeKeys[i];
1657
- if (key in voiceParams)
1658
- noteVoiceParams[key] = voiceParams[key];
1659
- }
1660
- if (note.portamento) {
1661
- this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
1662
- }
1663
- else {
1664
- this.setFilterEnvelope(channel, note, scheduleTime);
1665
- }
1666
- this.setPitchEnvelope(note, scheduleTime);
1637
+ if (note.portamento) {
1638
+ this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
1667
1639
  }
1668
- else if (volumeEnvelopeKeySet.has(key)) {
1669
- if (appliedVolumeEnvelope)
1670
- continue;
1671
- appliedVolumeEnvelope = true;
1672
- const noteVoiceParams = note.voiceParams;
1673
- for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
1674
- const key = volumeEnvelopeKeys[i];
1675
- if (key in voiceParams)
1676
- noteVoiceParams[key] = voiceParams[key];
1677
- }
1678
- this.setVolumeEnvelope(channel, note, scheduleTime);
1640
+ else {
1641
+ this.setFilterEnvelope(channel, note, scheduleTime);
1642
+ }
1643
+ this.setPitchEnvelope(note, scheduleTime);
1644
+ }
1645
+ else if (volumeEnvelopeKeySet.has(key)) {
1646
+ if (appliedVolumeEnvelope)
1647
+ continue;
1648
+ appliedVolumeEnvelope = true;
1649
+ const noteVoiceParams = note.voiceParams;
1650
+ for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
1651
+ const key = volumeEnvelopeKeys[i];
1652
+ if (key in voiceParams)
1653
+ noteVoiceParams[key] = voiceParams[key];
1679
1654
  }
1655
+ this.setVolumeEnvelope(channel, note, scheduleTime);
1680
1656
  }
1681
1657
  }
1682
1658
  });
@@ -1725,9 +1701,8 @@ class MidyGM2 {
1725
1701
  this.channels[channelNumber].bankMSB = msb;
1726
1702
  }
1727
1703
  updateModulation(channel, scheduleTime) {
1728
- scheduleTime ??= this.audioContext.currentTime;
1729
1704
  const depth = channel.state.modulationDepth * channel.modulationDepthRange;
1730
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1705
+ this.processScheduledNotes(channel, (note) => {
1731
1706
  if (note.modulationDepth) {
1732
1707
  note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
1733
1708
  }
@@ -1738,6 +1713,7 @@ class MidyGM2 {
1738
1713
  });
1739
1714
  }
1740
1715
  setModulationDepth(channelNumber, modulation, scheduleTime) {
1716
+ scheduleTime ??= this.audioContext.currentTime;
1741
1717
  const channel = this.channels[channelNumber];
1742
1718
  channel.state.modulationDepth = modulation / 127;
1743
1719
  this.updateModulation(channel, scheduleTime);
@@ -1748,8 +1724,7 @@ class MidyGM2 {
1748
1724
  channel.state.portamentoTime = Math.exp(factor * portamentoTime);
1749
1725
  }
1750
1726
  setKeyBasedVolume(channel, scheduleTime) {
1751
- scheduleTime ??= this.audioContext.currentTime;
1752
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1727
+ this.processScheduledNotes(channel, (note) => {
1753
1728
  const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
1754
1729
  if (keyBasedValue !== 0) {
1755
1730
  note.volumeNode.gain
@@ -1759,6 +1734,7 @@ class MidyGM2 {
1759
1734
  });
1760
1735
  }
1761
1736
  setVolume(channelNumber, volume, scheduleTime) {
1737
+ scheduleTime ??= this.audioContext.currentTime;
1762
1738
  const channel = this.channels[channelNumber];
1763
1739
  channel.state.volume = volume / 127;
1764
1740
  this.updateChannelVolume(channel, scheduleTime);
@@ -1772,8 +1748,7 @@ class MidyGM2 {
1772
1748
  };
1773
1749
  }
1774
1750
  setKeyBasedPan(channel, scheduleTime) {
1775
- scheduleTime ??= this.audioContext.currentTime;
1776
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1751
+ this.processScheduledNotes(channel, (note) => {
1777
1752
  const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
1778
1753
  if (keyBasedValue !== 0) {
1779
1754
  const { gainLeft, gainRight } = this.panToGain((keyBasedValue + 1) / 2);
@@ -1787,12 +1762,14 @@ class MidyGM2 {
1787
1762
  });
1788
1763
  }
1789
1764
  setPan(channelNumber, pan, scheduleTime) {
1765
+ scheduleTime ??= this.audioContext.currentTime;
1790
1766
  const channel = this.channels[channelNumber];
1791
1767
  channel.state.pan = pan / 127;
1792
1768
  this.updateChannelVolume(channel, scheduleTime);
1793
1769
  this.setKeyBasedPan(channel, scheduleTime);
1794
1770
  }
1795
1771
  setExpression(channelNumber, expression, scheduleTime) {
1772
+ scheduleTime ??= this.audioContext.currentTime;
1796
1773
  const channel = this.channels[channelNumber];
1797
1774
  channel.state.expression = expression / 127;
1798
1775
  this.updateChannelVolume(channel, scheduleTime);
@@ -1817,8 +1794,14 @@ class MidyGM2 {
1817
1794
  }
1818
1795
  setSustainPedal(channelNumber, value, scheduleTime) {
1819
1796
  scheduleTime ??= this.audioContext.currentTime;
1820
- this.channels[channelNumber].state.sustainPedal = value / 127;
1821
- if (value < 64) {
1797
+ const channel = this.channels[channelNumber];
1798
+ channel.state.sustainPedal = value / 127;
1799
+ if (64 <= value) {
1800
+ this.processScheduledNotes(channel, (note) => {
1801
+ channel.sustainNotes.push(note);
1802
+ });
1803
+ }
1804
+ else {
1822
1805
  this.releaseSustainPedal(channelNumber, value, scheduleTime);
1823
1806
  }
1824
1807
  }
@@ -1826,13 +1809,14 @@ class MidyGM2 {
1826
1809
  this.channels[channelNumber].state.portamento = value / 127;
1827
1810
  }
1828
1811
  setSostenutoPedal(channelNumber, value, scheduleTime) {
1812
+ scheduleTime ??= this.audioContext.currentTime;
1829
1813
  const channel = this.channels[channelNumber];
1830
1814
  channel.state.sostenutoPedal = value / 127;
1831
1815
  if (64 <= value) {
1832
1816
  channel.sostenutoNotes = this.getActiveNotes(channel, scheduleTime);
1833
1817
  }
1834
1818
  else {
1835
- this.releaseSostenutoPedal(channelNumber, value);
1819
+ this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
1836
1820
  }
1837
1821
  }
1838
1822
  setSoftPedal(channelNumber, softPedal, _scheduleTime) {
@@ -1840,6 +1824,7 @@ class MidyGM2 {
1840
1824
  channel.state.softPedal = softPedal / 127;
1841
1825
  }
1842
1826
  setReverbSendLevel(channelNumber, reverbSendLevel, scheduleTime) {
1827
+ scheduleTime ??= this.audioContext.currentTime;
1843
1828
  const channel = this.channels[channelNumber];
1844
1829
  const state = channel.state;
1845
1830
  const reverbEffect = this.reverbEffect;
@@ -1851,27 +1836,17 @@ class MidyGM2 {
1851
1836
  .setValueAtTime(state.reverbSendLevel, scheduleTime);
1852
1837
  }
1853
1838
  else {
1854
- channel.scheduledNotes.forEach((noteList) => {
1855
- for (let i = 0; i < noteList.length; i++) {
1856
- const note = noteList[i];
1857
- if (!note)
1858
- continue;
1859
- if (note.voiceParams.reverbEffectsSend <= 0)
1860
- continue;
1861
- note.reverbEffectsSend.disconnect();
1862
- }
1839
+ this.processScheduledNotes(channel, (note) => {
1840
+ if (note.voiceParams.reverbEffectsSend <= 0)
1841
+ return false;
1842
+ note.reverbEffectsSend.disconnect();
1863
1843
  });
1864
1844
  }
1865
1845
  }
1866
1846
  else {
1867
1847
  if (0 < reverbSendLevel) {
1868
- channel.scheduledNotes.forEach((noteList) => {
1869
- for (let i = 0; i < noteList.length; i++) {
1870
- const note = noteList[i];
1871
- if (!note)
1872
- continue;
1873
- this.setReverbEffectsSend(channel, note, 0, scheduleTime);
1874
- }
1848
+ this.processScheduledNotes(channel, (note) => {
1849
+ this.setReverbEffectsSend(channel, note, 0, scheduleTime);
1875
1850
  });
1876
1851
  state.reverbSendLevel = reverbSendLevel / 127;
1877
1852
  reverbEffect.input.gain
@@ -1881,6 +1856,7 @@ class MidyGM2 {
1881
1856
  }
1882
1857
  }
1883
1858
  setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
1859
+ scheduleTime ??= this.audioContext.currentTime;
1884
1860
  const channel = this.channels[channelNumber];
1885
1861
  const state = channel.state;
1886
1862
  const chorusEffect = this.chorusEffect;
@@ -1892,27 +1868,17 @@ class MidyGM2 {
1892
1868
  .setValueAtTime(state.chorusSendLevel, scheduleTime);
1893
1869
  }
1894
1870
  else {
1895
- channel.scheduledNotes.forEach((noteList) => {
1896
- for (let i = 0; i < noteList.length; i++) {
1897
- const note = noteList[i];
1898
- if (!note)
1899
- continue;
1900
- if (note.voiceParams.chorusEffectsSend <= 0)
1901
- continue;
1902
- note.chorusEffectsSend.disconnect();
1903
- }
1871
+ this.processScheduledNotes(channel, (note) => {
1872
+ if (note.voiceParams.chorusEffectsSend <= 0)
1873
+ return false;
1874
+ note.chorusEffectsSend.disconnect();
1904
1875
  });
1905
1876
  }
1906
1877
  }
1907
1878
  else {
1908
1879
  if (0 < chorusSendLevel) {
1909
- channel.scheduledNotes.forEach((noteList) => {
1910
- for (let i = 0; i < noteList.length; i++) {
1911
- const note = noteList[i];
1912
- if (!note)
1913
- continue;
1914
- this.setChorusEffectsSend(channel, note, 0, scheduleTime);
1915
- }
1880
+ this.processScheduledNotes(channel, (note) => {
1881
+ this.setChorusEffectsSend(channel, note, 0, scheduleTime);
1916
1882
  });
1917
1883
  state.chorusSendLevel = chorusSendLevel / 127;
1918
1884
  chorusEffect.input.gain
@@ -1955,13 +1921,13 @@ class MidyGM2 {
1955
1921
  this.handlePitchBendRangeRPN(channelNumber, scheduleTime);
1956
1922
  break;
1957
1923
  case 1:
1958
- this.handleFineTuningRPN(channelNumber);
1924
+ this.handleFineTuningRPN(channelNumber, scheduleTime);
1959
1925
  break;
1960
1926
  case 2:
1961
- this.handleCoarseTuningRPN(channelNumber);
1927
+ this.handleCoarseTuningRPN(channelNumber, scheduleTime);
1962
1928
  break;
1963
1929
  case 5:
1964
- this.handleModulationDepthRangeRPN(channelNumber);
1930
+ this.handleModulationDepthRangeRPN(channelNumber, scheduleTime);
1965
1931
  break;
1966
1932
  default:
1967
1933
  console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
@@ -1994,44 +1960,47 @@ class MidyGM2 {
1994
1960
  this.updateChannelDetune(channel, scheduleTime);
1995
1961
  this.applyVoiceParams(channel, 16, scheduleTime);
1996
1962
  }
1997
- handleFineTuningRPN(channelNumber) {
1963
+ handleFineTuningRPN(channelNumber, scheduleTime) {
1998
1964
  const channel = this.channels[channelNumber];
1999
1965
  this.limitData(channel, 0, 127, 0, 127);
2000
1966
  const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
2001
- this.setFineTuning(channelNumber, fineTuning);
1967
+ this.setFineTuning(channelNumber, fineTuning, scheduleTime);
2002
1968
  }
2003
- setFineTuning(channelNumber, value) {
1969
+ setFineTuning(channelNumber, value, scheduleTime) {
1970
+ scheduleTime ??= this.audioContext.currentTime;
2004
1971
  const channel = this.channels[channelNumber];
2005
1972
  const prev = channel.fineTuning;
2006
1973
  const next = (value - 8192) / 8.192; // cent
2007
1974
  channel.fineTuning = next;
2008
1975
  channel.detune += next - prev;
2009
- this.updateChannelDetune(channel);
1976
+ this.updateChannelDetune(channel, scheduleTime);
2010
1977
  }
2011
- handleCoarseTuningRPN(channelNumber) {
1978
+ handleCoarseTuningRPN(channelNumber, scheduleTime) {
2012
1979
  const channel = this.channels[channelNumber];
2013
1980
  this.limitDataMSB(channel, 0, 127);
2014
1981
  const coarseTuning = channel.dataMSB;
2015
- this.setCoarseTuning(channelNumber, coarseTuning);
1982
+ this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
2016
1983
  }
2017
- setCoarseTuning(channelNumber, value) {
1984
+ setCoarseTuning(channelNumber, value, scheduleTime) {
1985
+ scheduleTime ??= this.audioContext.currentTime;
2018
1986
  const channel = this.channels[channelNumber];
2019
1987
  const prev = channel.coarseTuning;
2020
1988
  const next = (value - 64) * 100; // cent
2021
1989
  channel.coarseTuning = next;
2022
1990
  channel.detune += next - prev;
2023
- this.updateChannelDetune(channel);
1991
+ this.updateChannelDetune(channel, scheduleTime);
2024
1992
  }
2025
- handleModulationDepthRangeRPN(channelNumber) {
1993
+ handleModulationDepthRangeRPN(channelNumber, scheduleTime) {
2026
1994
  const channel = this.channels[channelNumber];
2027
1995
  this.limitData(channel, 0, 127, 0, 127);
2028
1996
  const modulationDepthRange = (dataMSB + dataLSB / 128) * 100;
2029
- this.setModulationDepthRange(channelNumber, modulationDepthRange);
1997
+ this.setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime);
2030
1998
  }
2031
- setModulationDepthRange(channelNumber, modulationDepthRange) {
1999
+ setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime) {
2000
+ scheduleTime ??= this.audioContext.currentTime;
2032
2001
  const channel = this.channels[channelNumber];
2033
2002
  channel.modulationDepthRange = modulationDepthRange;
2034
- this.updateModulation(channel);
2003
+ this.updateModulation(channel, scheduleTime);
2035
2004
  }
2036
2005
  allSoundOff(channelNumber, _value, scheduleTime) {
2037
2006
  scheduleTime ??= this.audioContext.currentTime;
@@ -2067,17 +2036,21 @@ class MidyGM2 {
2067
2036
  scheduleTime ??= this.audioContext.currentTime;
2068
2037
  return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
2069
2038
  }
2070
- omniOff() {
2071
- this.omni = false;
2039
+ omniOff(channelNumber, value, scheduleTime) {
2040
+ this.allNotesOff(channelNumber, value, scheduleTime);
2072
2041
  }
2073
- omniOn() {
2074
- this.omni = true;
2042
+ omniOn(channelNumber, value, scheduleTime) {
2043
+ this.allNotesOff(channelNumber, value, scheduleTime);
2075
2044
  }
2076
- monoOn() {
2077
- this.mono = true;
2045
+ monoOn(channelNumber, value, scheduleTime) {
2046
+ const channel = this.channels[channelNumber];
2047
+ this.allNotesOff(channelNumber, value, scheduleTime);
2048
+ channel.mono = true;
2078
2049
  }
2079
- polyOn() {
2080
- this.mono = false;
2050
+ polyOn(channelNumber, value, scheduleTime) {
2051
+ const channel = this.channels[channelNumber];
2052
+ this.allNotesOff(channelNumber, value, scheduleTime);
2053
+ channel.mono = false;
2081
2054
  }
2082
2055
  handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
2083
2056
  switch (data[2]) {
@@ -2486,13 +2459,8 @@ class MidyGM2 {
2486
2459
  const slotSize = 6;
2487
2460
  const offset = controllerType * slotSize;
2488
2461
  const table = channel.controlTable.subarray(offset, offset + slotSize);
2489
- channel.scheduledNotes.forEach((noteList) => {
2490
- for (let i = 0; i < noteList.length; i++) {
2491
- const note = noteList[i];
2492
- if (!note)
2493
- continue;
2494
- this.setControllerParameters(channel, note, table);
2495
- }
2462
+ this.processScheduledNotes(channel, (note) => {
2463
+ this.setControllerParameters(channel, note, table);
2496
2464
  });
2497
2465
  }
2498
2466
  handleControlChangeSysEx(data) {
@@ -2560,6 +2528,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2560
2528
  dataLSB: 0,
2561
2529
  rpnMSB: 127,
2562
2530
  rpnLSB: 127,
2531
+ mono: false, // CC#124, CC#125
2563
2532
  fineTuning: 0, // cb
2564
2533
  coarseTuning: 0, // cb
2565
2534
  modulationDepthRange: 50, // cent
@@ -90,7 +90,7 @@ export class MidyGMLite {
90
90
  seekTo(second: any): void;
91
91
  calcTotalTime(): number;
92
92
  currentTime(): number;
93
- processScheduledNotes(channel: any, scheduleTime: any, callback: any): void;
93
+ processScheduledNotes(channel: any, callback: any): void;
94
94
  getActiveNotes(channel: any, scheduleTime: any): SparseMap;
95
95
  getActiveNote(noteList: any, scheduleTime: any): any;
96
96
  cbToRatio(cb: any): number;
@@ -112,7 +112,7 @@ export class MidyGMLite {
112
112
  stopNote(endTime: any, stopTime: any, scheduledNotes: any, index: any): Promise<any>;
113
113
  scheduleNoteOff(channelNumber: any, noteNumber: any, _velocity: any, endTime: any, force: any): Promise<any> | undefined;
114
114
  noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): Promise<any> | undefined;
115
- releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): any[];
115
+ releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
116
116
  handleMIDIMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
117
117
  handleProgramChange(channelNumber: any, program: any, _scheduleTime: any): void;
118
118
  handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAiJA;IAsBE;;;;;;;;;MASE;IAEF,+BAQC;IAxCD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,6BAAuC;IAcrC,kBAAgC;IAChC,kBAA8C;IAC9C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,8DASC;IAED,2EAqDC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,mGAoBC;IAED,wEAMC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,4EASC;IAED,2DASC;IAED,qDAQC;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,yGAgBC;IAED,gHAwCC;IAED,kGAgDC;IAED,6FAQC;IAED,qFAwBC;IAED,yHAuBC;IAED,yGASC;IAED,qFAUC;IAED,mGA2BC;IAED,gFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EAkDC;IAED;;;;;;;;;;;;;MAeC;IAED,kGAWC;IAED,wDAWC;IAED,iFAIC;IAED,oEAIC;IAED;;;MAMC;IAED,8DAIC;IAED,4EAIC;IAED,sEAGC;IAED,2DAUC;IAED,yEAMC;IAED,kFAeC;IAED,uDAYC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,gFAGC;IAED,8CAqBC;IAED,gFAGC;IAED,gFAgBC;IAED,oBAMC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAED,6DAUC;CACF;AA32CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;IASE,0FAMC;IAdD,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":"AAiJA;IAsBE;;;;;;;;;MASE;IAEF,+BAQC;IAxCD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,6BAAuC;IAcrC,kBAAgC;IAChC,kBAA8C;IAC9C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAWC;IAED,6DA2BC;IAED,8DASC;IAED,2EAsDC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,mGAgBC;IAED,wEAMC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,2DASC;IAED,qDAQC;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,yGAgBC;IAED,gHAwCC;IAED,kGAkDC;IAED,6FAQC;IAED,qFAwBC;IAED,yHAuBC;IAED,yGASC;IAED,4GAeC;IAED,mGA2BC;IAED,gFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EA2CC;IAED;;;;;;;;;;;;;MAeC;IAED,kGAWC;IAED,wDAWC;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,8CAqBC;IAED,gFAGC;IAED,gFAgBC;IAED,oBAMC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAED,6DAUC;CACF;AAj3CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;IASE,0FAMC;IAdD,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}