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