@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/esm/midy-GM2.js
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { parseMidi } from "midi-file";
|
|
2
2
|
import { parse, SoundFont } from "@marmooo/soundfont-parser";
|
|
3
3
|
class Note {
|
|
4
|
-
constructor(noteNumber, velocity, startTime
|
|
4
|
+
constructor(noteNumber, velocity, startTime) {
|
|
5
|
+
Object.defineProperty(this, "voice", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: void 0
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "voiceParams", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: void 0
|
|
16
|
+
});
|
|
5
17
|
Object.defineProperty(this, "index", {
|
|
6
18
|
enumerable: true,
|
|
7
19
|
configurable: true,
|
|
@@ -14,6 +26,12 @@ class Note {
|
|
|
14
26
|
writable: true,
|
|
15
27
|
value: false
|
|
16
28
|
});
|
|
29
|
+
Object.defineProperty(this, "pending", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: true
|
|
34
|
+
});
|
|
17
35
|
Object.defineProperty(this, "bufferSource", {
|
|
18
36
|
enumerable: true,
|
|
19
37
|
configurable: true,
|
|
@@ -89,8 +107,6 @@ class Note {
|
|
|
89
107
|
this.noteNumber = noteNumber;
|
|
90
108
|
this.velocity = velocity;
|
|
91
109
|
this.startTime = startTime;
|
|
92
|
-
this.voice = voice;
|
|
93
|
-
this.voiceParams = voiceParams;
|
|
94
110
|
}
|
|
95
111
|
}
|
|
96
112
|
const drumExclusiveClassesByKit = new Array(57);
|
|
@@ -135,12 +151,12 @@ const defaultControllerState = {
|
|
|
135
151
|
pitchWheelSensitivity: { type: 16, defaultValue: 2 / 128 },
|
|
136
152
|
link: { type: 127, defaultValue: 0 },
|
|
137
153
|
// bankMSB: { type: 128 + 0, defaultValue: 121, },
|
|
138
|
-
|
|
139
|
-
|
|
154
|
+
modulationDepthMSB: { type: 128 + 1, defaultValue: 0 },
|
|
155
|
+
portamentoTimeMSB: { type: 128 + 5, defaultValue: 0 },
|
|
140
156
|
// dataMSB: { type: 128 + 6, defaultValue: 0, },
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
157
|
+
volumeMSB: { type: 128 + 7, defaultValue: 100 / 127 },
|
|
158
|
+
panMSB: { type: 128 + 10, defaultValue: 64 / 127 },
|
|
159
|
+
expressionMSB: { type: 128 + 11, defaultValue: 1 },
|
|
144
160
|
// bankLSB: { type: 128 + 32, defaultValue: 0, },
|
|
145
161
|
// dataLSB: { type: 128 + 38, defaultValue: 0, },
|
|
146
162
|
sustainPedal: { type: 128 + 64, defaultValue: 0 },
|
|
@@ -338,6 +354,12 @@ export class MidyGM2 {
|
|
|
338
354
|
writable: true,
|
|
339
355
|
value: new Map()
|
|
340
356
|
});
|
|
357
|
+
Object.defineProperty(this, "realtimeVoiceCache", {
|
|
358
|
+
enumerable: true,
|
|
359
|
+
configurable: true,
|
|
360
|
+
writable: true,
|
|
361
|
+
value: new Map()
|
|
362
|
+
});
|
|
341
363
|
Object.defineProperty(this, "isPlaying", {
|
|
342
364
|
enumerable: true,
|
|
343
365
|
configurable: true,
|
|
@@ -524,7 +546,7 @@ export class MidyGM2 {
|
|
|
524
546
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
525
547
|
}
|
|
526
548
|
createChannelAudioNodes(audioContext) {
|
|
527
|
-
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.
|
|
549
|
+
const { gainLeft, gainRight } = this.panToGain(defaultControllerState.panMSB.defaultValue);
|
|
528
550
|
const gainL = new GainNode(audioContext, { gain: gainLeft });
|
|
529
551
|
const gainR = new GainNode(audioContext, { gain: gainRight });
|
|
530
552
|
const merger = new ChannelMergerNode(audioContext, { numberOfInputs: 2 });
|
|
@@ -565,10 +587,9 @@ export class MidyGM2 {
|
|
|
565
587
|
return channels;
|
|
566
588
|
}
|
|
567
589
|
async createAudioBuffer(voiceParams) {
|
|
568
|
-
const sample = voiceParams
|
|
569
|
-
const
|
|
570
|
-
const
|
|
571
|
-
const audioBuffer = await sample.toAudioBuffer(this.audioContext, sampleStart, sampleEnd);
|
|
590
|
+
const { sample, start, end } = voiceParams;
|
|
591
|
+
const sampleEnd = sample.data.length + end;
|
|
592
|
+
const audioBuffer = await sample.toAudioBuffer(this.audioContext, start, sampleEnd);
|
|
572
593
|
return audioBuffer;
|
|
573
594
|
}
|
|
574
595
|
isLoopDrum(channel, noteNumber) {
|
|
@@ -600,12 +621,10 @@ export class MidyGM2 {
|
|
|
600
621
|
const startTime = event.startTime + schedulingOffset;
|
|
601
622
|
switch (event.type) {
|
|
602
623
|
case "noteOn":
|
|
603
|
-
await this.
|
|
624
|
+
await this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
604
625
|
break;
|
|
605
626
|
case "noteOff": {
|
|
606
|
-
|
|
607
|
-
if (notePromise)
|
|
608
|
-
this.notePromises.push(notePromise);
|
|
627
|
+
this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
609
628
|
break;
|
|
610
629
|
}
|
|
611
630
|
case "controller":
|
|
@@ -639,6 +658,7 @@ export class MidyGM2 {
|
|
|
639
658
|
this.exclusiveClassNotes.fill(undefined);
|
|
640
659
|
this.drumExclusiveClassNotes.fill(undefined);
|
|
641
660
|
this.voiceCache.clear();
|
|
661
|
+
this.realtimeVoiceCache.clear();
|
|
642
662
|
for (let i = 0; i < this.channels.length; i++) {
|
|
643
663
|
this.channels[i].scheduledNotes = [];
|
|
644
664
|
this.resetChannelStates(i);
|
|
@@ -683,7 +703,6 @@ export class MidyGM2 {
|
|
|
683
703
|
finished = true;
|
|
684
704
|
break;
|
|
685
705
|
}
|
|
686
|
-
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
687
706
|
if (this.isPausing) {
|
|
688
707
|
await this.stopNotes(0, true, now);
|
|
689
708
|
await this.audioContext.suspend();
|
|
@@ -705,9 +724,16 @@ export class MidyGM2 {
|
|
|
705
724
|
this.isSeeking = false;
|
|
706
725
|
continue;
|
|
707
726
|
}
|
|
727
|
+
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
708
728
|
const waitTime = now + this.noteCheckInterval;
|
|
709
729
|
await this.scheduleTask(() => { }, waitTime);
|
|
710
730
|
}
|
|
731
|
+
if (this.timeline.length <= queueIndex) {
|
|
732
|
+
const now = this.audioContext.currentTime;
|
|
733
|
+
await this.stopNotes(0, true, now);
|
|
734
|
+
await this.audioContext.suspend();
|
|
735
|
+
finished = true;
|
|
736
|
+
}
|
|
711
737
|
if (finished) {
|
|
712
738
|
this.notePromises = [];
|
|
713
739
|
this.resetAllStates();
|
|
@@ -813,7 +839,7 @@ export class MidyGM2 {
|
|
|
813
839
|
const channel = this.channels[channelNumber];
|
|
814
840
|
const promises = [];
|
|
815
841
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
816
|
-
const promise = this.
|
|
842
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
817
843
|
this.notePromises.push(promise);
|
|
818
844
|
promises.push(promise);
|
|
819
845
|
});
|
|
@@ -823,7 +849,7 @@ export class MidyGM2 {
|
|
|
823
849
|
const channel = this.channels[channelNumber];
|
|
824
850
|
const promises = [];
|
|
825
851
|
this.processScheduledNotes(channel, (note) => {
|
|
826
|
-
const promise = this.
|
|
852
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
827
853
|
this.notePromises.push(promise);
|
|
828
854
|
promises.push(promise);
|
|
829
855
|
});
|
|
@@ -856,7 +882,7 @@ export class MidyGM2 {
|
|
|
856
882
|
if (!this.isPlaying || this.isPaused)
|
|
857
883
|
return;
|
|
858
884
|
const now = this.audioContext.currentTime;
|
|
859
|
-
this.resumeTime
|
|
885
|
+
this.resumeTime = now - this.startTime - this.startDelay;
|
|
860
886
|
this.isPausing = true;
|
|
861
887
|
await this.playPromise;
|
|
862
888
|
this.isPausing = false;
|
|
@@ -882,11 +908,13 @@ export class MidyGM2 {
|
|
|
882
908
|
if (totalTime < event.startTime)
|
|
883
909
|
totalTime = event.startTime;
|
|
884
910
|
}
|
|
885
|
-
return totalTime;
|
|
911
|
+
return totalTime + this.startDelay;
|
|
886
912
|
}
|
|
887
913
|
currentTime() {
|
|
914
|
+
if (!this.isPlaying)
|
|
915
|
+
return this.resumeTime;
|
|
888
916
|
const now = this.audioContext.currentTime;
|
|
889
|
-
return
|
|
917
|
+
return now + this.resumeTime - this.startTime;
|
|
890
918
|
}
|
|
891
919
|
processScheduledNotes(channel, callback) {
|
|
892
920
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -1114,7 +1142,7 @@ export class MidyGM2 {
|
|
|
1114
1142
|
}
|
|
1115
1143
|
getPortamentoTime(channel, note) {
|
|
1116
1144
|
const deltaSemitone = Math.abs(note.noteNumber - note.portamentoNoteNumber);
|
|
1117
|
-
const value = Math.ceil(channel.state.
|
|
1145
|
+
const value = Math.ceil(channel.state.portamentoTimeMSB * 127);
|
|
1118
1146
|
return deltaSemitone / this.getPitchIncrementSpeed(value) / 10;
|
|
1119
1147
|
}
|
|
1120
1148
|
getPitchIncrementSpeed(value) {
|
|
@@ -1313,31 +1341,42 @@ export class MidyGM2 {
|
|
|
1313
1341
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
1314
1342
|
note.vibratoDepth.connect(note.bufferSource.detune);
|
|
1315
1343
|
}
|
|
1316
|
-
async getAudioBuffer(channel, noteNumber, velocity, voiceParams) {
|
|
1344
|
+
async getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime) {
|
|
1317
1345
|
const audioBufferId = this.getVoiceId(channel, noteNumber, velocity);
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
this.voiceCache.delete(audioBufferId);
|
|
1323
|
-
}
|
|
1324
|
-
return cache.audioBuffer;
|
|
1325
|
-
}
|
|
1326
|
-
else {
|
|
1327
|
-
const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
|
|
1346
|
+
if (realtime) {
|
|
1347
|
+
const cachedAudioBuffer = this.realtimeVoiceCache.get(audioBufferId);
|
|
1348
|
+
if (cachedAudioBuffer)
|
|
1349
|
+
return cachedAudioBuffer;
|
|
1328
1350
|
const audioBuffer = await this.createAudioBuffer(voiceParams);
|
|
1329
|
-
|
|
1330
|
-
this.voiceCache.set(audioBufferId, cache);
|
|
1351
|
+
this.realtimeVoiceCache.set(audioBufferId, audioBuffer);
|
|
1331
1352
|
return audioBuffer;
|
|
1332
1353
|
}
|
|
1354
|
+
else {
|
|
1355
|
+
const cache = this.voiceCache.get(audioBufferId);
|
|
1356
|
+
if (cache) {
|
|
1357
|
+
cache.counter += 1;
|
|
1358
|
+
if (cache.maxCount <= cache.counter) {
|
|
1359
|
+
this.voiceCache.delete(audioBufferId);
|
|
1360
|
+
}
|
|
1361
|
+
return cache.audioBuffer;
|
|
1362
|
+
}
|
|
1363
|
+
else {
|
|
1364
|
+
const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
|
|
1365
|
+
const audioBuffer = await this.createAudioBuffer(voiceParams);
|
|
1366
|
+
const cache = { audioBuffer, maxCount, counter: 1 };
|
|
1367
|
+
this.voiceCache.set(audioBufferId, cache);
|
|
1368
|
+
return audioBuffer;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1333
1371
|
}
|
|
1334
|
-
async
|
|
1372
|
+
async setNoteAudioNode(channel, note, realtime) {
|
|
1335
1373
|
const now = this.audioContext.currentTime;
|
|
1374
|
+
const { noteNumber, velocity, startTime } = note;
|
|
1336
1375
|
const state = channel.state;
|
|
1337
1376
|
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
1338
|
-
const voiceParams = voice.getAllParams(controllerState);
|
|
1339
|
-
|
|
1340
|
-
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams);
|
|
1377
|
+
const voiceParams = note.voice.getAllParams(controllerState);
|
|
1378
|
+
note.voiceParams = voiceParams;
|
|
1379
|
+
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime);
|
|
1341
1380
|
note.bufferSource = this.createBufferSource(channel, noteNumber, voiceParams, audioBuffer);
|
|
1342
1381
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
1343
1382
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
@@ -1362,7 +1401,7 @@ export class MidyGM2 {
|
|
|
1362
1401
|
if (0 < state.vibratoDepth) {
|
|
1363
1402
|
this.startVibrato(channel, note, now);
|
|
1364
1403
|
}
|
|
1365
|
-
if (0 < state.
|
|
1404
|
+
if (0 < state.modulationDepthMSB) {
|
|
1366
1405
|
this.startModulation(channel, note, now);
|
|
1367
1406
|
}
|
|
1368
1407
|
if (channel.mono && channel.currentBufferSource) {
|
|
@@ -1373,7 +1412,13 @@ export class MidyGM2 {
|
|
|
1373
1412
|
note.filterNode.connect(note.volumeEnvelopeNode);
|
|
1374
1413
|
this.setChorusSend(channel, note, now);
|
|
1375
1414
|
this.setReverbSend(channel, note, now);
|
|
1376
|
-
|
|
1415
|
+
if (voiceParams.sample.type === "compressed") {
|
|
1416
|
+
const offset = voiceParams.start / audioBuffer.sampleRate;
|
|
1417
|
+
note.bufferSource.start(startTime, offset);
|
|
1418
|
+
}
|
|
1419
|
+
else {
|
|
1420
|
+
note.bufferSource.start(startTime);
|
|
1421
|
+
}
|
|
1377
1422
|
return note;
|
|
1378
1423
|
}
|
|
1379
1424
|
handleExclusiveClass(note, channelNumber, startTime) {
|
|
@@ -1384,7 +1429,7 @@ export class MidyGM2 {
|
|
|
1384
1429
|
if (prev) {
|
|
1385
1430
|
const [prevNote, prevChannelNumber] = prev;
|
|
1386
1431
|
if (prevNote && !prevNote.ending) {
|
|
1387
|
-
this.
|
|
1432
|
+
this.noteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1388
1433
|
startTime, true);
|
|
1389
1434
|
}
|
|
1390
1435
|
}
|
|
@@ -1404,27 +1449,14 @@ export class MidyGM2 {
|
|
|
1404
1449
|
channelNumber;
|
|
1405
1450
|
const prevNote = this.drumExclusiveClassNotes[index];
|
|
1406
1451
|
if (prevNote && !prevNote.ending) {
|
|
1407
|
-
this.
|
|
1452
|
+
this.noteOff(channelNumber, prevNote.noteNumber, 0, // velocity,
|
|
1408
1453
|
startTime, true);
|
|
1409
1454
|
}
|
|
1410
1455
|
this.drumExclusiveClassNotes[index] = note;
|
|
1411
1456
|
}
|
|
1412
|
-
|
|
1457
|
+
setNoteRouting(channelNumber, note, startTime) {
|
|
1413
1458
|
const channel = this.channels[channelNumber];
|
|
1414
|
-
const
|
|
1415
|
-
const bankTable = this.soundFontTable[programNumber];
|
|
1416
|
-
if (!bankTable)
|
|
1417
|
-
return;
|
|
1418
|
-
const bankLSB = channel.isDrum ? 128 : channel.bankLSB;
|
|
1419
|
-
const bank = bankTable[bankLSB] !== undefined ? bankLSB : 0;
|
|
1420
|
-
const soundFontIndex = bankTable[bank];
|
|
1421
|
-
if (soundFontIndex === undefined)
|
|
1422
|
-
return;
|
|
1423
|
-
const soundFont = this.soundFonts[soundFontIndex];
|
|
1424
|
-
const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
1425
|
-
if (!voice)
|
|
1426
|
-
return;
|
|
1427
|
-
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
|
|
1459
|
+
const { noteNumber, volumeEnvelopeNode } = note;
|
|
1428
1460
|
if (channel.isDrum) {
|
|
1429
1461
|
const { keyBasedGainLs, keyBasedGainRs } = channel;
|
|
1430
1462
|
let gainL = keyBasedGainLs[noteNumber];
|
|
@@ -1434,25 +1466,48 @@ export class MidyGM2 {
|
|
|
1434
1466
|
gainL = keyBasedGainLs[noteNumber] = audioNodes.gainL;
|
|
1435
1467
|
gainR = keyBasedGainRs[noteNumber] = audioNodes.gainR;
|
|
1436
1468
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1469
|
+
volumeEnvelopeNode.connect(gainL);
|
|
1470
|
+
volumeEnvelopeNode.connect(gainR);
|
|
1439
1471
|
}
|
|
1440
1472
|
else {
|
|
1441
|
-
|
|
1442
|
-
|
|
1473
|
+
volumeEnvelopeNode.connect(channel.gainL);
|
|
1474
|
+
volumeEnvelopeNode.connect(channel.gainR);
|
|
1443
1475
|
}
|
|
1444
1476
|
if (0.5 <= channel.state.sustainPedal) {
|
|
1445
1477
|
channel.sustainNotes.push(note);
|
|
1446
1478
|
}
|
|
1447
1479
|
this.handleExclusiveClass(note, channelNumber, startTime);
|
|
1448
1480
|
this.handleDrumExclusiveClass(note, channelNumber, startTime);
|
|
1481
|
+
}
|
|
1482
|
+
async noteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
1483
|
+
const channel = this.channels[channelNumber];
|
|
1484
|
+
const realtime = startTime === undefined;
|
|
1485
|
+
if (realtime)
|
|
1486
|
+
startTime = this.audioContext.currentTime;
|
|
1487
|
+
const note = new Note(noteNumber, velocity, startTime);
|
|
1449
1488
|
const scheduledNotes = channel.scheduledNotes;
|
|
1450
1489
|
note.index = scheduledNotes.length;
|
|
1451
1490
|
scheduledNotes.push(note);
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1491
|
+
const programNumber = channel.programNumber;
|
|
1492
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
1493
|
+
if (!bankTable)
|
|
1494
|
+
return;
|
|
1495
|
+
const bankLSB = channel.isDrum ? 128 : channel.bankLSB;
|
|
1496
|
+
const bank = bankTable[bankLSB] !== undefined ? bankLSB : 0;
|
|
1497
|
+
const soundFontIndex = bankTable[bank];
|
|
1498
|
+
if (soundFontIndex === undefined)
|
|
1499
|
+
return;
|
|
1500
|
+
const soundFont = this.soundFonts[soundFontIndex];
|
|
1501
|
+
note.voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
1502
|
+
if (!note.voice)
|
|
1503
|
+
return;
|
|
1504
|
+
await this.setNoteAudioNode(channel, note, realtime);
|
|
1505
|
+
this.setNoteRouting(channelNumber, note, startTime);
|
|
1506
|
+
note.pending = false;
|
|
1507
|
+
const off = note.offEvent;
|
|
1508
|
+
if (off) {
|
|
1509
|
+
this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
|
|
1510
|
+
}
|
|
1456
1511
|
}
|
|
1457
1512
|
disconnectNote(note) {
|
|
1458
1513
|
note.bufferSource.disconnect();
|
|
@@ -1475,6 +1530,7 @@ export class MidyGM2 {
|
|
|
1475
1530
|
}
|
|
1476
1531
|
}
|
|
1477
1532
|
releaseNote(channel, note, endTime) {
|
|
1533
|
+
endTime ??= this.audioContext.currentTime;
|
|
1478
1534
|
const volRelease = endTime + note.voiceParams.volRelease;
|
|
1479
1535
|
const modRelease = endTime + note.voiceParams.modRelease;
|
|
1480
1536
|
const stopTime = Math.min(volRelease, modRelease);
|
|
@@ -1495,7 +1551,7 @@ export class MidyGM2 {
|
|
|
1495
1551
|
}, stopTime);
|
|
1496
1552
|
});
|
|
1497
1553
|
}
|
|
1498
|
-
|
|
1554
|
+
noteOff(channelNumber, noteNumber, velocity, endTime, force) {
|
|
1499
1555
|
const channel = this.channels[channelNumber];
|
|
1500
1556
|
const state = channel.state;
|
|
1501
1557
|
if (!force) {
|
|
@@ -1514,9 +1570,15 @@ export class MidyGM2 {
|
|
|
1514
1570
|
if (index < 0)
|
|
1515
1571
|
return;
|
|
1516
1572
|
const note = channel.scheduledNotes[index];
|
|
1573
|
+
if (note.pending) {
|
|
1574
|
+
note.offEvent = { velocity, startTime: endTime };
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1517
1577
|
note.ending = true;
|
|
1518
1578
|
this.setNoteIndex(channel, index);
|
|
1519
|
-
this.releaseNote(channel, note, endTime);
|
|
1579
|
+
const promise = this.releaseNote(channel, note, endTime);
|
|
1580
|
+
this.notePromises.push(promise);
|
|
1581
|
+
return promise;
|
|
1520
1582
|
}
|
|
1521
1583
|
setNoteIndex(channel, index) {
|
|
1522
1584
|
let allEnds = true;
|
|
@@ -1544,16 +1606,12 @@ export class MidyGM2 {
|
|
|
1544
1606
|
}
|
|
1545
1607
|
return -1;
|
|
1546
1608
|
}
|
|
1547
|
-
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
1548
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
1549
|
-
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
1550
|
-
}
|
|
1551
1609
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
1552
1610
|
const velocity = halfVelocity * 2;
|
|
1553
1611
|
const channel = this.channels[channelNumber];
|
|
1554
1612
|
const promises = [];
|
|
1555
1613
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
1556
|
-
const promise = this.
|
|
1614
|
+
const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
1557
1615
|
promises.push(promise);
|
|
1558
1616
|
}
|
|
1559
1617
|
channel.sustainNotes = [];
|
|
@@ -1567,7 +1625,7 @@ export class MidyGM2 {
|
|
|
1567
1625
|
channel.state.sostenutoPedal = 0;
|
|
1568
1626
|
for (let i = 0; i < sostenutoNotes.length; i++) {
|
|
1569
1627
|
const note = sostenutoNotes[i];
|
|
1570
|
-
const promise = this.
|
|
1628
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime);
|
|
1571
1629
|
promises.push(promise);
|
|
1572
1630
|
}
|
|
1573
1631
|
channel.sostenutoNotes = [];
|
|
@@ -1652,7 +1710,8 @@ export class MidyGM2 {
|
|
|
1652
1710
|
if (note.modulationDepth) {
|
|
1653
1711
|
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1654
1712
|
this.getLFOPitchDepth(channel, note);
|
|
1655
|
-
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1713
|
+
const baseDepth = Math.abs(modLfoToPitch) +
|
|
1714
|
+
channel.state.modulationDepthMSB;
|
|
1656
1715
|
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1657
1716
|
note.modulationDepth.gain
|
|
1658
1717
|
.cancelScheduledValues(scheduleTime)
|
|
@@ -1784,7 +1843,7 @@ export class MidyGM2 {
|
|
|
1784
1843
|
createVoiceParamsHandlers() {
|
|
1785
1844
|
return {
|
|
1786
1845
|
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1787
|
-
if (0 < channel.state.
|
|
1846
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1788
1847
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1789
1848
|
}
|
|
1790
1849
|
},
|
|
@@ -1794,12 +1853,12 @@ export class MidyGM2 {
|
|
|
1794
1853
|
}
|
|
1795
1854
|
},
|
|
1796
1855
|
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1797
|
-
if (0 < channel.state.
|
|
1856
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1798
1857
|
this.setModLfoToFilterFc(channel, note, scheduleTime);
|
|
1799
1858
|
}
|
|
1800
1859
|
},
|
|
1801
1860
|
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1802
|
-
if (0 < channel.state.
|
|
1861
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1803
1862
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
1804
1863
|
}
|
|
1805
1864
|
},
|
|
@@ -1810,12 +1869,12 @@ export class MidyGM2 {
|
|
|
1810
1869
|
this.setReverbSend(channel, note, scheduleTime);
|
|
1811
1870
|
},
|
|
1812
1871
|
delayModLFO: (_channel, note, _scheduleTime) => {
|
|
1813
|
-
if (0 < channel.state.
|
|
1872
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1814
1873
|
this.setDelayModLFO(note);
|
|
1815
1874
|
}
|
|
1816
1875
|
},
|
|
1817
1876
|
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1818
|
-
if (0 < channel.state.
|
|
1877
|
+
if (0 < channel.state.modulationDepthMSB) {
|
|
1819
1878
|
this.setFreqModLFO(note, scheduleTime);
|
|
1820
1879
|
}
|
|
1821
1880
|
},
|
|
@@ -1917,7 +1976,8 @@ export class MidyGM2 {
|
|
|
1917
1976
|
this.channels[channelNumber].bankMSB = msb;
|
|
1918
1977
|
}
|
|
1919
1978
|
updateModulation(channel, scheduleTime) {
|
|
1920
|
-
const depth = channel.state.
|
|
1979
|
+
const depth = channel.state.modulationDepthMSB *
|
|
1980
|
+
channel.modulationDepthRange;
|
|
1921
1981
|
this.processScheduledNotes(channel, (note) => {
|
|
1922
1982
|
if (note.modulationDepth) {
|
|
1923
1983
|
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
@@ -1932,7 +1992,7 @@ export class MidyGM2 {
|
|
|
1932
1992
|
if (channel.isDrum)
|
|
1933
1993
|
return;
|
|
1934
1994
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1935
|
-
channel.state.
|
|
1995
|
+
channel.state.modulationDepthMSB = modulation / 127;
|
|
1936
1996
|
this.updateModulation(channel, scheduleTime);
|
|
1937
1997
|
}
|
|
1938
1998
|
updatePortamento(channel, scheduleTime) {
|
|
@@ -1954,9 +2014,9 @@ export class MidyGM2 {
|
|
|
1954
2014
|
});
|
|
1955
2015
|
}
|
|
1956
2016
|
setPortamentoTime(channelNumber, portamentoTime, scheduleTime) {
|
|
1957
|
-
const channel = this.channels[channelNumber];
|
|
1958
2017
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1959
|
-
channel
|
|
2018
|
+
const channel = this.channels[channelNumber];
|
|
2019
|
+
channel.state.portamentoTimeMSB = portamentoTime / 127;
|
|
1960
2020
|
if (channel.isDrum)
|
|
1961
2021
|
return;
|
|
1962
2022
|
this.updatePortamento(channel, scheduleTime);
|
|
@@ -1964,7 +2024,7 @@ export class MidyGM2 {
|
|
|
1964
2024
|
setVolume(channelNumber, volume, scheduleTime) {
|
|
1965
2025
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1966
2026
|
const channel = this.channels[channelNumber];
|
|
1967
|
-
channel.state.
|
|
2027
|
+
channel.state.volumeMSB = volume / 127;
|
|
1968
2028
|
if (channel.isDrum) {
|
|
1969
2029
|
for (let i = 0; i < 128; i++) {
|
|
1970
2030
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -1984,7 +2044,7 @@ export class MidyGM2 {
|
|
|
1984
2044
|
setPan(channelNumber, pan, scheduleTime) {
|
|
1985
2045
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1986
2046
|
const channel = this.channels[channelNumber];
|
|
1987
|
-
channel.state.
|
|
2047
|
+
channel.state.panMSB = pan / 127;
|
|
1988
2048
|
if (channel.isDrum) {
|
|
1989
2049
|
for (let i = 0; i < 128; i++) {
|
|
1990
2050
|
this.updateKeyBasedVolume(channel, i, scheduleTime);
|
|
@@ -1997,7 +2057,7 @@ export class MidyGM2 {
|
|
|
1997
2057
|
setExpression(channelNumber, expression, scheduleTime) {
|
|
1998
2058
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1999
2059
|
const channel = this.channels[channelNumber];
|
|
2000
|
-
channel.state.
|
|
2060
|
+
channel.state.expressionMSB = expression / 127;
|
|
2001
2061
|
this.updateChannelVolume(channel, scheduleTime);
|
|
2002
2062
|
}
|
|
2003
2063
|
setBankLSB(channelNumber, lsb) {
|
|
@@ -2009,8 +2069,8 @@ export class MidyGM2 {
|
|
|
2009
2069
|
}
|
|
2010
2070
|
updateChannelVolume(channel, scheduleTime) {
|
|
2011
2071
|
const state = channel.state;
|
|
2012
|
-
const volume = state.
|
|
2013
|
-
const { gainLeft, gainRight } = this.panToGain(state.
|
|
2072
|
+
const volume = state.volumeMSB * state.expressionMSB;
|
|
2073
|
+
const { gainLeft, gainRight } = this.panToGain(state.panMSB);
|
|
2014
2074
|
channel.gainL.gain
|
|
2015
2075
|
.cancelScheduledValues(scheduleTime)
|
|
2016
2076
|
.setValueAtTime(volume * gainLeft, scheduleTime);
|
|
@@ -2024,8 +2084,8 @@ export class MidyGM2 {
|
|
|
2024
2084
|
return;
|
|
2025
2085
|
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
2026
2086
|
const state = channel.state;
|
|
2027
|
-
const defaultVolume = state.
|
|
2028
|
-
const defaultPan = state.
|
|
2087
|
+
const defaultVolume = state.volumeMSB * state.expressionMSB;
|
|
2088
|
+
const defaultPan = state.panMSB;
|
|
2029
2089
|
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
2030
2090
|
const volume = (0 <= keyBasedVolume)
|
|
2031
2091
|
? defaultVolume * keyBasedVolume / 64
|
|
@@ -2276,8 +2336,8 @@ export class MidyGM2 {
|
|
|
2276
2336
|
const keys = [
|
|
2277
2337
|
"channelPressure",
|
|
2278
2338
|
"pitchWheel",
|
|
2279
|
-
"
|
|
2280
|
-
"
|
|
2339
|
+
"expressionMSB",
|
|
2340
|
+
"modulationDepthMSB",
|
|
2281
2341
|
"sustainPedal",
|
|
2282
2342
|
"portamento",
|
|
2283
2343
|
"sostenutoPedal",
|
package/esm/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
|
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":"
|
|
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"}
|