@marmooo/midy 0.3.6 → 0.3.7
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 +7 -9
- package/esm/midy-GM1.d.ts +27 -24
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +121 -89
- package/esm/midy-GM2.d.ts +30 -26
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +196 -144
- package/esm/midy-GMLite.d.ts +27 -24
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +114 -84
- package/esm/midy.d.ts +32 -27
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +294 -164
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +27 -24
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +121 -89
- package/script/midy-GM2.d.ts +30 -26
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +196 -144
- package/script/midy-GMLite.d.ts +27 -24
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +114 -84
- package/script/midy.d.ts +32 -27
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +294 -164
package/esm/midy-GM2.js
CHANGED
|
@@ -225,13 +225,13 @@ export class MidyGM2 {
|
|
|
225
225
|
configurable: true,
|
|
226
226
|
writable: true,
|
|
227
227
|
value: 0
|
|
228
|
-
}); //
|
|
228
|
+
}); // cent
|
|
229
229
|
Object.defineProperty(this, "masterCoarseTuning", {
|
|
230
230
|
enumerable: true,
|
|
231
231
|
configurable: true,
|
|
232
232
|
writable: true,
|
|
233
233
|
value: 0
|
|
234
|
-
}); //
|
|
234
|
+
}); // cent
|
|
235
235
|
Object.defineProperty(this, "reverb", {
|
|
236
236
|
enumerable: true,
|
|
237
237
|
configurable: true,
|
|
@@ -356,6 +356,12 @@ export class MidyGM2 {
|
|
|
356
356
|
writable: true,
|
|
357
357
|
value: false
|
|
358
358
|
});
|
|
359
|
+
Object.defineProperty(this, "playPromise", {
|
|
360
|
+
enumerable: true,
|
|
361
|
+
configurable: true,
|
|
362
|
+
writable: true,
|
|
363
|
+
value: void 0
|
|
364
|
+
});
|
|
359
365
|
Object.defineProperty(this, "timeline", {
|
|
360
366
|
enumerable: true,
|
|
361
367
|
configurable: true,
|
|
@@ -524,7 +530,7 @@ export class MidyGM2 {
|
|
|
524
530
|
channel.controlTable.fill(-1);
|
|
525
531
|
channel.scaleOctaveTuningTable.fill(0); // [-100, 100] cent
|
|
526
532
|
channel.channelPressureTable.fill(-1);
|
|
527
|
-
channel.
|
|
533
|
+
channel.keyBasedTable.fill(-1);
|
|
528
534
|
}
|
|
529
535
|
createChannels(audioContext) {
|
|
530
536
|
const channels = Array.from({ length: this.numChannels }, () => {
|
|
@@ -540,7 +546,7 @@ export class MidyGM2 {
|
|
|
540
546
|
controlTable: this.initControlTable(),
|
|
541
547
|
scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
|
|
542
548
|
channelPressureTable: new Int8Array(6).fill(-1),
|
|
543
|
-
|
|
549
|
+
keyBasedTable: new Int8Array(128 * 128).fill(-1),
|
|
544
550
|
keyBasedGainLs: new Array(128),
|
|
545
551
|
keyBasedGainRs: new Array(128),
|
|
546
552
|
};
|
|
@@ -615,72 +621,80 @@ export class MidyGM2 {
|
|
|
615
621
|
}
|
|
616
622
|
return 0;
|
|
617
623
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
624
|
+
resetAllStates() {
|
|
625
|
+
this.exclusiveClassNotes.fill(undefined);
|
|
626
|
+
this.drumExclusiveClassNotes.fill(undefined);
|
|
627
|
+
this.voiceCache.clear();
|
|
628
|
+
for (let i = 0; i < this.channels.length; i++) {
|
|
629
|
+
this.channels[i].scheduledNotes = [];
|
|
630
|
+
this.resetChannelStates(i);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
updateStates(queueIndex, nextQueueIndex) {
|
|
634
|
+
if (nextQueueIndex < queueIndex)
|
|
635
|
+
queueIndex = 0;
|
|
636
|
+
for (let i = queueIndex; i < nextQueueIndex; i++) {
|
|
637
|
+
const event = this.timeline[i];
|
|
638
|
+
switch (event.type) {
|
|
639
|
+
case "controller":
|
|
640
|
+
this.setControlChange(event.channel, event.controllerType, event.value, 0);
|
|
641
|
+
break;
|
|
642
|
+
case "programChange":
|
|
643
|
+
this.setProgramChange(event.channel, event.programNumber, 0);
|
|
644
|
+
break;
|
|
645
|
+
case "pitchBend":
|
|
646
|
+
this.setPitchBend(event.channel, event.value + 8192, 0);
|
|
647
|
+
break;
|
|
648
|
+
case "sysEx":
|
|
649
|
+
this.handleSysEx(event.data, 0);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
async playNotes() {
|
|
654
|
+
if (this.audioContext.state === "suspended") {
|
|
655
|
+
await this.audioContext.resume();
|
|
656
|
+
}
|
|
657
|
+
this.isPlaying = true;
|
|
658
|
+
this.isPaused = false;
|
|
659
|
+
this.startTime = this.audioContext.currentTime;
|
|
660
|
+
let queueIndex = this.getQueueIndex(this.resumeTime);
|
|
661
|
+
let resumeTime = this.resumeTime - this.startTime;
|
|
662
|
+
let finished = false;
|
|
663
|
+
this.notePromises = [];
|
|
664
|
+
while (queueIndex < this.timeline.length) {
|
|
665
|
+
const now = this.audioContext.currentTime;
|
|
666
|
+
const t = now + resumeTime;
|
|
667
|
+
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
668
|
+
if (this.isPausing) {
|
|
669
|
+
await this.stopNotes(0, true, now);
|
|
670
|
+
await this.audioContext.suspend();
|
|
671
|
+
this.notePromises = [];
|
|
672
|
+
break;
|
|
673
|
+
}
|
|
674
|
+
else if (this.isStopping) {
|
|
675
|
+
await this.stopNotes(0, true, now);
|
|
676
|
+
await this.audioContext.suspend();
|
|
677
|
+
finished = true;
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
else if (this.isSeeking) {
|
|
681
|
+
await this.stopNotes(0, true, now);
|
|
682
|
+
this.startTime = this.audioContext.currentTime;
|
|
683
|
+
const nextQueueIndex = this.getQueueIndex(this.resumeTime);
|
|
684
|
+
this.updateStates(queueIndex, nextQueueIndex);
|
|
685
|
+
queueIndex = nextQueueIndex;
|
|
686
|
+
resumeTime = this.resumeTime - this.startTime;
|
|
687
|
+
this.isSeeking = false;
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
const waitTime = now + this.noteCheckInterval;
|
|
691
|
+
await this.scheduleTask(() => { }, waitTime);
|
|
692
|
+
}
|
|
693
|
+
if (finished) {
|
|
625
694
|
this.notePromises = [];
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
this.notePromises = [];
|
|
630
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
631
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
632
|
-
this.voiceCache.clear();
|
|
633
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
634
|
-
this.channels[i].scheduledNotes = [];
|
|
635
|
-
this.resetAllStates(i);
|
|
636
|
-
}
|
|
637
|
-
resolve();
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
const now = this.audioContext.currentTime;
|
|
641
|
-
const t = now + resumeTime;
|
|
642
|
-
queueIndex = await this.scheduleTimelineEvents(t, resumeTime, queueIndex);
|
|
643
|
-
if (this.isPausing) {
|
|
644
|
-
await this.stopNotes(0, true, now);
|
|
645
|
-
this.notePromises = [];
|
|
646
|
-
this.isPausing = false;
|
|
647
|
-
this.isPaused = true;
|
|
648
|
-
resolve();
|
|
649
|
-
return;
|
|
650
|
-
}
|
|
651
|
-
else if (this.isStopping) {
|
|
652
|
-
await this.stopNotes(0, true, now);
|
|
653
|
-
this.notePromises = [];
|
|
654
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
655
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
656
|
-
this.voiceCache.clear();
|
|
657
|
-
for (let i = 0; i < this.channels.length; i++) {
|
|
658
|
-
this.channels[i].scheduledNotes = [];
|
|
659
|
-
this.resetAllStates(i);
|
|
660
|
-
}
|
|
661
|
-
this.isStopping = false;
|
|
662
|
-
this.isPaused = false;
|
|
663
|
-
resolve();
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
666
|
-
else if (this.isSeeking) {
|
|
667
|
-
this.stopNotes(0, true, now);
|
|
668
|
-
this.exclusiveClassNotes.fill(undefined);
|
|
669
|
-
this.drumExclusiveClassNotes.fill(undefined);
|
|
670
|
-
this.startTime = this.audioContext.currentTime;
|
|
671
|
-
queueIndex = this.getQueueIndex(this.resumeTime);
|
|
672
|
-
resumeTime = this.resumeTime - this.startTime;
|
|
673
|
-
this.isSeeking = false;
|
|
674
|
-
await schedulePlayback();
|
|
675
|
-
}
|
|
676
|
-
else {
|
|
677
|
-
const waitTime = now + this.noteCheckInterval;
|
|
678
|
-
await this.scheduleTask(() => { }, waitTime);
|
|
679
|
-
await schedulePlayback();
|
|
680
|
-
}
|
|
681
|
-
};
|
|
682
|
-
schedulePlayback();
|
|
683
|
-
});
|
|
695
|
+
this.resetAllStates();
|
|
696
|
+
}
|
|
697
|
+
this.isPlaying = false;
|
|
684
698
|
}
|
|
685
699
|
ticksToSecond(ticks, secondsPerBeat) {
|
|
686
700
|
return ticks * secondsPerBeat / this.ticksPerBeat;
|
|
@@ -817,26 +831,32 @@ export class MidyGM2 {
|
|
|
817
831
|
this.resumeTime = 0;
|
|
818
832
|
if (this.voiceCounter.size === 0)
|
|
819
833
|
this.cacheVoiceIds();
|
|
820
|
-
|
|
821
|
-
this.
|
|
834
|
+
this.playPromise = this.playNotes();
|
|
835
|
+
await this.playPromise;
|
|
822
836
|
}
|
|
823
|
-
stop() {
|
|
837
|
+
async stop() {
|
|
824
838
|
if (!this.isPlaying)
|
|
825
839
|
return;
|
|
826
840
|
this.isStopping = true;
|
|
841
|
+
await this.playPromise;
|
|
842
|
+
this.isStopping = false;
|
|
827
843
|
}
|
|
828
|
-
pause() {
|
|
844
|
+
async pause() {
|
|
829
845
|
if (!this.isPlaying || this.isPaused)
|
|
830
846
|
return;
|
|
831
847
|
const now = this.audioContext.currentTime;
|
|
832
848
|
this.resumeTime += now - this.startTime - this.startDelay;
|
|
833
849
|
this.isPausing = true;
|
|
850
|
+
await this.playPromise;
|
|
851
|
+
this.isPausing = false;
|
|
852
|
+
this.isPaused = true;
|
|
834
853
|
}
|
|
835
854
|
async resume() {
|
|
836
855
|
if (!this.isPaused)
|
|
837
856
|
return;
|
|
838
|
-
|
|
839
|
-
this.
|
|
857
|
+
this.playPromise = this.playNotes();
|
|
858
|
+
await this.playPromise;
|
|
859
|
+
this.isPaused = false;
|
|
840
860
|
}
|
|
841
861
|
seekTo(second) {
|
|
842
862
|
this.resumeTime = second;
|
|
@@ -1271,10 +1291,12 @@ export class MidyGM2 {
|
|
|
1271
1291
|
startVibrato(channel, note, scheduleTime) {
|
|
1272
1292
|
const { voiceParams } = note;
|
|
1273
1293
|
const state = channel.state;
|
|
1294
|
+
const vibratoRate = state.vibratoRate * 2;
|
|
1295
|
+
const vibratoDelay = state.vibratoDelay * 2;
|
|
1274
1296
|
note.vibratoLFO = new OscillatorNode(this.audioContext, {
|
|
1275
|
-
frequency: this.centToHz(voiceParams.freqVibLFO) *
|
|
1297
|
+
frequency: this.centToHz(voiceParams.freqVibLFO) * vibratoRate,
|
|
1276
1298
|
});
|
|
1277
|
-
note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO *
|
|
1299
|
+
note.vibratoLFO.start(note.startTime + voiceParams.delayVibLFO * vibratoDelay);
|
|
1278
1300
|
note.vibratoDepth = new GainNode(this.audioContext);
|
|
1279
1301
|
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
1280
1302
|
note.vibratoLFO.connect(note.vibratoDepth);
|
|
@@ -1586,6 +1608,7 @@ export class MidyGM2 {
|
|
|
1586
1608
|
break;
|
|
1587
1609
|
}
|
|
1588
1610
|
}
|
|
1611
|
+
channel.keyBasedTable.fill(-1);
|
|
1589
1612
|
}
|
|
1590
1613
|
setChannelPressure(channelNumber, value, scheduleTime) {
|
|
1591
1614
|
const channel = this.channels[channelNumber];
|
|
@@ -1601,7 +1624,7 @@ export class MidyGM2 {
|
|
|
1601
1624
|
}
|
|
1602
1625
|
const table = channel.channelPressureTable;
|
|
1603
1626
|
this.processActiveNotes(channel, scheduleTime, (note) => {
|
|
1604
|
-
this.setEffects(channel, note, table);
|
|
1627
|
+
this.setEffects(channel, note, table, scheduleTime);
|
|
1605
1628
|
});
|
|
1606
1629
|
this.applyVoiceParams(channel, 13);
|
|
1607
1630
|
}
|
|
@@ -1627,23 +1650,28 @@ export class MidyGM2 {
|
|
|
1627
1650
|
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1628
1651
|
this.getLFOPitchDepth(channel, note);
|
|
1629
1652
|
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1630
|
-
const
|
|
1653
|
+
const depth = baseDepth * Math.sign(modLfoToPitch);
|
|
1631
1654
|
note.modulationDepth.gain
|
|
1632
1655
|
.cancelScheduledValues(scheduleTime)
|
|
1633
|
-
.setValueAtTime(
|
|
1656
|
+
.setValueAtTime(depth, scheduleTime);
|
|
1634
1657
|
}
|
|
1635
1658
|
else {
|
|
1636
1659
|
this.startModulation(channel, note, scheduleTime);
|
|
1637
1660
|
}
|
|
1638
1661
|
}
|
|
1639
1662
|
setVibLfoToPitch(channel, note, scheduleTime) {
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
.
|
|
1646
|
-
|
|
1663
|
+
if (note.vibratoDepth) {
|
|
1664
|
+
const vibratoDepth = channel.state.vibratoDepth * 2;
|
|
1665
|
+
const vibLfoToPitch = note.voiceParams.vibLfoToPitch;
|
|
1666
|
+
const baseDepth = Math.abs(vibLfoToPitch) * vibratoDepth;
|
|
1667
|
+
const depth = baseDepth * Math.sign(vibLfoToPitch);
|
|
1668
|
+
note.vibratoDepth.gain
|
|
1669
|
+
.cancelScheduledValues(scheduleTime)
|
|
1670
|
+
.setValueAtTime(depth, scheduleTime);
|
|
1671
|
+
}
|
|
1672
|
+
else {
|
|
1673
|
+
this.startVibrato(channel, note, scheduleTime);
|
|
1674
|
+
}
|
|
1647
1675
|
}
|
|
1648
1676
|
setModLfoToFilterFc(channel, note, scheduleTime) {
|
|
1649
1677
|
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc +
|
|
@@ -1721,13 +1749,12 @@ export class MidyGM2 {
|
|
|
1721
1749
|
}
|
|
1722
1750
|
}
|
|
1723
1751
|
}
|
|
1724
|
-
setDelayModLFO(note
|
|
1725
|
-
const startTime = note.startTime;
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
note.modulationLFO.connect(note.filterDepth);
|
|
1752
|
+
setDelayModLFO(note) {
|
|
1753
|
+
const startTime = note.startTime + note.voiceParams.delayModLFO;
|
|
1754
|
+
try {
|
|
1755
|
+
note.modulationLFO.start(startTime);
|
|
1756
|
+
}
|
|
1757
|
+
catch { /* empty */ }
|
|
1731
1758
|
}
|
|
1732
1759
|
setFreqModLFO(note, scheduleTime) {
|
|
1733
1760
|
const freqModLFO = note.voiceParams.freqModLFO;
|
|
@@ -1736,54 +1763,65 @@ export class MidyGM2 {
|
|
|
1736
1763
|
.setValueAtTime(freqModLFO, scheduleTime);
|
|
1737
1764
|
}
|
|
1738
1765
|
setFreqVibLFO(channel, note, scheduleTime) {
|
|
1766
|
+
const vibratoRate = channel.state.vibratoRate * 2;
|
|
1739
1767
|
const freqVibLFO = note.voiceParams.freqVibLFO;
|
|
1740
1768
|
note.vibratoLFO.frequency
|
|
1741
1769
|
.cancelScheduledValues(scheduleTime)
|
|
1742
|
-
.setValueAtTime(freqVibLFO *
|
|
1770
|
+
.setValueAtTime(freqVibLFO * vibratoRate, scheduleTime);
|
|
1771
|
+
}
|
|
1772
|
+
setDelayVibLFO(channel, note) {
|
|
1773
|
+
const vibratoDelay = channel.state.vibratoDelay * 2;
|
|
1774
|
+
const value = note.voiceParams.delayVibLFO;
|
|
1775
|
+
const startTime = note.startTime + value * vibratoDelay;
|
|
1776
|
+
try {
|
|
1777
|
+
note.vibratoLFO.start(startTime);
|
|
1778
|
+
}
|
|
1779
|
+
catch { /* empty */ }
|
|
1743
1780
|
}
|
|
1744
1781
|
createVoiceParamsHandlers() {
|
|
1745
1782
|
return {
|
|
1746
|
-
modLfoToPitch: (channel, note,
|
|
1783
|
+
modLfoToPitch: (channel, note, scheduleTime) => {
|
|
1747
1784
|
if (0 < channel.state.modulationDepth) {
|
|
1748
1785
|
this.setModLfoToPitch(channel, note, scheduleTime);
|
|
1749
1786
|
}
|
|
1750
1787
|
},
|
|
1751
|
-
vibLfoToPitch: (channel, note,
|
|
1788
|
+
vibLfoToPitch: (channel, note, scheduleTime) => {
|
|
1752
1789
|
if (0 < channel.state.vibratoDepth) {
|
|
1753
1790
|
this.setVibLfoToPitch(channel, note, scheduleTime);
|
|
1754
1791
|
}
|
|
1755
1792
|
},
|
|
1756
|
-
modLfoToFilterFc: (channel, note,
|
|
1793
|
+
modLfoToFilterFc: (channel, note, scheduleTime) => {
|
|
1757
1794
|
if (0 < channel.state.modulationDepth) {
|
|
1758
1795
|
this.setModLfoToFilterFc(channel, note, scheduleTime);
|
|
1759
1796
|
}
|
|
1760
1797
|
},
|
|
1761
|
-
modLfoToVolume: (channel, note,
|
|
1798
|
+
modLfoToVolume: (channel, note, scheduleTime) => {
|
|
1762
1799
|
if (0 < channel.state.modulationDepth) {
|
|
1763
1800
|
this.setModLfoToVolume(channel, note, scheduleTime);
|
|
1764
1801
|
}
|
|
1765
1802
|
},
|
|
1766
|
-
chorusEffectsSend: (channel, note,
|
|
1767
|
-
this.
|
|
1803
|
+
chorusEffectsSend: (channel, note, scheduleTime) => {
|
|
1804
|
+
this.setChorusSend(channel, note, scheduleTime);
|
|
1805
|
+
},
|
|
1806
|
+
reverbEffectsSend: (channel, note, scheduleTime) => {
|
|
1807
|
+
this.setReverbSend(channel, note, scheduleTime);
|
|
1808
|
+
},
|
|
1809
|
+
delayModLFO: (_channel, note, _scheduleTime) => {
|
|
1810
|
+
if (0 < channel.state.modulationDepth) {
|
|
1811
|
+
this.setDelayModLFO(note);
|
|
1812
|
+
}
|
|
1768
1813
|
},
|
|
1769
|
-
|
|
1770
|
-
|
|
1814
|
+
freqModLFO: (_channel, note, scheduleTime) => {
|
|
1815
|
+
if (0 < channel.state.modulationDepth) {
|
|
1816
|
+
this.setFreqModLFO(note, scheduleTime);
|
|
1817
|
+
}
|
|
1771
1818
|
},
|
|
1772
|
-
|
|
1773
|
-
freqModLFO: (_channel, note, _prevValue, scheduleTime) => this.setFreqModLFO(note, scheduleTime),
|
|
1774
|
-
delayVibLFO: (channel, note, prevValue, scheduleTime) => {
|
|
1819
|
+
delayVibLFO: (channel, note, _scheduleTime) => {
|
|
1775
1820
|
if (0 < channel.state.vibratoDepth) {
|
|
1776
|
-
|
|
1777
|
-
const prevStartTime = note.startTime + prevValue * vibratoDelay;
|
|
1778
|
-
if (scheduleTime < prevStartTime)
|
|
1779
|
-
return;
|
|
1780
|
-
const value = note.voiceParams.delayVibLFO;
|
|
1781
|
-
const startTime = note.startTime + value * vibratoDelay;
|
|
1782
|
-
note.vibratoLFO.stop(scheduleTime);
|
|
1783
|
-
note.vibratoLFO.start(startTime);
|
|
1821
|
+
setDelayVibLFO(channel, note);
|
|
1784
1822
|
}
|
|
1785
1823
|
},
|
|
1786
|
-
freqVibLFO: (channel, note,
|
|
1824
|
+
freqVibLFO: (channel, note, scheduleTime) => {
|
|
1787
1825
|
if (0 < channel.state.vibratoDepth) {
|
|
1788
1826
|
this.setFreqVibLFO(channel, note, scheduleTime);
|
|
1789
1827
|
}
|
|
@@ -1811,7 +1849,7 @@ export class MidyGM2 {
|
|
|
1811
1849
|
continue;
|
|
1812
1850
|
note.voiceParams[key] = value;
|
|
1813
1851
|
if (key in this.voiceParamsHandlers) {
|
|
1814
|
-
this.voiceParamsHandlers[key](channel, note,
|
|
1852
|
+
this.voiceParamsHandlers[key](channel, note, scheduleTime);
|
|
1815
1853
|
}
|
|
1816
1854
|
else {
|
|
1817
1855
|
if (volumeEnvelopeKeySet.has(key))
|
|
@@ -1980,13 +2018,13 @@ export class MidyGM2 {
|
|
|
1980
2018
|
.setValueAtTime(volume * gainRight, scheduleTime);
|
|
1981
2019
|
}
|
|
1982
2020
|
updateKeyBasedVolume(channel, keyNumber, scheduleTime) {
|
|
1983
|
-
const state = channel.state;
|
|
1984
|
-
const defaultVolume = state.volume * state.expression;
|
|
1985
|
-
const defaultPan = state.pan;
|
|
1986
2021
|
const gainL = channel.keyBasedGainLs[keyNumber];
|
|
1987
|
-
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
1988
2022
|
if (!gainL)
|
|
1989
2023
|
return;
|
|
2024
|
+
const gainR = channel.keyBasedGainRs[keyNumber];
|
|
2025
|
+
const state = channel.state;
|
|
2026
|
+
const defaultVolume = state.volume * state.expression;
|
|
2027
|
+
const defaultPan = state.pan;
|
|
1990
2028
|
const keyBasedVolume = this.getKeyBasedValue(channel, keyNumber, 7);
|
|
1991
2029
|
const volume = (0 <= keyBasedVolume)
|
|
1992
2030
|
? defaultVolume * keyBasedVolume / 64
|
|
@@ -2138,8 +2176,8 @@ export class MidyGM2 {
|
|
|
2138
2176
|
}
|
|
2139
2177
|
handlePitchBendRangeRPN(channelNumber, scheduleTime) {
|
|
2140
2178
|
const channel = this.channels[channelNumber];
|
|
2141
|
-
this.limitData(channel, 0, 127, 0,
|
|
2142
|
-
const pitchBendRange = channel.dataMSB + channel.dataLSB / 100;
|
|
2179
|
+
this.limitData(channel, 0, 127, 0, 127);
|
|
2180
|
+
const pitchBendRange = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
2143
2181
|
this.setPitchBendRange(channelNumber, pitchBendRange, scheduleTime);
|
|
2144
2182
|
}
|
|
2145
2183
|
setPitchBendRange(channelNumber, value, scheduleTime) {
|
|
@@ -2149,7 +2187,7 @@ export class MidyGM2 {
|
|
|
2149
2187
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2150
2188
|
const state = channel.state;
|
|
2151
2189
|
const prev = state.pitchWheelSensitivity;
|
|
2152
|
-
const next = value /
|
|
2190
|
+
const next = value / 12800;
|
|
2153
2191
|
state.pitchWheelSensitivity = next;
|
|
2154
2192
|
channel.detune += (state.pitchWheel * 2 - 1) * (next - prev) * 12800;
|
|
2155
2193
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -2158,7 +2196,8 @@ export class MidyGM2 {
|
|
|
2158
2196
|
handleFineTuningRPN(channelNumber, scheduleTime) {
|
|
2159
2197
|
const channel = this.channels[channelNumber];
|
|
2160
2198
|
this.limitData(channel, 0, 127, 0, 127);
|
|
2161
|
-
const
|
|
2199
|
+
const value = channel.dataMSB * 128 + channel.dataLSB;
|
|
2200
|
+
const fineTuning = (value - 8192) / 8192 * 100;
|
|
2162
2201
|
this.setFineTuning(channelNumber, fineTuning, scheduleTime);
|
|
2163
2202
|
}
|
|
2164
2203
|
setFineTuning(channelNumber, value, scheduleTime) {
|
|
@@ -2167,7 +2206,7 @@ export class MidyGM2 {
|
|
|
2167
2206
|
return;
|
|
2168
2207
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2169
2208
|
const prev = channel.fineTuning;
|
|
2170
|
-
const next =
|
|
2209
|
+
const next = value;
|
|
2171
2210
|
channel.fineTuning = next;
|
|
2172
2211
|
channel.detune += next - prev;
|
|
2173
2212
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -2175,7 +2214,7 @@ export class MidyGM2 {
|
|
|
2175
2214
|
handleCoarseTuningRPN(channelNumber, scheduleTime) {
|
|
2176
2215
|
const channel = this.channels[channelNumber];
|
|
2177
2216
|
this.limitDataMSB(channel, 0, 127);
|
|
2178
|
-
const coarseTuning = channel.dataMSB;
|
|
2217
|
+
const coarseTuning = (channel.dataMSB - 64) * 100;
|
|
2179
2218
|
this.setCoarseTuning(channelNumber, coarseTuning, scheduleTime);
|
|
2180
2219
|
}
|
|
2181
2220
|
setCoarseTuning(channelNumber, value, scheduleTime) {
|
|
@@ -2184,7 +2223,7 @@ export class MidyGM2 {
|
|
|
2184
2223
|
return;
|
|
2185
2224
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2186
2225
|
const prev = channel.coarseTuning;
|
|
2187
|
-
const next =
|
|
2226
|
+
const next = value;
|
|
2188
2227
|
channel.coarseTuning = next;
|
|
2189
2228
|
channel.detune += next - prev;
|
|
2190
2229
|
this.updateChannelDetune(channel, scheduleTime);
|
|
@@ -2192,22 +2231,22 @@ export class MidyGM2 {
|
|
|
2192
2231
|
handleModulationDepthRangeRPN(channelNumber, scheduleTime) {
|
|
2193
2232
|
const channel = this.channels[channelNumber];
|
|
2194
2233
|
this.limitData(channel, 0, 127, 0, 127);
|
|
2195
|
-
const
|
|
2196
|
-
this.setModulationDepthRange(channelNumber,
|
|
2234
|
+
const value = (channel.dataMSB + channel.dataLSB / 128) * 100;
|
|
2235
|
+
this.setModulationDepthRange(channelNumber, value, scheduleTime);
|
|
2197
2236
|
}
|
|
2198
|
-
setModulationDepthRange(channelNumber,
|
|
2237
|
+
setModulationDepthRange(channelNumber, value, scheduleTime) {
|
|
2199
2238
|
const channel = this.channels[channelNumber];
|
|
2200
2239
|
if (channel.isDrum)
|
|
2201
2240
|
return;
|
|
2202
2241
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2203
|
-
channel.modulationDepthRange =
|
|
2242
|
+
channel.modulationDepthRange = value;
|
|
2204
2243
|
this.updateModulation(channel, scheduleTime);
|
|
2205
2244
|
}
|
|
2206
2245
|
allSoundOff(channelNumber, _value, scheduleTime) {
|
|
2207
2246
|
scheduleTime ??= this.audioContext.currentTime;
|
|
2208
2247
|
return this.stopActiveNotes(channelNumber, 0, true, scheduleTime);
|
|
2209
2248
|
}
|
|
2210
|
-
|
|
2249
|
+
resetChannelStates(channelNumber) {
|
|
2211
2250
|
const scheduleTime = this.audioContext.currentTime;
|
|
2212
2251
|
const channel = this.channels[channelNumber];
|
|
2213
2252
|
const state = channel.state;
|
|
@@ -2225,8 +2264,8 @@ export class MidyGM2 {
|
|
|
2225
2264
|
}
|
|
2226
2265
|
this.resetChannelTable(channel);
|
|
2227
2266
|
this.mode = "GM2";
|
|
2228
|
-
this.masterFineTuning = 0; //
|
|
2229
|
-
this.masterCoarseTuning = 0; //
|
|
2267
|
+
this.masterFineTuning = 0; // cent
|
|
2268
|
+
this.masterCoarseTuning = 0; // cent
|
|
2230
2269
|
}
|
|
2231
2270
|
// https://amei.or.jp/midistandardcommittee/Recommended_Practice/e/rp15.pdf
|
|
2232
2271
|
resetAllControllers(channelNumber, _value, scheduleTime) {
|
|
@@ -2395,12 +2434,13 @@ export class MidyGM2 {
|
|
|
2395
2434
|
}
|
|
2396
2435
|
}
|
|
2397
2436
|
handleMasterFineTuningSysEx(data, scheduleTime) {
|
|
2398
|
-
const
|
|
2437
|
+
const value = (data[5] * 128 + data[4]) / 16383;
|
|
2438
|
+
const fineTuning = (value - 8192) / 8192 * 100;
|
|
2399
2439
|
this.setMasterFineTuning(fineTuning, scheduleTime);
|
|
2400
2440
|
}
|
|
2401
2441
|
setMasterFineTuning(value, scheduleTime) {
|
|
2402
2442
|
const prev = this.masterFineTuning;
|
|
2403
|
-
const next =
|
|
2443
|
+
const next = value;
|
|
2404
2444
|
this.masterFineTuning = next;
|
|
2405
2445
|
const detuneChange = next - prev;
|
|
2406
2446
|
for (let i = 0; i < this.channels.length; i++) {
|
|
@@ -2412,12 +2452,12 @@ export class MidyGM2 {
|
|
|
2412
2452
|
}
|
|
2413
2453
|
}
|
|
2414
2454
|
handleMasterCoarseTuningSysEx(data, scheduleTime) {
|
|
2415
|
-
const coarseTuning = data[4];
|
|
2455
|
+
const coarseTuning = (data[4] - 64) * 100;
|
|
2416
2456
|
this.setMasterCoarseTuning(coarseTuning, scheduleTime);
|
|
2417
2457
|
}
|
|
2418
2458
|
setMasterCoarseTuning(value, scheduleTime) {
|
|
2419
2459
|
const prev = this.masterCoarseTuning;
|
|
2420
|
-
const next =
|
|
2460
|
+
const next = value;
|
|
2421
2461
|
this.masterCoarseTuning = next;
|
|
2422
2462
|
const detuneChange = next - prev;
|
|
2423
2463
|
for (let i = 0; i < this.channels.length; i++) {
|
|
@@ -2751,7 +2791,7 @@ export class MidyGM2 {
|
|
|
2751
2791
|
}
|
|
2752
2792
|
getKeyBasedValue(channel, keyNumber, controllerType) {
|
|
2753
2793
|
const index = keyNumber * 128 + controllerType;
|
|
2754
|
-
const controlValue = channel.
|
|
2794
|
+
const controlValue = channel.keyBasedTable[index];
|
|
2755
2795
|
return controlValue;
|
|
2756
2796
|
}
|
|
2757
2797
|
handleKeyBasedInstrumentControlSysEx(data, scheduleTime) {
|
|
@@ -2760,7 +2800,7 @@ export class MidyGM2 {
|
|
|
2760
2800
|
if (!channel.isDrum)
|
|
2761
2801
|
return;
|
|
2762
2802
|
const keyNumber = data[5];
|
|
2763
|
-
const table = channel.
|
|
2803
|
+
const table = channel.keyBasedTable;
|
|
2764
2804
|
for (let i = 6; i < data.length; i += 2) {
|
|
2765
2805
|
const controllerType = data[i];
|
|
2766
2806
|
const value = data[i + 1];
|
|
@@ -2771,8 +2811,20 @@ export class MidyGM2 {
|
|
|
2771
2811
|
case 10:
|
|
2772
2812
|
this.updateKeyBasedVolume(channel, keyNumber, scheduleTime);
|
|
2773
2813
|
break;
|
|
2774
|
-
|
|
2775
|
-
this.
|
|
2814
|
+
case 91:
|
|
2815
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2816
|
+
if (note.noteNumber === keyNumber) {
|
|
2817
|
+
this.setReverbSend(channel, note, scheduleTime);
|
|
2818
|
+
}
|
|
2819
|
+
});
|
|
2820
|
+
break;
|
|
2821
|
+
case 93:
|
|
2822
|
+
this.processScheduledNotes(channel, (note) => {
|
|
2823
|
+
if (note.noteNumber === keyNumber) {
|
|
2824
|
+
this.setChorusSend(channel, note, scheduleTime);
|
|
2825
|
+
}
|
|
2826
|
+
});
|
|
2827
|
+
break;
|
|
2776
2828
|
}
|
|
2777
2829
|
}
|
|
2778
2830
|
}
|
|
@@ -2823,7 +2875,7 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
2823
2875
|
rpnLSB: 127,
|
|
2824
2876
|
mono: false, // CC#124, CC#125
|
|
2825
2877
|
modulationDepthRange: 50, // cent
|
|
2826
|
-
fineTuning: 0, //
|
|
2827
|
-
coarseTuning: 0, //
|
|
2878
|
+
fineTuning: 0, // cent
|
|
2879
|
+
coarseTuning: 0, // cent
|
|
2828
2880
|
}
|
|
2829
2881
|
});
|