@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.
package/esm/midy-GM2.js CHANGED
@@ -284,18 +284,6 @@ export class MidyGM2 {
284
284
  delayTimes: this.generateDistributedArray(0.02, 2, 0.5),
285
285
  }
286
286
  });
287
- Object.defineProperty(this, "mono", {
288
- enumerable: true,
289
- configurable: true,
290
- writable: true,
291
- value: false
292
- }); // CC#124, CC#125
293
- Object.defineProperty(this, "omni", {
294
- enumerable: true,
295
- configurable: true,
296
- writable: true,
297
- value: false
298
- }); // CC#126, CC#127
299
287
  Object.defineProperty(this, "noteCheckInterval", {
300
288
  enumerable: true,
301
289
  configurable: true,
@@ -497,6 +485,7 @@ export class MidyGM2 {
497
485
  controlTable: this.initControlTable(),
498
486
  ...this.setChannelAudioNodes(audioContext),
499
487
  scheduledNotes: new SparseMap(128),
488
+ sustainNotes: [],
500
489
  sostenutoNotes: new SparseMap(128),
501
490
  scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
502
491
  channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
@@ -579,7 +568,7 @@ export class MidyGM2 {
579
568
  const portamentoTarget = this.findPortamentoTarget(queueIndex);
580
569
  if (portamentoTarget)
581
570
  portamentoTarget.portamento = true;
582
- const notePromise = this.scheduleNoteOff(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, startTime, false, // force
571
+ const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false, // force
583
572
  portamentoTarget?.noteNumber);
584
573
  if (notePromise) {
585
574
  this.notePromises.push(notePromise);
@@ -587,7 +576,7 @@ export class MidyGM2 {
587
576
  break;
588
577
  }
589
578
  case "controller":
590
- this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value, startTime);
579
+ this.handleControlChange(event.channel, event.controllerType, event.value, startTime);
591
580
  break;
592
581
  case "programChange":
593
582
  this.handleProgramChange(event.channel, event.programNumber, startTime);
@@ -783,15 +772,10 @@ export class MidyGM2 {
783
772
  stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
784
773
  const channel = this.channels[channelNumber];
785
774
  const promises = [];
786
- channel.scheduledNotes.forEach((noteList) => {
787
- for (let i = 0; i < noteList.length; i++) {
788
- const note = noteList[i];
789
- if (!note)
790
- continue;
791
- const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
792
- this.notePromises.push(promise);
793
- promises.push(promise);
794
- }
775
+ this.processScheduledNotes(channel, (note) => {
776
+ const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
777
+ this.notePromises.push(promise);
778
+ promises.push(promise);
795
779
  });
796
780
  channel.scheduledNotes.clear();
797
781
  return Promise.all(promises);
@@ -847,14 +831,12 @@ export class MidyGM2 {
847
831
  const now = this.audioContext.currentTime;
848
832
  return this.resumeTime + now - this.startTime - this.startDelay;
849
833
  }
850
- processScheduledNotes(channel, scheduleTime, callback) {
834
+ processScheduledNotes(channel, callback) {
851
835
  channel.scheduledNotes.forEach((noteList) => {
852
836
  for (let i = 0; i < noteList.length; i++) {
853
837
  const note = noteList[i];
854
838
  if (!note)
855
839
  continue;
856
- if (scheduleTime < note.startTime)
857
- continue;
858
840
  callback(note);
859
841
  }
860
842
  });
@@ -1035,7 +1017,7 @@ export class MidyGM2 {
1035
1017
  return channel.scaleOctaveTuningTable[note.noteNumber % 12];
1036
1018
  }
1037
1019
  updateChannelDetune(channel, scheduleTime) {
1038
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1020
+ this.processScheduledNotes(channel, (note) => {
1039
1021
  this.updateDetune(channel, note, scheduleTime);
1040
1022
  });
1041
1023
  }
@@ -1236,7 +1218,7 @@ export class MidyGM2 {
1236
1218
  if (0 < state.modulationDepth) {
1237
1219
  this.startModulation(channel, note, now);
1238
1220
  }
1239
- if (this.mono && channel.currentBufferSource) {
1221
+ if (channel.mono && channel.currentBufferSource) {
1240
1222
  channel.currentBufferSource.stop(startTime);
1241
1223
  channel.currentBufferSource = note.bufferSource;
1242
1224
  }
@@ -1277,8 +1259,8 @@ export class MidyGM2 {
1277
1259
  const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3);
1278
1260
  note.gainL.connect(channel.gainL);
1279
1261
  note.gainR.connect(channel.gainR);
1280
- if (channel.state.sostenutoPedal) {
1281
- channel.sostenutoNotes.set(noteNumber, note);
1262
+ if (0.5 <= channel.state.sustainPedal) {
1263
+ channel.sustainNotes.push(note);
1282
1264
  }
1283
1265
  const exclusiveClass = note.voiceParams.exclusiveClass;
1284
1266
  if (exclusiveClass !== 0) {
@@ -1347,7 +1329,7 @@ export class MidyGM2 {
1347
1329
  const channel = this.channels[channelNumber];
1348
1330
  const state = channel.state;
1349
1331
  if (!force) {
1350
- if (0.5 < state.sustainPedal)
1332
+ if (0.5 <= state.sustainPedal)
1351
1333
  return;
1352
1334
  if (channel.sostenutoNotes.has(noteNumber))
1353
1335
  return;
@@ -1391,28 +1373,27 @@ export class MidyGM2 {
1391
1373
  const velocity = halfVelocity * 2;
1392
1374
  const channel = this.channels[channelNumber];
1393
1375
  const promises = [];
1394
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1395
- const { noteNumber } = note;
1396
- const promise = this.noteOff(channelNumber, noteNumber, velocity);
1376
+ for (let i = 0; i < channel.sustainNotes.length; i++) {
1377
+ const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
1397
1378
  promises.push(promise);
1398
- });
1379
+ }
1380
+ channel.sustainNotes = [];
1399
1381
  return promises;
1400
1382
  }
1401
- releaseSostenutoPedal(channelNumber, halfVelocity) {
1383
+ releaseSostenutoPedal(channelNumber, halfVelocity, scheduleTime) {
1402
1384
  const velocity = halfVelocity * 2;
1403
1385
  const channel = this.channels[channelNumber];
1404
1386
  const promises = [];
1405
1387
  channel.state.sostenutoPedal = 0;
1406
- channel.sostenutoNotes.forEach((activeNote) => {
1407
- const { noteNumber } = activeNote;
1408
- const promise = this.noteOff(channelNumber, noteNumber, velocity);
1388
+ channel.sostenutoNotes.forEach((note) => {
1389
+ const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
1409
1390
  promises.push(promise);
1410
1391
  });
1411
1392
  channel.sostenutoNotes.clear();
1412
1393
  return promises;
1413
1394
  }
1414
1395
  handleMIDIMessage(statusByte, data1, data2, scheduleTime) {
1415
- const channelNumber = omni ? 0 : statusByte & 0x0F;
1396
+ const channelNumber = statusByte & 0x0F;
1416
1397
  const messageType = statusByte & 0xF0;
1417
1398
  switch (messageType) {
1418
1399
  case 0x80:
@@ -1627,53 +1608,48 @@ export class MidyGM2 {
1627
1608
  return state;
1628
1609
  }
1629
1610
  applyVoiceParams(channel, controllerType, scheduleTime) {
1630
- channel.scheduledNotes.forEach((noteList) => {
1631
- for (let i = 0; i < noteList.length; i++) {
1632
- const note = noteList[i];
1633
- if (!note)
1611
+ this.processScheduledNotes(channel, (note) => {
1612
+ const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
1613
+ const voiceParams = note.voice.getParams(controllerType, controllerState);
1614
+ let appliedFilterEnvelope = false;
1615
+ let appliedVolumeEnvelope = false;
1616
+ for (const [key, value] of Object.entries(voiceParams)) {
1617
+ const prevValue = note.voiceParams[key];
1618
+ if (value === prevValue)
1634
1619
  continue;
1635
- const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
1636
- const voiceParams = note.voice.getParams(controllerType, controllerState);
1637
- let appliedFilterEnvelope = false;
1638
- let appliedVolumeEnvelope = false;
1639
- for (const [key, value] of Object.entries(voiceParams)) {
1640
- const prevValue = note.voiceParams[key];
1641
- if (value === prevValue)
1620
+ note.voiceParams[key] = value;
1621
+ if (key in this.voiceParamsHandlers) {
1622
+ this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
1623
+ }
1624
+ else if (filterEnvelopeKeySet.has(key)) {
1625
+ if (appliedFilterEnvelope)
1642
1626
  continue;
1643
- note.voiceParams[key] = value;
1644
- if (key in this.voiceParamsHandlers) {
1645
- this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
1627
+ appliedFilterEnvelope = true;
1628
+ const noteVoiceParams = note.voiceParams;
1629
+ for (let i = 0; i < filterEnvelopeKeys.length; i++) {
1630
+ const key = filterEnvelopeKeys[i];
1631
+ if (key in voiceParams)
1632
+ noteVoiceParams[key] = voiceParams[key];
1646
1633
  }
1647
- else if (filterEnvelopeKeySet.has(key)) {
1648
- if (appliedFilterEnvelope)
1649
- continue;
1650
- appliedFilterEnvelope = true;
1651
- const noteVoiceParams = note.voiceParams;
1652
- for (let i = 0; i < filterEnvelopeKeys.length; i++) {
1653
- const key = filterEnvelopeKeys[i];
1654
- if (key in voiceParams)
1655
- noteVoiceParams[key] = voiceParams[key];
1656
- }
1657
- if (note.portamento) {
1658
- this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
1659
- }
1660
- else {
1661
- this.setFilterEnvelope(channel, note, scheduleTime);
1662
- }
1663
- this.setPitchEnvelope(note, scheduleTime);
1634
+ if (note.portamento) {
1635
+ this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
1664
1636
  }
1665
- else if (volumeEnvelopeKeySet.has(key)) {
1666
- if (appliedVolumeEnvelope)
1667
- continue;
1668
- appliedVolumeEnvelope = true;
1669
- const noteVoiceParams = note.voiceParams;
1670
- for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
1671
- const key = volumeEnvelopeKeys[i];
1672
- if (key in voiceParams)
1673
- noteVoiceParams[key] = voiceParams[key];
1674
- }
1675
- this.setVolumeEnvelope(channel, note, scheduleTime);
1637
+ else {
1638
+ this.setFilterEnvelope(channel, note, scheduleTime);
1639
+ }
1640
+ this.setPitchEnvelope(note, scheduleTime);
1641
+ }
1642
+ else if (volumeEnvelopeKeySet.has(key)) {
1643
+ if (appliedVolumeEnvelope)
1644
+ continue;
1645
+ appliedVolumeEnvelope = true;
1646
+ const noteVoiceParams = note.voiceParams;
1647
+ for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
1648
+ const key = volumeEnvelopeKeys[i];
1649
+ if (key in voiceParams)
1650
+ noteVoiceParams[key] = voiceParams[key];
1676
1651
  }
1652
+ this.setVolumeEnvelope(channel, note, scheduleTime);
1677
1653
  }
1678
1654
  }
1679
1655
  });
@@ -1722,9 +1698,8 @@ export class MidyGM2 {
1722
1698
  this.channels[channelNumber].bankMSB = msb;
1723
1699
  }
1724
1700
  updateModulation(channel, scheduleTime) {
1725
- scheduleTime ??= this.audioContext.currentTime;
1726
1701
  const depth = channel.state.modulationDepth * channel.modulationDepthRange;
1727
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1702
+ this.processScheduledNotes(channel, (note) => {
1728
1703
  if (note.modulationDepth) {
1729
1704
  note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
1730
1705
  }
@@ -1735,6 +1710,7 @@ export class MidyGM2 {
1735
1710
  });
1736
1711
  }
1737
1712
  setModulationDepth(channelNumber, modulation, scheduleTime) {
1713
+ scheduleTime ??= this.audioContext.currentTime;
1738
1714
  const channel = this.channels[channelNumber];
1739
1715
  channel.state.modulationDepth = modulation / 127;
1740
1716
  this.updateModulation(channel, scheduleTime);
@@ -1745,8 +1721,7 @@ export class MidyGM2 {
1745
1721
  channel.state.portamentoTime = Math.exp(factor * portamentoTime);
1746
1722
  }
1747
1723
  setKeyBasedVolume(channel, scheduleTime) {
1748
- scheduleTime ??= this.audioContext.currentTime;
1749
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1724
+ this.processScheduledNotes(channel, (note) => {
1750
1725
  const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
1751
1726
  if (keyBasedValue !== 0) {
1752
1727
  note.volumeNode.gain
@@ -1756,6 +1731,7 @@ export class MidyGM2 {
1756
1731
  });
1757
1732
  }
1758
1733
  setVolume(channelNumber, volume, scheduleTime) {
1734
+ scheduleTime ??= this.audioContext.currentTime;
1759
1735
  const channel = this.channels[channelNumber];
1760
1736
  channel.state.volume = volume / 127;
1761
1737
  this.updateChannelVolume(channel, scheduleTime);
@@ -1769,8 +1745,7 @@ export class MidyGM2 {
1769
1745
  };
1770
1746
  }
1771
1747
  setKeyBasedPan(channel, scheduleTime) {
1772
- scheduleTime ??= this.audioContext.currentTime;
1773
- this.processScheduledNotes(channel, scheduleTime, (note) => {
1748
+ this.processScheduledNotes(channel, (note) => {
1774
1749
  const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
1775
1750
  if (keyBasedValue !== 0) {
1776
1751
  const { gainLeft, gainRight } = this.panToGain((keyBasedValue + 1) / 2);
@@ -1784,12 +1759,14 @@ export class MidyGM2 {
1784
1759
  });
1785
1760
  }
1786
1761
  setPan(channelNumber, pan, scheduleTime) {
1762
+ scheduleTime ??= this.audioContext.currentTime;
1787
1763
  const channel = this.channels[channelNumber];
1788
1764
  channel.state.pan = pan / 127;
1789
1765
  this.updateChannelVolume(channel, scheduleTime);
1790
1766
  this.setKeyBasedPan(channel, scheduleTime);
1791
1767
  }
1792
1768
  setExpression(channelNumber, expression, scheduleTime) {
1769
+ scheduleTime ??= this.audioContext.currentTime;
1793
1770
  const channel = this.channels[channelNumber];
1794
1771
  channel.state.expression = expression / 127;
1795
1772
  this.updateChannelVolume(channel, scheduleTime);
@@ -1814,8 +1791,14 @@ export class MidyGM2 {
1814
1791
  }
1815
1792
  setSustainPedal(channelNumber, value, scheduleTime) {
1816
1793
  scheduleTime ??= this.audioContext.currentTime;
1817
- this.channels[channelNumber].state.sustainPedal = value / 127;
1818
- if (value < 64) {
1794
+ const channel = this.channels[channelNumber];
1795
+ channel.state.sustainPedal = value / 127;
1796
+ if (64 <= value) {
1797
+ this.processScheduledNotes(channel, (note) => {
1798
+ channel.sustainNotes.push(note);
1799
+ });
1800
+ }
1801
+ else {
1819
1802
  this.releaseSustainPedal(channelNumber, value, scheduleTime);
1820
1803
  }
1821
1804
  }
@@ -1823,13 +1806,14 @@ export class MidyGM2 {
1823
1806
  this.channels[channelNumber].state.portamento = value / 127;
1824
1807
  }
1825
1808
  setSostenutoPedal(channelNumber, value, scheduleTime) {
1809
+ scheduleTime ??= this.audioContext.currentTime;
1826
1810
  const channel = this.channels[channelNumber];
1827
1811
  channel.state.sostenutoPedal = value / 127;
1828
1812
  if (64 <= value) {
1829
1813
  channel.sostenutoNotes = this.getActiveNotes(channel, scheduleTime);
1830
1814
  }
1831
1815
  else {
1832
- this.releaseSostenutoPedal(channelNumber, value);
1816
+ this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
1833
1817
  }
1834
1818
  }
1835
1819
  setSoftPedal(channelNumber, softPedal, _scheduleTime) {
@@ -1837,6 +1821,7 @@ export class MidyGM2 {
1837
1821
  channel.state.softPedal = softPedal / 127;
1838
1822
  }
1839
1823
  setReverbSendLevel(channelNumber, reverbSendLevel, scheduleTime) {
1824
+ scheduleTime ??= this.audioContext.currentTime;
1840
1825
  const channel = this.channels[channelNumber];
1841
1826
  const state = channel.state;
1842
1827
  const reverbEffect = this.reverbEffect;
@@ -1848,27 +1833,17 @@ export class MidyGM2 {
1848
1833
  .setValueAtTime(state.reverbSendLevel, scheduleTime);
1849
1834
  }
1850
1835
  else {
1851
- channel.scheduledNotes.forEach((noteList) => {
1852
- for (let i = 0; i < noteList.length; i++) {
1853
- const note = noteList[i];
1854
- if (!note)
1855
- continue;
1856
- if (note.voiceParams.reverbEffectsSend <= 0)
1857
- continue;
1858
- note.reverbEffectsSend.disconnect();
1859
- }
1836
+ this.processScheduledNotes(channel, (note) => {
1837
+ if (note.voiceParams.reverbEffectsSend <= 0)
1838
+ return false;
1839
+ note.reverbEffectsSend.disconnect();
1860
1840
  });
1861
1841
  }
1862
1842
  }
1863
1843
  else {
1864
1844
  if (0 < reverbSendLevel) {
1865
- channel.scheduledNotes.forEach((noteList) => {
1866
- for (let i = 0; i < noteList.length; i++) {
1867
- const note = noteList[i];
1868
- if (!note)
1869
- continue;
1870
- this.setReverbEffectsSend(channel, note, 0, scheduleTime);
1871
- }
1845
+ this.processScheduledNotes(channel, (note) => {
1846
+ this.setReverbEffectsSend(channel, note, 0, scheduleTime);
1872
1847
  });
1873
1848
  state.reverbSendLevel = reverbSendLevel / 127;
1874
1849
  reverbEffect.input.gain
@@ -1878,6 +1853,7 @@ export class MidyGM2 {
1878
1853
  }
1879
1854
  }
1880
1855
  setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
1856
+ scheduleTime ??= this.audioContext.currentTime;
1881
1857
  const channel = this.channels[channelNumber];
1882
1858
  const state = channel.state;
1883
1859
  const chorusEffect = this.chorusEffect;
@@ -1889,27 +1865,17 @@ export class MidyGM2 {
1889
1865
  .setValueAtTime(state.chorusSendLevel, scheduleTime);
1890
1866
  }
1891
1867
  else {
1892
- channel.scheduledNotes.forEach((noteList) => {
1893
- for (let i = 0; i < noteList.length; i++) {
1894
- const note = noteList[i];
1895
- if (!note)
1896
- continue;
1897
- if (note.voiceParams.chorusEffectsSend <= 0)
1898
- continue;
1899
- note.chorusEffectsSend.disconnect();
1900
- }
1868
+ this.processScheduledNotes(channel, (note) => {
1869
+ if (note.voiceParams.chorusEffectsSend <= 0)
1870
+ return false;
1871
+ note.chorusEffectsSend.disconnect();
1901
1872
  });
1902
1873
  }
1903
1874
  }
1904
1875
  else {
1905
1876
  if (0 < chorusSendLevel) {
1906
- channel.scheduledNotes.forEach((noteList) => {
1907
- for (let i = 0; i < noteList.length; i++) {
1908
- const note = noteList[i];
1909
- if (!note)
1910
- continue;
1911
- this.setChorusEffectsSend(channel, note, 0, scheduleTime);
1912
- }
1877
+ this.processScheduledNotes(channel, (note) => {
1878
+ this.setChorusEffectsSend(channel, note, 0, scheduleTime);
1913
1879
  });
1914
1880
  state.chorusSendLevel = chorusSendLevel / 127;
1915
1881
  chorusEffect.input.gain
@@ -1952,13 +1918,13 @@ export class MidyGM2 {
1952
1918
  this.handlePitchBendRangeRPN(channelNumber, scheduleTime);
1953
1919
  break;
1954
1920
  case 1:
1955
- this.handleFineTuningRPN(channelNumber);
1921
+ this.handleFineTuningRPN(channelNumber, scheduleTime);
1956
1922
  break;
1957
1923
  case 2:
1958
- this.handleCoarseTuningRPN(channelNumber);
1924
+ this.handleCoarseTuningRPN(channelNumber, scheduleTime);
1959
1925
  break;
1960
1926
  case 5:
1961
- this.handleModulationDepthRangeRPN(channelNumber);
1927
+ this.handleModulationDepthRangeRPN(channelNumber, scheduleTime);
1962
1928
  break;
1963
1929
  default:
1964
1930
  console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
@@ -1991,44 +1957,47 @@ export class MidyGM2 {
1991
1957
  this.updateChannelDetune(channel, scheduleTime);
1992
1958
  this.applyVoiceParams(channel, 16, scheduleTime);
1993
1959
  }
1994
- handleFineTuningRPN(channelNumber) {
1960
+ handleFineTuningRPN(channelNumber, scheduleTime) {
1995
1961
  const channel = this.channels[channelNumber];
1996
1962
  this.limitData(channel, 0, 127, 0, 127);
1997
1963
  const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
1998
- this.setFineTuning(channelNumber, fineTuning);
1964
+ this.setFineTuning(channelNumber, fineTuning, scheduleTime);
1999
1965
  }
2000
- setFineTuning(channelNumber, value) {
1966
+ setFineTuning(channelNumber, value, scheduleTime) {
1967
+ scheduleTime ??= this.audioContext.currentTime;
2001
1968
  const channel = this.channels[channelNumber];
2002
1969
  const prev = channel.fineTuning;
2003
1970
  const next = (value - 8192) / 8.192; // cent
2004
1971
  channel.fineTuning = next;
2005
1972
  channel.detune += next - prev;
2006
- this.updateChannelDetune(channel);
1973
+ this.updateChannelDetune(channel, scheduleTime);
2007
1974
  }
2008
- handleCoarseTuningRPN(channelNumber) {
1975
+ handleCoarseTuningRPN(channelNumber, scheduleTime) {
2009
1976
  const channel = this.channels[channelNumber];
2010
1977
  this.limitDataMSB(channel, 0, 127);
2011
1978
  const coarseTuning = channel.dataMSB;
2012
- this.setCoarseTuning(channelNumber, coarseTuning);
1979
+ this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
2013
1980
  }
2014
- setCoarseTuning(channelNumber, value) {
1981
+ setCoarseTuning(channelNumber, value, scheduleTime) {
1982
+ scheduleTime ??= this.audioContext.currentTime;
2015
1983
  const channel = this.channels[channelNumber];
2016
1984
  const prev = channel.coarseTuning;
2017
1985
  const next = (value - 64) * 100; // cent
2018
1986
  channel.coarseTuning = next;
2019
1987
  channel.detune += next - prev;
2020
- this.updateChannelDetune(channel);
1988
+ this.updateChannelDetune(channel, scheduleTime);
2021
1989
  }
2022
- handleModulationDepthRangeRPN(channelNumber) {
1990
+ handleModulationDepthRangeRPN(channelNumber, scheduleTime) {
2023
1991
  const channel = this.channels[channelNumber];
2024
1992
  this.limitData(channel, 0, 127, 0, 127);
2025
1993
  const modulationDepthRange = (dataMSB + dataLSB / 128) * 100;
2026
- this.setModulationDepthRange(channelNumber, modulationDepthRange);
1994
+ this.setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime);
2027
1995
  }
2028
- setModulationDepthRange(channelNumber, modulationDepthRange) {
1996
+ setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime) {
1997
+ scheduleTime ??= this.audioContext.currentTime;
2029
1998
  const channel = this.channels[channelNumber];
2030
1999
  channel.modulationDepthRange = modulationDepthRange;
2031
- this.updateModulation(channel);
2000
+ this.updateModulation(channel, scheduleTime);
2032
2001
  }
2033
2002
  allSoundOff(channelNumber, _value, scheduleTime) {
2034
2003
  scheduleTime ??= this.audioContext.currentTime;
@@ -2064,17 +2033,21 @@ export class MidyGM2 {
2064
2033
  scheduleTime ??= this.audioContext.currentTime;
2065
2034
  return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
2066
2035
  }
2067
- omniOff() {
2068
- this.omni = false;
2036
+ omniOff(channelNumber, value, scheduleTime) {
2037
+ this.allNotesOff(channelNumber, value, scheduleTime);
2069
2038
  }
2070
- omniOn() {
2071
- this.omni = true;
2039
+ omniOn(channelNumber, value, scheduleTime) {
2040
+ this.allNotesOff(channelNumber, value, scheduleTime);
2072
2041
  }
2073
- monoOn() {
2074
- this.mono = true;
2042
+ monoOn(channelNumber, value, scheduleTime) {
2043
+ const channel = this.channels[channelNumber];
2044
+ this.allNotesOff(channelNumber, value, scheduleTime);
2045
+ channel.mono = true;
2075
2046
  }
2076
- polyOn() {
2077
- this.mono = false;
2047
+ polyOn(channelNumber, value, scheduleTime) {
2048
+ const channel = this.channels[channelNumber];
2049
+ this.allNotesOff(channelNumber, value, scheduleTime);
2050
+ channel.mono = false;
2078
2051
  }
2079
2052
  handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
2080
2053
  switch (data[2]) {
@@ -2483,13 +2456,8 @@ export class MidyGM2 {
2483
2456
  const slotSize = 6;
2484
2457
  const offset = controllerType * slotSize;
2485
2458
  const table = channel.controlTable.subarray(offset, offset + slotSize);
2486
- channel.scheduledNotes.forEach((noteList) => {
2487
- for (let i = 0; i < noteList.length; i++) {
2488
- const note = noteList[i];
2489
- if (!note)
2490
- continue;
2491
- this.setControllerParameters(channel, note, table);
2492
- }
2459
+ this.processScheduledNotes(channel, (note) => {
2460
+ this.setControllerParameters(channel, note, table);
2493
2461
  });
2494
2462
  }
2495
2463
  handleControlChangeSysEx(data) {
@@ -2556,6 +2524,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2556
2524
  dataLSB: 0,
2557
2525
  rpnMSB: 127,
2558
2526
  rpnLSB: 127,
2527
+ mono: false, // CC#124, CC#125
2559
2528
  fineTuning: 0, // cb
2560
2529
  coarseTuning: 0, // cb
2561
2530
  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"}