@marmooo/midy 0.4.3 → 0.4.5
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 +6 -1
- package/esm/midy-GM1.d.ts +10 -5
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +68 -41
- package/esm/midy-GM2.d.ts +15 -9
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +139 -93
- package/esm/midy-GMLite.d.ts +10 -5
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +71 -43
- package/esm/midy.d.ts +59 -7
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +286 -83
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +10 -5
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +68 -41
- package/script/midy-GM2.d.ts +15 -9
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +139 -93
- package/script/midy-GMLite.d.ts +10 -5
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +71 -43
- package/script/midy.d.ts +59 -7
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +286 -83
package/esm/midy-GM2.js
CHANGED
|
@@ -240,6 +240,16 @@ const releaseCurve = 1 / (-Math.log(cbToRatio(-600)));
|
|
|
240
240
|
export class MidyGM2 extends EventTarget {
|
|
241
241
|
constructor(audioContext) {
|
|
242
242
|
super();
|
|
243
|
+
// https://pmc.ncbi.nlm.nih.gov/articles/PMC4191557/
|
|
244
|
+
// https://pubmed.ncbi.nlm.nih.gov/12488797/
|
|
245
|
+
// Gap detection studies indicate humans detect temporal discontinuities
|
|
246
|
+
// around 2–3 ms. Smoothing over ~4 ms is perceived as continuous.
|
|
247
|
+
Object.defineProperty(this, "perceptualSmoothingTime", {
|
|
248
|
+
enumerable: true,
|
|
249
|
+
configurable: true,
|
|
250
|
+
writable: true,
|
|
251
|
+
value: 0.004
|
|
252
|
+
});
|
|
243
253
|
Object.defineProperty(this, "mode", {
|
|
244
254
|
enumerable: true,
|
|
245
255
|
configurable: true,
|
|
@@ -698,9 +708,9 @@ export class MidyGM2 extends EventTarget {
|
|
|
698
708
|
this.voiceCache.clear();
|
|
699
709
|
this.realtimeVoiceCache.clear();
|
|
700
710
|
const channels = this.channels;
|
|
701
|
-
for (let
|
|
702
|
-
channels[
|
|
703
|
-
this.resetChannelStates(
|
|
711
|
+
for (let ch = 0; i < channels.length; ch++) {
|
|
712
|
+
channels[ch].scheduledNotes = [];
|
|
713
|
+
this.resetChannelStates(ch);
|
|
704
714
|
}
|
|
705
715
|
}
|
|
706
716
|
updateStates(queueIndex, nextQueueIndex) {
|
|
@@ -928,8 +938,8 @@ export class MidyGM2 extends EventTarget {
|
|
|
928
938
|
}
|
|
929
939
|
stopNotes(velocity, force, scheduleTime) {
|
|
930
940
|
const channels = this.channels;
|
|
931
|
-
for (let
|
|
932
|
-
this.stopChannelNotes(
|
|
941
|
+
for (let ch = 0; ch < channels.length; ch++) {
|
|
942
|
+
this.stopChannelNotes(ch, velocity, force, scheduleTime);
|
|
933
943
|
}
|
|
934
944
|
const stopPromise = Promise.all(this.notePromises);
|
|
935
945
|
this.notePromises = [];
|
|
@@ -970,6 +980,13 @@ export class MidyGM2 extends EventTarget {
|
|
|
970
980
|
this.isSeeking = true;
|
|
971
981
|
}
|
|
972
982
|
}
|
|
983
|
+
tempoChange(tempo) {
|
|
984
|
+
const timeScale = this.tempo / tempo;
|
|
985
|
+
this.resumeTime = this.resumeTime * timeScale;
|
|
986
|
+
this.tempo = tempo;
|
|
987
|
+
this.totalTime = this.calcTotalTime();
|
|
988
|
+
this.seekTo(this.currentTime() * timeScale);
|
|
989
|
+
}
|
|
973
990
|
calcTotalTime() {
|
|
974
991
|
const totalTimeEventTypes = this.totalTimeEventTypes;
|
|
975
992
|
const timeline = this.timeline;
|
|
@@ -1192,31 +1209,24 @@ export class MidyGM2 extends EventTarget {
|
|
|
1192
1209
|
return tuning + pitch;
|
|
1193
1210
|
}
|
|
1194
1211
|
}
|
|
1195
|
-
calcNoteDetune(channel, note) {
|
|
1196
|
-
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
1197
|
-
}
|
|
1198
1212
|
updateChannelDetune(channel, scheduleTime) {
|
|
1199
1213
|
this.processScheduledNotes(channel, (note) => {
|
|
1200
|
-
this.
|
|
1214
|
+
if (this.isPortamento(channel, note)) {
|
|
1215
|
+
this.setPortamentoDetune(channel, note, scheduleTime);
|
|
1216
|
+
}
|
|
1217
|
+
else {
|
|
1218
|
+
this.setDetune(channel, note, scheduleTime);
|
|
1219
|
+
}
|
|
1201
1220
|
});
|
|
1202
1221
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
.cancelScheduledValues(scheduleTime)
|
|
1212
|
-
.setValueAtTime(detune - deltaCent, scheduleTime)
|
|
1213
|
-
.linearRampToValueAtTime(detune, portamentoTime);
|
|
1214
|
-
}
|
|
1215
|
-
else {
|
|
1216
|
-
note.bufferSource.detune
|
|
1217
|
-
.cancelScheduledValues(scheduleTime)
|
|
1218
|
-
.setValueAtTime(detune, scheduleTime);
|
|
1219
|
-
}
|
|
1222
|
+
calcScaleOctaveTuning(channel, note) {
|
|
1223
|
+
return channel.scaleOctaveTuningTable[note.noteNumber % 12];
|
|
1224
|
+
}
|
|
1225
|
+
calcNoteDetune(channel, note) {
|
|
1226
|
+
const noteDetune = note.voiceParams.detune +
|
|
1227
|
+
this.calcScaleOctaveTuning(channel, note);
|
|
1228
|
+
const pitchControl = this.getPitchControl(channel, note);
|
|
1229
|
+
return channel.detune + noteDetune + pitchControl;
|
|
1220
1230
|
}
|
|
1221
1231
|
getPortamentoTime(channel, note) {
|
|
1222
1232
|
const deltaSemitone = Math.abs(note.noteNumber - note.portamentoNoteNumber);
|
|
@@ -1287,7 +1297,7 @@ export class MidyGM2 extends EventTarget {
|
|
|
1287
1297
|
const portamentoTime = startTime + this.getPortamentoTime(channel, note);
|
|
1288
1298
|
note.volumeEnvelopeNode.gain
|
|
1289
1299
|
.cancelScheduledValues(scheduleTime)
|
|
1290
|
-
.
|
|
1300
|
+
.exponentialRampToValueAtTime(sustainVolume, portamentoTime);
|
|
1291
1301
|
}
|
|
1292
1302
|
setVolumeEnvelope(channel, note, scheduleTime) {
|
|
1293
1303
|
const { voiceParams, startTime } = note;
|
|
@@ -1301,38 +1311,56 @@ export class MidyGM2 extends EventTarget {
|
|
|
1301
1311
|
note.volumeEnvelopeNode.gain
|
|
1302
1312
|
.cancelScheduledValues(scheduleTime)
|
|
1303
1313
|
.setValueAtTime(0, startTime)
|
|
1304
|
-
.setValueAtTime(
|
|
1305
|
-
.
|
|
1314
|
+
.setValueAtTime(1e-6, volDelay)
|
|
1315
|
+
.exponentialRampToValueAtTime(attackVolume, volAttack)
|
|
1306
1316
|
.setValueAtTime(attackVolume, volHold)
|
|
1307
1317
|
.setTargetAtTime(sustainVolume, volHold, decayDuration * decayCurve);
|
|
1308
1318
|
}
|
|
1309
|
-
|
|
1319
|
+
setPortamentoDetune(channel, note, scheduleTime) {
|
|
1320
|
+
const detune = this.calcNoteDetune(channel, note);
|
|
1321
|
+
const startTime = note.startTime;
|
|
1322
|
+
const deltaCent = (note.noteNumber - note.portamentoNoteNumber) * 100;
|
|
1323
|
+
const portamentoTime = startTime + this.getPortamentoTime(channel, note);
|
|
1324
|
+
note.bufferSource.detune
|
|
1325
|
+
.cancelScheduledValues(scheduleTime)
|
|
1326
|
+
.setValueAtTime(detune - deltaCent, scheduleTime)
|
|
1327
|
+
.linearRampToValueAtTime(detune, portamentoTime);
|
|
1328
|
+
}
|
|
1329
|
+
setDetune(channel, note, scheduleTime) {
|
|
1330
|
+
const detune = this.calcNoteDetune(channel, note);
|
|
1331
|
+
note.bufferSource.detune
|
|
1332
|
+
.cancelScheduledValues(scheduleTime)
|
|
1333
|
+
.setValueAtTime(detune, scheduleTime);
|
|
1334
|
+
const timeConstant = this.perceptualSmoothingTime / 5; // 99.3% (5 * tau)
|
|
1335
|
+
note.bufferSource.detune
|
|
1336
|
+
.cancelAndHoldAtTime(scheduleTime)
|
|
1337
|
+
.setTargetAtTime(detune, scheduleTime, timeConstant);
|
|
1338
|
+
}
|
|
1339
|
+
setPortamentoPitchEnvelope(channel, note, scheduleTime) {
|
|
1310
1340
|
const baseRate = note.voiceParams.playbackRate;
|
|
1311
1341
|
const portamentoTime = note.startTime +
|
|
1312
1342
|
this.getPortamentoTime(channel, note);
|
|
1313
1343
|
note.bufferSource.playbackRate
|
|
1314
1344
|
.cancelScheduledValues(scheduleTime)
|
|
1315
|
-
.
|
|
1345
|
+
.exponentialRampToValueAtTime(baseRate, portamentoTime);
|
|
1316
1346
|
}
|
|
1317
1347
|
setPitchEnvelope(note, scheduleTime) {
|
|
1318
|
-
const { voiceParams } = note;
|
|
1348
|
+
const { bufferSource, voiceParams } = note;
|
|
1319
1349
|
const baseRate = voiceParams.playbackRate;
|
|
1320
|
-
|
|
1350
|
+
bufferSource.playbackRate
|
|
1321
1351
|
.cancelScheduledValues(scheduleTime)
|
|
1322
|
-
.setValueAtTime(baseRate,
|
|
1352
|
+
.setValueAtTime(baseRate, scheduleTime);
|
|
1323
1353
|
const modEnvToPitch = voiceParams.modEnvToPitch;
|
|
1324
1354
|
if (modEnvToPitch === 0)
|
|
1325
1355
|
return;
|
|
1326
|
-
const
|
|
1327
|
-
const peekPitch = basePitch + modEnvToPitch;
|
|
1328
|
-
const peekRate = this.centToRate(peekPitch);
|
|
1356
|
+
const peekRate = baseRate * this.centToRate(modEnvToPitch);
|
|
1329
1357
|
const modDelay = note.startTime + voiceParams.modDelay;
|
|
1330
1358
|
const modAttack = modDelay + voiceParams.modAttack;
|
|
1331
1359
|
const modHold = modAttack + voiceParams.modHold;
|
|
1332
1360
|
const decayDuration = voiceParams.modDecay;
|
|
1333
|
-
|
|
1361
|
+
bufferSource.playbackRate
|
|
1334
1362
|
.setValueAtTime(baseRate, modDelay)
|
|
1335
|
-
.
|
|
1363
|
+
.exponentialRampToValueAtTime(peekRate, modAttack)
|
|
1336
1364
|
.setValueAtTime(peekRate, modHold)
|
|
1337
1365
|
.setTargetAtTime(baseRate, modHold, decayDuration * decayCurve);
|
|
1338
1366
|
}
|
|
@@ -1348,10 +1376,10 @@ export class MidyGM2 extends EventTarget {
|
|
|
1348
1376
|
this.getFilterCutoffControl(channel);
|
|
1349
1377
|
const sustainCent = baseCent +
|
|
1350
1378
|
voiceParams.modEnvToFilterFc * (1 - voiceParams.modSustain);
|
|
1351
|
-
const baseFreq = this.centToHz(baseCent);
|
|
1352
|
-
const sustainFreq = this.centToHz(sustainCent);
|
|
1353
|
-
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq
|
|
1354
|
-
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq
|
|
1379
|
+
const baseFreq = this.centToHz(baseCent) * scale;
|
|
1380
|
+
const sustainFreq = this.centToHz(sustainCent) * scale;
|
|
1381
|
+
const adjustedBaseFreq = this.clampCutoffFrequency(baseFreq);
|
|
1382
|
+
const adjustedSustainFreq = this.clampCutoffFrequency(sustainFreq);
|
|
1355
1383
|
const portamentoTime = startTime + this.getPortamentoTime(channel, note);
|
|
1356
1384
|
const modDelay = startTime + voiceParams.modDelay;
|
|
1357
1385
|
note.adjustedBaseFreq = adjustedSustainFreq;
|
|
@@ -1359,7 +1387,7 @@ export class MidyGM2 extends EventTarget {
|
|
|
1359
1387
|
.cancelScheduledValues(scheduleTime)
|
|
1360
1388
|
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
1361
1389
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
1362
|
-
.
|
|
1390
|
+
.exponentialRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1363
1391
|
}
|
|
1364
1392
|
setFilterEnvelope(channel, note, scheduleTime) {
|
|
1365
1393
|
const { voiceParams, startTime } = note;
|
|
@@ -1385,7 +1413,7 @@ export class MidyGM2 extends EventTarget {
|
|
|
1385
1413
|
.cancelScheduledValues(scheduleTime)
|
|
1386
1414
|
.setValueAtTime(adjustedBaseFreq, startTime)
|
|
1387
1415
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
1388
|
-
.
|
|
1416
|
+
.exponentialRampToValueAtTime(adjustedPeekFreq, modAttack)
|
|
1389
1417
|
.setValueAtTime(adjustedPeekFreq, modHold)
|
|
1390
1418
|
.setTargetAtTime(adjustedSustainFreq, modHold, decayDuration * decayCurve);
|
|
1391
1419
|
}
|
|
@@ -1474,14 +1502,15 @@ export class MidyGM2 extends EventTarget {
|
|
|
1474
1502
|
if (!channel.isDrum && this.isPortamento(channel, note)) {
|
|
1475
1503
|
this.setPortamentoVolumeEnvelope(channel, note, now);
|
|
1476
1504
|
this.setPortamentoFilterEnvelope(channel, note, now);
|
|
1477
|
-
this.setPortamentoPitchEnvelope(note, now);
|
|
1505
|
+
this.setPortamentoPitchEnvelope(channel, note, now);
|
|
1506
|
+
this.setPortamentoDetune(channel, note, now);
|
|
1478
1507
|
}
|
|
1479
1508
|
else {
|
|
1480
1509
|
this.setVolumeEnvelope(channel, note, now);
|
|
1481
1510
|
this.setFilterEnvelope(channel, note, now);
|
|
1482
1511
|
this.setPitchEnvelope(note, now);
|
|
1512
|
+
this.setDetune(channel, note, now);
|
|
1483
1513
|
}
|
|
1484
|
-
this.updateDetune(channel, note, now);
|
|
1485
1514
|
if (0 < state.vibratoDepth) {
|
|
1486
1515
|
this.startVibrato(channel, note, now);
|
|
1487
1516
|
}
|
|
@@ -1615,15 +1644,14 @@ export class MidyGM2 extends EventTarget {
|
|
|
1615
1644
|
}
|
|
1616
1645
|
releaseNote(channel, note, endTime) {
|
|
1617
1646
|
endTime ??= this.audioContext.currentTime;
|
|
1618
|
-
const
|
|
1619
|
-
const volRelease = endTime +
|
|
1620
|
-
const modRelease = endTime + note.voiceParams.modRelease;
|
|
1647
|
+
const volDuration = note.voiceParams.volRelease;
|
|
1648
|
+
const volRelease = endTime + volDuration;
|
|
1621
1649
|
note.filterNode.frequency
|
|
1622
1650
|
.cancelScheduledValues(endTime)
|
|
1623
|
-
.
|
|
1651
|
+
.setTargetAtTime(note.adjustedBaseFreq, endTime, note.voiceParams.modRelease * releaseCurve);
|
|
1624
1652
|
note.volumeEnvelopeNode.gain
|
|
1625
1653
|
.cancelScheduledValues(endTime)
|
|
1626
|
-
.setTargetAtTime(0, endTime,
|
|
1654
|
+
.setTargetAtTime(0, endTime, volDuration * releaseCurve);
|
|
1627
1655
|
return new Promise((resolve) => {
|
|
1628
1656
|
this.scheduleTask(() => {
|
|
1629
1657
|
const bufferSource = note.bufferSource;
|
|
@@ -1909,13 +1937,6 @@ export class MidyGM2 extends EventTarget {
|
|
|
1909
1937
|
.cancelScheduledValues(scheduleTime)
|
|
1910
1938
|
.setValueAtTime(freqModLFO, scheduleTime);
|
|
1911
1939
|
}
|
|
1912
|
-
setFreqVibLFO(channel, note, scheduleTime) {
|
|
1913
|
-
const vibratoRate = channel.state.vibratoRate * 2;
|
|
1914
|
-
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1915
|
-
note.vibratoLFO.frequency
|
|
1916
|
-
.cancelScheduledValues(scheduleTime)
|
|
1917
|
-
.setValueAtTime(freqVibLFO * vibratoRate, scheduleTime);
|
|
1918
|
-
}
|
|
1919
1940
|
setDelayVibLFO(channel, note) {
|
|
1920
1941
|
const vibratoDelay = channel.state.vibratoDelay * 2;
|
|
1921
1942
|
const value = note.voiceParams.delayVibLFO;
|
|
@@ -1925,6 +1946,13 @@ export class MidyGM2 extends EventTarget {
|
|
|
1925
1946
|
}
|
|
1926
1947
|
catch { /* empty */ }
|
|
1927
1948
|
}
|
|
1949
|
+
setFreqVibLFO(channel, note, scheduleTime) {
|
|
1950
|
+
const vibratoRate = channel.state.vibratoRate * 2;
|
|
1951
|
+
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1952
|
+
note.vibratoLFO.frequency
|
|
1953
|
+
.cancelScheduledValues(scheduleTime)
|
|
1954
|
+
.setValueAtTime(freqVibLFO * vibratoRate, scheduleTime);
|
|
1955
|
+
}
|
|
1928
1956
|
createVoiceParamsHandlers() {
|
|
1929
1957
|
return {
|
|
1930
1958
|
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
@@ -1973,6 +2001,14 @@ export class MidyGM2 extends EventTarget {
|
|
|
1973
2001
|
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
1974
2002
|
}
|
|
1975
2003
|
},
|
|
2004
|
+
detune: (channel, note, scheduleTime) => {
|
|
2005
|
+
if (this.isPortamento(channel, note)) {
|
|
2006
|
+
this.setPortamentoDetune(channel, note, scheduleTime);
|
|
2007
|
+
}
|
|
2008
|
+
else {
|
|
2009
|
+
this.setDetune(channel, note, scheduleTime);
|
|
2010
|
+
}
|
|
2011
|
+
},
|
|
1976
2012
|
};
|
|
1977
2013
|
}
|
|
1978
2014
|
getControllerState(channel, noteNumber, velocity) {
|
|
@@ -2072,13 +2108,13 @@ export class MidyGM2 extends EventTarget {
|
|
|
2072
2108
|
}
|
|
2073
2109
|
});
|
|
2074
2110
|
}
|
|
2075
|
-
setModulationDepth(channelNumber,
|
|
2111
|
+
setModulationDepth(channelNumber, value, scheduleTime) {
|
|
2076
2112
|
const channel = this.channels[channelNumber];
|
|
2077
2113
|
if (channel.isDrum)
|
|
2078
2114
|
return;
|
|
2079
2115
|
if (!(0 <= scheduleTime))
|
|
2080
2116
|
scheduleTime = this.audioContext.currentTime;
|
|
2081
|
-
channel.state.modulationDepthMSB =
|
|
2117
|
+
channel.state.modulationDepthMSB = value / 127;
|
|
2082
2118
|
this.updateModulation(channel, scheduleTime);
|
|
2083
2119
|
}
|
|
2084
2120
|
updatePortamento(channel, scheduleTime) {
|
|
@@ -2088,31 +2124,31 @@ export class MidyGM2 extends EventTarget {
|
|
|
2088
2124
|
if (this.isPortamento(channel, note)) {
|
|
2089
2125
|
this.setPortamentoVolumeEnvelope(channel, note, scheduleTime);
|
|
2090
2126
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
2091
|
-
this.setPortamentoPitchEnvelope(note, scheduleTime);
|
|
2092
|
-
this.
|
|
2127
|
+
this.setPortamentoPitchEnvelope(channel, note, scheduleTime);
|
|
2128
|
+
this.setPortamentoDetune(channel, note, scheduleTime);
|
|
2093
2129
|
}
|
|
2094
2130
|
else {
|
|
2095
2131
|
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
2096
2132
|
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
2097
2133
|
this.setPitchEnvelope(note, scheduleTime);
|
|
2098
|
-
this.
|
|
2134
|
+
this.setDetune(channel, note, scheduleTime);
|
|
2099
2135
|
}
|
|
2100
2136
|
});
|
|
2101
2137
|
}
|
|
2102
|
-
setPortamentoTime(channelNumber,
|
|
2138
|
+
setPortamentoTime(channelNumber, value, scheduleTime) {
|
|
2103
2139
|
if (!(0 <= scheduleTime))
|
|
2104
2140
|
scheduleTime = this.audioContext.currentTime;
|
|
2105
2141
|
const channel = this.channels[channelNumber];
|
|
2106
|
-
channel.state.portamentoTimeMSB =
|
|
2142
|
+
channel.state.portamentoTimeMSB = value / 127;
|
|
2107
2143
|
if (channel.isDrum)
|
|
2108
2144
|
return;
|
|
2109
2145
|
this.updatePortamento(channel, scheduleTime);
|
|
2110
2146
|
}
|
|
2111
|
-
setVolume(channelNumber,
|
|
2147
|
+
setVolume(channelNumber, value, scheduleTime) {
|
|
2112
2148
|
if (!(0 <= scheduleTime))
|
|
2113
2149
|
scheduleTime = this.audioContext.currentTime;
|
|
2114
2150
|
const channel = this.channels[channelNumber];
|
|
2115
|
-
channel.state.volumeMSB =
|
|
2151
|
+
channel.state.volumeMSB = value / 127;
|
|
2116
2152
|
if (channel.isDrum) {
|
|
2117
2153
|
for (let i = 0; i < 128; i++) {
|
|
2118
2154
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2129,11 +2165,11 @@ export class MidyGM2 extends EventTarget {
|
|
|
2129
2165
|
gainRight: Math.sin(theta),
|
|
2130
2166
|
};
|
|
2131
2167
|
}
|
|
2132
|
-
setPan(channelNumber,
|
|
2168
|
+
setPan(channelNumber, value, scheduleTime) {
|
|
2133
2169
|
if (!(0 <= scheduleTime))
|
|
2134
2170
|
scheduleTime = this.audioContext.currentTime;
|
|
2135
2171
|
const channel = this.channels[channelNumber];
|
|
2136
|
-
channel.state.panMSB =
|
|
2172
|
+
channel.state.panMSB = value / 127;
|
|
2137
2173
|
if (channel.isDrum) {
|
|
2138
2174
|
for (let i = 0; i < 128; i++) {
|
|
2139
2175
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2143,11 +2179,11 @@ export class MidyGM2 extends EventTarget {
|
|
|
2143
2179
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2144
2180
|
}
|
|
2145
2181
|
}
|
|
2146
|
-
setExpression(channelNumber,
|
|
2182
|
+
setExpression(channelNumber, value, scheduleTime) {
|
|
2147
2183
|
if (!(0 <= scheduleTime))
|
|
2148
2184
|
scheduleTime = this.audioContext.currentTime;
|
|
2149
2185
|
const channel = this.channels[channelNumber];
|
|
2150
|
-
channel.state.expressionMSB =
|
|
2186
|
+
channel.state.expressionMSB = value / 127;
|
|
2151
2187
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2152
2188
|
}
|
|
2153
2189
|
setBankLSB(channelNumber, lsb) {
|
|
@@ -2159,14 +2195,14 @@ export class MidyGM2 extends EventTarget {
|
|
|
2159
2195
|
}
|
|
2160
2196
|
updateChannelVolume(channel, scheduleTime) {
|
|
2161
2197
|
const state = channel.state;
|
|
2162
|
-
const
|
|
2198
|
+
const gain = state.volumeMSB * state.expressionMSB;
|
|
2163
2199
|
const { gainLeft, gainRight } = this.panToGain(state.panMSB);
|
|
2164
2200
|
channel.gainL.gain
|
|
2165
2201
|
.cancelScheduledValues(scheduleTime)
|
|
2166
|
-
.setValueAtTime(
|
|
2202
|
+
.setValueAtTime(gain * gainLeft, scheduleTime);
|
|
2167
2203
|
channel.gainR.gain
|
|
2168
2204
|
.cancelScheduledValues(scheduleTime)
|
|
2169
|
-
.setValueAtTime(
|
|
2205
|
+
.setValueAtTime(gain * gainRight, scheduleTime);
|
|
2170
2206
|
}
|
|
2171
2207
|
updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
|
|
2172
2208
|
const gainL = channel.keyBasedGainLs[keyNumber];
|
|
@@ -2174,21 +2210,21 @@ export class MidyGM2 extends EventTarget {
|
|
|
2174
2210
|
return;
|
|
2175
2211
|
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
2176
2212
|
const state = channel.state;
|
|
2177
|
-
const
|
|
2213
|
+
const defaultGain = state.volumeMSB * state.expressionMSB;
|
|
2178
2214
|
const defaultPan = state.panMSB;
|
|
2179
2215
|
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
2180
|
-
const
|
|
2181
|
-
?
|
|
2182
|
-
:
|
|
2216
|
+
const gain = (0 <= keyBasedVolume)
|
|
2217
|
+
? defaultGain * keyBasedVolume / 64
|
|
2218
|
+
: defaultGain;
|
|
2183
2219
|
const keyBasedPan = this.getKeyBasedValue(channel, keyNumber, 10);
|
|
2184
2220
|
const pan = (0 <= keyBasedPan) ? keyBasedPan / 127 : defaultPan;
|
|
2185
2221
|
const { gainLeft, gainRight } = this.panToGain(pan);
|
|
2186
2222
|
gainL.gain
|
|
2187
2223
|
.cancelScheduledValues(scheduleTime)
|
|
2188
|
-
.setValueAtTime(
|
|
2224
|
+
.setValueAtTime(gain * gainLeft, scheduleTime);
|
|
2189
2225
|
gainR.gain
|
|
2190
2226
|
.cancelScheduledValues(scheduleTime)
|
|
2191
|
-
.setValueAtTime(
|
|
2227
|
+
.setValueAtTime(gain * gainRight, scheduleTime);
|
|
2192
2228
|
}
|
|
2193
2229
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
2194
2230
|
const channel = this.channels[channelNumber];
|
|
@@ -2320,6 +2356,8 @@ export class MidyGM2 extends EventTarget {
|
|
|
2320
2356
|
case 5:
|
|
2321
2357
|
this.handleModulationDepthRangeRPN(channelNumber, scheduleTime);
|
|
2322
2358
|
break;
|
|
2359
|
+
case 16383: // NULL
|
|
2360
|
+
break;
|
|
2323
2361
|
default:
|
|
2324
2362
|
console.warn(`Channel ${channelNumber}: Unsupported RPN MSB=${channel.rpnMSB} LSB=${channel.rpnLSB}`);
|
|
2325
2363
|
}
|
|
@@ -2521,9 +2559,9 @@ export class MidyGM2 extends EventTarget {
|
|
|
2521
2559
|
if (!(0 <= scheduleTime))
|
|
2522
2560
|
scheduleTime = this.audioContext.currentTime;
|
|
2523
2561
|
this.mode = "GM1";
|
|
2524
|
-
for (let
|
|
2525
|
-
this.allSoundOff(
|
|
2526
|
-
const channel = channels[
|
|
2562
|
+
for (let ch = 0; ch < channels.length; ch++) {
|
|
2563
|
+
this.allSoundOff(ch, 0, scheduleTime);
|
|
2564
|
+
const channel = channels[ch];
|
|
2527
2565
|
channel.bankMSB = 0;
|
|
2528
2566
|
channel.bankLSB = 0;
|
|
2529
2567
|
channel.isDrum = false;
|
|
@@ -2536,9 +2574,9 @@ export class MidyGM2 extends EventTarget {
|
|
|
2536
2574
|
if (!(0 <= scheduleTime))
|
|
2537
2575
|
scheduleTime = this.audioContext.currentTime;
|
|
2538
2576
|
this.mode = "GM2";
|
|
2539
|
-
for (let
|
|
2540
|
-
this.allSoundOff(
|
|
2541
|
-
const channel = channels[
|
|
2577
|
+
for (let ch = 0; ch < channels.length; ch++) {
|
|
2578
|
+
this.allSoundOff(ch, 0, scheduleTime);
|
|
2579
|
+
const channel = channels[ch];
|
|
2542
2580
|
channel.bankMSB = 121;
|
|
2543
2581
|
channel.bankLSB = 0;
|
|
2544
2582
|
channel.isDrum = false;
|
|
@@ -2605,8 +2643,9 @@ export class MidyGM2 extends EventTarget {
|
|
|
2605
2643
|
const next = value;
|
|
2606
2644
|
this.masterFineTuning = next;
|
|
2607
2645
|
const detuneChange = next - prev;
|
|
2608
|
-
|
|
2609
|
-
|
|
2646
|
+
const channels = this.channels;
|
|
2647
|
+
for (let ch = 0; i < channels.length; ch++) {
|
|
2648
|
+
const channel = this.channels[ch];
|
|
2610
2649
|
if (channel.isDrum)
|
|
2611
2650
|
continue;
|
|
2612
2651
|
channel.detune += detuneChange;
|
|
@@ -2622,8 +2661,9 @@ export class MidyGM2 extends EventTarget {
|
|
|
2622
2661
|
const next = value;
|
|
2623
2662
|
this.masterCoarseTuning = next;
|
|
2624
2663
|
const detuneChange = next - prev;
|
|
2625
|
-
|
|
2626
|
-
|
|
2664
|
+
const channels = this.channels;
|
|
2665
|
+
for (let ch = 0; ch < channels.length; ch++) {
|
|
2666
|
+
const channel = this.channels[ch];
|
|
2627
2667
|
if (channel.isDrum)
|
|
2628
2668
|
continue;
|
|
2629
2669
|
channel.detune += detuneChange;
|
|
@@ -2884,8 +2924,14 @@ export class MidyGM2 extends EventTarget {
|
|
|
2884
2924
|
return channelPressure / 127;
|
|
2885
2925
|
}
|
|
2886
2926
|
setEffects(channel, note, table, scheduleTime) {
|
|
2887
|
-
if (0 < table[0])
|
|
2888
|
-
this.
|
|
2927
|
+
if (0 < table[0]) {
|
|
2928
|
+
if (this.isPortamento(channel, note)) {
|
|
2929
|
+
this.setPortamentoDetune(channel, note, scheduleTime);
|
|
2930
|
+
}
|
|
2931
|
+
else {
|
|
2932
|
+
this.setDetune(channel, note, scheduleTime);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2889
2935
|
if (0.5 <= channel.state.portamemento && 0 <= note.portamentoNoteNumber) {
|
|
2890
2936
|
if (0 < table[1]) {
|
|
2891
2937
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
package/esm/midy-GMLite.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export class MidyGMLite extends EventTarget {
|
|
|
10
10
|
modulationDepthRange: number;
|
|
11
11
|
};
|
|
12
12
|
constructor(audioContext: any);
|
|
13
|
+
perceptualSmoothingTime: number;
|
|
13
14
|
mode: string;
|
|
14
15
|
numChannels: number;
|
|
15
16
|
ticksPerBeat: number;
|
|
@@ -54,6 +55,7 @@ export class MidyGMLite extends EventTarget {
|
|
|
54
55
|
freqModLFO: (_channel: any, note: any, scheduleTime: any) => void;
|
|
55
56
|
delayVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
56
57
|
freqVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
58
|
+
detune: (channel: any, note: any, scheduleTime: any) => void;
|
|
57
59
|
};
|
|
58
60
|
controlChangeHandlers: any[];
|
|
59
61
|
channels: any[];
|
|
@@ -91,6 +93,7 @@ export class MidyGMLite extends EventTarget {
|
|
|
91
93
|
pause(): Promise<void>;
|
|
92
94
|
resume(): Promise<void>;
|
|
93
95
|
seekTo(second: any): void;
|
|
96
|
+
tempoChange(tempo: any): void;
|
|
94
97
|
calcTotalTime(): number;
|
|
95
98
|
currentTime(): number;
|
|
96
99
|
processScheduledNotes(channel: any, callback: any): Promise<void>;
|
|
@@ -100,8 +103,9 @@ export class MidyGMLite extends EventTarget {
|
|
|
100
103
|
centToHz(cent: any): number;
|
|
101
104
|
calcChannelDetune(channel: any): number;
|
|
102
105
|
updateChannelDetune(channel: any, scheduleTime: any): void;
|
|
103
|
-
|
|
106
|
+
calcNoteDetune(channel: any, note: any): any;
|
|
104
107
|
setVolumeEnvelope(note: any, scheduleTime: any): void;
|
|
108
|
+
setDetune(channel: any, note: any, scheduleTime: any): void;
|
|
105
109
|
setPitchEnvelope(note: any, scheduleTime: any): void;
|
|
106
110
|
clampCutoffFrequency(frequency: any): number;
|
|
107
111
|
setFilterEnvelope(note: any, scheduleTime: any): void;
|
|
@@ -140,20 +144,21 @@ export class MidyGMLite extends EventTarget {
|
|
|
140
144
|
freqModLFO: (_channel: any, note: any, scheduleTime: any) => void;
|
|
141
145
|
delayVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
142
146
|
freqVibLFO: (_channel: any, _note: any, _scheduleTime: any) => void;
|
|
147
|
+
detune: (channel: any, note: any, scheduleTime: any) => void;
|
|
143
148
|
};
|
|
144
149
|
getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array<any>;
|
|
145
150
|
applyVoiceParams(channel: any, controllerType: any, scheduleTime: any): void;
|
|
146
151
|
createControlChangeHandlers(): any[];
|
|
147
152
|
setControlChange(channelNumber: any, controllerType: any, value: any, scheduleTime: any): void;
|
|
148
153
|
updateModulation(channel: any, scheduleTime: any): void;
|
|
149
|
-
setModulationDepth(channelNumber: any,
|
|
150
|
-
setVolume(channelNumber: any,
|
|
154
|
+
setModulationDepth(channelNumber: any, value: any, scheduleTime: any): void;
|
|
155
|
+
setVolume(channelNumber: any, value: any, scheduleTime: any): void;
|
|
151
156
|
panToGain(pan: any): {
|
|
152
157
|
gainLeft: number;
|
|
153
158
|
gainRight: number;
|
|
154
159
|
};
|
|
155
|
-
setPan(channelNumber: any,
|
|
156
|
-
setExpression(channelNumber: any,
|
|
160
|
+
setPan(channelNumber: any, value: any, scheduleTime: any): void;
|
|
161
|
+
setExpression(channelNumber: any, value: any, scheduleTime: any): void;
|
|
157
162
|
dataEntryLSB(channelNumber: any, value: any, scheduleTime: any): void;
|
|
158
163
|
updateChannelVolume(channel: any, scheduleTime: any): void;
|
|
159
164
|
setSustainPedal(channelNumber: any, value: any, scheduleTime: any): void;
|
package/esm/midy-GMLite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqHA;
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAqHA;IAuCE;;;;;;;;;MASE;IAEF,+BAgBC;IA7DD,gCAAgC;IAChC,aAAa;IACb,oBAAiB;IACjB,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,0BAAuD;IACvD,4BAAyB;IACzB,0BAAuB;IACvB,kCAA+B;IAC/B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,iCAEG;IACH,cAAU;IACV,cAAa;IACb,iBAAY;IACZ,gBAAc;IACd,oBAAkB;IAClB,sBAAwB;IACxB,2BAAqC;IACrC,+BAEE;IAeA,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF,uBAAmD;IACnD;;;;;;;;;;;;MAA2D;IAC3D,6BAA+D;IAC/D,gBAAiD;IAMnD,mCASC;IAED,2DAYC;IAED,yCAmBC;IAED,oCASC;IAED,sBAoCC;IAED,8DAeC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDASC;IAED,0EAUC;IAED,gEAsDC;IAED,mCASC;IAED,uBAUC;IAED,yDAqCC;IAED,2BA0EC;IAED,uDAEC;IAED,wDAEC;IAED,qCAKC;IAED;;;MAwDC;IAED,kGAeC;IAED,mGAeC;IAED,wEAQC;IAED,uBAMC;IAED,sBAIC;IAED,uBAMC;IAED,wBAIC;IAED,0BAKC;IAED,8BAMC;IAED,wBAYC;IAED,sBAIC;IAED,kEAWC;IAED,kFAYC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,2DAIC;IAED,6CAEC;IAED,sDAeC;IAED,4DASC;IAED,qDAkBC;IAED,6CAIC;IAED,sDA6BC;IAED,kEAqBC;IAED,4GAkCC;IAED,uEA6CC;IAED,0EAiBC;IAED,8EAiBC;IAED,oEAUC;IAED,0FAwBC;IAED,gCASC;IAED,iEAwBC;IAED,4FAsBC;IAED,6CAUC;IAED,qDAUC;IAED,qFAeC;IAED,+BAmBC;IAED,kDAOC;IAED,sFA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAYC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;;MAoCC;IAED,oFAMC;IAED,6EA2BC;IAED,qCAeC;IAED,+FAWC;IAED,wDAUC;IAED,4EAKC;IAED,mEAKC;IAED;;;MAMC;IAED,gEAKC;IAED,uEAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAWC;IAED,kFAeC;IAED,uDAcC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,gFAGC;IAED,6CAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAUC;IAED,4EAaC;IAED,4DAGC;IAED,qDAKC;IAED,gDAYC;IAGD,6DAgBC;CACF"}
|