@marmooo/midy 0.2.0 → 0.2.1
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/LICENSE +201 -0
- package/README.md +118 -0
- package/esm/midy-GM1.d.ts +10 -8
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +73 -66
- package/esm/midy-GM2.d.ts +17 -11
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +148 -101
- package/esm/midy-GMLite.d.ts +7 -5
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +50 -47
- package/esm/midy.d.ts +19 -13
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +233 -177
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +10 -8
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +73 -66
- package/script/midy-GM2.d.ts +17 -11
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +148 -101
- package/script/midy-GMLite.d.ts +7 -5
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +50 -47
- package/script/midy.d.ts +19 -13
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +233 -177
package/esm/midy.js
CHANGED
|
@@ -904,16 +904,41 @@ export class Midy {
|
|
|
904
904
|
cbToRatio(cb) {
|
|
905
905
|
return Math.pow(10, cb / 200);
|
|
906
906
|
}
|
|
907
|
+
rateToCent(rate) {
|
|
908
|
+
return 1200 * Math.log2(rate);
|
|
909
|
+
}
|
|
910
|
+
centToRate(cent) {
|
|
911
|
+
return Math.pow(2, cent / 1200);
|
|
912
|
+
}
|
|
907
913
|
centToHz(cent) {
|
|
908
|
-
return 8.176 *
|
|
914
|
+
return 8.176 * this.centToRate(cent);
|
|
909
915
|
}
|
|
910
|
-
|
|
916
|
+
calcChannelDetune(channel) {
|
|
911
917
|
const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
|
|
912
918
|
const channelTuning = channel.coarseTuning + channel.fineTuning;
|
|
919
|
+
const tuning = masterTuning + channelTuning;
|
|
913
920
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
914
|
-
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity *
|
|
921
|
+
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
915
922
|
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
916
|
-
return
|
|
923
|
+
return tuning + pitch;
|
|
924
|
+
}
|
|
925
|
+
calcNoteDetune(channel, note) {
|
|
926
|
+
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
927
|
+
}
|
|
928
|
+
updateDetune(channel) {
|
|
929
|
+
const now = this.audioContext.currentTime;
|
|
930
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
931
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
932
|
+
const note = noteList[i];
|
|
933
|
+
if (!note)
|
|
934
|
+
continue;
|
|
935
|
+
const noteDetune = this.calcNoteDetune(channel, note);
|
|
936
|
+
const detune = channel.detune + noteDetune;
|
|
937
|
+
note.bufferSource.detune
|
|
938
|
+
.cancelScheduledValues(now)
|
|
939
|
+
.setValueAtTime(detune, now);
|
|
940
|
+
}
|
|
941
|
+
});
|
|
917
942
|
}
|
|
918
943
|
setPortamentoStartVolumeEnvelope(channel, note) {
|
|
919
944
|
const now = this.audioContext.currentTime;
|
|
@@ -945,32 +970,28 @@ export class Midy {
|
|
|
945
970
|
.setValueAtTime(attackVolume, volHold)
|
|
946
971
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
947
972
|
}
|
|
948
|
-
|
|
973
|
+
setPitchEnvelope(note) {
|
|
949
974
|
const now = this.audioContext.currentTime;
|
|
975
|
+
const { voiceParams } = note;
|
|
976
|
+
const baseRate = voiceParams.playbackRate;
|
|
950
977
|
note.bufferSource.playbackRate
|
|
951
978
|
.cancelScheduledValues(now)
|
|
952
|
-
.setValueAtTime(
|
|
953
|
-
|
|
954
|
-
setPitch(channel, note) {
|
|
955
|
-
const now = this.audioContext.currentTime;
|
|
956
|
-
const { startTime } = note;
|
|
957
|
-
const basePitch = this.calcSemitoneOffset(channel) * 100;
|
|
958
|
-
note.bufferSource.detune
|
|
959
|
-
.cancelScheduledValues(now)
|
|
960
|
-
.setValueAtTime(basePitch, startTime);
|
|
961
|
-
const modEnvToPitch = note.voiceParams.modEnvToPitch;
|
|
979
|
+
.setValueAtTime(baseRate, now);
|
|
980
|
+
const modEnvToPitch = voiceParams.modEnvToPitch;
|
|
962
981
|
if (modEnvToPitch === 0)
|
|
963
982
|
return;
|
|
983
|
+
const basePitch = this.rateToCent(baseRate);
|
|
964
984
|
const peekPitch = basePitch + modEnvToPitch;
|
|
985
|
+
const peekRate = this.centToRate(peekPitch);
|
|
965
986
|
const modDelay = startTime + voiceParams.modDelay;
|
|
966
987
|
const modAttack = modDelay + voiceParams.modAttack;
|
|
967
988
|
const modHold = modAttack + voiceParams.modHold;
|
|
968
989
|
const modDecay = modHold + voiceParams.modDecay;
|
|
969
|
-
note.bufferSource.
|
|
970
|
-
.setValueAtTime(
|
|
971
|
-
.exponentialRampToValueAtTime(
|
|
972
|
-
.setValueAtTime(
|
|
973
|
-
.linearRampToValueAtTime(
|
|
990
|
+
note.bufferSource.playbackRate
|
|
991
|
+
.setValueAtTime(baseRate, modDelay)
|
|
992
|
+
.exponentialRampToValueAtTime(peekRate, modAttack)
|
|
993
|
+
.setValueAtTime(peekRate, modHold)
|
|
994
|
+
.linearRampToValueAtTime(baseRate, modDecay);
|
|
974
995
|
}
|
|
975
996
|
clampCutoffFrequency(frequency) {
|
|
976
997
|
const minFrequency = 20; // min Hz of initialFilterFc
|
|
@@ -1081,9 +1102,8 @@ export class Midy {
|
|
|
1081
1102
|
if (0 < state.vibratoDepth) {
|
|
1082
1103
|
this.startVibrato(channel, note, startTime);
|
|
1083
1104
|
}
|
|
1084
|
-
this.
|
|
1105
|
+
this.setPitchEnvelope(note);
|
|
1085
1106
|
if (0 < state.modulationDepth) {
|
|
1086
|
-
this.setPitch(channel, note);
|
|
1087
1107
|
this.startModulation(channel, note, startTime);
|
|
1088
1108
|
}
|
|
1089
1109
|
if (this.mono && channel.currentBufferSource) {
|
|
@@ -1217,11 +1237,12 @@ export class Midy {
|
|
|
1217
1237
|
}
|
|
1218
1238
|
else {
|
|
1219
1239
|
const portamentoTime = endTime + state.portamentoTime;
|
|
1220
|
-
const
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1240
|
+
const deltaNote = portamentoNoteNumber - noteNumber;
|
|
1241
|
+
const baseRate = note.voiceParams.playbackRate;
|
|
1242
|
+
const targetRate = baseRate * Math.pow(2, deltaNote / 12);
|
|
1243
|
+
note.bufferSource.playbackRate
|
|
1223
1244
|
.cancelScheduledValues(endTime)
|
|
1224
|
-
.linearRampToValueAtTime(
|
|
1245
|
+
.linearRampToValueAtTime(targetRate, portamentoTime);
|
|
1225
1246
|
return this.stopNote(endTime, portamentoTime, scheduledNotes, i);
|
|
1226
1247
|
}
|
|
1227
1248
|
}
|
|
@@ -1326,10 +1347,11 @@ export class Midy {
|
|
|
1326
1347
|
setPitchBend(channelNumber, value) {
|
|
1327
1348
|
const channel = this.channels[channelNumber];
|
|
1328
1349
|
const state = channel.state;
|
|
1350
|
+
const prev = state.pitchWheel * 2 - 1;
|
|
1351
|
+
const next = (value - 8192) / 8192;
|
|
1329
1352
|
state.pitchWheel = value / 16383;
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
this.updateDetune(channel, detuneChange);
|
|
1353
|
+
channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
|
|
1354
|
+
this.updateDetune(channel);
|
|
1333
1355
|
this.applyVoiceParams(channel, 14);
|
|
1334
1356
|
}
|
|
1335
1357
|
setModLfoToPitch(channel, note) {
|
|
@@ -1342,6 +1364,23 @@ export class Midy {
|
|
|
1342
1364
|
.cancelScheduledValues(now)
|
|
1343
1365
|
.setValueAtTime(modulationDepth * modulationDepthSign, now);
|
|
1344
1366
|
}
|
|
1367
|
+
setVibLfoToPitch(channel, note) {
|
|
1368
|
+
const now = this.audioContext.currentTime;
|
|
1369
|
+
const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
|
|
1370
|
+
const vibratoDepth = Math.abs(vibLfoToPitch) * channel.state.vibratoDepth *
|
|
1371
|
+
2;
|
|
1372
|
+
const vibratoDepthSign = 0 < vibLfoToPitch;
|
|
1373
|
+
note.vibratoDepth.gain
|
|
1374
|
+
.cancelScheduledValues(now)
|
|
1375
|
+
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1376
|
+
}
|
|
1377
|
+
setModLfoToFilterFc(note) {
|
|
1378
|
+
const now = this.audioContext.currentTime;
|
|
1379
|
+
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
1380
|
+
note.filterDepth.gain
|
|
1381
|
+
.cancelScheduledValues(now)
|
|
1382
|
+
.setValueAtTime(modLfoToFilterFc, now);
|
|
1383
|
+
}
|
|
1345
1384
|
setModLfoToVolume(note) {
|
|
1346
1385
|
const now = this.audioContext.currentTime;
|
|
1347
1386
|
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
@@ -1401,23 +1440,6 @@ export class Midy {
|
|
|
1401
1440
|
}
|
|
1402
1441
|
}
|
|
1403
1442
|
}
|
|
1404
|
-
setVibLfoToPitch(channel, note) {
|
|
1405
|
-
const now = this.audioContext.currentTime;
|
|
1406
|
-
const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
|
|
1407
|
-
const vibratoDepth = Math.abs(vibLfoToPitch) * channel.state.vibratoDepth *
|
|
1408
|
-
2;
|
|
1409
|
-
const vibratoDepthSign = 0 < vibLfoToPitch;
|
|
1410
|
-
note.vibratoDepth.gain
|
|
1411
|
-
.cancelScheduledValues(now)
|
|
1412
|
-
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1413
|
-
}
|
|
1414
|
-
setModLfoToFilterFc(note) {
|
|
1415
|
-
const now = this.audioContext.currentTime;
|
|
1416
|
-
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
1417
|
-
note.filterDepth.gain
|
|
1418
|
-
.cancelScheduledValues(now)
|
|
1419
|
-
.setValueAtTime(modLfoToFilterFc, now);
|
|
1420
|
-
}
|
|
1421
1443
|
setDelayModLFO(note) {
|
|
1422
1444
|
const now = this.audioContext.currentTime;
|
|
1423
1445
|
const startTime = note.startTime;
|
|
@@ -1526,7 +1548,7 @@ export class Midy {
|
|
|
1526
1548
|
else {
|
|
1527
1549
|
this.setFilterEnvelope(channel, note);
|
|
1528
1550
|
}
|
|
1529
|
-
this.
|
|
1551
|
+
this.setPitchEnvelope(note);
|
|
1530
1552
|
}
|
|
1531
1553
|
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1532
1554
|
if (appliedVolumeEnvelope)
|
|
@@ -1608,7 +1630,7 @@ export class Midy {
|
|
|
1608
1630
|
note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
|
|
1609
1631
|
}
|
|
1610
1632
|
else {
|
|
1611
|
-
this.
|
|
1633
|
+
this.setPitchEnvelope(note);
|
|
1612
1634
|
this.startModulation(channel, note, now);
|
|
1613
1635
|
}
|
|
1614
1636
|
}
|
|
@@ -1675,88 +1697,6 @@ export class Midy {
|
|
|
1675
1697
|
setPortamento(channelNumber, value) {
|
|
1676
1698
|
this.channels[channelNumber].state.portamento = value / 127;
|
|
1677
1699
|
}
|
|
1678
|
-
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1679
|
-
const channel = this.channels[channelNumber];
|
|
1680
|
-
const state = channel.state;
|
|
1681
|
-
const reverbEffect = this.reverbEffect;
|
|
1682
|
-
if (0 < state.reverbSendLevel) {
|
|
1683
|
-
if (0 < reverbSendLevel) {
|
|
1684
|
-
const now = this.audioContext.currentTime;
|
|
1685
|
-
state.reverbSendLevel = reverbSendLevel / 127;
|
|
1686
|
-
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1687
|
-
reverbEffect.input.gain.setValueAtTime(state.reverbSendLevel, now);
|
|
1688
|
-
}
|
|
1689
|
-
else {
|
|
1690
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1691
|
-
for (let i = 0; i < noteList.length; i++) {
|
|
1692
|
-
const note = noteList[i];
|
|
1693
|
-
if (!note)
|
|
1694
|
-
continue;
|
|
1695
|
-
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
1696
|
-
continue;
|
|
1697
|
-
note.reverbEffectsSend.disconnect();
|
|
1698
|
-
}
|
|
1699
|
-
});
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
|
-
else {
|
|
1703
|
-
if (0 < reverbSendLevel) {
|
|
1704
|
-
const now = this.audioContext.currentTime;
|
|
1705
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1706
|
-
for (let i = 0; i < noteList.length; i++) {
|
|
1707
|
-
const note = noteList[i];
|
|
1708
|
-
if (!note)
|
|
1709
|
-
continue;
|
|
1710
|
-
this.setReverbEffectsSend(note, 0);
|
|
1711
|
-
}
|
|
1712
|
-
});
|
|
1713
|
-
state.reverbSendLevel = reverbSendLevel / 127;
|
|
1714
|
-
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1715
|
-
reverbEffect.input.gain.setValueAtTime(state.reverbSendLevel, now);
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
setChorusSendLevel(channelNumber, chorusSendLevel) {
|
|
1720
|
-
const channel = this.channels[channelNumber];
|
|
1721
|
-
const state = channel.state;
|
|
1722
|
-
const chorusEffect = this.chorusEffect;
|
|
1723
|
-
if (0 < state.chorusSendLevel) {
|
|
1724
|
-
if (0 < chorusSendLevel) {
|
|
1725
|
-
const now = this.audioContext.currentTime;
|
|
1726
|
-
state.chorusSendLevel = chorusSendLevel / 127;
|
|
1727
|
-
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1728
|
-
chorusEffect.input.gain.setValueAtTime(state.chorusSendLevel, now);
|
|
1729
|
-
}
|
|
1730
|
-
else {
|
|
1731
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1732
|
-
for (let i = 0; i < noteList.length; i++) {
|
|
1733
|
-
const note = noteList[i];
|
|
1734
|
-
if (!note)
|
|
1735
|
-
continue;
|
|
1736
|
-
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
1737
|
-
continue;
|
|
1738
|
-
note.chorusEffectsSend.disconnect();
|
|
1739
|
-
}
|
|
1740
|
-
});
|
|
1741
|
-
}
|
|
1742
|
-
}
|
|
1743
|
-
else {
|
|
1744
|
-
if (0 < chorusSendLevel) {
|
|
1745
|
-
const now = this.audioContext.currentTime;
|
|
1746
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1747
|
-
for (let i = 0; i < noteList.length; i++) {
|
|
1748
|
-
const note = noteList[i];
|
|
1749
|
-
if (!note)
|
|
1750
|
-
continue;
|
|
1751
|
-
this.setChorusEffectsSend(note, 0);
|
|
1752
|
-
}
|
|
1753
|
-
});
|
|
1754
|
-
state.chorusSendLevel = chorusSendLevel / 127;
|
|
1755
|
-
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1756
|
-
chorusEffect.input.gain.setValueAtTime(state.chorusSendLevel, now);
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
}
|
|
1760
1700
|
setSostenutoPedal(channelNumber, value) {
|
|
1761
1701
|
const channel = this.channels[channelNumber];
|
|
1762
1702
|
channel.state.sostenutoPedal = value / 127;
|
|
@@ -1852,6 +1792,88 @@ export class Midy {
|
|
|
1852
1792
|
const channel = this.channels[channelNumber];
|
|
1853
1793
|
channel.state.vibratoDelay = vibratoDelay / 64;
|
|
1854
1794
|
}
|
|
1795
|
+
setReverbSendLevel(channelNumber, reverbSendLevel) {
|
|
1796
|
+
const channel = this.channels[channelNumber];
|
|
1797
|
+
const state = channel.state;
|
|
1798
|
+
const reverbEffect = this.reverbEffect;
|
|
1799
|
+
if (0 < state.reverbSendLevel) {
|
|
1800
|
+
if (0 < reverbSendLevel) {
|
|
1801
|
+
const now = this.audioContext.currentTime;
|
|
1802
|
+
state.reverbSendLevel = reverbSendLevel / 127;
|
|
1803
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1804
|
+
reverbEffect.input.gain.setValueAtTime(state.reverbSendLevel, now);
|
|
1805
|
+
}
|
|
1806
|
+
else {
|
|
1807
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1808
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1809
|
+
const note = noteList[i];
|
|
1810
|
+
if (!note)
|
|
1811
|
+
continue;
|
|
1812
|
+
if (note.voiceParams.reverbEffectsSend <= 0)
|
|
1813
|
+
continue;
|
|
1814
|
+
note.reverbEffectsSend.disconnect();
|
|
1815
|
+
}
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
else {
|
|
1820
|
+
if (0 < reverbSendLevel) {
|
|
1821
|
+
const now = this.audioContext.currentTime;
|
|
1822
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1823
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1824
|
+
const note = noteList[i];
|
|
1825
|
+
if (!note)
|
|
1826
|
+
continue;
|
|
1827
|
+
this.setReverbEffectsSend(note, 0);
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
state.reverbSendLevel = reverbSendLevel / 127;
|
|
1831
|
+
reverbEffect.input.gain.cancelScheduledValues(now);
|
|
1832
|
+
reverbEffect.input.gain.setValueAtTime(state.reverbSendLevel, now);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
setChorusSendLevel(channelNumber, chorusSendLevel) {
|
|
1837
|
+
const channel = this.channels[channelNumber];
|
|
1838
|
+
const state = channel.state;
|
|
1839
|
+
const chorusEffect = this.chorusEffect;
|
|
1840
|
+
if (0 < state.chorusSendLevel) {
|
|
1841
|
+
if (0 < chorusSendLevel) {
|
|
1842
|
+
const now = this.audioContext.currentTime;
|
|
1843
|
+
state.chorusSendLevel = chorusSendLevel / 127;
|
|
1844
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1845
|
+
chorusEffect.input.gain.setValueAtTime(state.chorusSendLevel, now);
|
|
1846
|
+
}
|
|
1847
|
+
else {
|
|
1848
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1849
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1850
|
+
const note = noteList[i];
|
|
1851
|
+
if (!note)
|
|
1852
|
+
continue;
|
|
1853
|
+
if (note.voiceParams.chorusEffectsSend <= 0)
|
|
1854
|
+
continue;
|
|
1855
|
+
note.chorusEffectsSend.disconnect();
|
|
1856
|
+
}
|
|
1857
|
+
});
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
else {
|
|
1861
|
+
if (0 < chorusSendLevel) {
|
|
1862
|
+
const now = this.audioContext.currentTime;
|
|
1863
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
1864
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
1865
|
+
const note = noteList[i];
|
|
1866
|
+
if (!note)
|
|
1867
|
+
continue;
|
|
1868
|
+
this.setChorusEffectsSend(note, 0);
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
state.chorusSendLevel = chorusSendLevel / 127;
|
|
1872
|
+
chorusEffect.input.gain.cancelScheduledValues(now);
|
|
1873
|
+
chorusEffect.input.gain.setValueAtTime(state.chorusSendLevel, now);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1855
1877
|
limitData(channel, minMSB, maxMSB, minLSB, maxLSB) {
|
|
1856
1878
|
if (maxLSB < channel.dataLSB) {
|
|
1857
1879
|
channel.dataMSB++;
|
|
@@ -1920,59 +1942,49 @@ export class Midy {
|
|
|
1920
1942
|
this.channels[channelNumber].dataMSB = value;
|
|
1921
1943
|
this.handleRPN(channelNumber, 0);
|
|
1922
1944
|
}
|
|
1923
|
-
updateDetune(channel, detune) {
|
|
1924
|
-
const now = this.audioContext.currentTime;
|
|
1925
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1926
|
-
for (let i = 0; i < noteList.length; i++) {
|
|
1927
|
-
const note = noteList[i];
|
|
1928
|
-
if (!note)
|
|
1929
|
-
continue;
|
|
1930
|
-
const { bufferSource } = note;
|
|
1931
|
-
bufferSource.detune
|
|
1932
|
-
.cancelScheduledValues(now)
|
|
1933
|
-
.setValueAtTime(detune, now);
|
|
1934
|
-
}
|
|
1935
|
-
});
|
|
1936
|
-
}
|
|
1937
1945
|
handlePitchBendRangeRPN(channelNumber) {
|
|
1938
1946
|
const channel = this.channels[channelNumber];
|
|
1939
1947
|
this.limitData(channel, 0, 127, 0, 99);
|
|
1940
1948
|
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
1941
1949
|
this.setPitchBendRange(channelNumber, pitchBendRange);
|
|
1942
1950
|
}
|
|
1943
|
-
setPitchBendRange(channelNumber,
|
|
1951
|
+
setPitchBendRange(channelNumber, value) {
|
|
1944
1952
|
const channel = this.channels[channelNumber];
|
|
1945
1953
|
const state = channel.state;
|
|
1946
|
-
|
|
1947
|
-
const
|
|
1948
|
-
|
|
1954
|
+
const prev = state.pitchWheelSensitivity;
|
|
1955
|
+
const next = value / 128;
|
|
1956
|
+
state.pitchWheelSensitivity = next;
|
|
1957
|
+
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
1958
|
+
this.updateDetune(channel);
|
|
1949
1959
|
this.applyVoiceParams(channel, 16);
|
|
1950
1960
|
}
|
|
1951
1961
|
handleFineTuningRPN(channelNumber) {
|
|
1952
1962
|
const channel = this.channels[channelNumber];
|
|
1953
1963
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1954
|
-
const fineTuning =
|
|
1964
|
+
const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
|
|
1955
1965
|
this.setFineTuning(channelNumber, fineTuning);
|
|
1956
1966
|
}
|
|
1957
|
-
setFineTuning(channelNumber,
|
|
1967
|
+
setFineTuning(channelNumber, value) {
|
|
1958
1968
|
const channel = this.channels[channelNumber];
|
|
1959
|
-
const
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1969
|
+
const prev = channel.fineTuning;
|
|
1970
|
+
const next = (value - 8192) / 8.192; // cent
|
|
1971
|
+
channel.fineTuning = next;
|
|
1972
|
+
channel.detune += next - prev;
|
|
1973
|
+
this.updateDetune(channel);
|
|
1963
1974
|
}
|
|
1964
1975
|
handleCoarseTuningRPN(channelNumber) {
|
|
1965
1976
|
const channel = this.channels[channelNumber];
|
|
1966
1977
|
this.limitDataMSB(channel, 0, 127);
|
|
1967
|
-
const coarseTuning = channel.dataMSB
|
|
1968
|
-
this.
|
|
1978
|
+
const coarseTuning = channel.dataMSB;
|
|
1979
|
+
this.setCoarseTuning(channelNumber, coarseTuning);
|
|
1969
1980
|
}
|
|
1970
|
-
setCoarseTuning(channelNumber,
|
|
1981
|
+
setCoarseTuning(channelNumber, value) {
|
|
1971
1982
|
const channel = this.channels[channelNumber];
|
|
1972
|
-
const
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1983
|
+
const prev = channel.coarseTuning;
|
|
1984
|
+
const next = (value - 64) * 100; // cent
|
|
1985
|
+
channel.coarseTuning = next;
|
|
1986
|
+
channel.detune += next - prev;
|
|
1987
|
+
this.updateDetune(channel);
|
|
1976
1988
|
}
|
|
1977
1989
|
handleModulationDepthRangeRPN(channelNumber) {
|
|
1978
1990
|
const channel = this.channels[channelNumber];
|
|
@@ -2032,6 +2044,15 @@ export class Midy {
|
|
|
2032
2044
|
}
|
|
2033
2045
|
handleUniversalNonRealTimeExclusiveMessage(data) {
|
|
2034
2046
|
switch (data[2]) {
|
|
2047
|
+
case 8:
|
|
2048
|
+
switch (data[3]) {
|
|
2049
|
+
case 8:
|
|
2050
|
+
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2051
|
+
return this.handleScaleOctaveTuning1ByteFormat(data);
|
|
2052
|
+
default:
|
|
2053
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2054
|
+
}
|
|
2055
|
+
break;
|
|
2035
2056
|
case 9:
|
|
2036
2057
|
switch (data[3]) {
|
|
2037
2058
|
case 1:
|
|
@@ -2088,9 +2109,10 @@ export class Midy {
|
|
|
2088
2109
|
break;
|
|
2089
2110
|
case 8:
|
|
2090
2111
|
switch (data[3]) {
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2112
|
+
case 8:
|
|
2113
|
+
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
2114
|
+
// TODO: realtime
|
|
2115
|
+
return this.handleScaleOctaveTuning1ByteFormat(data);
|
|
2094
2116
|
default:
|
|
2095
2117
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2096
2118
|
}
|
|
@@ -2135,27 +2157,59 @@ export class Midy {
|
|
|
2135
2157
|
}
|
|
2136
2158
|
}
|
|
2137
2159
|
handleMasterFineTuningSysEx(data) {
|
|
2138
|
-
const fineTuning =
|
|
2160
|
+
const fineTuning = data[5] * 128 + data[4];
|
|
2139
2161
|
this.setMasterFineTuning(fineTuning);
|
|
2140
2162
|
}
|
|
2141
|
-
setMasterFineTuning(
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
}
|
|
2163
|
+
setMasterFineTuning(value) {
|
|
2164
|
+
const prev = this.masterFineTuning;
|
|
2165
|
+
const next = (value - 8192) / 8.192; // cent
|
|
2166
|
+
this.masterFineTuning = next;
|
|
2167
|
+
channel.detune += next - prev;
|
|
2168
|
+
this.updateDetune(channel);
|
|
2148
2169
|
}
|
|
2149
2170
|
handleMasterCoarseTuningSysEx(data) {
|
|
2150
2171
|
const coarseTuning = data[4];
|
|
2151
2172
|
this.setMasterCoarseTuning(coarseTuning);
|
|
2152
2173
|
}
|
|
2153
|
-
setMasterCoarseTuning(
|
|
2154
|
-
|
|
2155
|
-
|
|
2174
|
+
setMasterCoarseTuning(value) {
|
|
2175
|
+
const prev = this.masterCoarseTuning;
|
|
2176
|
+
const next = (value - 64) * 100; // cent
|
|
2177
|
+
this.masterCoarseTuning = next;
|
|
2178
|
+
channel.detune += next - prev;
|
|
2179
|
+
this.updateDetune(channel);
|
|
2180
|
+
}
|
|
2181
|
+
getChannelBitmap(data) {
|
|
2182
|
+
const bitmap = new Array(16).fill(false);
|
|
2183
|
+
const ff = data[4] & 0b11;
|
|
2184
|
+
const gg = data[5] & 0x7F;
|
|
2185
|
+
const hh = data[6] & 0x7F;
|
|
2186
|
+
for (let bit = 0; bit < 7; bit++) {
|
|
2187
|
+
if (hh & (1 << bit))
|
|
2188
|
+
bitmap[bit] = true;
|
|
2189
|
+
}
|
|
2190
|
+
for (let bit = 0; bit < 7; bit++) {
|
|
2191
|
+
if (gg & (1 << bit))
|
|
2192
|
+
bitmap[bit + 7] = true;
|
|
2193
|
+
}
|
|
2194
|
+
for (let bit = 0; bit < 2; bit++) {
|
|
2195
|
+
if (ff & (1 << bit))
|
|
2196
|
+
bitmap[bit + 14] = true;
|
|
2197
|
+
}
|
|
2198
|
+
return bitmap;
|
|
2199
|
+
}
|
|
2200
|
+
handleScaleOctaveTuning1ByteFormat(data) {
|
|
2201
|
+
if (data.length < 18) {
|
|
2202
|
+
console.error("Data length is too short");
|
|
2203
|
+
return;
|
|
2156
2204
|
}
|
|
2157
|
-
|
|
2158
|
-
|
|
2205
|
+
const channelBitmap = this.getChannelBitmap(data);
|
|
2206
|
+
for (let i = 0; i < channelBitmap.length; i++) {
|
|
2207
|
+
if (!channelBitmap[i])
|
|
2208
|
+
continue;
|
|
2209
|
+
for (let j = 0; j < 12; j++) {
|
|
2210
|
+
const value = data[j + 7] - 64; // cent
|
|
2211
|
+
this.channels[i].scaleOctaveTuningTable[j] = value;
|
|
2212
|
+
}
|
|
2159
2213
|
}
|
|
2160
2214
|
}
|
|
2161
2215
|
handleGlobalParameterControlSysEx(data) {
|
|
@@ -2375,6 +2429,8 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
2375
2429
|
writable: true,
|
|
2376
2430
|
value: {
|
|
2377
2431
|
currentBufferSource: null,
|
|
2432
|
+
detune: 0,
|
|
2433
|
+
scaleOctaveTuningTable: new Array(12).fill(0), // cent
|
|
2378
2434
|
program: 0,
|
|
2379
2435
|
bank: 121 * 128,
|
|
2380
2436
|
bankMSB: 121,
|
package/package.json
CHANGED
package/script/midy-GM1.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class MidyGM1 {
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
currentBufferSource: null;
|
|
4
|
+
detune: number;
|
|
4
5
|
program: number;
|
|
5
6
|
bank: number;
|
|
6
7
|
dataMSB: number;
|
|
@@ -92,11 +93,13 @@ export class MidyGM1 {
|
|
|
92
93
|
getActiveNotes(channel: any, time: any): Map<any, any>;
|
|
93
94
|
getActiveNote(noteList: any, time: any): any;
|
|
94
95
|
cbToRatio(cb: any): number;
|
|
96
|
+
rateToCent(rate: any): number;
|
|
97
|
+
centToRate(cent: any): number;
|
|
95
98
|
centToHz(cent: any): number;
|
|
96
|
-
|
|
99
|
+
calcChannelDetune(channel: any): any;
|
|
100
|
+
updateDetune(channel: any): void;
|
|
97
101
|
setVolumeEnvelope(note: any): void;
|
|
98
|
-
|
|
99
|
-
setPitch(channel: any, note: any): void;
|
|
102
|
+
setPitchEnvelope(note: any): void;
|
|
100
103
|
clampCutoffFrequency(frequency: any): number;
|
|
101
104
|
setFilterEnvelope(note: any): void;
|
|
102
105
|
startModulation(channel: any, note: any, startTime: any): void;
|
|
@@ -112,9 +115,9 @@ export class MidyGM1 {
|
|
|
112
115
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
|
|
113
116
|
setPitchBend(channelNumber: any, value: any): void;
|
|
114
117
|
setModLfoToPitch(channel: any, note: any): void;
|
|
115
|
-
setModLfoToVolume(note: any): void;
|
|
116
118
|
setVibLfoToPitch(channel: any, note: any): void;
|
|
117
119
|
setModLfoToFilterFc(note: any): void;
|
|
120
|
+
setModLfoToVolume(note: any): void;
|
|
118
121
|
setDelayModLFO(note: any): void;
|
|
119
122
|
setFreqModLFO(note: any): void;
|
|
120
123
|
createVoiceParamsHandlers(): {
|
|
@@ -164,13 +167,12 @@ export class MidyGM1 {
|
|
|
164
167
|
setRPNMSB(channelNumber: any, value: any): void;
|
|
165
168
|
setRPNLSB(channelNumber: any, value: any): void;
|
|
166
169
|
dataEntryMSB(channelNumber: any, value: any): void;
|
|
167
|
-
updateDetune(channel: any, detune: any): void;
|
|
168
170
|
handlePitchBendRangeRPN(channelNumber: any): void;
|
|
169
|
-
setPitchBendRange(channelNumber: any,
|
|
171
|
+
setPitchBendRange(channelNumber: any, value: any): void;
|
|
170
172
|
handleFineTuningRPN(channelNumber: any): void;
|
|
171
|
-
setFineTuning(channelNumber: any,
|
|
173
|
+
setFineTuning(channelNumber: any, value: any): void;
|
|
172
174
|
handleCoarseTuningRPN(channelNumber: any): void;
|
|
173
|
-
setCoarseTuning(channelNumber: any,
|
|
175
|
+
setCoarseTuning(channelNumber: any, value: any): void;
|
|
174
176
|
allSoundOff(channelNumber: any): Promise<void>;
|
|
175
177
|
resetAllControllers(channelNumber: any): void;
|
|
176
178
|
allNotesOff(channelNumber: any): Promise<void>;
|
package/script/midy-GM1.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqFA;IAoBE
|
|
1
|
+
{"version":3,"file":"midy-GM1.d.ts","sourceRoot":"","sources":["../src/midy-GM1.js"],"names":[],"mappings":"AAqFA;IAoBE;;;;;;;;;;;;MAYE;IAEF,+BAQC;IAzCD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,iCAA8B;IAiB5B,kBAAgC;IAChC,gBAA4C;IAC5C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,iEAUC;IAED,2EA+CC;IAED,mCAOC;IAED,0BAkDC;IAED,uDAEC;IAED,wDAEC;IAED;;;MAgEC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,uDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,qCAMC;IAED,iCAWC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA2BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFA4BC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MA2CC;IAED,+EAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+EASC;IAED,qCAiBC;IAED,8DAKC;IACD,iDAIC;IAED;;;MAMC;IAED,2CAIC;IAED,yDAIC;IAED,mDAGC;IAED,wCAWC;IAED,sDAKC;IAED,kFAeC;IAED,2DAMC;IAED,oCAkBC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,8CAKC;IAED,oDAOC;IAED,gDAKC;IAED,sDAOC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAjzCD;IAUE,0FAMC;IAfD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAChB,gBAAW;IACX,kBAAa;IAGX,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
|