@marmooo/midy 0.3.7 → 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 +31 -11
- package/esm/midy-GM1.d.ts +9 -6
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +67 -57
- package/esm/midy-GM2.d.ts +13 -7
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +147 -154
- package/esm/midy-GMLite.d.ts +8 -6
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +68 -59
- package/esm/midy.d.ts +13 -7
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +208 -226
- package/package.json +2 -2
- package/script/midy-GM1.d.ts +9 -6
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +67 -57
- package/script/midy-GM2.d.ts +13 -7
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +147 -154
- package/script/midy-GMLite.d.ts +8 -6
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +68 -59
- package/script/midy.d.ts +13 -7
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +208 -226
package/script/midy.js
CHANGED
|
@@ -290,6 +290,18 @@ class Midy {
|
|
|
290
290
|
writable: true,
|
|
291
291
|
value: 0
|
|
292
292
|
});
|
|
293
|
+
Object.defineProperty(this, "lastActiveSensing", {
|
|
294
|
+
enumerable: true,
|
|
295
|
+
configurable: true,
|
|
296
|
+
writable: true,
|
|
297
|
+
value: 0
|
|
298
|
+
});
|
|
299
|
+
Object.defineProperty(this, "activeSensingThreshold", {
|
|
300
|
+
enumerable: true,
|
|
301
|
+
configurable: true,
|
|
302
|
+
writable: true,
|
|
303
|
+
value: 0.3
|
|
304
|
+
});
|
|
293
305
|
Object.defineProperty(this, "noteCheckInterval", {
|
|
294
306
|
enumerable: true,
|
|
295
307
|
configurable: true,
|
|
@@ -330,7 +342,7 @@ class Midy {
|
|
|
330
342
|
enumerable: true,
|
|
331
343
|
configurable: true,
|
|
332
344
|
writable: true,
|
|
333
|
-
value:
|
|
345
|
+
value: Array.from({ length: 128 }, () => [])
|
|
334
346
|
});
|
|
335
347
|
Object.defineProperty(this, "voiceCounter", {
|
|
336
348
|
enumerable: true,
|
|
@@ -417,8 +429,10 @@ class Midy {
|
|
|
417
429
|
length: 1,
|
|
418
430
|
sampleRate: audioContext.sampleRate,
|
|
419
431
|
});
|
|
432
|
+
this.messageHandlers = this.createMessageHandlers();
|
|
420
433
|
this.voiceParamsHandlers = this.createVoiceParamsHandlers();
|
|
421
434
|
this.controlChangeHandlers = this.createControlChangeHandlers();
|
|
435
|
+
this.keyBasedControllerHandlers = this.createKeyBasedControllerHandlers();
|
|
422
436
|
this.channels = this.createChannels(audioContext);
|
|
423
437
|
this.reverbEffect = this.createReverbEffect(audioContext);
|
|
424
438
|
this.chorusEffect = this.createChorusEffect(audioContext);
|
|
@@ -428,21 +442,14 @@ class Midy {
|
|
|
428
442
|
this.scheduler.connect(audioContext.destination);
|
|
429
443
|
this.GM2SystemOn();
|
|
430
444
|
}
|
|
431
|
-
initSoundFontTable() {
|
|
432
|
-
const table = new Array(128);
|
|
433
|
-
for (let i = 0; i < 128; i++) {
|
|
434
|
-
table[i] = new Map();
|
|
435
|
-
}
|
|
436
|
-
return table;
|
|
437
|
-
}
|
|
438
445
|
addSoundFont(soundFont) {
|
|
439
446
|
const index = this.soundFonts.length;
|
|
440
447
|
this.soundFonts.push(soundFont);
|
|
441
448
|
const presetHeaders = soundFont.parsed.presetHeaders;
|
|
449
|
+
const soundFontTable = this.soundFontTable;
|
|
442
450
|
for (let i = 0; i < presetHeaders.length; i++) {
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
banks.set(presetHeader.bank, index);
|
|
451
|
+
const { preset, bank } = presetHeaders[i];
|
|
452
|
+
soundFontTable[preset][bank] = index;
|
|
446
453
|
}
|
|
447
454
|
}
|
|
448
455
|
async toUint8Array(input) {
|
|
@@ -520,13 +527,17 @@ class Midy {
|
|
|
520
527
|
this.GM2SystemOn();
|
|
521
528
|
}
|
|
522
529
|
getVoiceId(channel, noteNumber, velocity) {
|
|
523
|
-
const
|
|
524
|
-
const
|
|
525
|
-
|
|
530
|
+
const programNumber = channel.programNumber;
|
|
531
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
532
|
+
if (!bankTable)
|
|
533
|
+
return;
|
|
534
|
+
const bankLSB = channel.isDrum ? 128 : channel.bankLSB;
|
|
535
|
+
const bank = bankTable[bankLSB] !== undefined ? bankLSB : 0;
|
|
536
|
+
const soundFontIndex = bankTable[bank];
|
|
526
537
|
if (soundFontIndex === undefined)
|
|
527
538
|
return;
|
|
528
539
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
529
|
-
const voice = soundFont.getVoice(
|
|
540
|
+
const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
530
541
|
const { instrument, sampleID } = voice.generators;
|
|
531
542
|
return soundFontIndex * (2 ** 32) + (instrument << 16) + sampleID;
|
|
532
543
|
}
|
|
@@ -597,13 +608,16 @@ class Midy {
|
|
|
597
608
|
}
|
|
598
609
|
return bufferSource;
|
|
599
610
|
}
|
|
600
|
-
async scheduleTimelineEvents(
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
611
|
+
async scheduleTimelineEvents(scheduleTime, queueIndex) {
|
|
612
|
+
const timeOffset = this.resumeTime - this.startTime;
|
|
613
|
+
const lookAheadCheckTime = scheduleTime + timeOffset + this.lookAhead;
|
|
614
|
+
const schedulingOffset = this.startDelay - timeOffset;
|
|
615
|
+
const timeline = this.timeline;
|
|
616
|
+
while (queueIndex < timeline.length) {
|
|
617
|
+
const event = timeline[queueIndex];
|
|
618
|
+
if (lookAheadCheckTime < event.startTime)
|
|
604
619
|
break;
|
|
605
|
-
const
|
|
606
|
-
const startTime = event.startTime + delay;
|
|
620
|
+
const startTime = event.startTime + schedulingOffset;
|
|
607
621
|
switch (event.type) {
|
|
608
622
|
case "noteOn":
|
|
609
623
|
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime);
|
|
@@ -681,13 +695,18 @@ class Midy {
|
|
|
681
695
|
this.isPaused = false;
|
|
682
696
|
this.startTime = this.audioContext.currentTime;
|
|
683
697
|
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
684
|
-
let resumeTime = this.resumeTime - this.startTime;
|
|
685
698
|
let finished = false;
|
|
686
699
|
this.notePromises = [];
|
|
687
700
|
while (queueIndex < this.timeline.length) {
|
|
688
701
|
const now = this.audioContext.currentTime;
|
|
689
|
-
|
|
690
|
-
|
|
702
|
+
if (0 < this.lastActiveSensing &&
|
|
703
|
+
this.activeSensingThreshold < performance.now() - this.lastActiveSensing) {
|
|
704
|
+
await this.stopNotes(0, true, now);
|
|
705
|
+
await this.audioContext.suspend();
|
|
706
|
+
finished = true;
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
queueIndex = await this.scheduleTimelineEvents(now, queueIndex);
|
|
691
710
|
if (this.isPausing) {
|
|
692
711
|
await this.stopNotes(0, true, now);
|
|
693
712
|
await this.audioContext.suspend();
|
|
@@ -706,7 +725,6 @@ class Midy {
|
|
|
706
725
|
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
707
726
|
this.updateStates(queueIndex, nextQueueIndex);
|
|
708
727
|
queueIndex = nextQueueIndex;
|
|
709
|
-
resumeTime = this.resumeTime - this.startTime;
|
|
710
728
|
this.isSeeking = false;
|
|
711
729
|
continue;
|
|
712
730
|
}
|
|
@@ -716,6 +734,7 @@ class Midy {
|
|
|
716
734
|
if (finished) {
|
|
717
735
|
this.notePromises = [];
|
|
718
736
|
this.resetAllStates();
|
|
737
|
+
this.lastActiveSensing = 0;
|
|
719
738
|
}
|
|
720
739
|
this.isPlaying = false;
|
|
721
740
|
}
|
|
@@ -725,17 +744,17 @@ class Midy {
|
|
|
725
744
|
secondToTicks(second, secondsPerBeat) {
|
|
726
745
|
return second * this.ticksPerBeat / secondsPerBeat;
|
|
727
746
|
}
|
|
747
|
+
getSoundFontId(channel) {
|
|
748
|
+
const programNumber = channel.programNumber;
|
|
749
|
+
const bankNumber = channel.isDrum ? 128 : channel.bankLSB;
|
|
750
|
+
const bank = bankNumber.toString().padStart(3, "0");
|
|
751
|
+
const program = programNumber.toString().padStart(3, "0");
|
|
752
|
+
return `${bank}:${program}`;
|
|
753
|
+
}
|
|
728
754
|
extractMidiData(midi) {
|
|
729
755
|
const instruments = new Set();
|
|
730
756
|
const timeline = [];
|
|
731
|
-
const
|
|
732
|
-
for (let i = 0; i < tmpChannels.length; i++) {
|
|
733
|
-
tmpChannels[i] = {
|
|
734
|
-
programNumber: -1,
|
|
735
|
-
bankMSB: this.channels[i].bankMSB,
|
|
736
|
-
bankLSB: this.channels[i].bankLSB,
|
|
737
|
-
};
|
|
738
|
-
}
|
|
757
|
+
const channels = this.channels;
|
|
739
758
|
for (let i = 0; i < midi.tracks.length; i++) {
|
|
740
759
|
const track = midi.tracks[i];
|
|
741
760
|
let currentTicks = 0;
|
|
@@ -745,48 +764,40 @@ class Midy {
|
|
|
745
764
|
event.ticks = currentTicks;
|
|
746
765
|
switch (event.type) {
|
|
747
766
|
case "noteOn": {
|
|
748
|
-
const channel =
|
|
749
|
-
|
|
750
|
-
channel.programNumber = event.programNumber;
|
|
751
|
-
switch (channel.bankMSB) {
|
|
752
|
-
case 120:
|
|
753
|
-
instruments.add(`128:0`);
|
|
754
|
-
break;
|
|
755
|
-
case 121:
|
|
756
|
-
instruments.add(`${channel.bankLSB}:0`);
|
|
757
|
-
break;
|
|
758
|
-
default: {
|
|
759
|
-
const bankNumber = channel.bankMSB * 128 + channel.bankLSB;
|
|
760
|
-
instruments.add(`${bankNumber}:0`);
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
channel.programNumber = 0;
|
|
764
|
-
}
|
|
767
|
+
const channel = channels[event.channel];
|
|
768
|
+
instruments.add(this.getSoundFontId(channel));
|
|
765
769
|
break;
|
|
766
770
|
}
|
|
767
771
|
case "controller":
|
|
768
772
|
switch (event.controllerType) {
|
|
769
773
|
case 0:
|
|
770
|
-
|
|
774
|
+
this.setBankMSB(event.channel, event.value);
|
|
771
775
|
break;
|
|
772
776
|
case 32:
|
|
773
|
-
|
|
777
|
+
this.setBankLSB(event.channel, event.value);
|
|
774
778
|
break;
|
|
775
779
|
}
|
|
776
780
|
break;
|
|
777
781
|
case "programChange": {
|
|
778
|
-
const channel =
|
|
779
|
-
channel
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
782
|
+
const channel = channels[event.channel];
|
|
783
|
+
this.setProgramChange(event.channel, event.programNumber);
|
|
784
|
+
instruments.add(this.getSoundFontId(channel));
|
|
785
|
+
break;
|
|
786
|
+
}
|
|
787
|
+
case "sysEx": {
|
|
788
|
+
const data = event.data;
|
|
789
|
+
if (data[0] === 126 && data[1] === 9 && data[2] === 3) {
|
|
790
|
+
switch (data[3]) {
|
|
791
|
+
case 1:
|
|
792
|
+
this.GM1SystemOn(scheduleTime);
|
|
793
|
+
break;
|
|
794
|
+
case 2: // GM System Off
|
|
795
|
+
break;
|
|
796
|
+
case 3:
|
|
797
|
+
this.GM2SystemOn(scheduleTime);
|
|
798
|
+
break;
|
|
799
|
+
default:
|
|
800
|
+
console.warn(`Unsupported Exclusive Message: ${data}`);
|
|
790
801
|
}
|
|
791
802
|
}
|
|
792
803
|
}
|
|
@@ -1110,7 +1121,7 @@ class Midy {
|
|
|
1110
1121
|
const noteDetune = this.calcNoteDetune(channel, note);
|
|
1111
1122
|
const pitchControl = this.getPitchControl(channel, note);
|
|
1112
1123
|
const detune = channel.detune + noteDetune + pitchControl;
|
|
1113
|
-
if (
|
|
1124
|
+
if (this.isPortamento(channel, note)) {
|
|
1114
1125
|
const startTime = note.startTime;
|
|
1115
1126
|
const deltaCent = (note.noteNumber - note.portamentoNoteNumber) * 100;
|
|
1116
1127
|
const portamentoTime = startTime + this.getPortamentoTime(channel, note);
|
|
@@ -1366,7 +1377,7 @@ class Midy {
|
|
|
1366
1377
|
if (prevNote && prevNote.noteNumber !== noteNumber) {
|
|
1367
1378
|
note.portamentoNoteNumber = prevNote.noteNumber;
|
|
1368
1379
|
}
|
|
1369
|
-
if (
|
|
1380
|
+
if (!channel.isDrum && this.isPortamento(channel, note)) {
|
|
1370
1381
|
this.setPortamentoVolumeEnvelope(channel, note, now);
|
|
1371
1382
|
this.setPortamentoFilterEnvelope(channel, note, now);
|
|
1372
1383
|
this.setPortamentoPitchEnvelope(note, now);
|
|
@@ -1394,22 +1405,6 @@ class Midy {
|
|
|
1394
1405
|
note.bufferSource.start(startTime);
|
|
1395
1406
|
return note;
|
|
1396
1407
|
}
|
|
1397
|
-
calcBank(channel) {
|
|
1398
|
-
switch (this.mode) {
|
|
1399
|
-
case "GM1":
|
|
1400
|
-
if (channel.isDrum)
|
|
1401
|
-
return 128;
|
|
1402
|
-
return 0;
|
|
1403
|
-
case "GM2":
|
|
1404
|
-
if (channel.bankMSB === 121)
|
|
1405
|
-
return 0;
|
|
1406
|
-
if (channel.isDrum)
|
|
1407
|
-
return 128;
|
|
1408
|
-
return channel.bank;
|
|
1409
|
-
default:
|
|
1410
|
-
return channel.bank;
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
1408
|
handleExclusiveClass(note, channelNumber, startTime) {
|
|
1414
1409
|
const exclusiveClass = note.voiceParams.exclusiveClass;
|
|
1415
1410
|
if (exclusiveClass === 0)
|
|
@@ -1445,13 +1440,17 @@ class Midy {
|
|
|
1445
1440
|
}
|
|
1446
1441
|
async scheduleNoteOn(channelNumber, noteNumber, velocity, startTime) {
|
|
1447
1442
|
const channel = this.channels[channelNumber];
|
|
1448
|
-
const
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1443
|
+
const programNumber = channel.programNumber;
|
|
1444
|
+
const bankTable = this.soundFontTable[programNumber];
|
|
1445
|
+
if (!bankTable)
|
|
1446
|
+
return;
|
|
1447
|
+
const bankLSB = channel.isDrum ? 128 : channel.bankLSB;
|
|
1448
|
+
const bank = bankTable[bankLSB] !== undefined ? bankLSB : 0;
|
|
1449
|
+
const soundFontIndex = bankTable[bank];
|
|
1451
1450
|
if (soundFontIndex === undefined)
|
|
1452
1451
|
return;
|
|
1453
1452
|
const soundFont = this.soundFonts[soundFontIndex];
|
|
1454
|
-
const voice = soundFont.getVoice(
|
|
1453
|
+
const voice = soundFont.getVoice(bank, programNumber, noteNumber, velocity);
|
|
1455
1454
|
if (!voice)
|
|
1456
1455
|
return;
|
|
1457
1456
|
const note = await this.createNote(channel, voice, noteNumber, velocity, startTime);
|
|
@@ -1604,27 +1603,42 @@ class Midy {
|
|
|
1604
1603
|
channel.sostenutoNotes = [];
|
|
1605
1604
|
return promises;
|
|
1606
1605
|
}
|
|
1607
|
-
|
|
1608
|
-
const
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1606
|
+
createMessageHandlers() {
|
|
1607
|
+
const handlers = new Array(256);
|
|
1608
|
+
// Channel Message
|
|
1609
|
+
handlers[0x80] = (data, scheduleTime) => this.noteOff(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1610
|
+
handlers[0x90] = (data, scheduleTime) => this.noteOn(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1611
|
+
handlers[0xA0] = (data, scheduleTime) => this.setPolyphonicKeyPressure(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1612
|
+
handlers[0xB0] = (data, scheduleTime) => this.setControlChange(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1613
|
+
handlers[0xC0] = (data, scheduleTime) => this.setProgramChange(data[0] & 0x0F, data[1], scheduleTime);
|
|
1614
|
+
handlers[0xD0] = (data, scheduleTime) => this.setChannelPressure(data[0] & 0x0F, data[1], scheduleTime);
|
|
1615
|
+
handlers[0xE0] = (data, scheduleTime) => this.handlePitchBendMessage(data[0] & 0x0F, data[1], data[2], scheduleTime);
|
|
1616
|
+
// System Common Message
|
|
1617
|
+
// handlers[0xF1] = (_data, _scheduleTime) => {}; // MTC Quarter Frame
|
|
1618
|
+
// handlers[0xF2] = (_data, _scheduleTime) => {}; // Song Position Pointer
|
|
1619
|
+
// handlers[0xF3] = (_data, _scheduleTime) => {}; // Song Select
|
|
1620
|
+
// handlers[0xF6] = (_data, _scheduleTime) => {}; // Tune Request
|
|
1621
|
+
// handlers[0xF7] = (_data, _scheduleTime) => {}; // End of Exclusive (EOX)
|
|
1622
|
+
// System Real Time Message
|
|
1623
|
+
// handlers[0xF8] = (_data, _scheduleTime) => {}; // Timing Clock
|
|
1624
|
+
// handlers[0xFA] = (_data, _scheduleTime) => {}; // Start
|
|
1625
|
+
// handlers[0xFB] = (_data, _scheduleTime) => {}; // Continue
|
|
1626
|
+
// handlers[0xFC] = (_data, _scheduleTime) => {}; // Stop
|
|
1627
|
+
handlers[0xFE] = (_data, _scheduleTime) => this.activeSensing();
|
|
1628
|
+
// handlers[0xFF] = (_data, _scheduleTime) => {}; // Reset
|
|
1629
|
+
return handlers;
|
|
1630
|
+
}
|
|
1631
|
+
handleMessage(data, scheduleTime) {
|
|
1632
|
+
const status = data[0];
|
|
1633
|
+
if (status === 0xF0) {
|
|
1634
|
+
return this.handleSysEx(data.subarray(1), scheduleTime);
|
|
1627
1635
|
}
|
|
1636
|
+
const handler = this.messageHandlers[status];
|
|
1637
|
+
if (handler)
|
|
1638
|
+
handler(data, scheduleTime);
|
|
1639
|
+
}
|
|
1640
|
+
activeSensing() {
|
|
1641
|
+
this.lastActiveSensing = performance.now();
|
|
1628
1642
|
}
|
|
1629
1643
|
setPolyphonicKeyPressure(channelNumber, noteNumber, pressure, scheduleTime) {
|
|
1630
1644
|
const channel = this.channels[channelNumber];
|
|
@@ -1639,19 +1653,18 @@ class Midy {
|
|
|
1639
1653
|
}
|
|
1640
1654
|
setProgramChange(channelNumber, programNumber, _scheduleTime) {
|
|
1641
1655
|
const channel = this.channels[channelNumber];
|
|
1642
|
-
channel.bank = channel.bankMSB * 128 + channel.bankLSB;
|
|
1643
1656
|
channel.programNumber = programNumber;
|
|
1644
1657
|
if (this.mode === "GM2") {
|
|
1645
1658
|
switch (channel.bankMSB) {
|
|
1646
1659
|
case 120:
|
|
1647
1660
|
channel.isDrum = true;
|
|
1661
|
+
channel.keyBasedTable.fill(-1);
|
|
1648
1662
|
break;
|
|
1649
1663
|
case 121:
|
|
1650
1664
|
channel.isDrum = false;
|
|
1651
1665
|
break;
|
|
1652
1666
|
}
|
|
1653
1667
|
}
|
|
1654
|
-
channel.keyBasedTable.fill(-1);
|
|
1655
1668
|
}
|
|
1656
1669
|
setChannelPressure(channelNumber, value, scheduleTime) {
|
|
1657
1670
|
const channel = this.channels[channelNumber];
|
|
@@ -1988,22 +2001,20 @@ class Midy {
|
|
|
1988
2001
|
this.updateModulation(channel, scheduleTime);
|
|
1989
2002
|
}
|
|
1990
2003
|
updatePortamento(channel, scheduleTime) {
|
|
2004
|
+
if (channel.isDrum)
|
|
2005
|
+
return;
|
|
1991
2006
|
this.processScheduledNotes(channel, (note) => {
|
|
1992
|
-
if (
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
this.updateDetune(channel, note, scheduleTime);
|
|
1998
|
-
}
|
|
2007
|
+
if (this.isPortamento(channel, note)) {
|
|
2008
|
+
this.setPortamentoVolumeEnvelope(channel, note, scheduleTime);
|
|
2009
|
+
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
2010
|
+
this.setPortamentoPitchEnvelope(note, scheduleTime);
|
|
2011
|
+
this.updateDetune(channel, note, scheduleTime);
|
|
1999
2012
|
}
|
|
2000
2013
|
else {
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
this.updateDetune(channel, note, scheduleTime);
|
|
2006
|
-
}
|
|
2014
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
2015
|
+
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
2016
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
2017
|
+
this.updateDetune(channel, note, scheduleTime);
|
|
2007
2018
|
}
|
|
2008
2019
|
});
|
|
2009
2020
|
}
|
|
@@ -2109,6 +2120,9 @@ class Midy {
|
|
|
2109
2120
|
this.releaseSustainPedal(channelNumber, value, scheduleTime);
|
|
2110
2121
|
}
|
|
2111
2122
|
}
|
|
2123
|
+
isPortamento(channel, note) {
|
|
2124
|
+
return 0.5 <= channel.state.portamento && 0 <= note.portamentoNoteNumber;
|
|
2125
|
+
}
|
|
2112
2126
|
setPortamento(channelNumber, value, scheduleTime) {
|
|
2113
2127
|
const channel = this.channels[channelNumber];
|
|
2114
2128
|
if (channel.isDrum)
|
|
@@ -2145,7 +2159,7 @@ class Midy {
|
|
|
2145
2159
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2146
2160
|
state.softPedal = softPedal / 127;
|
|
2147
2161
|
this.processScheduledNotes(channel, (note) => {
|
|
2148
|
-
if (
|
|
2162
|
+
if (this.isPortamento(channel, note)) {
|
|
2149
2163
|
this.setPortamentoVolumeEnvelope(channel, note, scheduleTime);
|
|
2150
2164
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
2151
2165
|
}
|
|
@@ -2203,7 +2217,7 @@ class Midy {
|
|
|
2203
2217
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2204
2218
|
state.brightness = brightness / 127;
|
|
2205
2219
|
this.processScheduledNotes(channel, (note) => {
|
|
2206
|
-
if (
|
|
2220
|
+
if (this.isPortamento(channel, note)) {
|
|
2207
2221
|
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
2208
2222
|
}
|
|
2209
2223
|
else {
|
|
@@ -2539,11 +2553,9 @@ class Midy {
|
|
|
2539
2553
|
const channel = this.channels[i];
|
|
2540
2554
|
channel.bankMSB = 0;
|
|
2541
2555
|
channel.bankLSB = 0;
|
|
2542
|
-
channel.bank = 0;
|
|
2543
2556
|
channel.isDrum = false;
|
|
2544
2557
|
}
|
|
2545
2558
|
this.channels[9].bankMSB = 1;
|
|
2546
|
-
this.channels[9].bank = 128;
|
|
2547
2559
|
this.channels[9].isDrum = true;
|
|
2548
2560
|
}
|
|
2549
2561
|
GM2SystemOn(scheduleTime) {
|
|
@@ -2554,11 +2566,9 @@ class Midy {
|
|
|
2554
2566
|
const channel = this.channels[i];
|
|
2555
2567
|
channel.bankMSB = 121;
|
|
2556
2568
|
channel.bankLSB = 0;
|
|
2557
|
-
channel.bank = 121 * 128;
|
|
2558
2569
|
channel.isDrum = false;
|
|
2559
2570
|
}
|
|
2560
2571
|
this.channels[9].bankMSB = 120;
|
|
2561
|
-
this.channels[9].bank = 120 * 128;
|
|
2562
2572
|
this.channels[9].isDrum = true;
|
|
2563
2573
|
}
|
|
2564
2574
|
handleUniversalRealTimeExclusiveMessage(data, scheduleTime) {
|
|
@@ -2616,16 +2626,11 @@ class Midy {
|
|
|
2616
2626
|
const volume = (data[5] * 128 + data[4]) / 16383;
|
|
2617
2627
|
this.setMasterVolume(volume, scheduleTime);
|
|
2618
2628
|
}
|
|
2619
|
-
setMasterVolume(
|
|
2629
|
+
setMasterVolume(value, scheduleTime) {
|
|
2620
2630
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
else {
|
|
2625
|
-
this.masterVolume.gain
|
|
2626
|
-
.cancelScheduledValues(scheduleTime)
|
|
2627
|
-
.setValueAtTime(volume * volume, scheduleTime);
|
|
2628
|
-
}
|
|
2631
|
+
this.masterVolume.gain
|
|
2632
|
+
.cancelScheduledValues(scheduleTime)
|
|
2633
|
+
.setValueAtTime(value * value, scheduleTime);
|
|
2629
2634
|
}
|
|
2630
2635
|
handleMasterFineTuningSysEx(data, scheduleTime) {
|
|
2631
2636
|
const value = (data[5] * 128 + data[4]) / 16383;
|
|
@@ -3040,6 +3045,70 @@ class Midy {
|
|
|
3040
3045
|
const controlValue = channel.keyBasedTable[index];
|
|
3041
3046
|
return controlValue;
|
|
3042
3047
|
}
|
|
3048
|
+
createKeyBasedControllerHandlers() {
|
|
3049
|
+
const handlers = new Array(128);
|
|
3050
|
+
handlers[7] = (channel, keyNumber, scheduleTime) => this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
|
|
3051
|
+
handlers[10] = (channel, keyNumber, scheduleTime) => this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
|
|
3052
|
+
handlers[71] = (channel, keyNumber, scheduleTime) => this.processScheduledNotes(channel, (note) => {
|
|
3053
|
+
if (note.noteNumber === keyNumber) {
|
|
3054
|
+
const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
|
|
3055
|
+
const Q = note.voiceParams.initialFilterQ / 5 * filterResonance;
|
|
3056
|
+
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
3057
|
+
}
|
|
3058
|
+
});
|
|
3059
|
+
handlers[73] = (channel, keyNumber, scheduleTime) => this.processScheduledNotes(channel, (note) => {
|
|
3060
|
+
if (note.noteNumber === keyNumber) {
|
|
3061
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
3062
|
+
}
|
|
3063
|
+
});
|
|
3064
|
+
handlers[74] = (channel, keyNumber, scheduleTime) => this.processScheduledNotes(channel, (note) => {
|
|
3065
|
+
if (note.noteNumber === keyNumber) {
|
|
3066
|
+
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
3067
|
+
}
|
|
3068
|
+
});
|
|
3069
|
+
handlers[75] = (channel, keyNumber, scheduleTime) => this.processScheduledNotes(channel, (note) => {
|
|
3070
|
+
if (note.noteNumber === keyNumber) {
|
|
3071
|
+
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
3072
|
+
}
|
|
3073
|
+
});
|
|
3074
|
+
handlers[76] = (channel, keyNumber, scheduleTime) => {
|
|
3075
|
+
if (channel.state.vibratoDepth <= 0)
|
|
3076
|
+
return;
|
|
3077
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3078
|
+
if (note.noteNumber === keyNumber) {
|
|
3079
|
+
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
3080
|
+
}
|
|
3081
|
+
});
|
|
3082
|
+
};
|
|
3083
|
+
handlers[77] = (channel, keyNumber, scheduleTime) => {
|
|
3084
|
+
if (channel.state.vibratoDepth <= 0)
|
|
3085
|
+
return;
|
|
3086
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3087
|
+
if (note.noteNumber === keyNumber) {
|
|
3088
|
+
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
3089
|
+
}
|
|
3090
|
+
});
|
|
3091
|
+
};
|
|
3092
|
+
handlers[78] = (channel, keyNumber) => {
|
|
3093
|
+
if (channel.state.vibratoDepth <= 0)
|
|
3094
|
+
return;
|
|
3095
|
+
this.processScheduledNotes(channel, (note) => {
|
|
3096
|
+
if (note.noteNumber === keyNumber)
|
|
3097
|
+
this.setDelayVibLFO(channel, note);
|
|
3098
|
+
});
|
|
3099
|
+
};
|
|
3100
|
+
handlers[91] = (channel, keyNumber, scheduleTime) => this.processScheduledNotes(channel, (note) => {
|
|
3101
|
+
if (note.noteNumber === keyNumber) {
|
|
3102
|
+
this.setReverbSend(channel, note, scheduleTime);
|
|
3103
|
+
}
|
|
3104
|
+
});
|
|
3105
|
+
handlers[93] = (channel, keyNumber, scheduleTime) => this.processScheduledNotes(channel, (note) => {
|
|
3106
|
+
if (note.noteNumber === keyNumber) {
|
|
3107
|
+
this.setChorusSend(channel, note, scheduleTime);
|
|
3108
|
+
}
|
|
3109
|
+
});
|
|
3110
|
+
return handlers;
|
|
3111
|
+
}
|
|
3043
3112
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
3044
3113
|
const channelNumber = data[4];
|
|
3045
3114
|
const channel = this.channels[channelNumber];
|
|
@@ -3052,95 +3121,9 @@ class Midy {
|
|
|
3052
3121
|
const value = data[i + 1];
|
|
3053
3122
|
const index = keyNumber * 128 + controllerType;
|
|
3054
3123
|
table[index] = value;
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
|
|
3059
|
-
break;
|
|
3060
|
-
case 71:
|
|
3061
|
-
this.processScheduledNotes(channel, (note) => {
|
|
3062
|
-
const filterResonance = this.getRelativeKeyBasedValue(channel, note, 71);
|
|
3063
|
-
if (note.noteNumber === keyNumber) {
|
|
3064
|
-
const Q = note.voiceParams.initialFilterQ / 5 * filterResonance;
|
|
3065
|
-
note.filterNode.Q.setValueAtTime(Q, scheduleTime);
|
|
3066
|
-
}
|
|
3067
|
-
});
|
|
3068
|
-
break;
|
|
3069
|
-
case 73:
|
|
3070
|
-
this.processScheduledNotes(channel, (note) => {
|
|
3071
|
-
if (note.noteNumber === keyNumber) {
|
|
3072
|
-
if (0.5 <= channel.state.portamento &&
|
|
3073
|
-
0 <= note.portamentoNoteNumber) {
|
|
3074
|
-
this.setPortamentoVolumeEnvelope(channel, note, scheduleTime);
|
|
3075
|
-
}
|
|
3076
|
-
else {
|
|
3077
|
-
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
3078
|
-
}
|
|
3079
|
-
}
|
|
3080
|
-
});
|
|
3081
|
-
break;
|
|
3082
|
-
case 74:
|
|
3083
|
-
this.processScheduledNotes(channel, (note) => {
|
|
3084
|
-
if (note.noteNumber === keyNumber) {
|
|
3085
|
-
if (0.5 <= channel.state.portamento &&
|
|
3086
|
-
0 <= note.portamentoNoteNumber) {
|
|
3087
|
-
this.setPortamentoFilterEnvelope(channel, note, scheduleTime);
|
|
3088
|
-
}
|
|
3089
|
-
else {
|
|
3090
|
-
this.setFilterEnvelope(channel, note, scheduleTime);
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
});
|
|
3094
|
-
break;
|
|
3095
|
-
case 75:
|
|
3096
|
-
this.processScheduledNotes(channel, (note) => {
|
|
3097
|
-
if (note.noteNumber === keyNumber) {
|
|
3098
|
-
this.setVolumeEnvelope(channel, note, scheduleTime);
|
|
3099
|
-
}
|
|
3100
|
-
});
|
|
3101
|
-
break;
|
|
3102
|
-
case 76:
|
|
3103
|
-
if (channel.state.vibratoDepth <= 0)
|
|
3104
|
-
break;
|
|
3105
|
-
this.processScheduledNotes(channel, (note) => {
|
|
3106
|
-
if (note.noteNumber === keyNumber) {
|
|
3107
|
-
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
3108
|
-
}
|
|
3109
|
-
});
|
|
3110
|
-
break;
|
|
3111
|
-
case 77:
|
|
3112
|
-
if (channel.state.vibratoDepth <= 0)
|
|
3113
|
-
break;
|
|
3114
|
-
this.processScheduledNotes(channel, (note) => {
|
|
3115
|
-
if (note.noteNumber === keyNumber) {
|
|
3116
|
-
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
3117
|
-
}
|
|
3118
|
-
});
|
|
3119
|
-
break;
|
|
3120
|
-
case 78:
|
|
3121
|
-
if (channel.state.vibratoDepth <= 0)
|
|
3122
|
-
break;
|
|
3123
|
-
this.processScheduledNotes(channel, (note) => {
|
|
3124
|
-
if (note.noteNumber === keyNumber) {
|
|
3125
|
-
this.setDelayVibLFO(channel, note);
|
|
3126
|
-
}
|
|
3127
|
-
});
|
|
3128
|
-
break;
|
|
3129
|
-
case 91:
|
|
3130
|
-
this.processScheduledNotes(channel, (note) => {
|
|
3131
|
-
if (note.noteNumber === keyNumber) {
|
|
3132
|
-
this.setReverbSend(channel, note, scheduleTime);
|
|
3133
|
-
}
|
|
3134
|
-
});
|
|
3135
|
-
break;
|
|
3136
|
-
case 93:
|
|
3137
|
-
this.processScheduledNotes(channel, (note) => {
|
|
3138
|
-
if (note.noteNumber === keyNumber) {
|
|
3139
|
-
this.setChorusSend(channel, note, scheduleTime);
|
|
3140
|
-
}
|
|
3141
|
-
});
|
|
3142
|
-
break;
|
|
3143
|
-
}
|
|
3124
|
+
const handler = this.keyBasedControllerHandlers[controllerType];
|
|
3125
|
+
if (handler)
|
|
3126
|
+
handler(channel, keyNumber, scheduleTime);
|
|
3144
3127
|
}
|
|
3145
3128
|
}
|
|
3146
3129
|
handleSysEx(data, scheduleTime) {
|
|
@@ -3182,7 +3165,6 @@ Object.defineProperty(Midy, "channelSettings", {
|
|
|
3182
3165
|
scheduleIndex: 0,
|
|
3183
3166
|
detune: 0,
|
|
3184
3167
|
programNumber: 0,
|
|
3185
|
-
bank: 121 * 128,
|
|
3186
3168
|
bankMSB: 121,
|
|
3187
3169
|
bankLSB: 0,
|
|
3188
3170
|
dataMSB: 0,
|