@marmooo/midy 0.2.8 → 0.2.9
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/esm/midy-GM1.d.ts +8 -4
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +54 -17
- package/esm/midy-GM2.d.ts +13 -8
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +147 -40
- package/esm/midy-GMLite.d.ts +9 -3
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +57 -16
- package/esm/midy.d.ts +13 -8
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +187 -51
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +8 -4
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +54 -17
- package/script/midy-GM2.d.ts +13 -8
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +147 -40
- package/script/midy-GMLite.d.ts +9 -3
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +57 -16
- package/script/midy.d.ts +13 -8
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +187 -51
package/esm/midy-GM2.js
CHANGED
|
@@ -239,6 +239,12 @@ const volumeEnvelopeKeys = [
|
|
|
239
239
|
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
240
240
|
export class MidyGM2 {
|
|
241
241
|
constructor(audioContext, options = this.defaultOptions) {
|
|
242
|
+
Object.defineProperty(this, "mode", {
|
|
243
|
+
enumerable: true,
|
|
244
|
+
configurable: true,
|
|
245
|
+
writable: true,
|
|
246
|
+
value: "GM2"
|
|
247
|
+
});
|
|
242
248
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
243
249
|
enumerable: true,
|
|
244
250
|
configurable: true,
|
|
@@ -417,6 +423,11 @@ export class MidyGM2 {
|
|
|
417
423
|
this.audioContext = audioContext;
|
|
418
424
|
this.options = { ...this.defaultOptions, ...options };
|
|
419
425
|
this.masterVolume = new GainNode(audioContext);
|
|
426
|
+
this.scheduler = new GainNode(audioContext, { gain: 0 });
|
|
427
|
+
this.schedulerBuffer = new AudioBuffer({
|
|
428
|
+
length: 1,
|
|
429
|
+
sampleRate: audioContext.sampleRate,
|
|
430
|
+
});
|
|
420
431
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
421
432
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
422
433
|
this.channels = this.createChannels(audioContext);
|
|
@@ -425,6 +436,7 @@ export class MidyGM2 {
|
|
|
425
436
|
this.chorusEffect.output.connect(this.masterVolume);
|
|
426
437
|
this.reverbEffect.output.connect(this.masterVolume);
|
|
427
438
|
this.masterVolume.connect(audioContext.destination);
|
|
439
|
+
this.scheduler.connect(audioContext.destination);
|
|
428
440
|
this.GM2SystemOn();
|
|
429
441
|
}
|
|
430
442
|
initSoundFontTable() {
|
|
@@ -523,10 +535,24 @@ export class MidyGM2 {
|
|
|
523
535
|
return audioBuffer;
|
|
524
536
|
}
|
|
525
537
|
}
|
|
526
|
-
|
|
538
|
+
calcLoopMode(channel, note, voiceParams) {
|
|
539
|
+
if (channel.isDrum) {
|
|
540
|
+
const noteNumber = note.noteNumber;
|
|
541
|
+
if (noteNumber === 88 || 47 <= noteNumber && noteNumber <= 84) {
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
return false;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
return voiceParams.sampleModes % 2 !== 0;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
createBufferSource(channel, note, voiceParams, audioBuffer) {
|
|
527
553
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
528
554
|
bufferSource.buffer = audioBuffer;
|
|
529
|
-
bufferSource.loop =
|
|
555
|
+
bufferSource.loop = this.calcLoopMode(channel, note, voiceParams);
|
|
530
556
|
if (bufferSource.loop) {
|
|
531
557
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
532
558
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -1003,7 +1029,9 @@ export class MidyGM2 {
|
|
|
1003
1029
|
return 8.176 * this.centToRate(cent);
|
|
1004
1030
|
}
|
|
1005
1031
|
calcChannelDetune(channel) {
|
|
1006
|
-
const masterTuning =
|
|
1032
|
+
const masterTuning = channel.isDrum
|
|
1033
|
+
? 0
|
|
1034
|
+
: this.masterCoarseTuning + this.masterFineTuning;
|
|
1007
1035
|
const channelTuning = channel.coarseTuning + channel.fineTuning;
|
|
1008
1036
|
const tuning = masterTuning + channelTuning;
|
|
1009
1037
|
const pitchWheel = channel.state.pitchWheel * 2 - 1;
|
|
@@ -1029,9 +1057,8 @@ export class MidyGM2 {
|
|
|
1029
1057
|
.setValueAtTime(detune, scheduleTime);
|
|
1030
1058
|
}
|
|
1031
1059
|
getPortamentoTime(channel) {
|
|
1032
|
-
const factor = 5 * Math.log(10)
|
|
1033
|
-
|
|
1034
|
-
return Math.log(time) / factor;
|
|
1060
|
+
const factor = 5 * Math.log(10) * 127;
|
|
1061
|
+
return channel.state.portamentoTime * factor;
|
|
1035
1062
|
}
|
|
1036
1063
|
setPortamentoStartVolumeEnvelope(channel, note, scheduleTime) {
|
|
1037
1064
|
const { voiceParams, startTime } = note;
|
|
@@ -1192,7 +1219,7 @@ export class MidyGM2 {
|
|
|
1192
1219
|
const voiceParams = voice.getAllParams(controllerState);
|
|
1193
1220
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
1194
1221
|
const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
|
|
1195
|
-
note.bufferSource = this.
|
|
1222
|
+
note.bufferSource = this.createBufferSource(channel, note, voiceParams, audioBuffer);
|
|
1196
1223
|
note.volumeNode = new GainNode(this.audioContext);
|
|
1197
1224
|
note.gainL = new GainNode(this.audioContext);
|
|
1198
1225
|
note.gainR = new GainNode(this.audioContext);
|
|
@@ -1236,14 +1263,21 @@ export class MidyGM2 {
|
|
|
1236
1263
|
note.bufferSource.start(startTime);
|
|
1237
1264
|
return note;
|
|
1238
1265
|
}
|
|
1239
|
-
calcBank(channel
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1266
|
+
calcBank(channel) {
|
|
1267
|
+
switch (this.mode) {
|
|
1268
|
+
case "GM1":
|
|
1269
|
+
if (channel.isDrum)
|
|
1270
|
+
return 128;
|
|
1271
|
+
return 0;
|
|
1272
|
+
case "GM2":
|
|
1273
|
+
if (channel.bankMSB === 121)
|
|
1274
|
+
return 0;
|
|
1275
|
+
if (channel.isDrum)
|
|
1276
|
+
return 128;
|
|
1277
|
+
return channel.bank;
|
|
1278
|
+
default:
|
|
1279
|
+
return channel.bank;
|
|
1245
1280
|
}
|
|
1246
|
-
return channel.bank;
|
|
1247
1281
|
}
|
|
1248
1282
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime, portamento) {
|
|
1249
1283
|
const channel = this.channels[channelNumber];
|
|
@@ -1267,7 +1301,7 @@ export class MidyGM2 {
|
|
|
1267
1301
|
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
1268
1302
|
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
1269
1303
|
const [prevNote, prevChannelNumber] = prevEntry;
|
|
1270
|
-
if (!prevNote.ending) {
|
|
1304
|
+
if (prevNote && !prevNote.ending) {
|
|
1271
1305
|
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1272
1306
|
startTime, true, // force
|
|
1273
1307
|
undefined);
|
|
@@ -1416,9 +1450,21 @@ export class MidyGM2 {
|
|
|
1416
1450
|
const channel = this.channels[channelNumber];
|
|
1417
1451
|
channel.bank = channel.bankMSB * 128 + channel.bankLSB;
|
|
1418
1452
|
channel.program = program;
|
|
1453
|
+
if (this.mode === "GM2") {
|
|
1454
|
+
switch (channel.bankMSB) {
|
|
1455
|
+
case 120:
|
|
1456
|
+
channel.isDrum = true;
|
|
1457
|
+
break;
|
|
1458
|
+
case 121:
|
|
1459
|
+
channel.isDrum = false;
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1419
1463
|
}
|
|
1420
1464
|
handleChannelPressure(channelNumber, value, scheduleTime) {
|
|
1421
1465
|
const channel = this.channels[channelNumber];
|
|
1466
|
+
if (channel.isDrum)
|
|
1467
|
+
return;
|
|
1422
1468
|
const prev = channel.state.channelPressure;
|
|
1423
1469
|
const next = value / 127;
|
|
1424
1470
|
channel.state.channelPressure = next;
|
|
@@ -1437,8 +1483,10 @@ export class MidyGM2 {
|
|
|
1437
1483
|
this.setPitchBend(channelNumber, pitchBend, scheduleTime);
|
|
1438
1484
|
}
|
|
1439
1485
|
setPitchBend(channelNumber, value, scheduleTime) {
|
|
1440
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1441
1486
|
const channel = this.channels[channelNumber];
|
|
1487
|
+
if (channel.isDrum)
|
|
1488
|
+
return;
|
|
1489
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1442
1490
|
const state = channel.state;
|
|
1443
1491
|
const prev = state.pitchWheel * 2 - 1;
|
|
1444
1492
|
const next = (value - 8192) / 8192;
|
|
@@ -1710,15 +1758,16 @@ export class MidyGM2 {
|
|
|
1710
1758
|
});
|
|
1711
1759
|
}
|
|
1712
1760
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1713
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1714
1761
|
const channel = this.channels[channelNumber];
|
|
1762
|
+
if (channel.isDrum)
|
|
1763
|
+
return;
|
|
1764
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1715
1765
|
channel.state.modulationDepth = modulation / 127;
|
|
1716
1766
|
this.updateModulation(channel, scheduleTime);
|
|
1717
1767
|
}
|
|
1718
1768
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1719
1769
|
const channel = this.channels[channelNumber];
|
|
1720
|
-
|
|
1721
|
-
channel.state.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1770
|
+
channel.state.portamentoTime = portamentoTime / 127;
|
|
1722
1771
|
}
|
|
1723
1772
|
setKeyBasedVolume(channel, scheduleTime) {
|
|
1724
1773
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -1790,8 +1839,10 @@ export class MidyGM2 {
|
|
|
1790
1839
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1791
1840
|
}
|
|
1792
1841
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
1793
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1794
1842
|
const channel = this.channels[channelNumber];
|
|
1843
|
+
if (channel.isDrum)
|
|
1844
|
+
return;
|
|
1845
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1795
1846
|
channel.state.sustainPedal = value / 127;
|
|
1796
1847
|
if (64 <= value) {
|
|
1797
1848
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -1803,11 +1854,16 @@ export class MidyGM2 {
|
|
|
1803
1854
|
}
|
|
1804
1855
|
}
|
|
1805
1856
|
setPortamento(channelNumber, value) {
|
|
1806
|
-
this.channels[channelNumber]
|
|
1857
|
+
const channel = this.channels[channelNumber];
|
|
1858
|
+
if (channel.isDrum)
|
|
1859
|
+
return;
|
|
1860
|
+
channel.state.portamento = value / 127;
|
|
1807
1861
|
}
|
|
1808
1862
|
setSostenutoPedal(channelNumber, value, scheduleTime) {
|
|
1809
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1810
1863
|
const channel = this.channels[channelNumber];
|
|
1864
|
+
if (channel.isDrum)
|
|
1865
|
+
return;
|
|
1866
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1811
1867
|
channel.state.sostenutoPedal = value / 127;
|
|
1812
1868
|
if (64 <= value) {
|
|
1813
1869
|
channel.sostenutoNotes = this.getActiveNotes(channel, scheduleTime);
|
|
@@ -1816,9 +1872,22 @@ export class MidyGM2 {
|
|
|
1816
1872
|
this.releaseSostenutoPedal(channelNumber, value, scheduleTime);
|
|
1817
1873
|
}
|
|
1818
1874
|
}
|
|
1819
|
-
setSoftPedal(channelNumber, softPedal,
|
|
1875
|
+
setSoftPedal(channelNumber, softPedal, scheduleTime) {
|
|
1820
1876
|
const channel = this.channels[channelNumber];
|
|
1877
|
+
if (channel.isDrum)
|
|
1878
|
+
return;
|
|
1879
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1821
1880
|
channel.state.softPedal = softPedal / 127;
|
|
1881
|
+
this.processScheduledNotes(channel, (note) => {
|
|
1882
|
+
if (note.portamento) {
|
|
1883
|
+
this.setPortamentoStartVolumeEnvelope(channel, note, scheduleTime);
|
|
1884
|
+
this.setPortamentoStartFilterEnvelope(channel, note, scheduleTime);
|
|
1885
|
+
}
|
|
1886
|
+
else {
|
|
1887
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
1888
|
+
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
1889
|
+
}
|
|
1890
|
+
});
|
|
1822
1891
|
}
|
|
1823
1892
|
setReverbSendLevel(channelNumber, reverbSendLevel, scheduleTime) {
|
|
1824
1893
|
scheduleTime ??= this.audioContext.currentTime;
|
|
@@ -1947,8 +2016,10 @@ export class MidyGM2 {
|
|
|
1947
2016
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1948
2017
|
}
|
|
1949
2018
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
1950
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1951
2019
|
const channel = this.channels[channelNumber];
|
|
2020
|
+
if (channel.isDrum)
|
|
2021
|
+
return;
|
|
2022
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1952
2023
|
const state = channel.state;
|
|
1953
2024
|
const prev = state.pitchWheelSensitivity;
|
|
1954
2025
|
const next = value / 128;
|
|
@@ -1964,8 +2035,10 @@ export class MidyGM2 {
|
|
|
1964
2035
|
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
1965
2036
|
}
|
|
1966
2037
|
setFineTuning(channelNumber, value, scheduleTime) {
|
|
1967
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1968
2038
|
const channel = this.channels[channelNumber];
|
|
2039
|
+
if (channel.isDrum)
|
|
2040
|
+
return;
|
|
2041
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1969
2042
|
const prev = channel.fineTuning;
|
|
1970
2043
|
const next = (value - 8192) / 8.192; // cent
|
|
1971
2044
|
channel.fineTuning = next;
|
|
@@ -1979,8 +2052,10 @@ export class MidyGM2 {
|
|
|
1979
2052
|
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
1980
2053
|
}
|
|
1981
2054
|
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
1982
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1983
2055
|
const channel = this.channels[channelNumber];
|
|
2056
|
+
if (channel.isDrum)
|
|
2057
|
+
return;
|
|
2058
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1984
2059
|
const prev = channel.coarseTuning;
|
|
1985
2060
|
const next = (value - 64) * 100; // cent
|
|
1986
2061
|
channel.coarseTuning = next;
|
|
@@ -1994,8 +2069,10 @@ export class MidyGM2 {
|
|
|
1994
2069
|
this.setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime);
|
|
1995
2070
|
}
|
|
1996
2071
|
setModulationDepthRange(channelNumber, modulationDepthRange, scheduleTime) {
|
|
1997
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1998
2072
|
const channel = this.channels[channelNumber];
|
|
2073
|
+
if (channel.isDrum)
|
|
2074
|
+
return;
|
|
2075
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1999
2076
|
channel.modulationDepthRange = modulationDepthRange;
|
|
2000
2077
|
this.updateModulation(channel, scheduleTime);
|
|
2001
2078
|
}
|
|
@@ -2063,12 +2140,12 @@ export class MidyGM2 {
|
|
|
2063
2140
|
case 9:
|
|
2064
2141
|
switch (data[3]) {
|
|
2065
2142
|
case 1:
|
|
2066
|
-
this.GM1SystemOn();
|
|
2143
|
+
this.GM1SystemOn(scheduleTime);
|
|
2067
2144
|
break;
|
|
2068
2145
|
case 2: // GM System Off
|
|
2069
2146
|
break;
|
|
2070
2147
|
case 3:
|
|
2071
|
-
this.GM2SystemOn();
|
|
2148
|
+
this.GM2SystemOn(scheduleTime);
|
|
2072
2149
|
break;
|
|
2073
2150
|
default:
|
|
2074
2151
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
@@ -2078,25 +2155,35 @@ export class MidyGM2 {
|
|
|
2078
2155
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
2079
2156
|
}
|
|
2080
2157
|
}
|
|
2081
|
-
GM1SystemOn() {
|
|
2158
|
+
GM1SystemOn(scheduleTime) {
|
|
2159
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
2160
|
+
this.mode = "GM1";
|
|
2082
2161
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2162
|
+
this.allSoundOff(i, 0, scheduleTime);
|
|
2083
2163
|
const channel = this.channels[i];
|
|
2084
2164
|
channel.bankMSB = 0;
|
|
2085
2165
|
channel.bankLSB = 0;
|
|
2086
2166
|
channel.bank = 0;
|
|
2167
|
+
channel.isDrum = false;
|
|
2087
2168
|
}
|
|
2088
2169
|
this.channels[9].bankMSB = 1;
|
|
2089
2170
|
this.channels[9].bank = 128;
|
|
2171
|
+
this.channels[9].isDrum = true;
|
|
2090
2172
|
}
|
|
2091
|
-
GM2SystemOn() {
|
|
2173
|
+
GM2SystemOn(scheduleTime) {
|
|
2174
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
2175
|
+
this.mode = "GM2";
|
|
2092
2176
|
for (let i = 0; i < this.channels.length; i++) {
|
|
2177
|
+
this.allSoundOff(i, 0, scheduleTime);
|
|
2093
2178
|
const channel = this.channels[i];
|
|
2094
2179
|
channel.bankMSB = 121;
|
|
2095
2180
|
channel.bankLSB = 0;
|
|
2096
2181
|
channel.bank = 121 * 128;
|
|
2182
|
+
channel.isDrum = false;
|
|
2097
2183
|
}
|
|
2098
2184
|
this.channels[9].bankMSB = 120;
|
|
2099
2185
|
this.channels[9].bank = 120 * 128;
|
|
2186
|
+
this.channels[9].isDrum = true;
|
|
2100
2187
|
}
|
|
2101
2188
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
2102
2189
|
switch (data[2]) {
|
|
@@ -2159,8 +2246,14 @@ export class MidyGM2 {
|
|
|
2159
2246
|
const prev = this.masterFineTuning;
|
|
2160
2247
|
const next = (value - 8192) / 8.192; // cent
|
|
2161
2248
|
this.masterFineTuning = next;
|
|
2162
|
-
|
|
2163
|
-
this.
|
|
2249
|
+
const detuneChange = next - prev;
|
|
2250
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
2251
|
+
const channel = this.channels[i];
|
|
2252
|
+
if (channel.isDrum)
|
|
2253
|
+
continue;
|
|
2254
|
+
channel.detune += detuneChange;
|
|
2255
|
+
this.updateChannelDetune(channel, scheduleTime);
|
|
2256
|
+
}
|
|
2164
2257
|
}
|
|
2165
2258
|
handleMasterCoarseTuningSysEx(data, scheduleTime) {
|
|
2166
2259
|
const coarseTuning = data[4];
|
|
@@ -2170,8 +2263,14 @@ export class MidyGM2 {
|
|
|
2170
2263
|
const prev = this.masterCoarseTuning;
|
|
2171
2264
|
const next = (value - 64) * 100; // cent
|
|
2172
2265
|
this.masterCoarseTuning = next;
|
|
2173
|
-
|
|
2174
|
-
this.
|
|
2266
|
+
const detuneChange = next - prev;
|
|
2267
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
2268
|
+
const channel = this.channels[i];
|
|
2269
|
+
if (channel.isDrum)
|
|
2270
|
+
continue;
|
|
2271
|
+
channel.detune += detuneChange;
|
|
2272
|
+
this.updateChannelDetune(channel, scheduleTime);
|
|
2273
|
+
}
|
|
2175
2274
|
}
|
|
2176
2275
|
handleGlobalParameterControlSysEx(data, scheduleTime) {
|
|
2177
2276
|
if (data[7] === 1) {
|
|
@@ -2499,13 +2598,20 @@ export class MidyGM2 {
|
|
|
2499
2598
|
}
|
|
2500
2599
|
scheduleTask(callback, scheduleTime) {
|
|
2501
2600
|
return new Promise((resolve) => {
|
|
2502
|
-
const bufferSource = new AudioBufferSourceNode(this.audioContext
|
|
2601
|
+
const bufferSource = new AudioBufferSourceNode(this.audioContext, {
|
|
2602
|
+
buffer: this.schedulerBuffer,
|
|
2603
|
+
});
|
|
2604
|
+
bufferSource.connect(this.scheduler);
|
|
2503
2605
|
bufferSource.onended = () => {
|
|
2504
|
-
|
|
2505
|
-
|
|
2606
|
+
try {
|
|
2607
|
+
callback();
|
|
2608
|
+
}
|
|
2609
|
+
finally {
|
|
2610
|
+
bufferSource.disconnect();
|
|
2611
|
+
resolve();
|
|
2612
|
+
}
|
|
2506
2613
|
};
|
|
2507
2614
|
bufferSource.start(scheduleTime);
|
|
2508
|
-
bufferSource.stop(scheduleTime);
|
|
2509
2615
|
});
|
|
2510
2616
|
}
|
|
2511
2617
|
}
|
|
@@ -2515,6 +2621,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
2515
2621
|
writable: true,
|
|
2516
2622
|
value: {
|
|
2517
2623
|
currentBufferSource: null,
|
|
2624
|
+
isDrum: false,
|
|
2518
2625
|
detune: 0,
|
|
2519
2626
|
program: 0,
|
|
2520
2627
|
bank: 121 * 128,
|
|
@@ -2525,8 +2632,8 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
2525
2632
|
rpnMSB: 127,
|
|
2526
2633
|
rpnLSB: 127,
|
|
2527
2634
|
mono: false, // CC#124, CC#125
|
|
2635
|
+
modulationDepthRange: 50, // cent
|
|
2528
2636
|
fineTuning: 0, // cb
|
|
2529
2637
|
coarseTuning: 0, // cb
|
|
2530
|
-
modulationDepthRange: 50, // cent
|
|
2531
2638
|
}
|
|
2532
2639
|
});
|
package/esm/midy-GMLite.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export class MidyGMLite {
|
|
2
2
|
static channelSettings: {
|
|
3
3
|
currentBufferSource: null;
|
|
4
|
+
isDrum: boolean;
|
|
4
5
|
detune: number;
|
|
5
6
|
program: number;
|
|
6
7
|
bank: number;
|
|
@@ -8,8 +9,10 @@ export class MidyGMLite {
|
|
|
8
9
|
dataLSB: number;
|
|
9
10
|
rpnMSB: number;
|
|
10
11
|
rpnLSB: number;
|
|
12
|
+
modulationDepthRange: number;
|
|
11
13
|
};
|
|
12
14
|
constructor(audioContext: any);
|
|
15
|
+
mode: string;
|
|
13
16
|
ticksPerBeat: number;
|
|
14
17
|
totalTime: number;
|
|
15
18
|
noteCheckInterval: number;
|
|
@@ -32,6 +35,8 @@ export class MidyGMLite {
|
|
|
32
35
|
exclusiveClassMap: SparseMap;
|
|
33
36
|
audioContext: any;
|
|
34
37
|
masterVolume: any;
|
|
38
|
+
scheduler: any;
|
|
39
|
+
schedulerBuffer: any;
|
|
35
40
|
voiceParamsHandlers: {
|
|
36
41
|
modLfoToPitch: (channel: any, note: any, _prevValue: any, scheduleTime: any) => void;
|
|
37
42
|
vibLfoToPitch: (_channel: any, _note: any, _prevValue: any, _scheduleTime: any) => void;
|
|
@@ -70,7 +75,8 @@ export class MidyGMLite {
|
|
|
70
75
|
};
|
|
71
76
|
createChannels(audioContext: any): any[];
|
|
72
77
|
createNoteBuffer(voiceParams: any, isSF3: any): Promise<any>;
|
|
73
|
-
|
|
78
|
+
calcLoopMode(channel: any, voiceParams: any): boolean;
|
|
79
|
+
createBufferSource(channel: any, voiceParams: any, audioBuffer: any): any;
|
|
74
80
|
scheduleTimelineEvents(t: any, offset: any, queueIndex: any): Promise<any>;
|
|
75
81
|
getQueueIndex(second: any): number;
|
|
76
82
|
playNotes(): Promise<any>;
|
|
@@ -173,8 +179,8 @@ export class MidyGMLite {
|
|
|
173
179
|
allSoundOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
174
180
|
resetAllControllers(channelNumber: any): void;
|
|
175
181
|
allNotesOff(channelNumber: any, _value: any, scheduleTime: any): Promise<any[]>;
|
|
176
|
-
handleUniversalNonRealTimeExclusiveMessage(data: any,
|
|
177
|
-
GM1SystemOn(): void;
|
|
182
|
+
handleUniversalNonRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
183
|
+
GM1SystemOn(scheduleTime: any): void;
|
|
178
184
|
handleUniversalRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
179
185
|
handleMasterVolumeSysEx(data: any, scheduleTime: any): void;
|
|
180
186
|
setMasterVolume(volume: 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":"AAiJA;
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAiJA;IAuBE;;;;;;;;;;;MAWE;IAEF,+BAcC;IAjDD,aAAa;IACb,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,6BAAuC;IAgBrC,kBAAgC;IAChC,kBAA8C;IAC9C,eAAwD;IACxD,qBAGE;IACF;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAMnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAWC;IAED,6DA2BC;IAED,sDAMC;IAED,0EASC;IAED,2EAsDC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,mGAgBC;IAED,wEAMC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,yDAQC;IAED,2DASC;IAED,qDAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,2DAIC;IAED,+DAIC;IAED,sDAeC;IAED,qDAoBC;IAED,6CAIC;IAED,sDAsBC;IAED,kEAoBC;IAED,yGAgBC;IAED,gHA4CC;IAED,kGAkDC;IAED,6FAQC;IAED,qFAwBC;IAED,yHAuBC;IAED,yGASC;IAED,4GAeC;IAED,mGA2BC;IAED,gFAGC;IAED,wFAGC;IAED,sEAWC;IAED,mEAQC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MA2BC;IAED,oFAMC;IAED,6EA2CC;IAED;;;;;;;;;;;;;MAeC;IAED,kGAWC;IAED,wDAUC;IAED,iFAMC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAYC;IAED,kFAeC;IAED,uDAYC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAWC;IAED,gFAGC;IAED,8CAqBC;IAED,gFAGC;IAED,+EAgBC;IAED,qCAWC;IAED,4EAaC;IAED,4DAGC;IAED,sDASC;IAED,gDAYC;IAED,6DAgBC;CACF;AAp5CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;IASE,0FAMC;IAdD,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
|
package/esm/midy-GMLite.js
CHANGED
|
@@ -170,6 +170,12 @@ const volumeEnvelopeKeys = [
|
|
|
170
170
|
const volumeEnvelopeKeySet = new Set(volumeEnvelopeKeys);
|
|
171
171
|
export class MidyGMLite {
|
|
172
172
|
constructor(audioContext) {
|
|
173
|
+
Object.defineProperty(this, "mode", {
|
|
174
|
+
enumerable: true,
|
|
175
|
+
configurable: true,
|
|
176
|
+
writable: true,
|
|
177
|
+
value: "GM1"
|
|
178
|
+
});
|
|
173
179
|
Object.defineProperty(this, "ticksPerBeat", {
|
|
174
180
|
enumerable: true,
|
|
175
181
|
configurable: true,
|
|
@@ -292,10 +298,16 @@ export class MidyGMLite {
|
|
|
292
298
|
});
|
|
293
299
|
this.audioContext = audioContext;
|
|
294
300
|
this.masterVolume = new GainNode(audioContext);
|
|
301
|
+
this.scheduler = new GainNode(audioContext, { gain: 0 });
|
|
302
|
+
this.schedulerBuffer = new AudioBuffer({
|
|
303
|
+
length: 1,
|
|
304
|
+
sampleRate: audioContext.sampleRate,
|
|
305
|
+
});
|
|
295
306
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
296
307
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
297
308
|
this.channels = this.createChannels(audioContext);
|
|
298
309
|
this.masterVolume.connect(audioContext.destination);
|
|
310
|
+
this.scheduler.connect(audioContext.destination);
|
|
299
311
|
this.GM1SystemOn();
|
|
300
312
|
}
|
|
301
313
|
initSoundFontTable() {
|
|
@@ -389,10 +401,18 @@ export class MidyGMLite {
|
|
|
389
401
|
return audioBuffer;
|
|
390
402
|
}
|
|
391
403
|
}
|
|
392
|
-
|
|
404
|
+
calcLoopMode(channel, voiceParams) {
|
|
405
|
+
if (channel.isDrum) {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
return voiceParams.sampleModes % 2 !== 0;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
createBufferSource(channel, voiceParams, audioBuffer) {
|
|
393
413
|
const bufferSource = new AudioBufferSourceNode(this.audioContext);
|
|
394
414
|
bufferSource.buffer = audioBuffer;
|
|
395
|
-
bufferSource.loop =
|
|
415
|
+
bufferSource.loop = this.calcLoopMode(channel, voiceParams);
|
|
396
416
|
if (bufferSource.loop) {
|
|
397
417
|
bufferSource.loopStart = voiceParams.loopStart / voiceParams.sampleRate;
|
|
398
418
|
bufferSource.loopEnd = voiceParams.loopEnd / voiceParams.sampleRate;
|
|
@@ -803,7 +823,7 @@ export class MidyGMLite {
|
|
|
803
823
|
const voiceParams = voice.getAllParams(controllerState);
|
|
804
824
|
const note = new Note(noteNumber, velocity, startTime, voice, voiceParams);
|
|
805
825
|
const audioBuffer = await this.getAudioBuffer(channel.program, noteNumber, velocity, voiceParams, isSF3);
|
|
806
|
-
note.bufferSource = this.
|
|
826
|
+
note.bufferSource = this.createBufferSource(channel, voiceParams, audioBuffer);
|
|
807
827
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
808
828
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
809
829
|
type: "lowpass",
|
|
@@ -842,7 +862,7 @@ export class MidyGMLite {
|
|
|
842
862
|
if (this.exclusiveClassMap.has(exclusiveClass)) {
|
|
843
863
|
const prevEntry = this.exclusiveClassMap.get(exclusiveClass);
|
|
844
864
|
const [prevNote, prevChannelNumber] = prevEntry;
|
|
845
|
-
if (!prevNote.ending) {
|
|
865
|
+
if (prevNote && !prevNote.ending) {
|
|
846
866
|
this.scheduleNoteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
847
867
|
startTime, true);
|
|
848
868
|
}
|
|
@@ -950,8 +970,10 @@ export class MidyGMLite {
|
|
|
950
970
|
this.setPitchBend(channelNumber, pitchBend, scheduleTime);
|
|
951
971
|
}
|
|
952
972
|
setPitchBend(channelNumber, value, scheduleTime) {
|
|
953
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
954
973
|
const channel = this.channels[channelNumber];
|
|
974
|
+
if (channel.isDrum)
|
|
975
|
+
return;
|
|
976
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
955
977
|
const state = channel.state;
|
|
956
978
|
const prev = state.pitchWheel * 2 - 1;
|
|
957
979
|
const next = (value - 8192) / 8192;
|
|
@@ -1100,7 +1122,6 @@ export class MidyGMLite {
|
|
|
1100
1122
|
}
|
|
1101
1123
|
}
|
|
1102
1124
|
updateModulation(channel, scheduleTime) {
|
|
1103
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1104
1125
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1105
1126
|
this.processScheduledNotes(channel, (note) => {
|
|
1106
1127
|
if (note.modulationDepth) {
|
|
@@ -1113,8 +1134,10 @@ export class MidyGMLite {
|
|
|
1113
1134
|
});
|
|
1114
1135
|
}
|
|
1115
1136
|
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1116
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1117
1137
|
const channel = this.channels[channelNumber];
|
|
1138
|
+
if (channel.isDrum)
|
|
1139
|
+
return;
|
|
1140
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1118
1141
|
channel.state.modulationDepth = modulation / 127;
|
|
1119
1142
|
this.updateModulation(channel, scheduleTime);
|
|
1120
1143
|
}
|
|
@@ -1159,8 +1182,10 @@ export class MidyGMLite {
|
|
|
1159
1182
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1160
1183
|
}
|
|
1161
1184
|
setSustainPedal(channelNumber, value, scheduleTime) {
|
|
1162
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1163
1185
|
const channel = this.channels[channelNumber];
|
|
1186
|
+
if (channel.isDrum)
|
|
1187
|
+
return;
|
|
1188
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1164
1189
|
channel.state.sustainPedal = value / 127;
|
|
1165
1190
|
if (64 <= value) {
|
|
1166
1191
|
this.processScheduledNotes(channel, (note) => {
|
|
@@ -1217,8 +1242,10 @@ export class MidyGMLite {
|
|
|
1217
1242
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1218
1243
|
}
|
|
1219
1244
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
1220
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1221
1245
|
const channel = this.channels[channelNumber];
|
|
1246
|
+
if (channel.isDrum)
|
|
1247
|
+
return;
|
|
1248
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1222
1249
|
const state = channel.state;
|
|
1223
1250
|
const prev = state.pitchWheelSensitivity;
|
|
1224
1251
|
const next = value / 128;
|
|
@@ -1257,12 +1284,12 @@ export class MidyGMLite {
|
|
|
1257
1284
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1258
1285
|
return this.stopChannelNotes(channelNumber, 0, false, scheduleTime);
|
|
1259
1286
|
}
|
|
1260
|
-
handleUniversalNonRealTimeExclusiveMessage(data,
|
|
1287
|
+
handleUniversalNonRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1261
1288
|
switch (data[2]) {
|
|
1262
1289
|
case 9:
|
|
1263
1290
|
switch (data[3]) {
|
|
1264
1291
|
case 1:
|
|
1265
|
-
this.GM1SystemOn();
|
|
1292
|
+
this.GM1SystemOn(scheduleTime);
|
|
1266
1293
|
break;
|
|
1267
1294
|
case 2: // GM System Off
|
|
1268
1295
|
break;
|
|
@@ -1274,12 +1301,17 @@ export class MidyGMLite {
|
|
|
1274
1301
|
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
1275
1302
|
}
|
|
1276
1303
|
}
|
|
1277
|
-
GM1SystemOn() {
|
|
1304
|
+
GM1SystemOn(scheduleTime) {
|
|
1305
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1306
|
+
this.mode = "GM1";
|
|
1278
1307
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1308
|
+
this.allSoundOff(i, 0, scheduleTime);
|
|
1279
1309
|
const channel = this.channels[i];
|
|
1280
1310
|
channel.bank = 0;
|
|
1311
|
+
channel.isDrum = false;
|
|
1281
1312
|
}
|
|
1282
1313
|
this.channels[9].bank = 128;
|
|
1314
|
+
this.channels[9].isDrum = true;
|
|
1283
1315
|
}
|
|
1284
1316
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
1285
1317
|
switch (data[2]) {
|
|
@@ -1322,13 +1354,20 @@ export class MidyGMLite {
|
|
|
1322
1354
|
}
|
|
1323
1355
|
scheduleTask(callback, scheduleTime) {
|
|
1324
1356
|
return new Promise((resolve) => {
|
|
1325
|
-
const bufferSource = new AudioBufferSourceNode(this.audioContext
|
|
1357
|
+
const bufferSource = new AudioBufferSourceNode(this.audioContext, {
|
|
1358
|
+
buffer: this.schedulerBuffer,
|
|
1359
|
+
});
|
|
1360
|
+
bufferSource.connect(this.scheduler);
|
|
1326
1361
|
bufferSource.onended = () => {
|
|
1327
|
-
|
|
1328
|
-
|
|
1362
|
+
try {
|
|
1363
|
+
callback();
|
|
1364
|
+
}
|
|
1365
|
+
finally {
|
|
1366
|
+
bufferSource.disconnect();
|
|
1367
|
+
resolve();
|
|
1368
|
+
}
|
|
1329
1369
|
};
|
|
1330
1370
|
bufferSource.start(scheduleTime);
|
|
1331
|
-
bufferSource.stop(scheduleTime);
|
|
1332
1371
|
});
|
|
1333
1372
|
}
|
|
1334
1373
|
}
|
|
@@ -1338,6 +1377,7 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
1338
1377
|
writable: true,
|
|
1339
1378
|
value: {
|
|
1340
1379
|
currentBufferSource: null,
|
|
1380
|
+
isDrum: false,
|
|
1341
1381
|
detune: 0,
|
|
1342
1382
|
program: 0,
|
|
1343
1383
|
bank: 0,
|
|
@@ -1345,5 +1385,6 @@ Object.defineProperty(MidyGMLite, "channelSettings", {
|
|
|
1345
1385
|
dataLSB: 0,
|
|
1346
1386
|
rpnMSB: 127,
|
|
1347
1387
|
rpnLSB: 127,
|
|
1388
|
+
modulationDepthRange: 50, // cent
|
|
1348
1389
|
}
|
|
1349
1390
|
});
|