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