@marmooo/midy 0.3.6 → 0.3.8
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 +38 -20
- package/esm/midy-GM1.d.ts +36 -30
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +184 -142
- package/esm/midy-GM2.d.ts +43 -33
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +324 -279
- package/esm/midy-GMLite.d.ts +35 -30
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +178 -139
- package/esm/midy.d.ts +45 -34
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +414 -302
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +36 -30
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +184 -142
- package/script/midy-GM2.d.ts +43 -33
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +324 -279
- package/script/midy-GMLite.d.ts +35 -30
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +178 -139
- package/script/midy.d.ts +45 -34
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +414 -302
package/script/midy-GM1.js
CHANGED
|
@@ -203,7 +203,7 @@ class MidyGM1 {
|
|
|
203
203
|
enumerable: true,
|
|
204
204
|
configurable: true,
|
|
205
205
|
writable: true,
|
|
206
|
-
value:
|
|
206
|
+
value: Array.from({ length: 128 }, () => [])
|
|
207
207
|
});
|
|
208
208
|
Object.defineProperty(this, "voiceCounter", {
|
|
209
209
|
enumerable: true,
|
|
@@ -247,6 +247,12 @@ class MidyGM1 {
|
|
|
247
247
|
writable: true,
|
|
248
248
|
value: false
|
|
249
249
|
});
|
|
250
|
+
Object.defineProperty(this, "playPromise", {
|
|
251
|
+
enumerable: true,
|
|
252
|
+
configurable: true,
|
|
253
|
+
writable: true,
|
|
254
|
+
value: void 0
|
|
255
|
+
});
|
|
250
256
|
Object.defineProperty(this, "timeline", {
|
|
251
257
|
enumerable: true,
|
|
252
258
|
configurable: true,
|
|
@@ -278,6 +284,7 @@ class MidyGM1 {
|
|
|
278
284
|
length: 1,
|
|
279
285
|
sampleRate: audioContext.sampleRate,
|
|
280
286
|
});
|
|
287
|
+
this.messageHandlers = this.createMessageHandlers();
|
|
281
288
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
282
289
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
283
290
|
this.channels = this.createChannels(audioContext);
|
|
@@ -285,21 +292,14 @@ class MidyGM1 {
|
|
|
285
292
|
this.scheduler.connect(audioContext.destination);
|
|
286
293
|
this.GM1SystemOn();
|
|
287
294
|
}
|
|
288
|
-
initSoundFontTable() {
|
|
289
|
-
const table = new Array(128);
|
|
290
|
-
for (let i = 0; i < 128; i++) {
|
|
291
|
-
table[i] = new Map();
|
|
292
|
-
}
|
|
293
|
-
return table;
|
|
294
|
-
}
|
|
295
295
|
addSoundFont(soundFont) {
|
|
296
296
|
const index = this.soundFonts.length;
|
|
297
297
|
this.soundFonts.push(soundFont);
|
|
298
298
|
const presetHeaders = soundFont.parsed.presetHeaders;
|
|
299
|
+
const soundFontTable = this.soundFontTable;
|
|
299
300
|
for (let i = 0; i < presetHeaders.length; i++) {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
banks.set(presetHeader.bank, index);
|
|
301
|
+
const { preset, bank } = presetHeaders[i];
|
|
302
|
+
soundFontTable[preset][bank] = index;
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
async toUint8Array(input) {
|
|
@@ -377,13 +377,16 @@ class MidyGM1 {
|
|
|
377
377
|
this.GM1SystemOn();
|
|
378
378
|
}
|
|
379
379
|
getVoiceId(channel, noteNumber, velocity) {
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
|
|
380
|
+
const programNumber = channel.programNumber;
|
|
381
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
382
|
+
if (!bankTable)
|
|
383
|
+
return;
|
|
384
|
+
const bank = channel.isDrum ? 128 : 0;
|
|
385
|
+
const soundFontIndex = bankTable[bank];
|
|
383
386
|
if (soundFontIndex === undefined)
|
|
384
387
|
return;
|
|
385
388
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
386
|
-
const voice = soundFont.getVoice(
|
|
389
|
+
const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
387
390
|
const { instrument, sampleID } = voice.generators;
|
|
388
391
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
389
392
|
}
|
|
@@ -432,13 +435,16 @@ class MidyGM1 {
|
|
|
432
435
|
}
|
|
433
436
|
return bufferSource;
|
|
434
437
|
}
|
|
435
|
-
async scheduleTimelineEvents(
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
438
|
+
async scheduleTimelineEvents(scheduleTime, queueIndex) {
|
|
439
|
+
const timeOffset = this.resumeTime - this.startTime;
|
|
440
|
+
const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
|
|
441
|
+
const schedulingOffset = this.startDelay - timeOffset;
|
|
442
|
+
const timeline = this.timeline;
|
|
443
|
+
while (queueIndex < timeline.length) {
|
|
444
|
+
const event = timeline[queueIndex];
|
|
445
|
+
if (lookAheadCheckTime < event.startTime)
|
|
439
446
|
break;
|
|
440
|
-
const
|
|
441
|
-
const startTime = event.startTime + delay;
|
|
447
|
+
const startTime = event.startTime + schedulingOffset;
|
|
442
448
|
switch (event.type) {
|
|
443
449
|
case "noteOn":
|
|
444
450
|
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
@@ -473,69 +479,77 @@ class MidyGM1 {
|
|
|
473
479
|
}
|
|
474
480
|
return 0;
|
|
475
481
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
482
|
+
resetAllStates() {
|
|
483
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
484
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
485
|
+
this.voiceCache.clear();
|
|
486
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
487
|
+
this.channels[i].scheduledNotes = [];
|
|
488
|
+
this.resetChannelStates(i);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
updateStates(queueIndex, nextQueueIndex) {
|
|
492
|
+
if (nextQueueIndex < queueIndex)
|
|
493
|
+
queueIndex = 0;
|
|
494
|
+
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
495
|
+
const event = this.timeline[i];
|
|
496
|
+
switch (event.type) {
|
|
497
|
+
case "controller":
|
|
498
|
+
this.setControlChange(event.channel, event.controllerType, event.value, 0);
|
|
499
|
+
break;
|
|
500
|
+
case "programChange":
|
|
501
|
+
this.setProgramChange(event.channel, event.programNumber, 0);
|
|
502
|
+
break;
|
|
503
|
+
case "pitchBend":
|
|
504
|
+
this.setPitchBend(event.channel, event.value + 8192, 0);
|
|
505
|
+
break;
|
|
506
|
+
case "sysEx":
|
|
507
|
+
this.handleSysEx(event.data, 0);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async playNotes() {
|
|
512
|
+
if (this.audioContext.state === "suspended") {
|
|
513
|
+
await this.audioContext.resume();
|
|
514
|
+
}
|
|
515
|
+
this.isPlaying = true;
|
|
516
|
+
this.isPaused = false;
|
|
517
|
+
this.startTime = this.audioContext.currentTime;
|
|
518
|
+
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
519
|
+
let finished = false;
|
|
520
|
+
this.notePromises = [];
|
|
521
|
+
while (queueIndex < this.timeline.length) {
|
|
522
|
+
const now = this.audioContext.currentTime;
|
|
523
|
+
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
524
|
+
if (this.isPausing) {
|
|
525
|
+
await this.stopNotes(0, true, now);
|
|
526
|
+
await this.audioContext.suspend();
|
|
527
|
+
this.notePromises = [];
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
else if (this.isStopping) {
|
|
531
|
+
await this.stopNotes(0, true, now);
|
|
532
|
+
await this.audioContext.suspend();
|
|
533
|
+
finished = true;
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
else if (this.isSeeking) {
|
|
537
|
+
await this.stopNotes(0, true, now);
|
|
538
|
+
this.startTime = this.audioContext.currentTime;
|
|
539
|
+
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
540
|
+
this.updateStates(queueIndex, nextQueueIndex);
|
|
541
|
+
queueIndex = nextQueueIndex;
|
|
542
|
+
this.isSeeking = false;
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const waitTime = now + this.noteCheckInterval;
|
|
546
|
+
await this.scheduleTask(() => { }, waitTime);
|
|
547
|
+
}
|
|
548
|
+
if (finished) {
|
|
483
549
|
this.notePromises = [];
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
this.notePromises = [];
|
|
488
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
489
|
-
this.voiceCache.clear();
|
|
490
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
491
|
-
this.channels[i].scheduledNotes = [];
|
|
492
|
-
this.resetAllStates(i);
|
|
493
|
-
}
|
|
494
|
-
resolve();
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
const now = this.audioContext.currentTime;
|
|
498
|
-
const t = now + resumeTime;
|
|
499
|
-
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
500
|
-
if (this.isPausing) {
|
|
501
|
-
await this.stopNotes(0, true, now);
|
|
502
|
-
this.notePromises = [];
|
|
503
|
-
this.isPausing = false;
|
|
504
|
-
this.isPaused = true;
|
|
505
|
-
resolve();
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
else if (this.isStopping) {
|
|
509
|
-
await this.stopNotes(0, true, now);
|
|
510
|
-
this.notePromises = [];
|
|
511
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
512
|
-
this.voiceCache.clear();
|
|
513
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
514
|
-
this.channels[i].scheduledNotes = [];
|
|
515
|
-
this.resetAllStates(i);
|
|
516
|
-
}
|
|
517
|
-
this.isStopping = false;
|
|
518
|
-
this.isPaused = false;
|
|
519
|
-
resolve();
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
else if (this.isSeeking) {
|
|
523
|
-
this.stopNotes(0, true, now);
|
|
524
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
525
|
-
this.startTime = this.audioContext.currentTime;
|
|
526
|
-
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
527
|
-
resumeTime = this.resumeTime - this.startTime;
|
|
528
|
-
this.isSeeking = false;
|
|
529
|
-
await schedulePlayback();
|
|
530
|
-
}
|
|
531
|
-
else {
|
|
532
|
-
const waitTime = now + this.noteCheckInterval;
|
|
533
|
-
await this.scheduleTask(() => { }, waitTime);
|
|
534
|
-
await schedulePlayback();
|
|
535
|
-
}
|
|
536
|
-
};
|
|
537
|
-
schedulePlayback();
|
|
538
|
-
});
|
|
550
|
+
this.resetAllStates();
|
|
551
|
+
}
|
|
552
|
+
this.isPlaying = false;
|
|
539
553
|
}
|
|
540
554
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
541
555
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -543,16 +557,16 @@ class MidyGM1 {
|
|
|
543
557
|
secondToTicks(second, secondsPerBeat) {
|
|
544
558
|
return second * this.ticksPerBeat / secondsPerBeat;
|
|
545
559
|
}
|
|
560
|
+
getSoundFontId(channel) {
|
|
561
|
+
const programNumber = channel.programNumber;
|
|
562
|
+
const bank = channel.isDrum ? "128" : "000";
|
|
563
|
+
const program = programNumber.toString().padStart(3, "0");
|
|
564
|
+
return `${bank}:${program}`;
|
|
565
|
+
}
|
|
546
566
|
extractMidiData(midi) {
|
|
547
567
|
const instruments = new Set();
|
|
548
568
|
const timeline = [];
|
|
549
|
-
const
|
|
550
|
-
for (let i = 0; i < tmpChannels.length; i++) {
|
|
551
|
-
tmpChannels[i] = {
|
|
552
|
-
programNumber: -1,
|
|
553
|
-
bank: this.channels[i].bank,
|
|
554
|
-
};
|
|
555
|
-
}
|
|
569
|
+
const channels = this.channels;
|
|
556
570
|
for (let i = 0; i < midi.tracks.length; i++) {
|
|
557
571
|
const track = midi.tracks[i];
|
|
558
572
|
let currentTicks = 0;
|
|
@@ -562,17 +576,15 @@ class MidyGM1 {
|
|
|
562
576
|
event.ticks = currentTicks;
|
|
563
577
|
switch (event.type) {
|
|
564
578
|
case "noteOn": {
|
|
565
|
-
const channel =
|
|
566
|
-
|
|
567
|
-
instruments.add(`${channel.bank}:0`);
|
|
568
|
-
channel.programNumber = 0;
|
|
569
|
-
}
|
|
579
|
+
const channel = channels[event.channel];
|
|
580
|
+
instruments.add(this.getSoundFontId(channel));
|
|
570
581
|
break;
|
|
571
582
|
}
|
|
572
583
|
case "programChange": {
|
|
573
|
-
const channel =
|
|
574
|
-
channel
|
|
575
|
-
instruments.add(
|
|
584
|
+
const channel = channels[event.channel];
|
|
585
|
+
this.setProgramChange(event.channel, event.programNumber);
|
|
586
|
+
instruments.add(this.getSoundFontId(channel));
|
|
587
|
+
break;
|
|
576
588
|
}
|
|
577
589
|
}
|
|
578
590
|
delete event.deltaTime;
|
|
@@ -636,26 +648,32 @@ class MidyGM1 {
|
|
|
636
648
|
this.resumeTime = 0;
|
|
637
649
|
if (this.voiceCounter.size === 0)
|
|
638
650
|
this.cacheVoiceIds();
|
|
639
|
-
|
|
640
|
-
this.
|
|
651
|
+
this.playPromise = this.playNotes();
|
|
652
|
+
await this.playPromise;
|
|
641
653
|
}
|
|
642
|
-
stop() {
|
|
654
|
+
async stop() {
|
|
643
655
|
if (!this.isPlaying)
|
|
644
656
|
return;
|
|
645
657
|
this.isStopping = true;
|
|
658
|
+
await this.playPromise;
|
|
659
|
+
this.isStopping = false;
|
|
646
660
|
}
|
|
647
|
-
pause() {
|
|
661
|
+
async pause() {
|
|
648
662
|
if (!this.isPlaying || this.isPaused)
|
|
649
663
|
return;
|
|
650
664
|
const now = this.audioContext.currentTime;
|
|
651
665
|
this.resumeTime += now - this.startTime - this.startDelay;
|
|
652
666
|
this.isPausing = true;
|
|
667
|
+
await this.playPromise;
|
|
668
|
+
this.isPausing = false;
|
|
669
|
+
this.isPaused = true;
|
|
653
670
|
}
|
|
654
671
|
async resume() {
|
|
655
672
|
if (!this.isPaused)
|
|
656
673
|
return;
|
|
657
|
-
|
|
658
|
-
this.
|
|
674
|
+
this.playPromise = this.playNotes();
|
|
675
|
+
await this.playPromise;
|
|
676
|
+
this.isPaused = false;
|
|
659
677
|
}
|
|
660
678
|
seekTo(second) {
|
|
661
679
|
this.resumeTime = second;
|
|
@@ -872,13 +890,16 @@ class MidyGM1 {
|
|
|
872
890
|
}
|
|
873
891
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
874
892
|
const channel = this.channels[channelNumber];
|
|
875
|
-
const
|
|
876
|
-
const
|
|
877
|
-
|
|
893
|
+
const programNumber = channel.programNumber;
|
|
894
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
895
|
+
if (!bankTable)
|
|
896
|
+
return;
|
|
897
|
+
const bank = channel.isDrum ? 128 : 0;
|
|
898
|
+
const soundFontIndex = bankTable[bank];
|
|
878
899
|
if (soundFontIndex === undefined)
|
|
879
900
|
return;
|
|
880
901
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
881
|
-
const voice = soundFont.getVoice(
|
|
902
|
+
const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
882
903
|
if (!voice)
|
|
883
904
|
return;
|
|
884
905
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
|
|
@@ -980,7 +1001,26 @@ class MidyGM1 {
|
|
|
980
1001
|
channel.sustainNotes = [];
|
|
981
1002
|
return promises;
|
|
982
1003
|
}
|
|
983
|
-
|
|
1004
|
+
createMessageHandlers() {
|
|
1005
|
+
const handlers = new Array(256);
|
|
1006
|
+
// Channel Message
|
|
1007
|
+
handlers[0x80] = (data, scheduleTime) => this.noteOff(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1008
|
+
handlers[0x90] = (data, scheduleTime) => this.noteOn(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1009
|
+
handlers[0xB0] = (data, scheduleTime) => this.setControlChange(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1010
|
+
handlers[0xC0] = (data, scheduleTime) => this.setProgramChange(data[0] & 0x0F, data[1], scheduleTime);
|
|
1011
|
+
handlers[0xE0] = (data, scheduleTime) => this.handlePitchBendMessage(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1012
|
+
return handlers;
|
|
1013
|
+
}
|
|
1014
|
+
handleMessage(data, scheduleTime) {
|
|
1015
|
+
const status = data[0];
|
|
1016
|
+
if (status === 0xF0) {
|
|
1017
|
+
return this.handleSysEx(data.subarray(1), scheduleTime);
|
|
1018
|
+
}
|
|
1019
|
+
const handler = this.messageHandlers[status];
|
|
1020
|
+
if (handler)
|
|
1021
|
+
handler(data, scheduleTime);
|
|
1022
|
+
}
|
|
1023
|
+
handleChannelMessage(statusByte, data1, data2, scheduleTime) {
|
|
984
1024
|
const channelNumber = statusByte & 0x0F;
|
|
985
1025
|
const messageType = statusByte & 0xF0;
|
|
986
1026
|
switch (messageType) {
|
|
@@ -1060,28 +1100,36 @@ class MidyGM1 {
|
|
|
1060
1100
|
}
|
|
1061
1101
|
createVoiceParamsHandlers() {
|
|
1062
1102
|
return {
|
|
1063
|
-
modLfoToPitch: (channel, note,
|
|
1103
|
+
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1064
1104
|
if (0 < channel.state.modulationDepth) {
|
|
1065
1105
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1066
1106
|
}
|
|
1067
1107
|
},
|
|
1068
|
-
vibLfoToPitch: (_channel, _note,
|
|
1069
|
-
modLfoToFilterFc: (channel, note,
|
|
1108
|
+
vibLfoToPitch: (_channel, _note, _scheduleTime) => { },
|
|
1109
|
+
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1070
1110
|
if (0 < channel.state.modulationDepth) {
|
|
1071
1111
|
this.setModLfoToFilterFc(note, scheduleTime);
|
|
1072
1112
|
}
|
|
1073
1113
|
},
|
|
1074
|
-
modLfoToVolume: (channel, note,
|
|
1114
|
+
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1075
1115
|
if (0 < channel.state.modulationDepth) {
|
|
1076
1116
|
this.setModLfoToVolume(note, scheduleTime);
|
|
1077
1117
|
}
|
|
1078
1118
|
},
|
|
1079
|
-
chorusEffectsSend: (_channel, _note,
|
|
1080
|
-
reverbEffectsSend: (_channel, _note,
|
|
1081
|
-
delayModLFO: (_channel, note,
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1119
|
+
chorusEffectsSend: (_channel, _note, _scheduleTime) => { },
|
|
1120
|
+
reverbEffectsSend: (_channel, _note, _scheduleTime) => { },
|
|
1121
|
+
delayModLFO: (_channel, note, scheduleTime) => {
|
|
1122
|
+
if (0 < channel.state.modulationDepth) {
|
|
1123
|
+
this.setDelayModLFO(note, scheduleTime);
|
|
1124
|
+
}
|
|
1125
|
+
},
|
|
1126
|
+
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1127
|
+
if (0 < channel.state.modulationDepth) {
|
|
1128
|
+
this.setFreqModLFO(note, scheduleTime);
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
delayVibLFO: (_channel, _note, _scheduleTime) => { },
|
|
1132
|
+
freqVibLFO: (_channel, _note, _scheduleTime) => { },
|
|
1085
1133
|
};
|
|
1086
1134
|
}
|
|
1087
1135
|
getControllerState(channel, noteNumber, velocity) {
|
|
@@ -1104,7 +1152,7 @@ class MidyGM1 {
|
|
|
1104
1152
|
continue;
|
|
1105
1153
|
note.voiceParams[key] = value;
|
|
1106
1154
|
if (key in this.voiceParamsHandlers) {
|
|
1107
|
-
this.voiceParamsHandlers[key](channel, note,
|
|
1155
|
+
this.voiceParamsHandlers[key](channel, note, scheduleTime);
|
|
1108
1156
|
}
|
|
1109
1157
|
else {
|
|
1110
1158
|
if (volumeEnvelopeKeySet.has(key))
|
|
@@ -1275,8 +1323,8 @@ class MidyGM1 {
|
|
|
1275
1323
|
}
|
|
1276
1324
|
handlePitchBendRangeRPN(channelNumber, scheduleTime) {
|
|
1277
1325
|
const channel = this.channels[channelNumber];
|
|
1278
|
-
this.limitData(channel, 0, 127, 0,
|
|
1279
|
-
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
1326
|
+
this.limitData(channel, 0, 127, 0, 127);
|
|
1327
|
+
const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
1280
1328
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
1281
1329
|
}
|
|
1282
1330
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
@@ -1284,7 +1332,7 @@ class MidyGM1 {
|
|
|
1284
1332
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1285
1333
|
const state = channel.state;
|
|
1286
1334
|
const prev = state.pitchWheelSensitivity;
|
|
1287
|
-
const next = value /
|
|
1335
|
+
const next = value / 12800;
|
|
1288
1336
|
state.pitchWheelSensitivity = next;
|
|
1289
1337
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
1290
1338
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1293,14 +1341,15 @@ class MidyGM1 {
|
|
|
1293
1341
|
handleFineTuningRPN(channelNumber, scheduleTime) {
|
|
1294
1342
|
const channel = this.channels[channelNumber];
|
|
1295
1343
|
this.limitData(channel, 0, 127, 0, 127);
|
|
1296
|
-
const
|
|
1344
|
+
const value = channel.dataMSB * 128 + channel.dataLSB;
|
|
1345
|
+
const fineTuning = (value - 8192) / 8192 * 100;
|
|
1297
1346
|
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
1298
1347
|
}
|
|
1299
1348
|
setFineTuning(channelNumber, value, scheduleTime) {
|
|
1300
1349
|
const channel = this.channels[channelNumber];
|
|
1301
1350
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1302
1351
|
const prev = channel.fineTuning;
|
|
1303
|
-
const next =
|
|
1352
|
+
const next = value;
|
|
1304
1353
|
channel.fineTuning = next;
|
|
1305
1354
|
channel.detune += next - prev;
|
|
1306
1355
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1308,14 +1357,14 @@ class MidyGM1 {
|
|
|
1308
1357
|
handleCoarseTuningRPN(channelNumber, scheduleTime) {
|
|
1309
1358
|
const channel = this.channels[channelNumber];
|
|
1310
1359
|
this.limitDataMSB(channel, 0, 127);
|
|
1311
|
-
const coarseTuning = channel.dataMSB;
|
|
1360
|
+
const coarseTuning = (channel.dataMSB - 64) * 100;
|
|
1312
1361
|
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
1313
1362
|
}
|
|
1314
1363
|
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
1315
1364
|
const channel = this.channels[channelNumber];
|
|
1316
1365
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1317
1366
|
const prev = channel.coarseTuning;
|
|
1318
|
-
const next =
|
|
1367
|
+
const next = value;
|
|
1319
1368
|
channel.coarseTuning = next;
|
|
1320
1369
|
channel.detune += next - prev;
|
|
1321
1370
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -1324,7 +1373,7 @@ class MidyGM1 {
|
|
|
1324
1373
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1325
1374
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
1326
1375
|
}
|
|
1327
|
-
|
|
1376
|
+
resetChannelStates(channelNumber) {
|
|
1328
1377
|
const scheduleTime = this.audioContext.currentTime;
|
|
1329
1378
|
const channel = this.channels[channelNumber];
|
|
1330
1379
|
const state = channel.state;
|
|
@@ -1399,10 +1448,8 @@ class MidyGM1 {
|
|
|
1399
1448
|
for (let i = 0; i < this.channels.length; i++) {
|
|
1400
1449
|
this.allSoundOff(i, 0, scheduleTime);
|
|
1401
1450
|
const channel = this.channels[i];
|
|
1402
|
-
channel.bank = 0;
|
|
1403
1451
|
channel.isDrum = false;
|
|
1404
1452
|
}
|
|
1405
|
-
this.channels[9].bank = 128;
|
|
1406
1453
|
this.channels[9].isDrum = true;
|
|
1407
1454
|
}
|
|
1408
1455
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
@@ -1423,16 +1470,11 @@ class MidyGM1 {
|
|
|
1423
1470
|
const volume = (data[5] * 128 + data[4]) / 16383;
|
|
1424
1471
|
this.setMasterVolume(volume, scheduleTime);
|
|
1425
1472
|
}
|
|
1426
|
-
setMasterVolume(
|
|
1473
|
+
setMasterVolume(value, scheduleTime) {
|
|
1427
1474
|
scheduleTime ??= this.audioContext.currentTime;
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
else {
|
|
1432
|
-
this.masterVolume.gain
|
|
1433
|
-
.cancelScheduledValues(scheduleTime)
|
|
1434
|
-
.setValueAtTime(volume * volume, scheduleTime);
|
|
1435
|
-
}
|
|
1475
|
+
this.masterVolume.gain
|
|
1476
|
+
.cancelScheduledValues(scheduleTime)
|
|
1477
|
+
.setValueAtTime(value * value, scheduleTime);
|
|
1436
1478
|
}
|
|
1437
1479
|
handleSysEx(data, scheduleTime) {
|
|
1438
1480
|
switch (data[0]) {
|
|
@@ -1470,15 +1512,15 @@ Object.defineProperty(MidyGM1, "channelSettings", {
|
|
|
1470
1512
|
configurable: true,
|
|
1471
1513
|
writable: true,
|
|
1472
1514
|
value: {
|
|
1515
|
+
scheduleIndex: 0,
|
|
1473
1516
|
detune: 0,
|
|
1474
1517
|
programNumber: 0,
|
|
1475
|
-
bank: 0,
|
|
1476
1518
|
dataMSB: 0,
|
|
1477
1519
|
dataLSB: 0,
|
|
1478
1520
|
rpnMSB: 127,
|
|
1479
1521
|
rpnLSB: 127,
|
|
1480
1522
|
modulationDepthRange: 50, // cent
|
|
1481
|
-
fineTuning: 0, //
|
|
1482
|
-
coarseTuning: 0, //
|
|
1523
|
+
fineTuning: 0, // cent
|
|
1524
|
+
coarseTuning: 0, // cent
|
|
1483
1525
|
}
|
|
1484
1526
|
});
|