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