@marmooo/midy 0.3.8 → 0.4.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/README.md +2 -1
- package/esm/midy-GM1.d.ts +8 -26
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +140 -80
- package/esm/midy-GM2.d.ts +8 -31
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +155 -95
- package/esm/midy-GMLite.d.ts +8 -26
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +134 -75
- package/esm/midy.d.ts +15 -37
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +228 -111
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +8 -26
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +140 -80
- package/script/midy-GM2.d.ts +8 -31
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +155 -95
- package/script/midy-GMLite.d.ts +8 -26
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +134 -75
- package/script/midy.d.ts +15 -37
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +228 -111
package/script/midy-GM2.js
CHANGED
|
@@ -4,7 +4,19 @@ exports.MidyGM2 = void 0;
|
|
|
4
4
|
const midi_file_1 = require("midi-file");
|
|
5
5
|
const soundfont_parser_1 = require("@marmooo/soundfont-parser");
|
|
6
6
|
class Note {
|
|
7
|
-
constructor(noteNumber, velocity, startTime
|
|
7
|
+
constructor(noteNumber, velocity, startTime) {
|
|
8
|
+
Object.defineProperty(this, "voice", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true,
|
|
12
|
+
value: void 0
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(this, "voiceParams", {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
configurable: true,
|
|
17
|
+
writable: true,
|
|
18
|
+
value: void 0
|
|
19
|
+
});
|
|
8
20
|
Object.defineProperty(this, "index", {
|
|
9
21
|
enumerable: true,
|
|
10
22
|
configurable: true,
|
|
@@ -17,6 +29,12 @@ class Note {
|
|
|
17
29
|
writable: true,
|
|
18
30
|
value: false
|
|
19
31
|
});
|
|
32
|
+
Object.defineProperty(this, "pending", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
writable: true,
|
|
36
|
+
value: true
|
|
37
|
+
});
|
|
20
38
|
Object.defineProperty(this, "bufferSource", {
|
|
21
39
|
enumerable: true,
|
|
22
40
|
configurable: true,
|
|
@@ -92,8 +110,6 @@ class Note {
|
|
|
92
110
|
this.noteNumber = noteNumber;
|
|
93
111
|
this.velocity = velocity;
|
|
94
112
|
this.startTime = startTime;
|
|
95
|
-
this.voice = voice;
|
|
96
|
-
this.voiceParams = voiceParams;
|
|
97
113
|
}
|
|
98
114
|
}
|
|
99
115
|
const drumExclusiveClassesByKit = new Array(57);
|
|
@@ -138,12 +154,12 @@ const defaultControllerState = {
|
|
|
138
154
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
139
155
|
link: { type: 127, defaultValue: 0 },
|
|
140
156
|
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
141
|
-
|
|
142
|
-
|
|
157
|
+
modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
|
|
158
|
+
portamentoTimeMSB: { type: 128 + 5, defaultValue: 0 },
|
|
143
159
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
160
|
+
volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
161
|
+
panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
162
|
+
expressionMSB: { type: 128 + 11, defaultValue: 1 },
|
|
147
163
|
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
148
164
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
149
165
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
@@ -341,6 +357,12 @@ class MidyGM2 {
|
|
|
341
357
|
writable: true,
|
|
342
358
|
value: new Map()
|
|
343
359
|
});
|
|
360
|
+
Object.defineProperty(this, "realtimeVoiceCache", {
|
|
361
|
+
enumerable: true,
|
|
362
|
+
configurable: true,
|
|
363
|
+
writable: true,
|
|
364
|
+
value: new Map()
|
|
365
|
+
});
|
|
344
366
|
Object.defineProperty(this, "isPlaying", {
|
|
345
367
|
enumerable: true,
|
|
346
368
|
configurable: true,
|
|
@@ -527,7 +549,7 @@ class MidyGM2 {
|
|
|
527
549
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
528
550
|
}
|
|
529
551
|
createChannelAudioNodes(audioContext) {
|
|
530
|
-
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.
|
|
552
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
|
|
531
553
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
532
554
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
533
555
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -568,10 +590,9 @@ class MidyGM2 {
|
|
|
568
590
|
return channels;
|
|
569
591
|
}
|
|
570
592
|
async createAudioBuffer(voiceParams) {
|
|
571
|
-
const sample = voiceParams
|
|
572
|
-
const
|
|
573
|
-
const
|
|
574
|
-
const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
|
|
593
|
+
const { sample, start, end } = voiceParams;
|
|
594
|
+
const sampleEnd = sample.data.length + end;
|
|
595
|
+
const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
|
|
575
596
|
return audioBuffer;
|
|
576
597
|
}
|
|
577
598
|
isLoopDrum(channel, noteNumber) {
|
|
@@ -603,12 +624,10 @@ class MidyGM2 {
|
|
|
603
624
|
const startTime = event.startTime + schedulingOffset;
|
|
604
625
|
switch (event.type) {
|
|
605
626
|
case "noteOn":
|
|
606
|
-
await this.
|
|
627
|
+
await this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
607
628
|
break;
|
|
608
629
|
case "noteOff": {
|
|
609
|
-
|
|
610
|
-
if (notePromise)
|
|
611
|
-
this.notePromises.push(notePromise);
|
|
630
|
+
this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
612
631
|
break;
|
|
613
632
|
}
|
|
614
633
|
case "controller":
|
|
@@ -642,6 +661,7 @@ class MidyGM2 {
|
|
|
642
661
|
this.exclusiveClassNotes.fill(undefined);
|
|
643
662
|
this.drumExclusiveClassNotes.fill(undefined);
|
|
644
663
|
this.voiceCache.clear();
|
|
664
|
+
this.realtimeVoiceCache.clear();
|
|
645
665
|
for (let i = 0; i < this.channels.length; i++) {
|
|
646
666
|
this.channels[i].scheduledNotes = [];
|
|
647
667
|
this.resetChannelStates(i);
|
|
@@ -686,7 +706,6 @@ class MidyGM2 {
|
|
|
686
706
|
finished = true;
|
|
687
707
|
break;
|
|
688
708
|
}
|
|
689
|
-
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
690
709
|
if (this.isPausing) {
|
|
691
710
|
await this.stopNotes(0, true, now);
|
|
692
711
|
await this.audioContext.suspend();
|
|
@@ -708,9 +727,16 @@ class MidyGM2 {
|
|
|
708
727
|
this.isSeeking = false;
|
|
709
728
|
continue;
|
|
710
729
|
}
|
|
730
|
+
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
711
731
|
const waitTime = now + this.noteCheckInterval;
|
|
712
732
|
await this.scheduleTask(() => { }, waitTime);
|
|
713
733
|
}
|
|
734
|
+
if (this.timeline.length <= queueIndex) {
|
|
735
|
+
const now = this.audioContext.currentTime;
|
|
736
|
+
await this.stopNotes(0, true, now);
|
|
737
|
+
await this.audioContext.suspend();
|
|
738
|
+
finished = true;
|
|
739
|
+
}
|
|
714
740
|
if (finished) {
|
|
715
741
|
this.notePromises = [];
|
|
716
742
|
this.resetAllStates();
|
|
@@ -816,7 +842,7 @@ class MidyGM2 {
|
|
|
816
842
|
const channel = this.channels[channelNumber];
|
|
817
843
|
const promises = [];
|
|
818
844
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
819
|
-
const promise = this.
|
|
845
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
820
846
|
this.notePromises.push(promise);
|
|
821
847
|
promises.push(promise);
|
|
822
848
|
});
|
|
@@ -826,7 +852,7 @@ class MidyGM2 {
|
|
|
826
852
|
const channel = this.channels[channelNumber];
|
|
827
853
|
const promises = [];
|
|
828
854
|
this.processScheduledNotes(channel, (note) => {
|
|
829
|
-
const promise = this.
|
|
855
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
830
856
|
this.notePromises.push(promise);
|
|
831
857
|
promises.push(promise);
|
|
832
858
|
});
|
|
@@ -859,7 +885,7 @@ class MidyGM2 {
|
|
|
859
885
|
if (!this.isPlaying || this.isPaused)
|
|
860
886
|
return;
|
|
861
887
|
const now = this.audioContext.currentTime;
|
|
862
|
-
this.resumeTime
|
|
888
|
+
this.resumeTime = now - this.startTime - this.startDelay;
|
|
863
889
|
this.isPausing = true;
|
|
864
890
|
await this.playPromise;
|
|
865
891
|
this.isPausing = false;
|
|
@@ -885,11 +911,13 @@ class MidyGM2 {
|
|
|
885
911
|
if (totalTime < event.startTime)
|
|
886
912
|
totalTime = event.startTime;
|
|
887
913
|
}
|
|
888
|
-
return totalTime;
|
|
914
|
+
return totalTime + this.startDelay;
|
|
889
915
|
}
|
|
890
916
|
currentTime() {
|
|
917
|
+
if (!this.isPlaying)
|
|
918
|
+
return this.resumeTime;
|
|
891
919
|
const now = this.audioContext.currentTime;
|
|
892
|
-
return
|
|
920
|
+
return now + this.resumeTime - this.startTime;
|
|
893
921
|
}
|
|
894
922
|
processScheduledNotes(channel, callback) {
|
|
895
923
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -1117,7 +1145,7 @@ class MidyGM2 {
|
|
|
1117
1145
|
}
|
|
1118
1146
|
getPortamentoTime(channel, note) {
|
|
1119
1147
|
const deltaSemitone = Math.abs(note.noteNumber - note.portamentoNoteNumber);
|
|
1120
|
-
const value = Math.ceil(channel.state.
|
|
1148
|
+
const value = Math.ceil(channel.state.portamentoTimeMSB * 127);
|
|
1121
1149
|
return deltaSemitone / this.getPitchIncrementSpeed(value) / 10;
|
|
1122
1150
|
}
|
|
1123
1151
|
getPitchIncrementSpeed(value) {
|
|
@@ -1316,31 +1344,42 @@ class MidyGM2 {
|
|
|
1316
1344
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
1317
1345
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
1318
1346
|
}
|
|
1319
|
-
async getAudioBuffer(channel, noteNumber, velocity, voiceParams) {
|
|
1347
|
+
async getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime) {
|
|
1320
1348
|
const audioBufferId = this.getVoiceId(channel, noteNumber, velocity);
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
this.voiceCache.delete(audioBufferId);
|
|
1326
|
-
}
|
|
1327
|
-
return cache.audioBuffer;
|
|
1328
|
-
}
|
|
1329
|
-
else {
|
|
1330
|
-
const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
|
|
1349
|
+
if (realtime) {
|
|
1350
|
+
const cachedAudioBuffer = this.realtimeVoiceCache.get(audioBufferId);
|
|
1351
|
+
if (cachedAudioBuffer)
|
|
1352
|
+
return cachedAudioBuffer;
|
|
1331
1353
|
const audioBuffer = await this.createAudioBuffer(voiceParams);
|
|
1332
|
-
|
|
1333
|
-
this.voiceCache.set(audioBufferId, cache);
|
|
1354
|
+
this.realtimeVoiceCache.set(audioBufferId, audioBuffer);
|
|
1334
1355
|
return audioBuffer;
|
|
1335
1356
|
}
|
|
1357
|
+
else {
|
|
1358
|
+
const cache = this.voiceCache.get(audioBufferId);
|
|
1359
|
+
if (cache) {
|
|
1360
|
+
cache.counter += 1;
|
|
1361
|
+
if (cache.maxCount <= cache.counter) {
|
|
1362
|
+
this.voiceCache.delete(audioBufferId);
|
|
1363
|
+
}
|
|
1364
|
+
return cache.audioBuffer;
|
|
1365
|
+
}
|
|
1366
|
+
else {
|
|
1367
|
+
const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
|
|
1368
|
+
const audioBuffer = await this.createAudioBuffer(voiceParams);
|
|
1369
|
+
const cache = { audioBuffer, maxCount, counter: 1 };
|
|
1370
|
+
this.voiceCache.set(audioBufferId, cache);
|
|
1371
|
+
return audioBuffer;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1336
1374
|
}
|
|
1337
|
-
async
|
|
1375
|
+
async setNoteAudioNode(channel, note, realtime) {
|
|
1338
1376
|
const now = this.audioContext.currentTime;
|
|
1377
|
+
const { noteNumber, velocity, startTime } = note;
|
|
1339
1378
|
const state = channel.state;
|
|
1340
1379
|
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
1341
|
-
const voiceParams = voice.getAllParams(controllerState);
|
|
1342
|
-
|
|
1343
|
-
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams);
|
|
1380
|
+
const voiceParams = note.voice.getAllParams(controllerState);
|
|
1381
|
+
note.voiceParams = voiceParams;
|
|
1382
|
+
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime);
|
|
1344
1383
|
note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
|
|
1345
1384
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
1346
1385
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
@@ -1365,7 +1404,7 @@ class MidyGM2 {
|
|
|
1365
1404
|
if (0 < state.vibratoDepth) {
|
|
1366
1405
|
this.startVibrato(channel, note, now);
|
|
1367
1406
|
}
|
|
1368
|
-
if (0 < state.
|
|
1407
|
+
if (0 < state.modulationDepthMSB) {
|
|
1369
1408
|
this.startModulation(channel, note, now);
|
|
1370
1409
|
}
|
|
1371
1410
|
if (channel.mono && channel.currentBufferSource) {
|
|
@@ -1376,7 +1415,13 @@ class MidyGM2 {
|
|
|
1376
1415
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1377
1416
|
this.setChorusSend(channel, note, now);
|
|
1378
1417
|
this.setReverbSend(channel, note, now);
|
|
1379
|
-
|
|
1418
|
+
if (voiceParams.sample.type === "compressed") {
|
|
1419
|
+
const offset = voiceParams.start / audioBuffer.sampleRate;
|
|
1420
|
+
note.bufferSource.start(startTime, offset);
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
note.bufferSource.start(startTime);
|
|
1424
|
+
}
|
|
1380
1425
|
return note;
|
|
1381
1426
|
}
|
|
1382
1427
|
handleExclusiveClass(note, channelNumber, startTime) {
|
|
@@ -1387,7 +1432,7 @@ class MidyGM2 {
|
|
|
1387
1432
|
if (prev) {
|
|
1388
1433
|
const [prevNote, prevChannelNumber] = prev;
|
|
1389
1434
|
if (prevNote && !prevNote.ending) {
|
|
1390
|
-
this.
|
|
1435
|
+
this.noteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1391
1436
|
startTime, true);
|
|
1392
1437
|
}
|
|
1393
1438
|
}
|
|
@@ -1407,27 +1452,14 @@ class MidyGM2 {
|
|
|
1407
1452
|
channelNumber;
|
|
1408
1453
|
const prevNote = this.drumExclusiveClassNotes[index];
|
|
1409
1454
|
if (prevNote && !prevNote.ending) {
|
|
1410
|
-
this.
|
|
1455
|
+
this.noteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1411
1456
|
startTime, true);
|
|
1412
1457
|
}
|
|
1413
1458
|
this.drumExclusiveClassNotes[index] = note;
|
|
1414
1459
|
}
|
|
1415
|
-
|
|
1460
|
+
setNoteRouting(channelNumber, note, startTime) {
|
|
1416
1461
|
const channel = this.channels[channelNumber];
|
|
1417
|
-
const
|
|
1418
|
-
const bankTable = this.soundFontTable[programNumber];
|
|
1419
|
-
if (!bankTable)
|
|
1420
|
-
return;
|
|
1421
|
-
const bankLSB = channel.isDrum ? 128 : channel.bankLSB;
|
|
1422
|
-
const bank = bankTable[bankLSB] !== undefined ? bankLSB : 0;
|
|
1423
|
-
const soundFontIndex = bankTable[bank];
|
|
1424
|
-
if (soundFontIndex === undefined)
|
|
1425
|
-
return;
|
|
1426
|
-
const soundFont = this.soundFonts[soundFontIndex];
|
|
1427
|
-
const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
1428
|
-
if (!voice)
|
|
1429
|
-
return;
|
|
1430
|
-
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
|
|
1462
|
+
const { noteNumber, volumeEnvelopeNode } = note;
|
|
1431
1463
|
if (channel.isDrum) {
|
|
1432
1464
|
const { keyBasedGainLs, keyBasedGainRs } = channel;
|
|
1433
1465
|
let gainL = keyBasedGainLs[noteNumber];
|
|
@@ -1437,25 +1469,48 @@ class MidyGM2 {
|
|
|
1437
1469
|
gainL = keyBasedGainLs[noteNumber] = audioNodes.gainL;
|
|
1438
1470
|
gainR = keyBasedGainRs[noteNumber] = audioNodes.gainR;
|
|
1439
1471
|
}
|
|
1440
|
-
|
|
1441
|
-
|
|
1472
|
+
volumeEnvelopeNode.connect(gainL);
|
|
1473
|
+
volumeEnvelopeNode.connect(gainR);
|
|
1442
1474
|
}
|
|
1443
1475
|
else {
|
|
1444
|
-
|
|
1445
|
-
|
|
1476
|
+
volumeEnvelopeNode.connect(channel.gainL);
|
|
1477
|
+
volumeEnvelopeNode.connect(channel.gainR);
|
|
1446
1478
|
}
|
|
1447
1479
|
if (0.5 <= channel.state.sustainPedal) {
|
|
1448
1480
|
channel.sustainNotes.push(note);
|
|
1449
1481
|
}
|
|
1450
1482
|
this.handleExclusiveClass(note, channelNumber, startTime);
|
|
1451
1483
|
this.handleDrumExclusiveClass(note, channelNumber, startTime);
|
|
1484
|
+
}
|
|
1485
|
+
async noteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
1486
|
+
const channel = this.channels[channelNumber];
|
|
1487
|
+
const realtime = startTime === undefined;
|
|
1488
|
+
if (realtime)
|
|
1489
|
+
startTime = this.audioContext.currentTime;
|
|
1490
|
+
const note = new Note(noteNumber, velocity, startTime);
|
|
1452
1491
|
const scheduledNotes = channel.scheduledNotes;
|
|
1453
1492
|
note.index = scheduledNotes.length;
|
|
1454
1493
|
scheduledNotes.push(note);
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1494
|
+
const programNumber = channel.programNumber;
|
|
1495
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
1496
|
+
if (!bankTable)
|
|
1497
|
+
return;
|
|
1498
|
+
const bankLSB = channel.isDrum ? 128 : channel.bankLSB;
|
|
1499
|
+
const bank = bankTable[bankLSB] !== undefined ? bankLSB : 0;
|
|
1500
|
+
const soundFontIndex = bankTable[bank];
|
|
1501
|
+
if (soundFontIndex === undefined)
|
|
1502
|
+
return;
|
|
1503
|
+
const soundFont = this.soundFonts[soundFontIndex];
|
|
1504
|
+
note.voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
1505
|
+
if (!note.voice)
|
|
1506
|
+
return;
|
|
1507
|
+
await this.setNoteAudioNode(channel, note, realtime);
|
|
1508
|
+
this.setNoteRouting(channelNumber, note, startTime);
|
|
1509
|
+
note.pending = false;
|
|
1510
|
+
const off = note.offEvent;
|
|
1511
|
+
if (off) {
|
|
1512
|
+
this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
|
|
1513
|
+
}
|
|
1459
1514
|
}
|
|
1460
1515
|
disconnectNote(note) {
|
|
1461
1516
|
note.bufferSource.disconnect();
|
|
@@ -1478,6 +1533,7 @@ class MidyGM2 {
|
|
|
1478
1533
|
}
|
|
1479
1534
|
}
|
|
1480
1535
|
releaseNote(channel, note, endTime) {
|
|
1536
|
+
endTime ??= this.audioContext.currentTime;
|
|
1481
1537
|
const volRelease = endTime + note.voiceParams.volRelease;
|
|
1482
1538
|
const modRelease = endTime + note.voiceParams.modRelease;
|
|
1483
1539
|
const stopTime = Math.min(volRelease, modRelease);
|
|
@@ -1498,7 +1554,7 @@ class MidyGM2 {
|
|
|
1498
1554
|
}, stopTime);
|
|
1499
1555
|
});
|
|
1500
1556
|
}
|
|
1501
|
-
|
|
1557
|
+
noteOff(channelNumber, noteNumber, velocity, endTime, force) {
|
|
1502
1558
|
const channel = this.channels[channelNumber];
|
|
1503
1559
|
const state = channel.state;
|
|
1504
1560
|
if (!force) {
|
|
@@ -1517,9 +1573,15 @@ class MidyGM2 {
|
|
|
1517
1573
|
if (index < 0)
|
|
1518
1574
|
return;
|
|
1519
1575
|
const note = channel.scheduledNotes[index];
|
|
1576
|
+
if (note.pending) {
|
|
1577
|
+
note.offEvent = { velocity, startTime: endTime };
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1520
1580
|
note.ending = true;
|
|
1521
1581
|
this.setNoteIndex(channel, index);
|
|
1522
|
-
this.releaseNote(channel, note, endTime);
|
|
1582
|
+
const promise = this.releaseNote(channel, note, endTime);
|
|
1583
|
+
this.notePromises.push(promise);
|
|
1584
|
+
return promise;
|
|
1523
1585
|
}
|
|
1524
1586
|
setNoteIndex(channel, index) {
|
|
1525
1587
|
let allEnds = true;
|
|
@@ -1547,16 +1609,12 @@ class MidyGM2 {
|
|
|
1547
1609
|
}
|
|
1548
1610
|
return -1;
|
|
1549
1611
|
}
|
|
1550
|
-
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1551
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1552
|
-
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
1553
|
-
}
|
|
1554
1612
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
1555
1613
|
const velocity = halfVelocity * 2;
|
|
1556
1614
|
const channel = this.channels[channelNumber];
|
|
1557
1615
|
const promises = [];
|
|
1558
1616
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
1559
|
-
const promise = this.
|
|
1617
|
+
const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
1560
1618
|
promises.push(promise);
|
|
1561
1619
|
}
|
|
1562
1620
|
channel.sustainNotes = [];
|
|
@@ -1570,7 +1628,7 @@ class MidyGM2 {
|
|
|
1570
1628
|
channel.state.sostenutoPedal = 0;
|
|
1571
1629
|
for (let i = 0; i < sostenutoNotes.length; i++) {
|
|
1572
1630
|
const note = sostenutoNotes[i];
|
|
1573
|
-
const promise = this.
|
|
1631
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
|
|
1574
1632
|
promises.push(promise);
|
|
1575
1633
|
}
|
|
1576
1634
|
channel.sostenutoNotes = [];
|
|
@@ -1655,7 +1713,8 @@ class MidyGM2 {
|
|
|
1655
1713
|
if (note.modulationDepth) {
|
|
1656
1714
|
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1657
1715
|
this.getLFOPitchDepth(channel, note);
|
|
1658
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1716
|
+
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1717
|
+
channel.state.modulationDepthMSB;
|
|
1659
1718
|
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1660
1719
|
note.modulationDepth.gain
|
|
1661
1720
|
.cancelScheduledValues(scheduleTime)
|
|
@@ -1787,7 +1846,7 @@ class MidyGM2 {
|
|
|
1787
1846
|
createVoiceParamsHandlers() {
|
|
1788
1847
|
return {
|
|
1789
1848
|
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1790
|
-
if (0 < channel.state.
|
|
1849
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1791
1850
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1792
1851
|
}
|
|
1793
1852
|
},
|
|
@@ -1797,12 +1856,12 @@ class MidyGM2 {
|
|
|
1797
1856
|
}
|
|
1798
1857
|
},
|
|
1799
1858
|
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1800
|
-
if (0 < channel.state.
|
|
1859
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1801
1860
|
this.setModLfoToFilterFc(channel, note, scheduleTime);
|
|
1802
1861
|
}
|
|
1803
1862
|
},
|
|
1804
1863
|
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1805
|
-
if (0 < channel.state.
|
|
1864
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1806
1865
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
1807
1866
|
}
|
|
1808
1867
|
},
|
|
@@ -1813,12 +1872,12 @@ class MidyGM2 {
|
|
|
1813
1872
|
this.setReverbSend(channel, note, scheduleTime);
|
|
1814
1873
|
},
|
|
1815
1874
|
delayModLFO: (_channel, note, _scheduleTime) => {
|
|
1816
|
-
if (0 < channel.state.
|
|
1875
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1817
1876
|
this.setDelayModLFO(note);
|
|
1818
1877
|
}
|
|
1819
1878
|
},
|
|
1820
1879
|
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1821
|
-
if (0 < channel.state.
|
|
1880
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1822
1881
|
this.setFreqModLFO(note, scheduleTime);
|
|
1823
1882
|
}
|
|
1824
1883
|
},
|
|
@@ -1920,7 +1979,8 @@ class MidyGM2 {
|
|
|
1920
1979
|
this.channels[channelNumber].bankMSB = msb;
|
|
1921
1980
|
}
|
|
1922
1981
|
updateModulation(channel, scheduleTime) {
|
|
1923
|
-
const depth = channel.state.
|
|
1982
|
+
const depth = channel.state.modulationDepthMSB *
|
|
1983
|
+
channel.modulationDepthRange;
|
|
1924
1984
|
this.processScheduledNotes(channel, (note) => {
|
|
1925
1985
|
if (note.modulationDepth) {
|
|
1926
1986
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
@@ -1935,7 +1995,7 @@ class MidyGM2 {
|
|
|
1935
1995
|
if (channel.isDrum)
|
|
1936
1996
|
return;
|
|
1937
1997
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1938
|
-
channel.state.
|
|
1998
|
+
channel.state.modulationDepthMSB = modulation / 127;
|
|
1939
1999
|
this.updateModulation(channel, scheduleTime);
|
|
1940
2000
|
}
|
|
1941
2001
|
updatePortamento(channel, scheduleTime) {
|
|
@@ -1957,9 +2017,9 @@ class MidyGM2 {
|
|
|
1957
2017
|
});
|
|
1958
2018
|
}
|
|
1959
2019
|
setPortamentoTime(channelNumber, portamentoTime, scheduleTime) {
|
|
1960
|
-
const channel = this.channels[channelNumber];
|
|
1961
2020
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1962
|
-
channel
|
|
2021
|
+
const channel = this.channels[channelNumber];
|
|
2022
|
+
channel.state.portamentoTimeMSB = portamentoTime / 127;
|
|
1963
2023
|
if (channel.isDrum)
|
|
1964
2024
|
return;
|
|
1965
2025
|
this.updatePortamento(channel, scheduleTime);
|
|
@@ -1967,7 +2027,7 @@ class MidyGM2 {
|
|
|
1967
2027
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1968
2028
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1969
2029
|
const channel = this.channels[channelNumber];
|
|
1970
|
-
channel.state.
|
|
2030
|
+
channel.state.volumeMSB = volume / 127;
|
|
1971
2031
|
if (channel.isDrum) {
|
|
1972
2032
|
for (let i = 0; i < 128; i++) {
|
|
1973
2033
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -1987,7 +2047,7 @@ class MidyGM2 {
|
|
|
1987
2047
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1988
2048
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1989
2049
|
const channel = this.channels[channelNumber];
|
|
1990
|
-
channel.state.
|
|
2050
|
+
channel.state.panMSB = pan / 127;
|
|
1991
2051
|
if (channel.isDrum) {
|
|
1992
2052
|
for (let i = 0; i < 128; i++) {
|
|
1993
2053
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -2000,7 +2060,7 @@ class MidyGM2 {
|
|
|
2000
2060
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
2001
2061
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2002
2062
|
const channel = this.channels[channelNumber];
|
|
2003
|
-
channel.state.
|
|
2063
|
+
channel.state.expressionMSB = expression / 127;
|
|
2004
2064
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2005
2065
|
}
|
|
2006
2066
|
setBankLSB(channelNumber, lsb) {
|
|
@@ -2012,8 +2072,8 @@ class MidyGM2 {
|
|
|
2012
2072
|
}
|
|
2013
2073
|
updateChannelVolume(channel, scheduleTime) {
|
|
2014
2074
|
const state = channel.state;
|
|
2015
|
-
const volume = state.
|
|
2016
|
-
const { gainLeft, gainRight } = this.panToGain(state.
|
|
2075
|
+
const volume = state.volumeMSB * state.expressionMSB;
|
|
2076
|
+
const { gainLeft, gainRight } = this.panToGain(state.panMSB);
|
|
2017
2077
|
channel.gainL.gain
|
|
2018
2078
|
.cancelScheduledValues(scheduleTime)
|
|
2019
2079
|
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
@@ -2027,8 +2087,8 @@ class MidyGM2 {
|
|
|
2027
2087
|
return;
|
|
2028
2088
|
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
2029
2089
|
const state = channel.state;
|
|
2030
|
-
const defaultVolume = state.
|
|
2031
|
-
const defaultPan = state.
|
|
2090
|
+
const defaultVolume = state.volumeMSB * state.expressionMSB;
|
|
2091
|
+
const defaultPan = state.panMSB;
|
|
2032
2092
|
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
2033
2093
|
const volume = (0 <= keyBasedVolume)
|
|
2034
2094
|
? defaultVolume * keyBasedVolume / 64
|
|
@@ -2279,8 +2339,8 @@ class MidyGM2 {
|
|
|
2279
2339
|
const keys = [
|
|
2280
2340
|
"channelPressure",
|
|
2281
2341
|
"pitchWheel",
|
|
2282
|
-
"
|
|
2283
|
-
"
|
|
2342
|
+
"expressionMSB",
|
|
2343
|
+
"modulationDepthMSB",
|
|
2284
2344
|
"sustainPedal",
|
|
2285
2345
|
"portamento",
|
|
2286
2346
|
"sostenutoPedal",
|
package/script/midy-GMLite.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ export class MidyGMLite {
|
|
|
23
23
|
soundFontTable: never[][];
|
|
24
24
|
voiceCounter: Map<any, any>;
|
|
25
25
|
voiceCache: Map<any, any>;
|
|
26
|
+
realtimeVoiceCache: Map<any, any>;
|
|
26
27
|
isPlaying: boolean;
|
|
27
28
|
isPausing: boolean;
|
|
28
29
|
isPaused: boolean;
|
|
@@ -103,22 +104,21 @@ export class MidyGMLite {
|
|
|
103
104
|
clampCutoffFrequency(frequency: any): number;
|
|
104
105
|
setFilterEnvelope(note: any, scheduleTime: any): void;
|
|
105
106
|
startModulation(channel: any, note: any, scheduleTime: any): void;
|
|
106
|
-
getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any): Promise<any>;
|
|
107
|
-
|
|
107
|
+
getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any, realtime: any): Promise<any>;
|
|
108
|
+
setNoteAudioNode(channel: any, note: any, realtime: any): Promise<any>;
|
|
108
109
|
handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
109
110
|
handleDrumExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
110
|
-
|
|
111
|
-
noteOn(channelNumber: any, noteNumber: any, velocity: any,
|
|
111
|
+
setNoteRouting(channelNumber: any, note: any, startTime: any): void;
|
|
112
|
+
noteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
112
113
|
disconnectNote(note: any): void;
|
|
113
114
|
releaseNote(channel: any, note: any, endTime: any): Promise<any>;
|
|
114
|
-
|
|
115
|
+
noteOff(channelNumber: any, noteNumber: any, velocity: any, endTime: any, force: any): Promise<any> | undefined;
|
|
115
116
|
setNoteIndex(channel: any, index: any): void;
|
|
116
117
|
findNoteOffIndex(channel: any, noteNumber: any): any;
|
|
117
|
-
|
|
118
|
-
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
|
|
118
|
+
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): (Promise<any> | undefined)[];
|
|
119
119
|
createMessageHandlers(): any[];
|
|
120
120
|
handleMessage(data: any, scheduleTime: any): void;
|
|
121
|
-
handleChannelMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<
|
|
121
|
+
handleChannelMessage(statusByte: any, data1: any, data2: any, scheduleTime: any): void | Promise<any>;
|
|
122
122
|
setProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
|
|
123
123
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
124
124
|
setPitchBend(channelNumber: any, value: any, scheduleTime: any): void;
|
|
@@ -174,22 +174,4 @@ export class MidyGMLite {
|
|
|
174
174
|
handleSysEx(data: any, scheduleTime: any): void;
|
|
175
175
|
scheduleTask(callback: any, scheduleTime: any): Promise<any>;
|
|
176
176
|
}
|
|
177
|
-
declare class Note {
|
|
178
|
-
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
179
|
-
index: number;
|
|
180
|
-
ending: boolean;
|
|
181
|
-
bufferSource: any;
|
|
182
|
-
filterNode: any;
|
|
183
|
-
filterDepth: any;
|
|
184
|
-
volumeEnvelopeNode: any;
|
|
185
|
-
volumeDepth: any;
|
|
186
|
-
modulationLFO: any;
|
|
187
|
-
modulationDepth: any;
|
|
188
|
-
noteNumber: any;
|
|
189
|
-
velocity: any;
|
|
190
|
-
startTime: any;
|
|
191
|
-
voice: any;
|
|
192
|
-
voiceParams: any;
|
|
193
|
-
}
|
|
194
|
-
export {};
|
|
195
177
|
//# sourceMappingURL=midy-GMLite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AA2GA;IA6BE;;;;;;;;;MASE;IAEF,+BAeC;IAtDD,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,iBAAY;IACZ,gBAAc;IACd,oBAAkB;IAClB,sBAAwB;IACxB,2BAAqC;IACrC,+BAEE;IAcA,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,8DAWC;IAED;;;;MAeC;IAED,yCAaC;IAED,kDASC;IAED,0EAUC;IAED,yEAoDC;IAED,mCAOC;IAED,uBASC;IAED,yDA2BC;IAED,2BA8CC;IAED,uDAEC;IAED,wDAEC;IAED,qCAKC;IAED;;;MAwDC;IAED,kGAeC;IAED,mGAeC;IAED,wEAMC;IAED,uBAMC;IAED,sBAKC;IAED,uBAQC;IAED,wBAKC;IAED,0BAKC;IAED,wBAOC;IAED,sBAIC;IAED,yDAQC;IAED,yEASC;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,4GAkCC;IAED,uEA4CC;IAED,0EAiBC;IAED,8EAiBC;IAED,oEAUC;IAED,0FAwBC;IAED,gCASC;IAED,iEAqBC;IAED,gHAwBC;IAED,6CAUC;IAED,qDAUC;IAED,4GAeC;IAED,+BAmBC;IAED,kDAOC;IAED,sGA2BC;IAED,mFAGC;IAED,wFAGC;IAED,sEAUC;IAED,mEAYC;IAED,wDAKC;IAED,sDAOC;IAED,mDAMC;IAED,kDAKC;IAED;;;;;;;;;;;MAiCC;IAED,oFAMC;IAED,6EA2BC;IAED,qCAeC;IAED,+FAWC;IAED,wDASC;IAED,iFAKC;IAED,oEAKC;IAED;;;MAMC;IAED,8DAKC;IAED,4EAKC;IAED,sEAGC;IAED,2DAUC;IAED,yEAWC;IAED,kFAeC;IAED,uDAYC;IAED,gDAEC;IAED,gDAEC;IAED,sEAGC;IAED,qEAKC;IAED,2EAUC;IAED,gFAGC;IAED,6CAqBC;IAGD,8EAgCC;IAED,gFAGC;IAED,+EAgBC;IAED,qCASC;IAED,4EAaC;IAED,4DAGC;IAED,qDAKC;IAED,gDAYC;IAGD,6DAgBC;CACF"}
|