@marmooo/midy 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -69,31 +69,37 @@ class Note {
69
69
  writable: true,
70
70
  value: void 0
71
71
  });
72
+ Object.defineProperty(this, "filterDepth", {
73
+ enumerable: true,
74
+ configurable: true,
75
+ writable: true,
76
+ value: void 0
77
+ });
72
78
  Object.defineProperty(this, "volumeEnvelopeNode", {
73
79
  enumerable: true,
74
80
  configurable: true,
75
81
  writable: true,
76
82
  value: void 0
77
83
  });
78
- Object.defineProperty(this, "volumeNode", {
84
+ Object.defineProperty(this, "volumeDepth", {
79
85
  enumerable: true,
80
86
  configurable: true,
81
87
  writable: true,
82
88
  value: void 0
83
89
  });
84
- Object.defineProperty(this, "gainL", {
90
+ Object.defineProperty(this, "volumeNode", {
85
91
  enumerable: true,
86
92
  configurable: true,
87
93
  writable: true,
88
94
  value: void 0
89
95
  });
90
- Object.defineProperty(this, "gainR", {
96
+ Object.defineProperty(this, "gainL", {
91
97
  enumerable: true,
92
98
  configurable: true,
93
99
  writable: true,
94
100
  value: void 0
95
101
  });
96
- Object.defineProperty(this, "volumeDepth", {
102
+ Object.defineProperty(this, "gainR", {
97
103
  enumerable: true,
98
104
  configurable: true,
99
105
  writable: true,
@@ -495,6 +501,9 @@ class MidyGM2 {
495
501
  ...this.setChannelAudioNodes(audioContext),
496
502
  scheduledNotes: new SparseMap(128),
497
503
  sostenutoNotes: new SparseMap(128),
504
+ scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
505
+ channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
506
+ keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
498
507
  };
499
508
  });
500
509
  return channels;
@@ -561,10 +570,11 @@ class MidyGM2 {
561
570
  const event = this.timeline[queueIndex];
562
571
  if (event.startTime > t + this.lookAhead)
563
572
  break;
573
+ const startTime = event.startTime + this.startDelay - offset;
564
574
  switch (event.type) {
565
575
  case "noteOn":
566
576
  if (event.velocity !== 0) {
567
- await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, event.portamento);
577
+ await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime, event.portamento);
568
578
  break;
569
579
  }
570
580
  /* falls through */
@@ -572,26 +582,26 @@ class MidyGM2 {
572
582
  const portamentoTarget = this.findPortamentoTarget(queueIndex);
573
583
  if (portamentoTarget)
574
584
  portamentoTarget.portamento = true;
575
- const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, event.startTime + this.startDelay - offset, portamentoTarget?.noteNumber, false);
585
+ const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, startTime, portamentoTarget?.noteNumber, false);
576
586
  if (notePromise) {
577
587
  this.notePromises.push(notePromise);
578
588
  }
579
589
  break;
580
590
  }
581
591
  case "controller":
582
- this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
592
+ this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value, startTime);
583
593
  break;
584
594
  case "programChange":
585
- this.handleProgramChange(event.channel, event.programNumber);
595
+ this.handleProgramChange(event.channel, event.programNumber, startTime);
586
596
  break;
587
597
  case "channelAftertouch":
588
- this.handleChannelPressure(event.channel, event.amount);
598
+ this.handleChannelPressure(event.channel, event.amount, startTime);
589
599
  break;
590
600
  case "pitchBend":
591
- this.setPitchBend(event.channel, event.value + 8192);
601
+ this.setPitchBend(event.channel, event.value + 8192, startTime);
592
602
  break;
593
603
  case "sysEx":
594
- this.handleSysEx(event.data);
604
+ this.handleSysEx(event.data, startTime);
595
605
  }
596
606
  queueIndex++;
597
607
  }
@@ -838,6 +848,18 @@ class MidyGM2 {
838
848
  const now = this.audioContext.currentTime;
839
849
  return this.resumeTime + now - this.startTime - this.startDelay;
840
850
  }
851
+ processScheduledNotes(channel, scheduleTime, callback) {
852
+ channel.scheduledNotes.forEach((noteList) => {
853
+ for (let i = 0; i < noteList.length; i++) {
854
+ const note = noteList[i];
855
+ if (!note)
856
+ continue;
857
+ if (scheduleTime < note.startTime)
858
+ continue;
859
+ callback(note);
860
+ }
861
+ });
862
+ }
841
863
  getActiveNotes(channel, time) {
842
864
  const activeNotes = new SparseMap(128);
843
865
  channel.scheduledNotes.forEach((noteList) => {
@@ -1019,14 +1041,14 @@ class MidyGM2 {
1019
1041
  const note = noteList[i];
1020
1042
  if (!note)
1021
1043
  continue;
1022
- this.updateDetune(channel, note, 0);
1044
+ this.updateDetune(channel, note);
1023
1045
  }
1024
1046
  });
1025
1047
  }
1026
- updateDetune(channel, note, pressure) {
1048
+ updateDetune(channel, note) {
1027
1049
  const now = this.audioContext.currentTime;
1028
1050
  const noteDetune = this.calcNoteDetune(channel, note);
1029
- const detune = channel.detune + noteDetune + pressure;
1051
+ const detune = channel.detune + noteDetune;
1030
1052
  note.bufferSource.detune
1031
1053
  .cancelScheduledValues(now)
1032
1054
  .setValueAtTime(detune, now);
@@ -1048,11 +1070,11 @@ class MidyGM2 {
1048
1070
  .setValueAtTime(0, volDelay)
1049
1071
  .linearRampToValueAtTime(sustainVolume, portamentoTime);
1050
1072
  }
1051
- setVolumeEnvelope(note, pressure) {
1073
+ setVolumeEnvelope(channel, note) {
1052
1074
  const now = this.audioContext.currentTime;
1053
1075
  const { voiceParams, startTime } = note;
1054
1076
  const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
1055
- (1 + pressure);
1077
+ (1 + this.getAmplitudeControl(channel));
1056
1078
  const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
1057
1079
  const volDelay = startTime + voiceParams.volDelay;
1058
1080
  const volAttack = volDelay + voiceParams.volAttack;
@@ -1066,20 +1088,20 @@ class MidyGM2 {
1066
1088
  .setValueAtTime(attackVolume, volHold)
1067
1089
  .linearRampToValueAtTime(sustainVolume, volDecay);
1068
1090
  }
1069
- setPitchEnvelope(note) {
1070
- const now = this.audioContext.currentTime;
1091
+ setPitchEnvelope(note, scheduleTime) {
1092
+ scheduleTime ??= this.audioContext.currentTime;
1071
1093
  const { voiceParams } = note;
1072
1094
  const baseRate = voiceParams.playbackRate;
1073
1095
  note.bufferSource.playbackRate
1074
- .cancelScheduledValues(now)
1075
- .setValueAtTime(baseRate, now);
1096
+ .cancelScheduledValues(scheduleTime)
1097
+ .setValueAtTime(baseRate, scheduleTime);
1076
1098
  const modEnvToPitch = voiceParams.modEnvToPitch;
1077
1099
  if (modEnvToPitch === 0)
1078
1100
  return;
1079
1101
  const basePitch = this.rateToCent(baseRate);
1080
1102
  const peekPitch = basePitch + modEnvToPitch;
1081
1103
  const peekRate = this.centToRate(peekPitch);
1082
- const modDelay = startTime + voiceParams.modDelay;
1104
+ const modDelay = note.startTime + voiceParams.modDelay;
1083
1105
  const modAttack = modDelay + voiceParams.modAttack;
1084
1106
  const modHold = modAttack + voiceParams.modHold;
1085
1107
  const modDecay = modHold + voiceParams.modDecay;
@@ -1115,13 +1137,14 @@ class MidyGM2 {
1115
1137
  .setValueAtTime(adjustedBaseFreq, modDelay)
1116
1138
  .linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
1117
1139
  }
1118
- setFilterEnvelope(channel, note, pressure) {
1140
+ setFilterEnvelope(channel, note) {
1119
1141
  const now = this.audioContext.currentTime;
1120
1142
  const state = channel.state;
1121
1143
  const { voiceParams, noteNumber, startTime } = note;
1122
1144
  const softPedalFactor = 1 -
1123
1145
  (0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
1124
- const baseCent = voiceParams.initialFilterFc + pressure;
1146
+ const baseCent = voiceParams.initialFilterFc +
1147
+ this.getFilterCutoffControl(channel);
1125
1148
  const baseFreq = this.centToHz(baseCent) * softPedalFactor;
1126
1149
  const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
1127
1150
  softPedalFactor;
@@ -1151,9 +1174,9 @@ class MidyGM2 {
1151
1174
  gain: voiceParams.modLfoToFilterFc,
1152
1175
  });
1153
1176
  note.modulationDepth = new GainNode(this.audioContext);
1154
- this.setModLfoToPitch(channel, note, 0);
1177
+ this.setModLfoToPitch(channel, note);
1155
1178
  note.volumeDepth = new GainNode(this.audioContext);
1156
- this.setModLfoToVolume(note, 0);
1179
+ this.setModLfoToVolume(channel, note);
1157
1180
  note.modulationLFO.start(startTime + voiceParams.delayModLFO);
1158
1181
  note.modulationLFO.connect(note.filterDepth);
1159
1182
  note.filterDepth.connect(note.filterNode.frequency);
@@ -1214,8 +1237,8 @@ class MidyGM2 {
1214
1237
  }
1215
1238
  else {
1216
1239
  note.portamento = false;
1217
- this.setVolumeEnvelope(note, 0);
1218
- this.setFilterEnvelope(channel, note, 0);
1240
+ this.setVolumeEnvelope(channel, note);
1241
+ this.setFilterEnvelope(channel, note);
1219
1242
  }
1220
1243
  if (0 < state.vibratoDepth) {
1221
1244
  this.startVibrato(channel, note, startTime);
@@ -1429,8 +1452,9 @@ class MidyGM2 {
1429
1452
  channel.bank = channel.bankMSB * 128 + channel.bankLSB;
1430
1453
  channel.program = program;
1431
1454
  }
1432
- handleChannelPressure(channelNumber, value) {
1433
- const now = this.audioContext.currentTime;
1455
+ handleChannelPressure(channelNumber, value, startTime) {
1456
+ if (!startTime)
1457
+ startTime = this.audioContext.currentTime;
1434
1458
  const channel = this.channels[channelNumber];
1435
1459
  const prev = channel.state.channelPressure;
1436
1460
  const next = value / 127;
@@ -1440,8 +1464,8 @@ class MidyGM2 {
1440
1464
  channel.detune += pressureDepth * (next - prev);
1441
1465
  }
1442
1466
  const table = channel.channelPressureTable;
1443
- this.getActiveNotes(channel, now).forEach((note) => {
1444
- this.applyDestinationSettings(channel, note, table);
1467
+ this.getActiveNotes(channel, startTime).forEach((note) => {
1468
+ this.setControllerParameters(channel, note, table);
1445
1469
  });
1446
1470
  // this.applyVoiceParams(channel, 13);
1447
1471
  }
@@ -1459,9 +1483,10 @@ class MidyGM2 {
1459
1483
  this.updateChannelDetune(channel);
1460
1484
  this.applyVoiceParams(channel, 14);
1461
1485
  }
1462
- setModLfoToPitch(channel, note, pressure) {
1486
+ setModLfoToPitch(channel, note) {
1463
1487
  const now = this.audioContext.currentTime;
1464
- const modLfoToPitch = note.voiceParams.modLfoToPitch + pressure;
1488
+ const modLfoToPitch = note.voiceParams.modLfoToPitch +
1489
+ this.getLFOPitchDepth(channel);
1465
1490
  const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
1466
1491
  const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
1467
1492
  note.modulationDepth.gain
@@ -1478,18 +1503,20 @@ class MidyGM2 {
1478
1503
  .cancelScheduledValues(now)
1479
1504
  .setValueAtTime(vibratoDepth * vibratoDepthSign, now);
1480
1505
  }
1481
- setModLfoToFilterFc(note, pressure) {
1506
+ setModLfoToFilterFc(channel, note) {
1482
1507
  const now = this.audioContext.currentTime;
1483
- const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc + pressure;
1508
+ const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc +
1509
+ this.getLFOFilterDepth(channel);
1484
1510
  note.filterDepth.gain
1485
1511
  .cancelScheduledValues(now)
1486
1512
  .setValueAtTime(modLfoToFilterFc, now);
1487
1513
  }
1488
- setModLfoToVolume(note, pressure) {
1514
+ setModLfoToVolume(channel, note) {
1489
1515
  const now = this.audioContext.currentTime;
1490
1516
  const modLfoToVolume = note.voiceParams.modLfoToVolume;
1491
1517
  const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
1492
- const volumeDepth = baseDepth * Math.sign(modLfoToVolume) * (1 + pressure);
1518
+ const volumeDepth = baseDepth * Math.sign(modLfoToVolume) *
1519
+ (1 + this.getLFOAmplitudeDepth(channel));
1493
1520
  note.volumeDepth.gain
1494
1521
  .cancelScheduledValues(now)
1495
1522
  .setValueAtTime(volumeDepth, now);
@@ -1573,7 +1600,7 @@ class MidyGM2 {
1573
1600
  return {
1574
1601
  modLfoToPitch: (channel, note, _prevValue) => {
1575
1602
  if (0 < channel.state.modulationDepth) {
1576
- this.setModLfoToPitch(channel, note, 0);
1603
+ this.setModLfoToPitch(channel, note);
1577
1604
  }
1578
1605
  },
1579
1606
  vibLfoToPitch: (channel, note, _prevValue) => {
@@ -1583,12 +1610,12 @@ class MidyGM2 {
1583
1610
  },
1584
1611
  modLfoToFilterFc: (channel, note, _prevValue) => {
1585
1612
  if (0 < channel.state.modulationDepth) {
1586
- this.setModLfoToFilterFc(note, 0);
1613
+ this.setModLfoToFilterFc(channel, note);
1587
1614
  }
1588
1615
  },
1589
1616
  modLfoToVolume: (channel, note, _prevValue) => {
1590
1617
  if (0 < channel.state.modulationDepth) {
1591
- this.setModLfoToVolume(note, 0);
1618
+ this.setModLfoToVolume(channel, note);
1592
1619
  }
1593
1620
  },
1594
1621
  chorusEffectsSend: (channel, note, prevValue) => {
@@ -1658,7 +1685,7 @@ class MidyGM2 {
1658
1685
  this.setPortamentoStartFilterEnvelope(channel, note);
1659
1686
  }
1660
1687
  else {
1661
- this.setFilterEnvelope(channel, note, 0);
1688
+ this.setFilterEnvelope(channel, note);
1662
1689
  }
1663
1690
  this.setPitchEnvelope(note);
1664
1691
  }
@@ -1672,7 +1699,7 @@ class MidyGM2 {
1672
1699
  if (key in voiceParams)
1673
1700
  noteVoiceParams[key] = voiceParams[key];
1674
1701
  }
1675
- this.setVolumeEnvelope(note, 0);
1702
+ this.setVolumeEnvelope(channel, note);
1676
1703
  }
1677
1704
  }
1678
1705
  }
@@ -1706,10 +1733,10 @@ class MidyGM2 {
1706
1733
  127: this.polyOn,
1707
1734
  };
1708
1735
  }
1709
- handleControlChange(channelNumber, controllerType, value) {
1736
+ handleControlChange(channelNumber, controllerType, value, startTime) {
1710
1737
  const handler = this.controlChangeHandlers[controllerType];
1711
1738
  if (handler) {
1712
- handler.call(this, channelNumber, value);
1739
+ handler.call(this, channelNumber, value, startTime);
1713
1740
  const channel = this.channels[channelNumber];
1714
1741
  this.applyVoiceParams(channel, controllerType + 128);
1715
1742
  this.applyControlTable(channel, controllerType);
@@ -1721,55 +1748,45 @@ class MidyGM2 {
1721
1748
  setBankMSB(channelNumber, msb) {
1722
1749
  this.channels[channelNumber].bankMSB = msb;
1723
1750
  }
1724
- updateModulation(channel) {
1725
- const now = this.audioContext.currentTime;
1751
+ updateModulation(channel, scheduleTime) {
1752
+ scheduleTime ??= this.audioContext.currentTime;
1726
1753
  const depth = channel.state.modulationDepth * channel.modulationDepthRange;
1727
- channel.scheduledNotes.forEach((noteList) => {
1728
- for (let i = 0; i < noteList.length; i++) {
1729
- const note = noteList[i];
1730
- if (!note)
1731
- continue;
1732
- if (note.modulationDepth) {
1733
- note.modulationDepth.gain.setValueAtTime(depth, now);
1734
- }
1735
- else {
1736
- this.setPitchEnvelope(note);
1737
- this.startModulation(channel, note, now);
1738
- }
1754
+ this.processScheduledNotes(channel, scheduleTime, (note) => {
1755
+ if (note.modulationDepth) {
1756
+ note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
1757
+ }
1758
+ else {
1759
+ this.setPitchEnvelope(note, scheduleTime);
1760
+ this.startModulation(channel, note, scheduleTime);
1739
1761
  }
1740
1762
  });
1741
1763
  }
1742
- setModulationDepth(channelNumber, modulation) {
1764
+ setModulationDepth(channelNumber, modulation, scheduleTime) {
1743
1765
  const channel = this.channels[channelNumber];
1744
1766
  channel.state.modulationDepth = modulation / 127;
1745
- this.updateModulation(channel);
1767
+ this.updateModulation(channel, scheduleTime);
1746
1768
  }
1747
1769
  setPortamentoTime(channelNumber, portamentoTime) {
1748
1770
  const channel = this.channels[channelNumber];
1749
1771
  const factor = 5 * Math.log(10) / 127;
1750
1772
  channel.state.portamentoTime = Math.exp(factor * portamentoTime);
1751
1773
  }
1752
- setKeyBasedVolume(channel) {
1753
- const now = this.audioContext.currentTime;
1754
- channel.scheduledNotes.forEach((noteList) => {
1755
- for (let i = 0; i < noteList.length; i++) {
1756
- const note = noteList[i];
1757
- if (!note)
1758
- continue;
1759
- const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
1760
- if (keyBasedValue === 0)
1761
- continue;
1774
+ setKeyBasedVolume(channel, scheduleTime) {
1775
+ scheduleTime ??= this.audioContext.currentTime;
1776
+ this.processScheduledNotes(channel, scheduleTime, (note) => {
1777
+ const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
1778
+ if (keyBasedValue !== 0) {
1762
1779
  note.volumeNode.gain
1763
- .cancelScheduledValues(now)
1764
- .setValueAtTime(1 + keyBasedValue, now);
1780
+ .cancelScheduledValues(scheduleTime)
1781
+ .setValueAtTime(1 + keyBasedValue, scheduleTime);
1765
1782
  }
1766
1783
  });
1767
1784
  }
1768
- setVolume(channelNumber, volume) {
1785
+ setVolume(channelNumber, volume, scheduleTime) {
1769
1786
  const channel = this.channels[channelNumber];
1770
1787
  channel.state.volume = volume / 127;
1771
- this.updateChannelVolume(channel);
1772
- this.setKeyBasedVolume(channel);
1788
+ this.updateChannelVolume(channel, scheduleTime);
1789
+ this.setKeyBasedVolume(channel, scheduleTime);
1773
1790
  }
1774
1791
  panToGain(pan) {
1775
1792
  const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
@@ -1778,36 +1795,31 @@ class MidyGM2 {
1778
1795
  gainRight: Math.sin(theta),
1779
1796
  };
1780
1797
  }
1781
- setKeyBasedPan(channel) {
1782
- const now = this.audioContext.currentTime;
1783
- channel.scheduledNotes.forEach((noteList) => {
1784
- for (let i = 0; i < noteList.length; i++) {
1785
- const note = noteList[i];
1786
- if (!note)
1787
- continue;
1788
- const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
1789
- if (keyBasedValue === 0)
1790
- continue;
1798
+ setKeyBasedPan(channel, scheduleTime) {
1799
+ scheduleTime ??= this.audioContext.currentTime;
1800
+ this.processScheduledNotes(channel, scheduleTime, (note) => {
1801
+ const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
1802
+ if (keyBasedValue !== 0) {
1791
1803
  const { gainLeft, gainRight } = this.panToGain((keyBasedValue + 1) / 2);
1792
1804
  note.gainL.gain
1793
- .cancelScheduledValues(now)
1794
- .setValueAtTime(gainLeft, now);
1805
+ .cancelScheduledValues(scheduleTime)
1806
+ .setValueAtTime(gainLeft, scheduleTime);
1795
1807
  note.gainR.gain
1796
- .cancelScheduledValues(now)
1797
- .setValueAtTime(gainRight, now);
1808
+ .cancelScheduledValues(scheduleTime)
1809
+ .setValueAtTime(gainRight, scheduleTime);
1798
1810
  }
1799
1811
  });
1800
1812
  }
1801
- setPan(channelNumber, pan) {
1813
+ setPan(channelNumber, pan, scheduleTime) {
1802
1814
  const channel = this.channels[channelNumber];
1803
1815
  channel.state.pan = pan / 127;
1804
- this.updateChannelVolume(channel);
1805
- this.setKeyBasedPan(channel);
1816
+ this.updateChannelVolume(channel, scheduleTime);
1817
+ this.setKeyBasedPan(channel, scheduleTime);
1806
1818
  }
1807
- setExpression(channelNumber, expression) {
1819
+ setExpression(channelNumber, expression, scheduleTime) {
1808
1820
  const channel = this.channels[channelNumber];
1809
1821
  channel.state.expression = expression / 127;
1810
- this.updateChannelVolume(channel);
1822
+ this.updateChannelVolume(channel, scheduleTime);
1811
1823
  }
1812
1824
  setBankLSB(channelNumber, lsb) {
1813
1825
  this.channels[channelNumber].bankLSB = lsb;
@@ -2435,42 +2447,46 @@ class MidyGM2 {
2435
2447
  this.updateChannelDetune(channel);
2436
2448
  }
2437
2449
  }
2438
- applyDestinationSettings(channel, note, table) {
2439
- if (table[0] !== 64) {
2440
- this.updateDetune(channel, note, 0);
2441
- }
2450
+ getFilterCutoffControl(channel) {
2451
+ const channelPressure = (channel.channelPressureTable[1] - 64) *
2452
+ channel.state.channelPressure;
2453
+ return channelPressure * 15;
2454
+ }
2455
+ getAmplitudeControl(channel) {
2456
+ const channelPressure = channel.channelPressureTable[2] *
2457
+ channel.state.channelPressure;
2458
+ return channelPressure / 64;
2459
+ }
2460
+ getLFOPitchDepth(channel) {
2461
+ const channelPressure = channel.channelPressureTable[3] *
2462
+ channel.state.channelPressure;
2463
+ return channelPressure / 127 * 600;
2464
+ }
2465
+ getLFOFilterDepth(channel) {
2466
+ const channelPressure = channel.channelPressureTable[4] *
2467
+ channel.state.channelPressure;
2468
+ return channelPressure / 127 * 2400;
2469
+ }
2470
+ getLFOAmplitudeDepth(channel) {
2471
+ const channelPressure = channel.channelPressureTable[5] *
2472
+ channel.state.channelPressure;
2473
+ return channelPressure / 127;
2474
+ }
2475
+ setControllerParameters(channel, note, table) {
2476
+ if (table[0] !== 64)
2477
+ this.updateDetune(channel, note);
2442
2478
  if (!note.portamento) {
2443
- if (table[1] !== 64) {
2444
- const channelPressure = channel.channelPressureTable[1] *
2445
- channel.state.channelPressure;
2446
- const pressure = (channelPressure - 64) * 15;
2447
- this.setFilterEnvelope(channel, note, pressure);
2448
- }
2449
- if (table[2] !== 64) {
2450
- const channelPressure = channel.channelPressureTable[2] *
2451
- channel.state.channelPressure;
2452
- const pressure = channelPressure / 64;
2453
- this.setVolumeEnvelope(note, pressure);
2454
- }
2455
- }
2456
- if (table[3] !== 0) {
2457
- const channelPressure = channel.channelPressureTable[3] *
2458
- channel.state.channelPressure;
2459
- const pressure = channelPressure / 127 * 600;
2460
- this.setModLfoToPitch(channel, note, pressure);
2461
- }
2462
- if (table[4] !== 0) {
2463
- const channelPressure = channel.channelPressureTable[4] *
2464
- channel.state.channelPressure;
2465
- const pressure = channelPressure / 127 * 2400;
2466
- this.setModLfoToFilterFc(note, pressure);
2467
- }
2468
- if (table[5] !== 0) {
2469
- const channelPressure = channel.channelPressureTable[5] *
2470
- channel.state.channelPressure;
2471
- const pressure = channelPressure / 127;
2472
- this.setModLfoToVolume(note, pressure);
2473
- }
2479
+ if (table[1] !== 64)
2480
+ this.setFilterEnvelope(channel, note);
2481
+ if (table[2] !== 64)
2482
+ this.setVolumeEnvelope(channel, note);
2483
+ }
2484
+ if (table[3] !== 0)
2485
+ this.setModLfoToPitch(channel, note);
2486
+ if (table[4] !== 0)
2487
+ this.setModLfoToFilterFc(channel, note);
2488
+ if (table[5] !== 0)
2489
+ this.setModLfoToVolume(channel, note);
2474
2490
  }
2475
2491
  handleChannelPressureSysEx(data, tableName) {
2476
2492
  const channelNumber = data[4];
@@ -2501,7 +2517,7 @@ class MidyGM2 {
2501
2517
  const note = noteList[i];
2502
2518
  if (!note)
2503
2519
  continue;
2504
- this.applyDestinationSettings(channel, note, table);
2520
+ this.setControllerParameters(channel, note, table);
2505
2521
  }
2506
2522
  });
2507
2523
  }
@@ -2565,9 +2581,6 @@ Object.defineProperty(MidyGM2, "channelSettings", {
2565
2581
  value: {
2566
2582
  currentBufferSource: null,
2567
2583
  detune: 0,
2568
- scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
2569
- channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
2570
- keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
2571
2584
  program: 0,
2572
2585
  bank: 121 * 128,
2573
2586
  bankMSB: 121,
@@ -45,11 +45,11 @@ export class MidyGMLite {
45
45
  freqVibLFO: (_channel: any, _note: any, _prevValue: any) => void;
46
46
  };
47
47
  controlChangeHandlers: {
48
- 1: (channelNumber: any, modulation: any) => void;
48
+ 1: (channelNumber: any, modulation: any, scheduleTime: any) => void;
49
49
  6: (channelNumber: any, value: any) => void;
50
- 7: (channelNumber: any, volume: any) => void;
51
- 10: (channelNumber: any, pan: any) => void;
52
- 11: (channelNumber: any, expression: any) => void;
50
+ 7: (channelNumber: any, volume: any, scheduleTime: any) => void;
51
+ 10: (channelNumber: any, pan: any, scheduleTime: any) => void;
52
+ 11: (channelNumber: any, expression: any, scheduleTime: any) => void;
53
53
  38: (channelNumber: any, value: any) => void;
54
54
  64: (channelNumber: any, value: any) => void;
55
55
  100: (channelNumber: any, value: any) => void;
@@ -90,6 +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
94
  getActiveNotes(channel: any, time: any): SparseMap;
94
95
  getActiveNote(noteList: any, time: any): any;
95
96
  cbToRatio(cb: any): number;
@@ -100,7 +101,7 @@ export class MidyGMLite {
100
101
  updateChannelDetune(channel: any): void;
101
102
  updateDetune(channel: any, note: any): void;
102
103
  setVolumeEnvelope(note: any): void;
103
- setPitchEnvelope(note: any): void;
104
+ setPitchEnvelope(note: any, scheduleTime: any): void;
104
105
  clampCutoffFrequency(frequency: any): number;
105
106
  setFilterEnvelope(note: any): void;
106
107
  startModulation(channel: any, note: any, startTime: any): void;
@@ -136,11 +137,11 @@ export class MidyGMLite {
136
137
  getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array<any>;
137
138
  applyVoiceParams(channel: any, controllerType: any): void;
138
139
  createControlChangeHandlers(): {
139
- 1: (channelNumber: any, modulation: any) => void;
140
+ 1: (channelNumber: any, modulation: any, scheduleTime: any) => void;
140
141
  6: (channelNumber: any, value: any) => void;
141
- 7: (channelNumber: any, volume: any) => void;
142
- 10: (channelNumber: any, pan: any) => void;
143
- 11: (channelNumber: any, expression: any) => void;
142
+ 7: (channelNumber: any, volume: any, scheduleTime: any) => void;
143
+ 10: (channelNumber: any, pan: any, scheduleTime: any) => void;
144
+ 11: (channelNumber: any, expression: any, scheduleTime: any) => void;
144
145
  38: (channelNumber: any, value: any) => void;
145
146
  64: (channelNumber: any, value: any) => void;
146
147
  100: (channelNumber: any, value: any) => void;
@@ -149,18 +150,18 @@ export class MidyGMLite {
149
150
  121: (channelNumber: any) => void;
150
151
  123: (channelNumber: any) => Promise<void>;
151
152
  };
152
- handleControlChange(channelNumber: any, controllerType: any, value: any): void;
153
- updateModulation(channel: any): void;
154
- setModulationDepth(channelNumber: any, modulation: any): void;
155
- setVolume(channelNumber: any, volume: any): void;
153
+ handleControlChange(channelNumber: any, controllerType: any, value: any, startTime: any): void;
154
+ updateModulation(channel: any, scheduleTime: any): void;
155
+ setModulationDepth(channelNumber: any, modulation: any, scheduleTime: any): void;
156
+ setVolume(channelNumber: any, volume: any, scheduleTime: any): void;
156
157
  panToGain(pan: any): {
157
158
  gainLeft: number;
158
159
  gainRight: number;
159
160
  };
160
- setPan(channelNumber: any, pan: any): void;
161
- setExpression(channelNumber: any, expression: any): void;
161
+ setPan(channelNumber: any, pan: any, scheduleTime: any): void;
162
+ setExpression(channelNumber: any, expression: any, scheduleTime: any): void;
162
163
  dataEntryLSB(channelNumber: any, value: any): void;
163
- updateChannelVolume(channel: any): void;
164
+ updateChannelVolume(channel: any, scheduleTime: any): void;
164
165
  setSustainPedal(channelNumber: any, value: any): void;
165
166
  limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
166
167
  handleRPN(channelNumber: any): void;
@@ -198,6 +199,7 @@ declare class Note {
198
199
  constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
199
200
  bufferSource: any;
200
201
  filterNode: any;
202
+ filterDepth: any;
201
203
  volumeEnvelopeNode: any;
202
204
  volumeDepth: any;
203
205
  modulationLFO: any;
@@ -1 +1 @@
1
- {"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAgJA;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,2EA+CC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,mDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,wCAQC;IAED,4CAKC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,yGAgBC;IAED,gHAuCC;IAED,kGAgDC;IAED,0EAGC;IAED,qFAwBC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAqBC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EAWC;IAED,qCAeC;IAED,8DAIC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAv0CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;IAQE,0FAMC;IAbD,kBAAa;IACb,gBAAW;IACX,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,yCAUC;IAED,6DA2BC;IAED,8DASC;IAED,2EAqDC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,4EASC;IAED,mDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,wCAQC;IAED,4CAKC;IAED,mCAgBC;IAED,qDAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,yGAgBC;IAED,gHAuCC;IAED,kGAgDC;IAED,0EAGC;IAED,qFAwBC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAqBC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+FAWC;IAED,wDAWC;IAED,iFAIC;IAED,oEAIC;IAED;;;MAMC;IAED,8DAIC;IAED,4EAIC;IAED,mDAGC;IAED,2DAWC;IAED,sDAKC;IAED,kFAeC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAt1CD;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"}