@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/script/midy-GM2.js
CHANGED
|
@@ -901,16 +901,42 @@ class MidyGM2 {
|
|
|
901
901
|
cbToRatio(cb) {
|
|
902
902
|
return Math.pow(10, cb / 200);
|
|
903
903
|
}
|
|
904
|
+
rateToCent(rate) {
|
|
905
|
+
return 1200 * Math.log2(rate);
|
|
906
|
+
}
|
|
907
|
+
centToRate(cent) {
|
|
908
|
+
return Math.pow(2, cent / 1200);
|
|
909
|
+
}
|
|
904
910
|
centToHz(cent) {
|
|
905
|
-
return 8.176 *
|
|
911
|
+
return 8.176 * this.centToRate(cent);
|
|
906
912
|
}
|
|
907
|
-
|
|
913
|
+
calcDetune(channel, note) {
|
|
908
914
|
const masterTuning = this.masterCoarseTuning + this.masterFineTuning;
|
|
909
915
|
const channelTuning = channel.coarseTuning + channel.fineTuning;
|
|
916
|
+
const scaleOctaveTuning = channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
917
|
+
const tuning = masterTuning + channelTuning + scaleOctaveTuning;
|
|
910
918
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
911
|
-
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity *
|
|
919
|
+
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
912
920
|
const pitch = pitchWheel * pitchWheelSensitivity;
|
|
913
|
-
return
|
|
921
|
+
return tuning + pitch;
|
|
922
|
+
}
|
|
923
|
+
calcNoteDetune(channel, note) {
|
|
924
|
+
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
925
|
+
}
|
|
926
|
+
updateDetune(channel) {
|
|
927
|
+
const now = this.audioContext.currentTime;
|
|
928
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
929
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
930
|
+
const note = noteList[i];
|
|
931
|
+
if (!note)
|
|
932
|
+
continue;
|
|
933
|
+
const noteDetune = this.calcNoteDetune(channel, note);
|
|
934
|
+
const detune = channel.detune + noteDetune;
|
|
935
|
+
note.bufferSource.detune
|
|
936
|
+
.cancelScheduledValues(now)
|
|
937
|
+
.setValueAtTime(detune, now);
|
|
938
|
+
}
|
|
939
|
+
});
|
|
914
940
|
}
|
|
915
941
|
setPortamentoStartVolumeEnvelope(channel, note) {
|
|
916
942
|
const now = this.audioContext.currentTime;
|
|
@@ -941,32 +967,28 @@ class MidyGM2 {
|
|
|
941
967
|
.setValueAtTime(attackVolume, volHold)
|
|
942
968
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
943
969
|
}
|
|
944
|
-
|
|
970
|
+
setPitchEnvelope(note) {
|
|
945
971
|
const now = this.audioContext.currentTime;
|
|
972
|
+
const { voiceParams } = note;
|
|
973
|
+
const baseRate = voiceParams.playbackRate;
|
|
946
974
|
note.bufferSource.playbackRate
|
|
947
975
|
.cancelScheduledValues(now)
|
|
948
|
-
.setValueAtTime(
|
|
949
|
-
|
|
950
|
-
setPitch(channel, note) {
|
|
951
|
-
const now = this.audioContext.currentTime;
|
|
952
|
-
const { startTime } = note;
|
|
953
|
-
const basePitch = this.calcSemitoneOffset(channel) * 100;
|
|
954
|
-
note.bufferSource.detune
|
|
955
|
-
.cancelScheduledValues(now)
|
|
956
|
-
.setValueAtTime(basePitch, startTime);
|
|
957
|
-
const modEnvToPitch = note.voiceParams.modEnvToPitch;
|
|
976
|
+
.setValueAtTime(baseRate, now);
|
|
977
|
+
const modEnvToPitch = voiceParams.modEnvToPitch;
|
|
958
978
|
if (modEnvToPitch === 0)
|
|
959
979
|
return;
|
|
980
|
+
const basePitch = this.rateToCent(baseRate);
|
|
960
981
|
const peekPitch = basePitch + modEnvToPitch;
|
|
982
|
+
const peekRate = this.centToRate(peekPitch);
|
|
961
983
|
const modDelay = startTime + voiceParams.modDelay;
|
|
962
984
|
const modAttack = modDelay + voiceParams.modAttack;
|
|
963
985
|
const modHold = modAttack + voiceParams.modHold;
|
|
964
986
|
const modDecay = modHold + voiceParams.modDecay;
|
|
965
|
-
note.bufferSource.
|
|
966
|
-
.setValueAtTime(
|
|
967
|
-
.exponentialRampToValueAtTime(
|
|
968
|
-
.setValueAtTime(
|
|
969
|
-
.linearRampToValueAtTime(
|
|
987
|
+
note.bufferSource.playbackRate
|
|
988
|
+
.setValueAtTime(baseRate, modDelay)
|
|
989
|
+
.exponentialRampToValueAtTime(peekRate, modAttack)
|
|
990
|
+
.setValueAtTime(peekRate, modHold)
|
|
991
|
+
.linearRampToValueAtTime(baseRate, modDecay);
|
|
970
992
|
}
|
|
971
993
|
clampCutoffFrequency(frequency) {
|
|
972
994
|
const minFrequency = 20; // min Hz of initialFilterFc
|
|
@@ -1077,9 +1099,8 @@ class MidyGM2 {
|
|
|
1077
1099
|
if (0 < state.vibratoDepth) {
|
|
1078
1100
|
this.startVibrato(channel, note, startTime);
|
|
1079
1101
|
}
|
|
1080
|
-
this.
|
|
1102
|
+
this.setPitchEnvelope(note);
|
|
1081
1103
|
if (0 < state.modulationDepth) {
|
|
1082
|
-
this.setPitch(channel, note);
|
|
1083
1104
|
this.startModulation(channel, note, startTime);
|
|
1084
1105
|
}
|
|
1085
1106
|
if (this.mono && channel.currentBufferSource) {
|
|
@@ -1212,11 +1233,12 @@ class MidyGM2 {
|
|
|
1212
1233
|
}
|
|
1213
1234
|
else {
|
|
1214
1235
|
const portamentoTime = endTime + state.portamentoTime;
|
|
1215
|
-
const
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1236
|
+
const deltaNote = portamentoNoteNumber - noteNumber;
|
|
1237
|
+
const baseRate = note.voiceParams.playbackRate;
|
|
1238
|
+
const targetRate = baseRate * Math.pow(2, deltaNote / 12);
|
|
1239
|
+
note.bufferSource.playbackRate
|
|
1218
1240
|
.cancelScheduledValues(endTime)
|
|
1219
|
-
.linearRampToValueAtTime(
|
|
1241
|
+
.linearRampToValueAtTime(targetRate, portamentoTime);
|
|
1220
1242
|
return this.stopNote(endTime, portamentoTime, scheduledNotes, i);
|
|
1221
1243
|
}
|
|
1222
1244
|
}
|
|
@@ -1303,10 +1325,11 @@ class MidyGM2 {
|
|
|
1303
1325
|
setPitchBend(channelNumber, value) {
|
|
1304
1326
|
const channel = this.channels[channelNumber];
|
|
1305
1327
|
const state = channel.state;
|
|
1328
|
+
const prev = state.pitchWheel * 2 - 1;
|
|
1329
|
+
const next = (value - 8192) / 8192;
|
|
1306
1330
|
state.pitchWheel = value / 16383;
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
this.updateDetune(channel, detuneChange);
|
|
1331
|
+
channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
|
|
1332
|
+
this.updateDetune(channel);
|
|
1310
1333
|
this.applyVoiceParams(channel, 14);
|
|
1311
1334
|
}
|
|
1312
1335
|
setModLfoToPitch(channel, note) {
|
|
@@ -1319,6 +1342,23 @@ class MidyGM2 {
|
|
|
1319
1342
|
.cancelScheduledValues(now)
|
|
1320
1343
|
.setValueAtTime(modulationDepth * modulationDepthSign, now);
|
|
1321
1344
|
}
|
|
1345
|
+
setVibLfoToPitch(channel, note) {
|
|
1346
|
+
const now = this.audioContext.currentTime;
|
|
1347
|
+
const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
|
|
1348
|
+
const vibratoDepth = Math.abs(vibLfoToPitch) * channel.state.vibratoDepth *
|
|
1349
|
+
2;
|
|
1350
|
+
const vibratoDepthSign = 0 < vibLfoToPitch;
|
|
1351
|
+
note.vibratoDepth.gain
|
|
1352
|
+
.cancelScheduledValues(now)
|
|
1353
|
+
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1354
|
+
}
|
|
1355
|
+
setModLfoToFilterFc(note) {
|
|
1356
|
+
const now = this.audioContext.currentTime;
|
|
1357
|
+
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
1358
|
+
note.filterDepth.gain
|
|
1359
|
+
.cancelScheduledValues(now)
|
|
1360
|
+
.setValueAtTime(modLfoToFilterFc, now);
|
|
1361
|
+
}
|
|
1322
1362
|
setModLfoToVolume(note) {
|
|
1323
1363
|
const now = this.audioContext.currentTime;
|
|
1324
1364
|
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
@@ -1378,23 +1418,6 @@ class MidyGM2 {
|
|
|
1378
1418
|
}
|
|
1379
1419
|
}
|
|
1380
1420
|
}
|
|
1381
|
-
setVibLfoToPitch(channel, note) {
|
|
1382
|
-
const now = this.audioContext.currentTime;
|
|
1383
|
-
const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
|
|
1384
|
-
const vibratoDepth = Math.abs(vibLfoToPitch) * channel.state.vibratoDepth *
|
|
1385
|
-
2;
|
|
1386
|
-
const vibratoDepthSign = 0 < vibLfoToPitch;
|
|
1387
|
-
note.vibratoDepth.gain
|
|
1388
|
-
.cancelScheduledValues(now)
|
|
1389
|
-
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1390
|
-
}
|
|
1391
|
-
setModLfoToFilterFc(note) {
|
|
1392
|
-
const now = this.audioContext.currentTime;
|
|
1393
|
-
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
1394
|
-
note.filterDepth.gain
|
|
1395
|
-
.cancelScheduledValues(now)
|
|
1396
|
-
.setValueAtTime(modLfoToFilterFc, now);
|
|
1397
|
-
}
|
|
1398
1421
|
setDelayModLFO(note) {
|
|
1399
1422
|
const now = this.audioContext.currentTime;
|
|
1400
1423
|
const startTime = note.startTime;
|
|
@@ -1503,7 +1526,7 @@ class MidyGM2 {
|
|
|
1503
1526
|
else {
|
|
1504
1527
|
this.setFilterEnvelope(channel, note);
|
|
1505
1528
|
}
|
|
1506
|
-
this.
|
|
1529
|
+
this.setPitchEnvelope(note);
|
|
1507
1530
|
}
|
|
1508
1531
|
else if (volumeEnvelopeKeySet.has(key)) {
|
|
1509
1532
|
if (appliedVolumeEnvelope)
|
|
@@ -1575,7 +1598,7 @@ class MidyGM2 {
|
|
|
1575
1598
|
note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
|
|
1576
1599
|
}
|
|
1577
1600
|
else {
|
|
1578
|
-
this.
|
|
1601
|
+
this.setPitchEnvelope(note);
|
|
1579
1602
|
this.startModulation(channel, note, now);
|
|
1580
1603
|
}
|
|
1581
1604
|
}
|
|
@@ -1796,59 +1819,49 @@ class MidyGM2 {
|
|
|
1796
1819
|
this.channels[channelNumber].dataMSB = value;
|
|
1797
1820
|
this.handleRPN(channelNumber);
|
|
1798
1821
|
}
|
|
1799
|
-
updateDetune(channel, detune) {
|
|
1800
|
-
const now = this.audioContext.currentTime;
|
|
1801
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1802
|
-
for (let i = 0; i < noteList.length; i++) {
|
|
1803
|
-
const note = noteList[i];
|
|
1804
|
-
if (!note)
|
|
1805
|
-
continue;
|
|
1806
|
-
const { bufferSource } = note;
|
|
1807
|
-
bufferSource.detune
|
|
1808
|
-
.cancelScheduledValues(now)
|
|
1809
|
-
.setValueAtTime(detune, now);
|
|
1810
|
-
}
|
|
1811
|
-
});
|
|
1812
|
-
}
|
|
1813
1822
|
handlePitchBendRangeRPN(channelNumber) {
|
|
1814
1823
|
const channel = this.channels[channelNumber];
|
|
1815
1824
|
this.limitData(channel, 0, 127, 0, 99);
|
|
1816
1825
|
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
1817
1826
|
this.setPitchBendRange(channelNumber, pitchBendRange);
|
|
1818
1827
|
}
|
|
1819
|
-
setPitchBendRange(channelNumber,
|
|
1828
|
+
setPitchBendRange(channelNumber, value) {
|
|
1820
1829
|
const channel = this.channels[channelNumber];
|
|
1821
1830
|
const state = channel.state;
|
|
1822
|
-
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1831
|
+
const prev = state.pitchWheelSensitivity;
|
|
1832
|
+
const next = value / 128;
|
|
1833
|
+
state.pitchWheelSensitivity = next;
|
|
1834
|
+
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
1835
|
+
this.updateDetune(channel);
|
|
1825
1836
|
this.applyVoiceParams(channel, 16);
|
|
1826
1837
|
}
|
|
1827
1838
|
handleFineTuningRPN(channelNumber) {
|
|
1828
1839
|
const channel = this.channels[channelNumber];
|
|
1829
1840
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1830
|
-
const fineTuning =
|
|
1841
|
+
const fineTuning = channel.dataMSB * 128 + channel.dataLSB;
|
|
1831
1842
|
this.setFineTuning(channelNumber, fineTuning);
|
|
1832
1843
|
}
|
|
1833
|
-
setFineTuning(channelNumber,
|
|
1844
|
+
setFineTuning(channelNumber, value) {
|
|
1834
1845
|
const channel = this.channels[channelNumber];
|
|
1835
|
-
const
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1846
|
+
const prev = channel.fineTuning;
|
|
1847
|
+
const next = (value - 8192) / 8.192; // cent
|
|
1848
|
+
channel.fineTuning = next;
|
|
1849
|
+
channel.detune += next - prev;
|
|
1850
|
+
this.updateDetune(channel);
|
|
1839
1851
|
}
|
|
1840
1852
|
handleCoarseTuningRPN(channelNumber) {
|
|
1841
1853
|
const channel = this.channels[channelNumber];
|
|
1842
1854
|
this.limitDataMSB(channel, 0, 127);
|
|
1843
|
-
const coarseTuning = channel.dataMSB
|
|
1844
|
-
this.
|
|
1855
|
+
const coarseTuning = channel.dataMSB;
|
|
1856
|
+
this.setCoarseTuning(channelNumber, coarseTuning);
|
|
1845
1857
|
}
|
|
1846
|
-
setCoarseTuning(channelNumber,
|
|
1858
|
+
setCoarseTuning(channelNumber, value) {
|
|
1847
1859
|
const channel = this.channels[channelNumber];
|
|
1848
|
-
const
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1860
|
+
const prev = channel.coarseTuning;
|
|
1861
|
+
const next = (value - 64) * 100; // cent
|
|
1862
|
+
channel.coarseTuning = next;
|
|
1863
|
+
channel.detune += next - prev;
|
|
1864
|
+
this.updateDetune(channel);
|
|
1852
1865
|
}
|
|
1853
1866
|
handleModulationDepthRangeRPN(channelNumber) {
|
|
1854
1867
|
const channel = this.channels[channelNumber];
|
|
@@ -1908,6 +1921,15 @@ class MidyGM2 {
|
|
|
1908
1921
|
}
|
|
1909
1922
|
handleUniversalNonRealTimeExclusiveMessage(data) {
|
|
1910
1923
|
switch (data[2]) {
|
|
1924
|
+
case 8:
|
|
1925
|
+
switch (data[3]) {
|
|
1926
|
+
case 8:
|
|
1927
|
+
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/ca21.pdf
|
|
1928
|
+
return this.handleScaleOctaveTuning1ByteFormat(data);
|
|
1929
|
+
default:
|
|
1930
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1931
|
+
}
|
|
1932
|
+
break;
|
|
1911
1933
|
case 9:
|
|
1912
1934
|
switch (data[3]) {
|
|
1913
1935
|
case 1:
|
|
@@ -1962,15 +1984,6 @@ class MidyGM2 {
|
|
|
1962
1984
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1963
1985
|
}
|
|
1964
1986
|
break;
|
|
1965
|
-
case 8:
|
|
1966
|
-
switch (data[3]) {
|
|
1967
|
-
// case 8:
|
|
1968
|
-
// // TODO
|
|
1969
|
-
// return this.handleScaleOctaveTuning1ByteFormat();
|
|
1970
|
-
default:
|
|
1971
|
-
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1972
|
-
}
|
|
1973
|
-
break;
|
|
1974
1987
|
case 9:
|
|
1975
1988
|
switch (data[3]) {
|
|
1976
1989
|
// case 1:
|
|
@@ -2011,27 +2024,59 @@ class MidyGM2 {
|
|
|
2011
2024
|
}
|
|
2012
2025
|
}
|
|
2013
2026
|
handleMasterFineTuningSysEx(data) {
|
|
2014
|
-
const fineTuning =
|
|
2027
|
+
const fineTuning = data[5] * 128 + data[4];
|
|
2015
2028
|
this.setMasterFineTuning(fineTuning);
|
|
2016
2029
|
}
|
|
2017
|
-
setMasterFineTuning(
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
}
|
|
2030
|
+
setMasterFineTuning(value) {
|
|
2031
|
+
const prev = this.masterFineTuning;
|
|
2032
|
+
const next = (value - 8192) / 8.192; // cent
|
|
2033
|
+
this.masterFineTuning = next;
|
|
2034
|
+
channel.detune += next - prev;
|
|
2035
|
+
this.updateDetune(channel);
|
|
2024
2036
|
}
|
|
2025
2037
|
handleMasterCoarseTuningSysEx(data) {
|
|
2026
2038
|
const coarseTuning = data[4];
|
|
2027
2039
|
this.setMasterCoarseTuning(coarseTuning);
|
|
2028
2040
|
}
|
|
2029
|
-
setMasterCoarseTuning(
|
|
2030
|
-
|
|
2031
|
-
|
|
2041
|
+
setMasterCoarseTuning(value) {
|
|
2042
|
+
const prev = this.masterCoarseTuning;
|
|
2043
|
+
const next = (value - 64) * 100; // cent
|
|
2044
|
+
this.masterCoarseTuning = next;
|
|
2045
|
+
channel.detune += next - prev;
|
|
2046
|
+
this.updateDetune(channel);
|
|
2047
|
+
}
|
|
2048
|
+
getChannelBitmap(data) {
|
|
2049
|
+
const bitmap = new Array(16).fill(false);
|
|
2050
|
+
const ff = data[4] & 0b11;
|
|
2051
|
+
const gg = data[5] & 0x7F;
|
|
2052
|
+
const hh = data[6] & 0x7F;
|
|
2053
|
+
for (let bit = 0; bit < 7; bit++) {
|
|
2054
|
+
if (hh & (1 << bit))
|
|
2055
|
+
bitmap[bit] = true;
|
|
2056
|
+
}
|
|
2057
|
+
for (let bit = 0; bit < 7; bit++) {
|
|
2058
|
+
if (gg & (1 << bit))
|
|
2059
|
+
bitmap[bit + 7] = true;
|
|
2060
|
+
}
|
|
2061
|
+
for (let bit = 0; bit < 2; bit++) {
|
|
2062
|
+
if (ff & (1 << bit))
|
|
2063
|
+
bitmap[bit + 14] = true;
|
|
2064
|
+
}
|
|
2065
|
+
return bitmap;
|
|
2066
|
+
}
|
|
2067
|
+
handleScaleOctaveTuning1ByteFormat(data) {
|
|
2068
|
+
if (data.length < 18) {
|
|
2069
|
+
console.error("Data length is too short");
|
|
2070
|
+
return;
|
|
2032
2071
|
}
|
|
2033
|
-
|
|
2034
|
-
|
|
2072
|
+
const channelBitmap = this.getChannelBitmap(data);
|
|
2073
|
+
for (let i = 0; i < channelBitmap.length; i++) {
|
|
2074
|
+
if (!channelBitmap[i])
|
|
2075
|
+
continue;
|
|
2076
|
+
for (let j = 0; j < 12; j++) {
|
|
2077
|
+
const value = data[j + 7] - 64; // cent
|
|
2078
|
+
this.channels[i].scaleOctaveTuningTable[j] = value;
|
|
2079
|
+
}
|
|
2035
2080
|
}
|
|
2036
2081
|
}
|
|
2037
2082
|
handleGlobalParameterControlSysEx(data) {
|
|
@@ -2252,6 +2297,8 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
2252
2297
|
writable: true,
|
|
2253
2298
|
value: {
|
|
2254
2299
|
currentBufferSource: null,
|
|
2300
|
+
detune: 0,
|
|
2301
|
+
scaleOctaveTuningTable: new Array(12).fill(0), // cent
|
|
2255
2302
|
program: 0,
|
|
2256
2303
|
bank: 121 * 128,
|
|
2257
2304
|
bankMSB: 121,
|
package/script/midy-GMLite.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class MidyGMLite {
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
currentBufferSource: null;
|
|
4
|
+
detune: number;
|
|
4
5
|
program: number;
|
|
5
6
|
bank: number;
|
|
6
7
|
dataMSB: number;
|
|
@@ -89,11 +90,13 @@ export class MidyGMLite {
|
|
|
89
90
|
getActiveNotes(channel: any, time: any): Map<any, any>;
|
|
90
91
|
getActiveNote(noteList: any, time: any): any;
|
|
91
92
|
cbToRatio(cb: any): number;
|
|
93
|
+
rateToCent(rate: any): number;
|
|
94
|
+
centToRate(cent: any): number;
|
|
92
95
|
centToHz(cent: any): number;
|
|
93
|
-
|
|
96
|
+
calcChannelDetune(channel: any): number;
|
|
97
|
+
updateDetune(channel: any): void;
|
|
94
98
|
setVolumeEnvelope(note: any): void;
|
|
95
|
-
|
|
96
|
-
setPitch(channel: any, note: any): void;
|
|
99
|
+
setPitchEnvelope(note: any): void;
|
|
97
100
|
clampCutoffFrequency(frequency: any): number;
|
|
98
101
|
setFilterEnvelope(note: any): void;
|
|
99
102
|
startModulation(channel: any, note: any, startTime: any): void;
|
|
@@ -109,8 +112,8 @@ export class MidyGMLite {
|
|
|
109
112
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any): void;
|
|
110
113
|
setPitchBend(channelNumber: any, value: any): void;
|
|
111
114
|
setModLfoToPitch(channel: any, note: any): void;
|
|
112
|
-
setModLfoToVolume(note: any): void;
|
|
113
115
|
setModLfoToFilterFc(note: any): void;
|
|
116
|
+
setModLfoToVolume(note: any): void;
|
|
114
117
|
setDelayModLFO(note: any): void;
|
|
115
118
|
setFreqModLFO(note: any): void;
|
|
116
119
|
createVoiceParamsHandlers(): {
|
|
@@ -159,7 +162,6 @@ export class MidyGMLite {
|
|
|
159
162
|
setRPNMSB(channelNumber: any, value: any): void;
|
|
160
163
|
setRPNLSB(channelNumber: any, value: any): void;
|
|
161
164
|
dataEntryMSB(channelNumber: any, value: any): void;
|
|
162
|
-
updateDetune(channel: any, detune: any): void;
|
|
163
165
|
handlePitchBendRangeRPN(channelNumber: any): void;
|
|
164
166
|
setPitchBendRange(channelNumber: any, pitchWheelSensitivity: any): void;
|
|
165
167
|
allSoundOff(channelNumber: any): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAmFA;IAoBE
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAmFA;IAoBE;;;;;;;;;MASE;IAEF,+BAQC;IAtCD,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;IAc5B,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,wCAIC;IAED,iCAWC;IAED,mCAgBC;IAED,kCAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,gHA2BC;IAED,kGAgDC;IAED,0EAGC;IAED,qFAwBC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAqBC;IAED,+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,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wEAOC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AArtCD;IAQE,0FAMC;IAbD,kBAAa;IACb,gBAAW;IACX,gBAAW;IACX,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
|
package/script/midy-GMLite.js
CHANGED
|
@@ -584,14 +584,33 @@ class MidyGMLite {
|
|
|
584
584
|
cbToRatio(cb) {
|
|
585
585
|
return Math.pow(10, cb / 200);
|
|
586
586
|
}
|
|
587
|
+
rateToCent(rate) {
|
|
588
|
+
return 1200 * Math.log2(rate);
|
|
589
|
+
}
|
|
590
|
+
centToRate(cent) {
|
|
591
|
+
return Math.pow(2, cent / 1200);
|
|
592
|
+
}
|
|
587
593
|
centToHz(cent) {
|
|
588
|
-
return 8.176 *
|
|
594
|
+
return 8.176 * this.centToRate(cent);
|
|
589
595
|
}
|
|
590
|
-
|
|
596
|
+
calcChannelDetune(channel) {
|
|
591
597
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
592
|
-
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity *
|
|
598
|
+
const pitchWheelSensitivity = channel.state.pitchWheelSensitivity * 12800;
|
|
593
599
|
return pitchWheel * pitchWheelSensitivity;
|
|
594
600
|
}
|
|
601
|
+
updateDetune(channel) {
|
|
602
|
+
const now = this.audioContext.currentTime;
|
|
603
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
604
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
605
|
+
const note = noteList[i];
|
|
606
|
+
if (!note)
|
|
607
|
+
continue;
|
|
608
|
+
note.bufferSource.detune
|
|
609
|
+
.cancelScheduledValues(now)
|
|
610
|
+
.setValueAtTime(channel.detune, now);
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
}
|
|
595
614
|
setVolumeEnvelope(note) {
|
|
596
615
|
const now = this.audioContext.currentTime;
|
|
597
616
|
const { voiceParams, startTime } = note;
|
|
@@ -609,32 +628,28 @@ class MidyGMLite {
|
|
|
609
628
|
.setValueAtTime(attackVolume, volHold)
|
|
610
629
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
611
630
|
}
|
|
612
|
-
|
|
631
|
+
setPitchEnvelope(note) {
|
|
613
632
|
const now = this.audioContext.currentTime;
|
|
633
|
+
const { voiceParams } = note;
|
|
634
|
+
const baseRate = voiceParams.playbackRate;
|
|
614
635
|
note.bufferSource.playbackRate
|
|
615
636
|
.cancelScheduledValues(now)
|
|
616
|
-
.setValueAtTime(
|
|
617
|
-
|
|
618
|
-
setPitch(channel, note) {
|
|
619
|
-
const now = this.audioContext.currentTime;
|
|
620
|
-
const { startTime } = note;
|
|
621
|
-
const basePitch = this.calcSemitoneOffset(channel) * 100;
|
|
622
|
-
note.bufferSource.detune
|
|
623
|
-
.cancelScheduledValues(now)
|
|
624
|
-
.setValueAtTime(basePitch, startTime);
|
|
625
|
-
const modEnvToPitch = note.voiceParams.modEnvToPitch;
|
|
637
|
+
.setValueAtTime(baseRate, now);
|
|
638
|
+
const modEnvToPitch = voiceParams.modEnvToPitch;
|
|
626
639
|
if (modEnvToPitch === 0)
|
|
627
640
|
return;
|
|
641
|
+
const basePitch = this.rateToCent(baseRate);
|
|
628
642
|
const peekPitch = basePitch + modEnvToPitch;
|
|
643
|
+
const peekRate = this.centToRate(peekPitch);
|
|
629
644
|
const modDelay = startTime + voiceParams.modDelay;
|
|
630
645
|
const modAttack = modDelay + voiceParams.modAttack;
|
|
631
646
|
const modHold = modAttack + voiceParams.modHold;
|
|
632
647
|
const modDecay = modHold + voiceParams.modDecay;
|
|
633
|
-
note.bufferSource.
|
|
634
|
-
.setValueAtTime(
|
|
635
|
-
.exponentialRampToValueAtTime(
|
|
636
|
-
.setValueAtTime(
|
|
637
|
-
.linearRampToValueAtTime(
|
|
648
|
+
note.bufferSource.playbackRate
|
|
649
|
+
.setValueAtTime(baseRate, modDelay)
|
|
650
|
+
.exponentialRampToValueAtTime(peekRate, modAttack)
|
|
651
|
+
.setValueAtTime(peekRate, modHold)
|
|
652
|
+
.linearRampToValueAtTime(baseRate, modDecay);
|
|
638
653
|
}
|
|
639
654
|
clampCutoffFrequency(frequency) {
|
|
640
655
|
const minFrequency = 20; // min Hz of initialFilterFc
|
|
@@ -695,9 +710,8 @@ class MidyGMLite {
|
|
|
695
710
|
});
|
|
696
711
|
this.setVolumeEnvelope(note);
|
|
697
712
|
this.setFilterEnvelope(note);
|
|
698
|
-
this.
|
|
713
|
+
this.setPitchEnvelope(note);
|
|
699
714
|
if (0 < state.modulationDepth) {
|
|
700
|
-
this.setPitch(channel, note);
|
|
701
715
|
this.startModulation(channel, note, startTime);
|
|
702
716
|
}
|
|
703
717
|
note.bufferSource.connect(note.filterNode);
|
|
@@ -841,10 +855,12 @@ class MidyGMLite {
|
|
|
841
855
|
setPitchBend(channelNumber, value) {
|
|
842
856
|
const channel = this.channels[channelNumber];
|
|
843
857
|
const state = channel.state;
|
|
858
|
+
const prev = state.pitchWheel * 2 - 1;
|
|
859
|
+
const next = (value - 8192) / 8192;
|
|
844
860
|
state.pitchWheel = value / 16383;
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
this.
|
|
861
|
+
channel.detune += (next - prev) * state.pitchWheelSensitivity * 12800;
|
|
862
|
+
this.updateDetune(channel);
|
|
863
|
+
this.applyVoiceParams(channel, 14);
|
|
848
864
|
}
|
|
849
865
|
setModLfoToPitch(channel, note) {
|
|
850
866
|
const now = this.audioContext.currentTime;
|
|
@@ -856,6 +872,13 @@ class MidyGMLite {
|
|
|
856
872
|
.cancelScheduledValues(now)
|
|
857
873
|
.setValueAtTime(modulationDepth * modulationDepthSign, now);
|
|
858
874
|
}
|
|
875
|
+
setModLfoToFilterFc(note) {
|
|
876
|
+
const now = this.audioContext.currentTime;
|
|
877
|
+
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
878
|
+
note.filterDepth.gain
|
|
879
|
+
.cancelScheduledValues(now)
|
|
880
|
+
.setValueAtTime(modLfoToFilterFc, now);
|
|
881
|
+
}
|
|
859
882
|
setModLfoToVolume(note) {
|
|
860
883
|
const now = this.audioContext.currentTime;
|
|
861
884
|
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
@@ -865,13 +888,6 @@ class MidyGMLite {
|
|
|
865
888
|
.cancelScheduledValues(now)
|
|
866
889
|
.setValueAtTime(volumeDepth * volumeDepthSign, now);
|
|
867
890
|
}
|
|
868
|
-
setModLfoToFilterFc(note) {
|
|
869
|
-
const now = this.audioContext.currentTime;
|
|
870
|
-
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc;
|
|
871
|
-
note.filterDepth.gain
|
|
872
|
-
.cancelScheduledValues(now)
|
|
873
|
-
.setValueAtTime(modLfoToFilterFc, now);
|
|
874
|
-
}
|
|
875
891
|
setDelayModLFO(note) {
|
|
876
892
|
const now = this.audioContext.currentTime;
|
|
877
893
|
const startTime = note.startTime;
|
|
@@ -948,7 +964,7 @@ class MidyGMLite {
|
|
|
948
964
|
noteVoiceParams[key] = voiceParams[key];
|
|
949
965
|
}
|
|
950
966
|
this.setFilterEnvelope(channel, note);
|
|
951
|
-
this.
|
|
967
|
+
this.setPitchEnvelope(note);
|
|
952
968
|
}
|
|
953
969
|
else if (volumeEnvelopeKeySet.has(key)) {
|
|
954
970
|
if (appliedVolumeEnvelope)
|
|
@@ -1002,7 +1018,7 @@ class MidyGMLite {
|
|
|
1002
1018
|
note.modulationDepth.gain.setValueAtTime(channel.state.modulationDepth, now);
|
|
1003
1019
|
}
|
|
1004
1020
|
else {
|
|
1005
|
-
this.
|
|
1021
|
+
this.setPitchEnvelope(note);
|
|
1006
1022
|
this.startModulation(channel, note, now);
|
|
1007
1023
|
}
|
|
1008
1024
|
}
|
|
@@ -1097,20 +1113,6 @@ class MidyGMLite {
|
|
|
1097
1113
|
this.channels[channelNumber].dataMSB = value;
|
|
1098
1114
|
this.handleRPN(channelNumber);
|
|
1099
1115
|
}
|
|
1100
|
-
updateDetune(channel, detune) {
|
|
1101
|
-
const now = this.audioContext.currentTime;
|
|
1102
|
-
channel.scheduledNotes.forEach((noteList) => {
|
|
1103
|
-
for (let i = 0; i < noteList.length; i++) {
|
|
1104
|
-
const note = noteList[i];
|
|
1105
|
-
if (!note)
|
|
1106
|
-
continue;
|
|
1107
|
-
const { bufferSource } = note;
|
|
1108
|
-
bufferSource.detune
|
|
1109
|
-
.cancelScheduledValues(now)
|
|
1110
|
-
.setValueAtTime(detune, now);
|
|
1111
|
-
}
|
|
1112
|
-
});
|
|
1113
|
-
}
|
|
1114
1116
|
handlePitchBendRangeRPN(channelNumber) {
|
|
1115
1117
|
const channel = this.channels[channelNumber];
|
|
1116
1118
|
this.limitData(channel, 0, 127, 0, 99);
|
|
@@ -1237,6 +1239,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
1237
1239
|
writable: true,
|
|
1238
1240
|
value: {
|
|
1239
1241
|
currentBufferSource: null,
|
|
1242
|
+
detune: 0,
|
|
1240
1243
|
program: 0,
|
|
1241
1244
|
bank: 0,
|
|
1242
1245
|
dataMSB: 0,
|