@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/README.md +2 -2
- package/esm/midy-GM1.d.ts +6 -6
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +75 -73
- package/esm/midy-GM2.d.ts +22 -23
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +122 -153
- package/esm/midy-GMLite.d.ts +2 -2
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +63 -62
- package/esm/midy.d.ts +22 -23
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +153 -216
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +6 -6
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +75 -73
- package/script/midy-GM2.d.ts +22 -23
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +122 -153
- package/script/midy-GMLite.d.ts +2 -2
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +63 -62
- package/script/midy.d.ts +22 -23
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +153 -216
package/script/midy.js
CHANGED
|
@@ -294,18 +294,6 @@ class Midy {
|
|
|
294
294
|
delayTimes: this.generateDistributedArray(0.02, 2, 0.5),
|
|
295
295
|
}
|
|
296
296
|
});
|
|
297
|
-
Object.defineProperty(this, "mono", {
|
|
298
|
-
enumerable: true,
|
|
299
|
-
configurable: true,
|
|
300
|
-
writable: true,
|
|
301
|
-
value: false
|
|
302
|
-
}); // CC#124, CC#125
|
|
303
|
-
Object.defineProperty(this, "omni", {
|
|
304
|
-
enumerable: true,
|
|
305
|
-
configurable: true,
|
|
306
|
-
writable: true,
|
|
307
|
-
value: false
|
|
308
|
-
}); // CC#126, CC#127
|
|
309
297
|
Object.defineProperty(this, "noteCheckInterval", {
|
|
310
298
|
enumerable: true,
|
|
311
299
|
configurable: true,
|
|
@@ -507,6 +495,7 @@ class Midy {
|
|
|
507
495
|
controlTable: this.initControlTable(),
|
|
508
496
|
...this.setChannelAudioNodes(audioContext),
|
|
509
497
|
scheduledNotes: new SparseMap(128),
|
|
498
|
+
sustainNotes: [],
|
|
510
499
|
sostenutoNotes: new SparseMap(128),
|
|
511
500
|
scaleOctaveTuningTable: new Float32Array(12), // [-100, 100] cent
|
|
512
501
|
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
@@ -590,7 +579,7 @@ class Midy {
|
|
|
590
579
|
const portamentoTarget = this.findPortamentoTarget(queueIndex);
|
|
591
580
|
if (portamentoTarget)
|
|
592
581
|
portamentoTarget.portamento = true;
|
|
593
|
-
const notePromise = this.scheduleNoteOff(
|
|
582
|
+
const notePromise = this.scheduleNoteOff(event.channel, event.noteNumber, event.velocity, startTime, false, // force
|
|
594
583
|
portamentoTarget?.noteNumber);
|
|
595
584
|
if (notePromise) {
|
|
596
585
|
this.notePromises.push(notePromise);
|
|
@@ -601,7 +590,7 @@ class Midy {
|
|
|
601
590
|
this.handlePolyphonicKeyPressure(event.channel, event.noteNumber, event.amount, startTime);
|
|
602
591
|
break;
|
|
603
592
|
case "controller":
|
|
604
|
-
this.handleControlChange(
|
|
593
|
+
this.handleControlChange(event.channel, event.controllerType, event.value, startTime);
|
|
605
594
|
break;
|
|
606
595
|
case "programChange":
|
|
607
596
|
this.handleProgramChange(event.channel, event.programNumber, startTime);
|
|
@@ -797,15 +786,10 @@ class Midy {
|
|
|
797
786
|
stopChannelNotes(channelNumber, velocity, force, scheduleTime) {
|
|
798
787
|
const channel = this.channels[channelNumber];
|
|
799
788
|
const promises = [];
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
continue;
|
|
805
|
-
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
|
|
806
|
-
this.notePromises.push(promise);
|
|
807
|
-
promises.push(promise);
|
|
808
|
-
}
|
|
789
|
+
this.processScheduledNotes(channel, (note) => {
|
|
790
|
+
const promise = this.scheduleNoteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force, undefined);
|
|
791
|
+
this.notePromises.push(promise);
|
|
792
|
+
promises.push(promise);
|
|
809
793
|
});
|
|
810
794
|
channel.scheduledNotes.clear();
|
|
811
795
|
return Promise.all(promises);
|
|
@@ -861,14 +845,12 @@ class Midy {
|
|
|
861
845
|
const now = this.audioContext.currentTime;
|
|
862
846
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
863
847
|
}
|
|
864
|
-
processScheduledNotes(channel,
|
|
848
|
+
processScheduledNotes(channel, callback) {
|
|
865
849
|
channel.scheduledNotes.forEach((noteList) => {
|
|
866
850
|
for (let i = 0; i < noteList.length; i++) {
|
|
867
851
|
const note = noteList[i];
|
|
868
852
|
if (!note)
|
|
869
853
|
continue;
|
|
870
|
-
if (scheduleTime < note.startTime)
|
|
871
|
-
continue;
|
|
872
854
|
callback(note);
|
|
873
855
|
}
|
|
874
856
|
});
|
|
@@ -1049,7 +1031,7 @@ class Midy {
|
|
|
1049
1031
|
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
1050
1032
|
}
|
|
1051
1033
|
updateChannelDetune(channel, scheduleTime) {
|
|
1052
|
-
this.processScheduledNotes(channel,
|
|
1034
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1053
1035
|
this.updateDetune(channel, note, scheduleTime);
|
|
1054
1036
|
});
|
|
1055
1037
|
}
|
|
@@ -1254,7 +1236,7 @@ class Midy {
|
|
|
1254
1236
|
if (0 < state.modulationDepth) {
|
|
1255
1237
|
this.startModulation(channel, note, now);
|
|
1256
1238
|
}
|
|
1257
|
-
if (
|
|
1239
|
+
if (channel.mono && channel.currentBufferSource) {
|
|
1258
1240
|
channel.currentBufferSource.stop(startTime);
|
|
1259
1241
|
channel.currentBufferSource = note.bufferSource;
|
|
1260
1242
|
}
|
|
@@ -1295,8 +1277,8 @@ class Midy {
|
|
|
1295
1277
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime, portamento, isSF3);
|
|
1296
1278
|
note.gainL.connect(channel.gainL);
|
|
1297
1279
|
note.gainR.connect(channel.gainR);
|
|
1298
|
-
if (channel.state.
|
|
1299
|
-
channel.
|
|
1280
|
+
if (0.5 <= channel.state.sustainPedal) {
|
|
1281
|
+
channel.sustainNotes.push(note);
|
|
1300
1282
|
}
|
|
1301
1283
|
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
1302
1284
|
if (exclusiveClass !== 0) {
|
|
@@ -1365,7 +1347,7 @@ class Midy {
|
|
|
1365
1347
|
const channel = this.channels[channelNumber];
|
|
1366
1348
|
const state = channel.state;
|
|
1367
1349
|
if (!force) {
|
|
1368
|
-
if (0.5
|
|
1350
|
+
if (0.5 <= state.sustainPedal)
|
|
1369
1351
|
return;
|
|
1370
1352
|
if (channel.sostenutoNotes.has(noteNumber))
|
|
1371
1353
|
return;
|
|
@@ -1410,28 +1392,27 @@ class Midy {
|
|
|
1410
1392
|
const velocity = halfVelocity * 2;
|
|
1411
1393
|
const channel = this.channels[channelNumber];
|
|
1412
1394
|
const promises = [];
|
|
1413
|
-
|
|
1414
|
-
const
|
|
1415
|
-
const promise = this.noteOff(channelNumber, noteNumber, velocity);
|
|
1395
|
+
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
1396
|
+
const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
1416
1397
|
promises.push(promise);
|
|
1417
|
-
}
|
|
1398
|
+
}
|
|
1399
|
+
channel.sustainNotes = [];
|
|
1418
1400
|
return promises;
|
|
1419
1401
|
}
|
|
1420
|
-
releaseSostenutoPedal(channelNumber, halfVelocity) {
|
|
1402
|
+
releaseSostenutoPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
1421
1403
|
const velocity = halfVelocity * 2;
|
|
1422
1404
|
const channel = this.channels[channelNumber];
|
|
1423
1405
|
const promises = [];
|
|
1424
1406
|
channel.state.sostenutoPedal = 0;
|
|
1425
|
-
channel.sostenutoNotes.forEach((
|
|
1426
|
-
const
|
|
1427
|
-
const promise = this.noteOff(channelNumber, noteNumber, velocity);
|
|
1407
|
+
channel.sostenutoNotes.forEach((note) => {
|
|
1408
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
|
|
1428
1409
|
promises.push(promise);
|
|
1429
1410
|
});
|
|
1430
1411
|
channel.sostenutoNotes.clear();
|
|
1431
1412
|
return promises;
|
|
1432
1413
|
}
|
|
1433
1414
|
handleMIDIMessage(statusByte, data1, data2, scheduleTime) {
|
|
1434
|
-
const channelNumber =
|
|
1415
|
+
const channelNumber = statusByte & 0x0F;
|
|
1435
1416
|
const messageType = statusByte & 0xF0;
|
|
1436
1417
|
switch (messageType) {
|
|
1437
1418
|
case 0x80:
|
|
@@ -1659,53 +1640,48 @@ class Midy {
|
|
|
1659
1640
|
return state;
|
|
1660
1641
|
}
|
|
1661
1642
|
applyVoiceParams(channel, controllerType, scheduleTime) {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1643
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1644
|
+
const controllerState = this.getControllerState(channel, note.noteNumber, note.velocity);
|
|
1645
|
+
const voiceParams = note.voice.getParams(controllerType, controllerState);
|
|
1646
|
+
let appliedFilterEnvelope = false;
|
|
1647
|
+
let appliedVolumeEnvelope = false;
|
|
1648
|
+
for (const [key, value] of Object.entries(voiceParams)) {
|
|
1649
|
+
const prevValue = note.voiceParams[key];
|
|
1650
|
+
if (value === prevValue)
|
|
1666
1651
|
continue;
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
if (value === prevValue)
|
|
1652
|
+
note.voiceParams[key] = value;
|
|
1653
|
+
if (key in this.voiceParamsHandlers) {
|
|
1654
|
+
this.voiceParamsHandlers[key](channel, note, prevValue, scheduleTime);
|
|
1655
|
+
}
|
|
1656
|
+
else if (filterEnvelopeKeySet.has(key)) {
|
|
1657
|
+
if (appliedFilterEnvelope)
|
|
1674
1658
|
continue;
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1659
|
+
appliedFilterEnvelope = true;
|
|
1660
|
+
const noteVoiceParams = note.voiceParams;
|
|
1661
|
+
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1662
|
+
const key = filterEnvelopeKeys[i];
|
|
1663
|
+
if (key in voiceParams)
|
|
1664
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1678
1665
|
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
continue;
|
|
1682
|
-
appliedFilterEnvelope = true;
|
|
1683
|
-
const noteVoiceParams = note.voiceParams;
|
|
1684
|
-
for (let i = 0; i < filterEnvelopeKeys.length; i++) {
|
|
1685
|
-
const key = filterEnvelopeKeys[i];
|
|
1686
|
-
if (key in voiceParams)
|
|
1687
|
-
noteVoiceParams[key] = voiceParams[key];
|
|
1688
|
-
}
|
|
1689
|
-
if (note.portamento) {
|
|
1690
|
-
this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
|
|
1691
|
-
}
|
|
1692
|
-
else {
|
|
1693
|
-
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
1694
|
-
}
|
|
1695
|
-
this.setPitchEnvelope(note, scheduleTime);
|
|
1666
|
+
if (note.portamento) {
|
|
1667
|
+
this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
|
|
1696
1668
|
}
|
|
1697
|
-
else
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1669
|
+
else {
|
|
1670
|
+
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
1671
|
+
}
|
|
1672
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1673
|
+
}
|
|
1674
|
+
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1675
|
+
if (appliedVolumeEnvelope)
|
|
1676
|
+
continue;
|
|
1677
|
+
appliedVolumeEnvelope = true;
|
|
1678
|
+
const noteVoiceParams = note.voiceParams;
|
|
1679
|
+
for (let i = 0; i < volumeEnvelopeKeys.length; i++) {
|
|
1680
|
+
const key = volumeEnvelopeKeys[i];
|
|
1681
|
+
if (key in voiceParams)
|
|
1682
|
+
noteVoiceParams[key] = voiceParams[key];
|
|
1708
1683
|
}
|
|
1684
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
1709
1685
|
}
|
|
1710
1686
|
}
|
|
1711
1687
|
});
|
|
@@ -1764,9 +1740,8 @@ class Midy {
|
|
|
1764
1740
|
this.channels[channelNumber].bankMSB = msb;
|
|
1765
1741
|
}
|
|
1766
1742
|
updateModulation(channel, scheduleTime) {
|
|
1767
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1768
1743
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1769
|
-
this.processScheduledNotes(channel,
|
|
1744
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1770
1745
|
if (note.modulationDepth) {
|
|
1771
1746
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1772
1747
|
}
|
|
@@ -1777,6 +1752,7 @@ class Midy {
|
|
|
1777
1752
|
});
|
|
1778
1753
|
}
|
|
1779
1754
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1755
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1780
1756
|
const channel = this.channels[channelNumber];
|
|
1781
1757
|
channel.state.modulationDepth = modulation / 127;
|
|
1782
1758
|
this.updateModulation(channel, scheduleTime);
|
|
@@ -1787,8 +1763,7 @@ class Midy {
|
|
|
1787
1763
|
channel.state.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1788
1764
|
}
|
|
1789
1765
|
setKeyBasedVolume(channel, scheduleTime) {
|
|
1790
|
-
|
|
1791
|
-
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1766
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1792
1767
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
|
|
1793
1768
|
if (keyBasedValue !== 0) {
|
|
1794
1769
|
note.volumeNode.gain
|
|
@@ -1798,6 +1773,7 @@ class Midy {
|
|
|
1798
1773
|
});
|
|
1799
1774
|
}
|
|
1800
1775
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1776
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1801
1777
|
const channel = this.channels[channelNumber];
|
|
1802
1778
|
channel.state.volume = volume / 127;
|
|
1803
1779
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1811,8 +1787,7 @@ class Midy {
|
|
|
1811
1787
|
};
|
|
1812
1788
|
}
|
|
1813
1789
|
setKeyBasedPan(channel, scheduleTime) {
|
|
1814
|
-
|
|
1815
|
-
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1790
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1816
1791
|
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
|
|
1817
1792
|
if (keyBasedValue !== 0) {
|
|
1818
1793
|
const { gainLeft, gainRight } = this.panToGain((keyBasedValue + 1) / 2);
|
|
@@ -1826,12 +1801,14 @@ class Midy {
|
|
|
1826
1801
|
});
|
|
1827
1802
|
}
|
|
1828
1803
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1804
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1829
1805
|
const channel = this.channels[channelNumber];
|
|
1830
1806
|
channel.state.pan = pan / 127;
|
|
1831
1807
|
this.updateChannelVolume(channel, scheduleTime);
|
|
1832
1808
|
this.setKeyBasedPan(channel, scheduleTime);
|
|
1833
1809
|
}
|
|
1834
1810
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1811
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1835
1812
|
const channel = this.channels[channelNumber];
|
|
1836
1813
|
channel.state.expression = expression / 127;
|
|
1837
1814
|
this.updateChannelVolume(channel, scheduleTime);
|
|
@@ -1856,8 +1833,14 @@ class Midy {
|
|
|
1856
1833
|
}
|
|
1857
1834
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
1858
1835
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1859
|
-
this.channels[channelNumber]
|
|
1860
|
-
|
|
1836
|
+
const channel = this.channels[channelNumber];
|
|
1837
|
+
channel.state.sustainPedal = value / 127;
|
|
1838
|
+
if (64 <= value) {
|
|
1839
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1840
|
+
channel.sustainNotes.push(note);
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
else {
|
|
1861
1844
|
this.releaseSustainPedal(channelNumber, value, scheduleTime);
|
|
1862
1845
|
}
|
|
1863
1846
|
}
|
|
@@ -1865,13 +1848,14 @@ class Midy {
|
|
|
1865
1848
|
this.channels[channelNumber].state.portamento = value / 127;
|
|
1866
1849
|
}
|
|
1867
1850
|
setSostenutoPedal(channelNumber, value, scheduleTime) {
|
|
1851
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1868
1852
|
const channel = this.channels[channelNumber];
|
|
1869
1853
|
channel.state.sostenutoPedal = value / 127;
|
|
1870
1854
|
if (64 <= value) {
|
|
1871
1855
|
channel.sostenutoNotes = this.getActiveNotes(channel, scheduleTime);
|
|
1872
1856
|
}
|
|
1873
1857
|
else {
|
|
1874
|
-
this.releaseSostenutoPedal(channelNumber, value);
|
|
1858
|
+
this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
|
|
1875
1859
|
}
|
|
1876
1860
|
}
|
|
1877
1861
|
setSoftPedal(channelNumber, softPedal, _scheduleTime) {
|
|
@@ -1879,120 +1863,89 @@ class Midy {
|
|
|
1879
1863
|
channel.state.softPedal = softPedal / 127;
|
|
1880
1864
|
}
|
|
1881
1865
|
setFilterResonance(channelNumber, filterResonance, scheduleTime) {
|
|
1866
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1882
1867
|
const channel = this.channels[channelNumber];
|
|
1883
1868
|
const state = channel.state;
|
|
1884
1869
|
state.filterResonance = filterResonance / 64;
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
if (!note)
|
|
1889
|
-
continue;
|
|
1890
|
-
const Q = note.voiceParams.initialFilterQ / 5 * state.filterResonance;
|
|
1891
|
-
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
1892
|
-
}
|
|
1870
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1871
|
+
const Q = note.voiceParams.initialFilterQ / 5 * state.filterResonance;
|
|
1872
|
+
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
1893
1873
|
});
|
|
1894
1874
|
}
|
|
1895
1875
|
setReleaseTime(channelNumber, releaseTime, _scheduleTime) {
|
|
1876
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1896
1877
|
const channel = this.channels[channelNumber];
|
|
1897
1878
|
channel.state.releaseTime = releaseTime / 64;
|
|
1898
1879
|
}
|
|
1899
1880
|
setAttackTime(channelNumber, attackTime, scheduleTime) {
|
|
1881
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1900
1882
|
const channel = this.channels[channelNumber];
|
|
1901
1883
|
channel.state.attackTime = attackTime / 64;
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
continue;
|
|
1907
|
-
if (note.startTime < scheduleTime)
|
|
1908
|
-
continue;
|
|
1909
|
-
this.setVolumeEnvelope(channel, note);
|
|
1910
|
-
}
|
|
1884
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1885
|
+
if (note.startTime < scheduleTime)
|
|
1886
|
+
return false;
|
|
1887
|
+
this.setVolumeEnvelope(channel, note);
|
|
1911
1888
|
});
|
|
1912
1889
|
}
|
|
1913
1890
|
setBrightness(channelNumber, brightness, scheduleTime) {
|
|
1891
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1914
1892
|
const channel = this.channels[channelNumber];
|
|
1915
1893
|
channel.state.brightness = brightness / 64;
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
|
|
1923
|
-
}
|
|
1924
|
-
else {
|
|
1925
|
-
this.setFilterEnvelope(channel, note);
|
|
1926
|
-
}
|
|
1894
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1895
|
+
if (note.portamento) {
|
|
1896
|
+
this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
|
|
1897
|
+
}
|
|
1898
|
+
else {
|
|
1899
|
+
this.setFilterEnvelope(channel, note);
|
|
1927
1900
|
}
|
|
1928
1901
|
});
|
|
1929
1902
|
}
|
|
1930
1903
|
setDecayTime(channelNumber, dacayTime, scheduleTime) {
|
|
1904
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1931
1905
|
const channel = this.channels[channelNumber];
|
|
1932
1906
|
channel.state.decayTime = dacayTime / 64;
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
const note = noteList[i];
|
|
1936
|
-
if (!note)
|
|
1937
|
-
continue;
|
|
1938
|
-
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
1939
|
-
}
|
|
1907
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1908
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
1940
1909
|
});
|
|
1941
1910
|
}
|
|
1942
1911
|
setVibratoRate(channelNumber, vibratoRate, scheduleTime) {
|
|
1912
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1943
1913
|
const channel = this.channels[channelNumber];
|
|
1944
1914
|
channel.state.vibratoRate = vibratoRate / 64;
|
|
1945
1915
|
if (channel.vibratoDepth <= 0)
|
|
1946
1916
|
return;
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
const note = noteList[i];
|
|
1950
|
-
if (!note)
|
|
1951
|
-
continue;
|
|
1952
|
-
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
1953
|
-
}
|
|
1917
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1918
|
+
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
1954
1919
|
});
|
|
1955
1920
|
}
|
|
1956
1921
|
setVibratoDepth(channelNumber, vibratoDepth, scheduleTime) {
|
|
1922
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1957
1923
|
const channel = this.channels[channelNumber];
|
|
1958
1924
|
const prev = channel.state.vibratoDepth;
|
|
1959
1925
|
channel.state.vibratoDepth = vibratoDepth / 64;
|
|
1960
1926
|
if (0 < prev) {
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
const note = noteList[i];
|
|
1964
|
-
if (!note)
|
|
1965
|
-
continue;
|
|
1966
|
-
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
1967
|
-
}
|
|
1927
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1928
|
+
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
1968
1929
|
});
|
|
1969
1930
|
}
|
|
1970
1931
|
else {
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
const note = noteList[i];
|
|
1974
|
-
if (!note)
|
|
1975
|
-
continue;
|
|
1976
|
-
this.startVibrato(channel, note, scheduleTime);
|
|
1977
|
-
}
|
|
1932
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1933
|
+
this.startVibrato(channel, note, scheduleTime);
|
|
1978
1934
|
});
|
|
1979
1935
|
}
|
|
1980
1936
|
}
|
|
1981
1937
|
setVibratoDelay(channelNumber, vibratoDelay) {
|
|
1938
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1982
1939
|
const channel = this.channels[channelNumber];
|
|
1983
1940
|
channel.state.vibratoDelay = vibratoDelay / 64;
|
|
1984
1941
|
if (0 < channel.state.vibratoDepth) {
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
const note = noteList[i];
|
|
1988
|
-
if (!note)
|
|
1989
|
-
continue;
|
|
1990
|
-
this.startVibrato(channel, note, scheduleTime);
|
|
1991
|
-
}
|
|
1942
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1943
|
+
this.startVibrato(channel, note, scheduleTime);
|
|
1992
1944
|
});
|
|
1993
1945
|
}
|
|
1994
1946
|
}
|
|
1995
1947
|
setReverbSendLevel(channelNumber, reverbSendLevel, scheduleTime) {
|
|
1948
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1996
1949
|
const channel = this.channels[channelNumber];
|
|
1997
1950
|
const state = channel.state;
|
|
1998
1951
|
const reverbEffect = this.reverbEffect;
|
|
@@ -2004,27 +1957,17 @@ class Midy {
|
|
|
2004
1957
|
.setValueAtTime(state.reverbSendLevel, scheduleTime);
|
|
2005
1958
|
}
|
|
2006
1959
|
else {
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
continue;
|
|
2012
|
-
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
2013
|
-
continue;
|
|
2014
|
-
note.reverbEffectsSend.disconnect();
|
|
2015
|
-
}
|
|
1960
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1961
|
+
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
1962
|
+
return false;
|
|
1963
|
+
note.reverbEffectsSend.disconnect();
|
|
2016
1964
|
});
|
|
2017
1965
|
}
|
|
2018
1966
|
}
|
|
2019
1967
|
else {
|
|
2020
1968
|
if (0 < reverbSendLevel) {
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
const note = noteList[i];
|
|
2024
|
-
if (!note)
|
|
2025
|
-
continue;
|
|
2026
|
-
this.setReverbEffectsSend(channel, note, 0, scheduleTime);
|
|
2027
|
-
}
|
|
1969
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1970
|
+
this.setReverbEffectsSend(channel, note, 0, scheduleTime);
|
|
2028
1971
|
});
|
|
2029
1972
|
state.reverbSendLevel = reverbSendLevel / 127;
|
|
2030
1973
|
reverbEffect.input.gain
|
|
@@ -2034,6 +1977,7 @@ class Midy {
|
|
|
2034
1977
|
}
|
|
2035
1978
|
}
|
|
2036
1979
|
setChorusSendLevel(channelNumber, chorusSendLevel, scheduleTime) {
|
|
1980
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
2037
1981
|
const channel = this.channels[channelNumber];
|
|
2038
1982
|
const state = channel.state;
|
|
2039
1983
|
const chorusEffect = this.chorusEffect;
|
|
@@ -2045,27 +1989,17 @@ class Midy {
|
|
|
2045
1989
|
.setValueAtTime(state.chorusSendLevel, scheduleTime);
|
|
2046
1990
|
}
|
|
2047
1991
|
else {
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
continue;
|
|
2053
|
-
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
2054
|
-
continue;
|
|
2055
|
-
note.chorusEffectsSend.disconnect();
|
|
2056
|
-
}
|
|
1992
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1993
|
+
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
1994
|
+
return false;
|
|
1995
|
+
note.chorusEffectsSend.disconnect();
|
|
2057
1996
|
});
|
|
2058
1997
|
}
|
|
2059
1998
|
}
|
|
2060
1999
|
else {
|
|
2061
2000
|
if (0 < chorusSendLevel) {
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
const note = noteList[i];
|
|
2065
|
-
if (!note)
|
|
2066
|
-
continue;
|
|
2067
|
-
this.setChorusEffectsSend(channel, note, 0, scheduleTime);
|
|
2068
|
-
}
|
|
2001
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2002
|
+
this.setChorusEffectsSend(channel, note, 0, scheduleTime);
|
|
2069
2003
|
});
|
|
2070
2004
|
state.chorusSendLevel = chorusSendLevel / 127;
|
|
2071
2005
|
chorusEffect.input.gain
|
|
@@ -2110,15 +2044,15 @@ class Midy {
|
|
|
2110
2044
|
break;
|
|
2111
2045
|
case 1:
|
|
2112
2046
|
channel.dataLSB += value;
|
|
2113
|
-
this.handleFineTuningRPN(channelNumber);
|
|
2047
|
+
this.handleFineTuningRPN(channelNumber, scheduleTime);
|
|
2114
2048
|
break;
|
|
2115
2049
|
case 2:
|
|
2116
2050
|
channel.dataMSB += value;
|
|
2117
|
-
this.handleCoarseTuningRPN(channelNumber);
|
|
2051
|
+
this.handleCoarseTuningRPN(channelNumber, scheduleTime);
|
|
2118
2052
|
break;
|
|
2119
2053
|
case 5:
|
|
2120
2054
|
channel.dataLSB += value;
|
|
2121
|
-
this.handleModulationDepthRangeRPN(channelNumber);
|
|
2055
|
+
this.handleModulationDepthRangeRPN(channelNumber, scheduleTime);
|
|
2122
2056
|
break;
|
|
2123
2057
|
default:
|
|
2124
2058
|
console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
|
|
@@ -2159,44 +2093,47 @@ class Midy {
|
|
|
2159
2093
|
this.updateChannelDetune(channel, scheduleTime);
|
|
2160
2094
|
this.applyVoiceParams(channel, 16, scheduleTime);
|
|
2161
2095
|
}
|
|
2162
|
-
handleFineTuningRPN(channelNumber) {
|
|
2096
|
+
handleFineTuningRPN(channelNumber, scheduleTime) {
|
|
2163
2097
|
const channel = this.channels[channelNumber];
|
|
2164
2098
|
this.limitData(channel, 0, 127, 0, 127);
|
|
2165
2099
|
const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
|
|
2166
|
-
this.setFineTuning(channelNumber, fineTuning);
|
|
2100
|
+
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
2167
2101
|
}
|
|
2168
|
-
setFineTuning(channelNumber, value) {
|
|
2102
|
+
setFineTuning(channelNumber, value, scheduleTime) {
|
|
2103
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
2169
2104
|
const channel = this.channels[channelNumber];
|
|
2170
2105
|
const prev = channel.fineTuning;
|
|
2171
2106
|
const next = (value - 8192) / 8.192; // cent
|
|
2172
2107
|
channel.fineTuning = next;
|
|
2173
2108
|
channel.detune += next - prev;
|
|
2174
|
-
this.updateChannelDetune(channel);
|
|
2109
|
+
this.updateChannelDetune(channel, scheduleTime);
|
|
2175
2110
|
}
|
|
2176
|
-
handleCoarseTuningRPN(channelNumber) {
|
|
2111
|
+
handleCoarseTuningRPN(channelNumber, scheduleTime) {
|
|
2177
2112
|
const channel = this.channels[channelNumber];
|
|
2178
2113
|
this.limitDataMSB(channel, 0, 127);
|
|
2179
2114
|
const coarseTuning = channel.dataMSB;
|
|
2180
|
-
this.setCoarseTuning(channelNumber, coarseTuning);
|
|
2115
|
+
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
2181
2116
|
}
|
|
2182
|
-
setCoarseTuning(channelNumber, value) {
|
|
2117
|
+
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
2118
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
2183
2119
|
const channel = this.channels[channelNumber];
|
|
2184
2120
|
const prev = channel.coarseTuning;
|
|
2185
2121
|
const next = (value - 64) * 100; // cent
|
|
2186
2122
|
channel.coarseTuning = next;
|
|
2187
2123
|
channel.detune += next - prev;
|
|
2188
|
-
this.updateChannelDetune(channel);
|
|
2124
|
+
this.updateChannelDetune(channel, scheduleTime);
|
|
2189
2125
|
}
|
|
2190
|
-
handleModulationDepthRangeRPN(channelNumber) {
|
|
2126
|
+
handleModulationDepthRangeRPN(channelNumber, scheduleTime) {
|
|
2191
2127
|
const channel = this.channels[channelNumber];
|
|
2192
2128
|
this.limitData(channel, 0, 127, 0, 127);
|
|
2193
2129
|
const modulationDepthRange = (dataMSB + dataLSB / 128) * 100;
|
|
2194
|
-
this.setModulationDepthRange(channelNumber, modulationDepthRange);
|
|
2130
|
+
this.setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime);
|
|
2195
2131
|
}
|
|
2196
|
-
setModulationDepthRange(channelNumber, modulationDepthRange) {
|
|
2132
|
+
setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime) {
|
|
2133
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
2197
2134
|
const channel = this.channels[channelNumber];
|
|
2198
2135
|
channel.modulationDepthRange = modulationDepthRange;
|
|
2199
|
-
this.updateModulation(channel);
|
|
2136
|
+
this.updateModulation(channel, scheduleTime);
|
|
2200
2137
|
}
|
|
2201
2138
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
2202
2139
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -2232,17 +2169,21 @@ class Midy {
|
|
|
2232
2169
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2233
2170
|
return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
|
|
2234
2171
|
}
|
|
2235
|
-
omniOff() {
|
|
2236
|
-
this.
|
|
2172
|
+
omniOff(channelNumber, value, scheduleTime) {
|
|
2173
|
+
this.allNotesOff(channelNumber, value, scheduleTime);
|
|
2237
2174
|
}
|
|
2238
|
-
omniOn() {
|
|
2239
|
-
this.
|
|
2175
|
+
omniOn(channelNumber, value, scheduleTime) {
|
|
2176
|
+
this.allNotesOff(channelNumber, value, scheduleTime);
|
|
2240
2177
|
}
|
|
2241
|
-
monoOn() {
|
|
2242
|
-
|
|
2178
|
+
monoOn(channelNumber, value, scheduleTime) {
|
|
2179
|
+
const channel = this.channels[channelNumber];
|
|
2180
|
+
this.allNotesOff(channelNumber, value, scheduleTime);
|
|
2181
|
+
channel.mono = true;
|
|
2243
2182
|
}
|
|
2244
|
-
polyOn() {
|
|
2245
|
-
|
|
2183
|
+
polyOn(channelNumber, value, scheduleTime) {
|
|
2184
|
+
const channel = this.channels[channelNumber];
|
|
2185
|
+
this.allNotesOff(channelNumber, value, scheduleTime);
|
|
2186
|
+
channel.mono = false;
|
|
2246
2187
|
}
|
|
2247
2188
|
handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
|
|
2248
2189
|
switch (data[2]) {
|
|
@@ -2704,13 +2645,8 @@ class Midy {
|
|
|
2704
2645
|
const slotSize = 6;
|
|
2705
2646
|
const offset = controllerType * slotSize;
|
|
2706
2647
|
const table = channel.controlTable.subarray(offset, offset + slotSize);
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
const note = noteList[i];
|
|
2710
|
-
if (!note)
|
|
2711
|
-
continue;
|
|
2712
|
-
this.setControllerParameters(channel, note, table);
|
|
2713
|
-
}
|
|
2648
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2649
|
+
this.setControllerParameters(channel, note, table);
|
|
2714
2650
|
});
|
|
2715
2651
|
}
|
|
2716
2652
|
handleControlChangeSysEx(data) {
|
|
@@ -2778,6 +2714,7 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2778
2714
|
dataLSB: 0,
|
|
2779
2715
|
rpnMSB: 127,
|
|
2780
2716
|
rpnLSB: 127,
|
|
2717
|
+
mono: false, // CC#124, CC#125
|
|
2781
2718
|
fineTuning: 0, // cb
|
|
2782
2719
|
coarseTuning: 0, // cb
|
|
2783
2720
|
modulationDepthRange: 50, // cent
|