@marmooo/midy 0.3.7 → 0.4.0
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 +31 -11
- package/esm/midy-GM1.d.ts +15 -30
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +161 -104
- package/esm/midy-GM2.d.ts +19 -36
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +245 -205
- package/esm/midy-GMLite.d.ts +14 -30
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +163 -107
- package/esm/midy.d.ts +19 -37
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +306 -277
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +15 -30
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +161 -104
- package/script/midy-GM2.d.ts +19 -36
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +245 -205
- package/script/midy-GMLite.d.ts +14 -30
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +163 -107
- package/script/midy.d.ts +19 -37
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +306 -277
package/esm/midy-GM1.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,
|
|
@@ -59,8 +77,6 @@ class Note {
|
|
|
59
77
|
this.noteNumber = noteNumber;
|
|
60
78
|
this.velocity = velocity;
|
|
61
79
|
this.startTime = startTime;
|
|
62
|
-
this.voice = voice;
|
|
63
|
-
this.voiceParams = voiceParams;
|
|
64
80
|
}
|
|
65
81
|
}
|
|
66
82
|
// normalized to 0-1 for use with the SF2 modulator model
|
|
@@ -200,7 +216,7 @@ export class MidyGM1 {
|
|
|
200
216
|
enumerable: true,
|
|
201
217
|
configurable: true,
|
|
202
218
|
writable: true,
|
|
203
|
-
value:
|
|
219
|
+
value: Array.from({ length: 128 }, () => [])
|
|
204
220
|
});
|
|
205
221
|
Object.defineProperty(this, "voiceCounter", {
|
|
206
222
|
enumerable: true,
|
|
@@ -214,6 +230,12 @@ export class MidyGM1 {
|
|
|
214
230
|
writable: true,
|
|
215
231
|
value: new Map()
|
|
216
232
|
});
|
|
233
|
+
Object.defineProperty(this, "realtimeVoiceCache", {
|
|
234
|
+
enumerable: true,
|
|
235
|
+
configurable: true,
|
|
236
|
+
writable: true,
|
|
237
|
+
value: new Map()
|
|
238
|
+
});
|
|
217
239
|
Object.defineProperty(this, "isPlaying", {
|
|
218
240
|
enumerable: true,
|
|
219
241
|
configurable: true,
|
|
@@ -281,6 +303,7 @@ export class MidyGM1 {
|
|
|
281
303
|
length: 1,
|
|
282
304
|
sampleRate: audioContext.sampleRate,
|
|
283
305
|
});
|
|
306
|
+
this.messageHandlers = this.createMessageHandlers();
|
|
284
307
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
285
308
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
286
309
|
this.channels = this.createChannels(audioContext);
|
|
@@ -288,21 +311,14 @@ export class MidyGM1 {
|
|
|
288
311
|
this.scheduler.connect(audioContext.destination);
|
|
289
312
|
this.GM1SystemOn();
|
|
290
313
|
}
|
|
291
|
-
initSoundFontTable() {
|
|
292
|
-
const table = new Array(128);
|
|
293
|
-
for (let i = 0; i < 128; i++) {
|
|
294
|
-
table[i] = new Map();
|
|
295
|
-
}
|
|
296
|
-
return table;
|
|
297
|
-
}
|
|
298
314
|
addSoundFont(soundFont) {
|
|
299
315
|
const index = this.soundFonts.length;
|
|
300
316
|
this.soundFonts.push(soundFont);
|
|
301
317
|
const presetHeaders = soundFont.parsed.presetHeaders;
|
|
318
|
+
const soundFontTable = this.soundFontTable;
|
|
302
319
|
for (let i = 0; i < presetHeaders.length; i++) {
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
banks.set(presetHeader.bank, index);
|
|
320
|
+
const { preset, bank } = presetHeaders[i];
|
|
321
|
+
soundFontTable[preset][bank] = index;
|
|
306
322
|
}
|
|
307
323
|
}
|
|
308
324
|
async toUint8Array(input) {
|
|
@@ -380,13 +396,16 @@ export class MidyGM1 {
|
|
|
380
396
|
this.GM1SystemOn();
|
|
381
397
|
}
|
|
382
398
|
getVoiceId(channel, noteNumber, velocity) {
|
|
383
|
-
const
|
|
384
|
-
const
|
|
385
|
-
|
|
399
|
+
const programNumber = channel.programNumber;
|
|
400
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
401
|
+
if (!bankTable)
|
|
402
|
+
return;
|
|
403
|
+
const bank = channel.isDrum ? 128 : 0;
|
|
404
|
+
const soundFontIndex = bankTable[bank];
|
|
386
405
|
if (soundFontIndex === undefined)
|
|
387
406
|
return;
|
|
388
407
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
389
|
-
const voice = soundFont.getVoice(
|
|
408
|
+
const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
390
409
|
const { instrument, sampleID } = voice.generators;
|
|
391
410
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
392
411
|
}
|
|
@@ -435,19 +454,22 @@ export class MidyGM1 {
|
|
|
435
454
|
}
|
|
436
455
|
return bufferSource;
|
|
437
456
|
}
|
|
438
|
-
async scheduleTimelineEvents(
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
457
|
+
async scheduleTimelineEvents(scheduleTime, queueIndex) {
|
|
458
|
+
const timeOffset = this.resumeTime - this.startTime;
|
|
459
|
+
const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
|
|
460
|
+
const schedulingOffset = this.startDelay - timeOffset;
|
|
461
|
+
const timeline = this.timeline;
|
|
462
|
+
while (queueIndex < timeline.length) {
|
|
463
|
+
const event = timeline[queueIndex];
|
|
464
|
+
if (lookAheadCheckTime < event.startTime)
|
|
442
465
|
break;
|
|
443
|
-
const
|
|
444
|
-
const startTime = event.startTime + delay;
|
|
466
|
+
const startTime = event.startTime + schedulingOffset;
|
|
445
467
|
switch (event.type) {
|
|
446
468
|
case "noteOn":
|
|
447
|
-
await this.
|
|
469
|
+
await this.noteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
448
470
|
break;
|
|
449
471
|
case "noteOff": {
|
|
450
|
-
const notePromise = this.
|
|
472
|
+
const notePromise = this.noteOff(event.channel, event.noteNumber, event.velocity, startTime, false);
|
|
451
473
|
if (notePromise)
|
|
452
474
|
this.notePromises.push(notePromise);
|
|
453
475
|
break;
|
|
@@ -480,6 +502,7 @@ export class MidyGM1 {
|
|
|
480
502
|
this.exclusiveClassNotes.fill(undefined);
|
|
481
503
|
this.drumExclusiveClassNotes.fill(undefined);
|
|
482
504
|
this.voiceCache.clear();
|
|
505
|
+
this.realtimeVoiceCache.clear();
|
|
483
506
|
for (let i = 0; i < this.channels.length; i++) {
|
|
484
507
|
this.channels[i].scheduledNotes = [];
|
|
485
508
|
this.resetChannelStates(i);
|
|
@@ -513,13 +536,10 @@ export class MidyGM1 {
|
|
|
513
536
|
this.isPaused = false;
|
|
514
537
|
this.startTime = this.audioContext.currentTime;
|
|
515
538
|
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
516
|
-
let resumeTime = this.resumeTime - this.startTime;
|
|
517
539
|
let finished = false;
|
|
518
540
|
this.notePromises = [];
|
|
519
541
|
while (queueIndex < this.timeline.length) {
|
|
520
542
|
const now = this.audioContext.currentTime;
|
|
521
|
-
const t = now + resumeTime;
|
|
522
|
-
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
523
543
|
if (this.isPausing) {
|
|
524
544
|
await this.stopNotes(0, true, now);
|
|
525
545
|
await this.audioContext.suspend();
|
|
@@ -538,10 +558,10 @@ export class MidyGM1 {
|
|
|
538
558
|
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
539
559
|
this.updateStates(queueIndex, nextQueueIndex);
|
|
540
560
|
queueIndex = nextQueueIndex;
|
|
541
|
-
resumeTime = this.resumeTime - this.startTime;
|
|
542
561
|
this.isSeeking = false;
|
|
543
562
|
continue;
|
|
544
563
|
}
|
|
564
|
+
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
545
565
|
const waitTime = now + this.noteCheckInterval;
|
|
546
566
|
await this.scheduleTask(() => { }, waitTime);
|
|
547
567
|
}
|
|
@@ -557,16 +577,16 @@ export class MidyGM1 {
|
|
|
557
577
|
secondToTicks(second, secondsPerBeat) {
|
|
558
578
|
return second * this.ticksPerBeat / secondsPerBeat;
|
|
559
579
|
}
|
|
580
|
+
getSoundFontId(channel) {
|
|
581
|
+
const programNumber = channel.programNumber;
|
|
582
|
+
const bank = channel.isDrum ? "128" : "000";
|
|
583
|
+
const program = programNumber.toString().padStart(3, "0");
|
|
584
|
+
return `${bank}:${program}`;
|
|
585
|
+
}
|
|
560
586
|
extractMidiData(midi) {
|
|
561
587
|
const instruments = new Set();
|
|
562
588
|
const timeline = [];
|
|
563
|
-
const
|
|
564
|
-
for (let i = 0; i < tmpChannels.length; i++) {
|
|
565
|
-
tmpChannels[i] = {
|
|
566
|
-
programNumber: -1,
|
|
567
|
-
bank: this.channels[i].bank,
|
|
568
|
-
};
|
|
569
|
-
}
|
|
589
|
+
const channels = this.channels;
|
|
570
590
|
for (let i = 0; i < midi.tracks.length; i++) {
|
|
571
591
|
const track = midi.tracks[i];
|
|
572
592
|
let currentTicks = 0;
|
|
@@ -576,17 +596,15 @@ export class MidyGM1 {
|
|
|
576
596
|
event.ticks = currentTicks;
|
|
577
597
|
switch (event.type) {
|
|
578
598
|
case "noteOn": {
|
|
579
|
-
const channel =
|
|
580
|
-
|
|
581
|
-
instruments.add(`${channel.bank}:0`);
|
|
582
|
-
channel.programNumber = 0;
|
|
583
|
-
}
|
|
599
|
+
const channel = channels[event.channel];
|
|
600
|
+
instruments.add(this.getSoundFontId(channel));
|
|
584
601
|
break;
|
|
585
602
|
}
|
|
586
603
|
case "programChange": {
|
|
587
|
-
const channel =
|
|
588
|
-
channel
|
|
589
|
-
instruments.add(
|
|
604
|
+
const channel = channels[event.channel];
|
|
605
|
+
this.setProgramChange(event.channel, event.programNumber);
|
|
606
|
+
instruments.add(this.getSoundFontId(channel));
|
|
607
|
+
break;
|
|
590
608
|
}
|
|
591
609
|
}
|
|
592
610
|
delete event.deltaTime;
|
|
@@ -621,7 +639,7 @@ export class MidyGM1 {
|
|
|
621
639
|
const channel = this.channels[channelNumber];
|
|
622
640
|
const promises = [];
|
|
623
641
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
624
|
-
const promise = this.
|
|
642
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
625
643
|
this.notePromises.push(promise);
|
|
626
644
|
promises.push(promise);
|
|
627
645
|
});
|
|
@@ -631,7 +649,7 @@ export class MidyGM1 {
|
|
|
631
649
|
const channel = this.channels[channelNumber];
|
|
632
650
|
const promises = [];
|
|
633
651
|
this.processScheduledNotes(channel, (note) => {
|
|
634
|
-
const promise = this.
|
|
652
|
+
const promise = this.noteOff(channelNumber, note.noteNumber, velocity, scheduleTime, force);
|
|
635
653
|
this.notePromises.push(promise);
|
|
636
654
|
promises.push(promise);
|
|
637
655
|
});
|
|
@@ -664,7 +682,7 @@ export class MidyGM1 {
|
|
|
664
682
|
if (!this.isPlaying || this.isPaused)
|
|
665
683
|
return;
|
|
666
684
|
const now = this.audioContext.currentTime;
|
|
667
|
-
this.resumeTime
|
|
685
|
+
this.resumeTime = now - this.startTime - this.startDelay;
|
|
668
686
|
this.isPausing = true;
|
|
669
687
|
await this.playPromise;
|
|
670
688
|
this.isPausing = false;
|
|
@@ -690,11 +708,13 @@ export class MidyGM1 {
|
|
|
690
708
|
if (totalTime < event.startTime)
|
|
691
709
|
totalTime = event.startTime;
|
|
692
710
|
}
|
|
693
|
-
return totalTime;
|
|
711
|
+
return totalTime + this.startDelay;
|
|
694
712
|
}
|
|
695
713
|
currentTime() {
|
|
714
|
+
if (!this.isPlaying)
|
|
715
|
+
return this.resumeTime;
|
|
696
716
|
const now = this.audioContext.currentTime;
|
|
697
|
-
return
|
|
717
|
+
return now + this.resumeTime - this.startTime;
|
|
698
718
|
}
|
|
699
719
|
processScheduledNotes(channel, callback) {
|
|
700
720
|
const scheduledNotes = channel.scheduledNotes;
|
|
@@ -833,31 +853,42 @@ export class MidyGM1 {
|
|
|
833
853
|
note.modulationLFO.connect(note.volumeDepth);
|
|
834
854
|
note.volumeDepth.connect(note.volumeEnvelopeNode.gain);
|
|
835
855
|
}
|
|
836
|
-
async getAudioBuffer(channel, noteNumber, velocity, voiceParams) {
|
|
856
|
+
async getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime) {
|
|
837
857
|
const audioBufferId = this.getVoiceId(channel, noteNumber, velocity);
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
this.voiceCache.delete(audioBufferId);
|
|
843
|
-
}
|
|
844
|
-
return cache.audioBuffer;
|
|
845
|
-
}
|
|
846
|
-
else {
|
|
847
|
-
const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
|
|
858
|
+
if (realtime) {
|
|
859
|
+
const cachedAudioBuffer = this.realtimeVoiceCache.get(audioBufferId);
|
|
860
|
+
if (cachedAudioBuffer)
|
|
861
|
+
return cachedAudioBuffer;
|
|
848
862
|
const audioBuffer = await this.createAudioBuffer(voiceParams);
|
|
849
|
-
|
|
850
|
-
this.voiceCache.set(audioBufferId, cache);
|
|
863
|
+
this.realtimeVoiceCache.set(audioBufferId, audioBuffer);
|
|
851
864
|
return audioBuffer;
|
|
852
865
|
}
|
|
866
|
+
else {
|
|
867
|
+
const cache = this.voiceCache.get(audioBufferId);
|
|
868
|
+
if (cache) {
|
|
869
|
+
cache.counter += 1;
|
|
870
|
+
if (cache.maxCount <= cache.counter) {
|
|
871
|
+
this.voiceCache.delete(audioBufferId);
|
|
872
|
+
}
|
|
873
|
+
return cache.audioBuffer;
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
const maxCount = this.voiceCounter.get(audioBufferId) ?? 0;
|
|
877
|
+
const audioBuffer = await this.createAudioBuffer(voiceParams);
|
|
878
|
+
const cache = { audioBuffer, maxCount, counter: 1 };
|
|
879
|
+
this.voiceCache.set(audioBufferId, cache);
|
|
880
|
+
return audioBuffer;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
853
883
|
}
|
|
854
|
-
async
|
|
884
|
+
async setNoteAudioNode(channel, note, realtime) {
|
|
855
885
|
const now = this.audioContext.currentTime;
|
|
886
|
+
const { noteNumber, velocity, startTime } = note;
|
|
856
887
|
const state = channel.state;
|
|
857
888
|
const controllerState = this.getControllerState(channel, noteNumber, velocity);
|
|
858
|
-
const voiceParams = voice.getAllParams(controllerState);
|
|
859
|
-
|
|
860
|
-
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams);
|
|
889
|
+
const voiceParams = note.voice.getAllParams(controllerState);
|
|
890
|
+
note.voiceParams = voiceParams;
|
|
891
|
+
const audioBuffer = await this.getAudioBuffer(channel, noteNumber, velocity, voiceParams, realtime);
|
|
861
892
|
note.bufferSource = this.createBufferSource(voiceParams, audioBuffer);
|
|
862
893
|
note.volumeEnvelopeNode = new GainNode(this.audioContext);
|
|
863
894
|
note.filterNode = new BiquadFilterNode(this.audioContext, {
|
|
@@ -884,37 +915,50 @@ export class MidyGM1 {
|
|
|
884
915
|
if (prev) {
|
|
885
916
|
const [prevNote, prevChannelNumber] = prev;
|
|
886
917
|
if (prevNote && !prevNote.ending) {
|
|
887
|
-
this.
|
|
918
|
+
this.noteOff(prevChannelNumber, prevNote.noteNumber, 0, // velocity,
|
|
888
919
|
startTime, true);
|
|
889
920
|
}
|
|
890
921
|
}
|
|
891
922
|
this.exclusiveClassNotes[exclusiveClass] = [note, channelNumber];
|
|
892
923
|
}
|
|
893
|
-
|
|
924
|
+
setNoteRouting(channelNumber, note, startTime) {
|
|
894
925
|
const channel = this.channels[channelNumber];
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
if (soundFontIndex === undefined)
|
|
899
|
-
return;
|
|
900
|
-
const soundFont = this.soundFonts[soundFontIndex];
|
|
901
|
-
const voice = soundFont.getVoice(bankNumber, channel.programNumber, noteNumber, velocity);
|
|
902
|
-
if (!voice)
|
|
903
|
-
return;
|
|
904
|
-
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
|
|
905
|
-
note.volumeEnvelopeNode.connect(channel.gainL);
|
|
906
|
-
note.volumeEnvelopeNode.connect(channel.gainR);
|
|
926
|
+
const volumeEnvelopeNode = note.volumeEnvelopeNode;
|
|
927
|
+
volumeEnvelopeNode.connect(channel.gainL);
|
|
928
|
+
volumeEnvelopeNode.connect(channel.gainR);
|
|
907
929
|
if (0.5 <= channel.state.sustainPedal) {
|
|
908
930
|
channel.sustainNotes.push(note);
|
|
909
931
|
}
|
|
910
932
|
this.handleExclusiveClass(note, channelNumber, startTime);
|
|
933
|
+
}
|
|
934
|
+
async noteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
935
|
+
const channel = this.channels[channelNumber];
|
|
936
|
+
const realtime = startTime === undefined;
|
|
937
|
+
if (realtime)
|
|
938
|
+
startTime = this.audioContext.currentTime;
|
|
939
|
+
const note = new Note(noteNumber, velocity, startTime);
|
|
911
940
|
const scheduledNotes = channel.scheduledNotes;
|
|
912
941
|
note.index = scheduledNotes.length;
|
|
913
942
|
scheduledNotes.push(note);
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
943
|
+
const programNumber = channel.programNumber;
|
|
944
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
945
|
+
if (!bankTable)
|
|
946
|
+
return;
|
|
947
|
+
const bank = channel.isDrum ? 128 : 0;
|
|
948
|
+
const soundFontIndex = bankTable[bank];
|
|
949
|
+
if (soundFontIndex === undefined)
|
|
950
|
+
return;
|
|
951
|
+
const soundFont = this.soundFonts[soundFontIndex];
|
|
952
|
+
note.voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
953
|
+
if (!note.voice)
|
|
954
|
+
return;
|
|
955
|
+
await this.setNoteAudioNode(channel, note, realtime);
|
|
956
|
+
this.setNoteRouting(channelNumber, note, startTime);
|
|
957
|
+
note.pending = false;
|
|
958
|
+
const off = note.offEvent;
|
|
959
|
+
if (off) {
|
|
960
|
+
this.noteOff(channelNumber, noteNumber, off.velocity, off.startTime);
|
|
961
|
+
}
|
|
918
962
|
}
|
|
919
963
|
disconnectNote(note) {
|
|
920
964
|
note.bufferSource.disconnect();
|
|
@@ -927,6 +971,7 @@ export class MidyGM1 {
|
|
|
927
971
|
}
|
|
928
972
|
}
|
|
929
973
|
releaseNote(channel, note, endTime) {
|
|
974
|
+
endTime ??= this.audioContext.currentTime;
|
|
930
975
|
const volRelease = endTime + note.voiceParams.volRelease;
|
|
931
976
|
const modRelease = endTime + note.voiceParams.modRelease;
|
|
932
977
|
const stopTime = Math.min(volRelease, modRelease);
|
|
@@ -947,7 +992,7 @@ export class MidyGM1 {
|
|
|
947
992
|
}, stopTime);
|
|
948
993
|
});
|
|
949
994
|
}
|
|
950
|
-
|
|
995
|
+
noteOff(channelNumber, noteNumber, velocity, endTime, force) {
|
|
951
996
|
const channel = this.channels[channelNumber];
|
|
952
997
|
if (!force && 0.5 <= channel.state.sustainPedal)
|
|
953
998
|
return;
|
|
@@ -955,6 +1000,10 @@ export class MidyGM1 {
|
|
|
955
1000
|
if (index < 0)
|
|
956
1001
|
return;
|
|
957
1002
|
const note = channel.scheduledNotes[index];
|
|
1003
|
+
if (note.pending) {
|
|
1004
|
+
note.offEvent = { velocity, startTime: endTime };
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
958
1007
|
note.ending = true;
|
|
959
1008
|
this.setNoteIndex(channel, index);
|
|
960
1009
|
this.releaseNote(channel, note, endTime);
|
|
@@ -985,22 +1034,37 @@ export class MidyGM1 {
|
|
|
985
1034
|
}
|
|
986
1035
|
return -1;
|
|
987
1036
|
}
|
|
988
|
-
noteOff(channelNumber, noteNumber, velocity, scheduleTime) {
|
|
989
|
-
scheduleTime ??= this.audioContext.currentTime;
|
|
990
|
-
return this.scheduleNoteOff(channelNumber, noteNumber, velocity, scheduleTime, false);
|
|
991
|
-
}
|
|
992
1037
|
releaseSustainPedal(channelNumber, halfVelocity, scheduleTime) {
|
|
993
1038
|
const velocity = halfVelocity * 2;
|
|
994
1039
|
const channel = this.channels[channelNumber];
|
|
995
1040
|
const promises = [];
|
|
996
1041
|
for (let i = 0; i < channel.sustainNotes.length; i++) {
|
|
997
|
-
const promise = this.
|
|
1042
|
+
const promise = this.noteOff(channelNumber, channel.sustainNotes[i].noteNumber, velocity, scheduleTime);
|
|
998
1043
|
promises.push(promise);
|
|
999
1044
|
}
|
|
1000
1045
|
channel.sustainNotes = [];
|
|
1001
1046
|
return promises;
|
|
1002
1047
|
}
|
|
1003
|
-
|
|
1048
|
+
createMessageHandlers() {
|
|
1049
|
+
const handlers = new Array(256);
|
|
1050
|
+
// Channel Message
|
|
1051
|
+
handlers[0x80] = (data, scheduleTime) => this.noteOff(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1052
|
+
handlers[0x90] = (data, scheduleTime) => this.noteOn(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1053
|
+
handlers[0xB0] = (data, scheduleTime) => this.setControlChange(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1054
|
+
handlers[0xC0] = (data, scheduleTime) => this.setProgramChange(data[0] & 0x0F, data[1], scheduleTime);
|
|
1055
|
+
handlers[0xE0] = (data, scheduleTime) => this.handlePitchBendMessage(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1056
|
+
return handlers;
|
|
1057
|
+
}
|
|
1058
|
+
handleMessage(data, scheduleTime) {
|
|
1059
|
+
const status = data[0];
|
|
1060
|
+
if (status === 0xF0) {
|
|
1061
|
+
return this.handleSysEx(data.subarray(1), scheduleTime);
|
|
1062
|
+
}
|
|
1063
|
+
const handler = this.messageHandlers[status];
|
|
1064
|
+
if (handler)
|
|
1065
|
+
handler(data, scheduleTime);
|
|
1066
|
+
}
|
|
1067
|
+
handleChannelMessage(statusByte, data1, data2, scheduleTime) {
|
|
1004
1068
|
const channelNumber = statusByte & 0x0F;
|
|
1005
1069
|
const messageType = statusByte & 0xF0;
|
|
1006
1070
|
switch (messageType) {
|
|
@@ -1428,10 +1492,8 @@ export class MidyGM1 {
|
|
|
1428
1492
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1429
1493
|
this.allSoundOff(i, 0, scheduleTime);
|
|
1430
1494
|
const channel = this.channels[i];
|
|
1431
|
-
channel.bank = 0;
|
|
1432
1495
|
channel.isDrum = false;
|
|
1433
1496
|
}
|
|
1434
|
-
this.channels[9].bank = 128;
|
|
1435
1497
|
this.channels[9].isDrum = true;
|
|
1436
1498
|
}
|
|
1437
1499
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
@@ -1452,16 +1514,11 @@ export class MidyGM1 {
|
|
|
1452
1514
|
const volume = (data[5] * 128 + data[4]) / 16383;
|
|
1453
1515
|
this.setMasterVolume(volume, scheduleTime);
|
|
1454
1516
|
}
|
|
1455
|
-
setMasterVolume(
|
|
1517
|
+
setMasterVolume(value, scheduleTime) {
|
|
1456
1518
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
else {
|
|
1461
|
-
this.masterVolume.gain
|
|
1462
|
-
.cancelScheduledValues(scheduleTime)
|
|
1463
|
-
.setValueAtTime(volume * volume, scheduleTime);
|
|
1464
|
-
}
|
|
1519
|
+
this.masterVolume.gain
|
|
1520
|
+
.cancelScheduledValues(scheduleTime)
|
|
1521
|
+
.setValueAtTime(value * value, scheduleTime);
|
|
1465
1522
|
}
|
|
1466
1523
|
handleSysEx(data, scheduleTime) {
|
|
1467
1524
|
switch (data[0]) {
|
|
@@ -1498,9 +1555,9 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1498
1555
|
configurable: true,
|
|
1499
1556
|
writable: true,
|
|
1500
1557
|
value: {
|
|
1558
|
+
scheduleIndex: 0,
|
|
1501
1559
|
detune: 0,
|
|
1502
1560
|
programNumber: 0,
|
|
1503
|
-
bank: 0,
|
|
1504
1561
|
dataMSB: 0,
|
|
1505
1562
|
dataLSB: 0,
|
|
1506
1563
|
rpnMSB: 127,
|
package/esm/midy-GM2.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ export class MidyGM2 {
|
|
|
3
3
|
scheduleIndex: number;
|
|
4
4
|
detune: number;
|
|
5
5
|
programNumber: number;
|
|
6
|
-
bank: number;
|
|
7
6
|
bankMSB: number;
|
|
8
7
|
bankLSB: number;
|
|
9
8
|
dataMSB: number;
|
|
@@ -34,15 +33,18 @@ export class MidyGM2 {
|
|
|
34
33
|
numChannels: number;
|
|
35
34
|
ticksPerBeat: number;
|
|
36
35
|
totalTime: number;
|
|
36
|
+
lastActiveSensing: number;
|
|
37
|
+
activeSensingThreshold: number;
|
|
37
38
|
noteCheckInterval: number;
|
|
38
39
|
lookAhead: number;
|
|
39
40
|
startDelay: number;
|
|
40
41
|
startTime: number;
|
|
41
42
|
resumeTime: number;
|
|
42
43
|
soundFonts: any[];
|
|
43
|
-
soundFontTable:
|
|
44
|
+
soundFontTable: never[][];
|
|
44
45
|
voiceCounter: Map<any, any>;
|
|
45
46
|
voiceCache: Map<any, any>;
|
|
47
|
+
realtimeVoiceCache: Map<any, any>;
|
|
46
48
|
isPlaying: boolean;
|
|
47
49
|
isPausing: boolean;
|
|
48
50
|
isPaused: boolean;
|
|
@@ -58,6 +60,7 @@ export class MidyGM2 {
|
|
|
58
60
|
masterVolume: any;
|
|
59
61
|
scheduler: any;
|
|
60
62
|
schedulerBuffer: any;
|
|
63
|
+
messageHandlers: any[];
|
|
61
64
|
voiceParamsHandlers: {
|
|
62
65
|
modLfoToPitch: (channel: any, note: any, scheduleTime: any) => void;
|
|
63
66
|
vibLfoToPitch: (channel: any, note: any, scheduleTime: any) => void;
|
|
@@ -71,6 +74,7 @@ export class MidyGM2 {
|
|
|
71
74
|
freqVibLFO: (channel: any, note: any, scheduleTime: any) => void;
|
|
72
75
|
};
|
|
73
76
|
controlChangeHandlers: any[];
|
|
77
|
+
keyBasedControllerHandlers: any[];
|
|
74
78
|
channels: any[];
|
|
75
79
|
reverbEffect: {
|
|
76
80
|
input: any;
|
|
@@ -85,7 +89,6 @@ export class MidyGM2 {
|
|
|
85
89
|
delayNodes: any[];
|
|
86
90
|
feedbackGains: any[];
|
|
87
91
|
};
|
|
88
|
-
initSoundFontTable(): any[];
|
|
89
92
|
addSoundFont(soundFont: any): void;
|
|
90
93
|
toUint8Array(input: any): Promise<Uint8Array<ArrayBuffer>>;
|
|
91
94
|
loadSoundFont(input: any): Promise<void>;
|
|
@@ -102,13 +105,14 @@ export class MidyGM2 {
|
|
|
102
105
|
createAudioBuffer(voiceParams: any): Promise<any>;
|
|
103
106
|
isLoopDrum(channel: any, noteNumber: any): boolean;
|
|
104
107
|
createBufferSource(channel: any, noteNumber: any, voiceParams: any, audioBuffer: any): any;
|
|
105
|
-
scheduleTimelineEvents(
|
|
108
|
+
scheduleTimelineEvents(scheduleTime: any, queueIndex: any): Promise<any>;
|
|
106
109
|
getQueueIndex(second: any): number;
|
|
107
110
|
resetAllStates(): void;
|
|
108
111
|
updateStates(queueIndex: any, nextQueueIndex: any): void;
|
|
109
112
|
playNotes(): Promise<void>;
|
|
110
113
|
ticksToSecond(ticks: any, secondsPerBeat: any): number;
|
|
111
114
|
secondToTicks(second: any, secondsPerBeat: any): number;
|
|
115
|
+
getSoundFontId(channel: any): string;
|
|
112
116
|
extractMidiData(midi: any): {
|
|
113
117
|
instruments: Set<any>;
|
|
114
118
|
timeline: any[];
|
|
@@ -170,22 +174,22 @@ export class MidyGM2 {
|
|
|
170
174
|
setFilterEnvelope(channel: any, note: any, scheduleTime: any): void;
|
|
171
175
|
startModulation(channel: any, note: any, scheduleTime: any): void;
|
|
172
176
|
startVibrato(channel: any, note: any, scheduleTime: any): void;
|
|
173
|
-
getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any): Promise<any>;
|
|
174
|
-
|
|
175
|
-
calcBank(channel: any): any;
|
|
177
|
+
getAudioBuffer(channel: any, noteNumber: any, velocity: any, voiceParams: any, realtime: any): Promise<any>;
|
|
178
|
+
setNoteAudioNode(channel: any, note: any, realtime: any): Promise<any>;
|
|
176
179
|
handleExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
177
180
|
handleDrumExclusiveClass(note: any, channelNumber: any, startTime: any): void;
|
|
178
|
-
|
|
179
|
-
noteOn(channelNumber: any, noteNumber: any, velocity: any,
|
|
181
|
+
setNoteRouting(channelNumber: any, note: any, startTime: any): void;
|
|
182
|
+
noteOn(channelNumber: any, noteNumber: any, velocity: any, startTime: any): Promise<void>;
|
|
180
183
|
disconnectNote(note: any): void;
|
|
181
184
|
releaseNote(channel: any, note: any, endTime: any): Promise<any>;
|
|
182
|
-
|
|
185
|
+
noteOff(channelNumber: any, noteNumber: any, velocity: any, endTime: any, force: any): void;
|
|
183
186
|
setNoteIndex(channel: any, index: any): void;
|
|
184
187
|
findNoteOffIndex(channel: any, noteNumber: any): any;
|
|
185
|
-
noteOff(channelNumber: any, noteNumber: any, velocity: any, scheduleTime: any): void;
|
|
186
188
|
releaseSustainPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
|
|
187
189
|
releaseSostenutoPedal(channelNumber: any, halfVelocity: any, scheduleTime: any): void[];
|
|
188
|
-
|
|
190
|
+
createMessageHandlers(): any[];
|
|
191
|
+
handleMessage(data: any, scheduleTime: any): void;
|
|
192
|
+
activeSensing(): void;
|
|
189
193
|
setProgramChange(channelNumber: any, programNumber: any, _scheduleTime: any): void;
|
|
190
194
|
setChannelPressure(channelNumber: any, value: any, scheduleTime: any): void;
|
|
191
195
|
handlePitchBendMessage(channelNumber: any, lsb: any, msb: any, scheduleTime: any): void;
|
|
@@ -233,6 +237,7 @@ export class MidyGM2 {
|
|
|
233
237
|
updateChannelVolume(channel: any, scheduleTime: any): void;
|
|
234
238
|
updateKeyBasedVolume(channel: any, keyNumber: any, scheduleTime: any): void;
|
|
235
239
|
setSustainPedal(channelNumber: any, value: any, scheduleTime: any): void;
|
|
240
|
+
isPortamento(channel: any, note: any): boolean;
|
|
236
241
|
setPortamento(channelNumber: any, value: any, scheduleTime: any): void;
|
|
237
242
|
setSostenutoPedal(channelNumber: any, value: any, scheduleTime: any): void;
|
|
238
243
|
getSoftPedalFactor(channel: any, note: any): number;
|
|
@@ -266,7 +271,7 @@ export class MidyGM2 {
|
|
|
266
271
|
GM2SystemOn(scheduleTime: any): void;
|
|
267
272
|
handleUniversalRealTimeExclusiveMessage(data: any, scheduleTime: any): void;
|
|
268
273
|
handleMasterVolumeSysEx(data: any, scheduleTime: any): void;
|
|
269
|
-
setMasterVolume(
|
|
274
|
+
setMasterVolume(value: any, scheduleTime: any): void;
|
|
270
275
|
handleMasterFineTuningSysEx(data: any, scheduleTime: any): void;
|
|
271
276
|
setMasterFineTuning(value: any, scheduleTime: any): void;
|
|
272
277
|
handleMasterCoarseTuningSysEx(data: any, scheduleTime: any): void;
|
|
@@ -302,31 +307,9 @@ export class MidyGM2 {
|
|
|
302
307
|
setControlChangeEffects(channel: any, controllerType: any, scheduleTime: any): void;
|
|
303
308
|
handleControlChangeSysEx(data: any, scheduleTime: any): void;
|
|
304
309
|
getKeyBasedValue(channel: any, keyNumber: any, controllerType: any): any;
|
|
310
|
+
createKeyBasedControllerHandlers(): any[];
|
|
305
311
|
handleKeyBasedInstrumentControlSysEx(data: any, scheduleTime: any): void;
|
|
306
312
|
handleSysEx(data: any, scheduleTime: any): void;
|
|
307
313
|
scheduleTask(callback: any, scheduleTime: any): Promise<any>;
|
|
308
314
|
}
|
|
309
|
-
declare class Note {
|
|
310
|
-
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
311
|
-
index: number;
|
|
312
|
-
ending: boolean;
|
|
313
|
-
bufferSource: any;
|
|
314
|
-
filterNode: any;
|
|
315
|
-
filterDepth: any;
|
|
316
|
-
volumeEnvelopeNode: any;
|
|
317
|
-
volumeDepth: any;
|
|
318
|
-
modulationLFO: any;
|
|
319
|
-
modulationDepth: any;
|
|
320
|
-
vibratoLFO: any;
|
|
321
|
-
vibratoDepth: any;
|
|
322
|
-
reverbSend: any;
|
|
323
|
-
chorusSend: any;
|
|
324
|
-
portamentoNoteNumber: number;
|
|
325
|
-
noteNumber: any;
|
|
326
|
-
velocity: any;
|
|
327
|
-
startTime: any;
|
|
328
|
-
voice: any;
|
|
329
|
-
voiceParams: any;
|
|
330
|
-
}
|
|
331
|
-
export {};
|
|
332
315
|
//# sourceMappingURL=midy-GM2.d.ts.map
|
package/esm/midy-GM2.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GM2.d.ts","sourceRoot":"","sources":["../src/midy-GM2.js"],"names":[],"mappings":"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"}
|