@marmooo/midy 0.2.5 → 0.2.6
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/esm/midy-GM1.d.ts +24 -25
- package/esm/midy-GM1.d.ts.map +1 -1
- package/esm/midy-GM1.js +55 -91
- package/esm/midy-GM2.d.ts +33 -29
- package/esm/midy-GM2.d.ts.map +1 -1
- package/esm/midy-GM2.js +146 -133
- package/esm/midy-GMLite.d.ts +18 -16
- package/esm/midy-GMLite.d.ts.map +1 -1
- package/esm/midy-GMLite.js +53 -39
- package/esm/midy.d.ts +35 -31
- package/esm/midy.d.ts.map +1 -1
- package/esm/midy.js +172 -161
- package/package.json +1 -1
- package/script/midy-GM1.d.ts +24 -25
- package/script/midy-GM1.d.ts.map +1 -1
- package/script/midy-GM1.js +55 -91
- package/script/midy-GM2.d.ts +33 -29
- package/script/midy-GM2.d.ts.map +1 -1
- package/script/midy-GM2.js +146 -133
- package/script/midy-GMLite.d.ts +18 -16
- package/script/midy-GMLite.d.ts.map +1 -1
- package/script/midy-GMLite.js +53 -39
- package/script/midy.d.ts +35 -31
- package/script/midy.d.ts.map +1 -1
- package/script/midy.js +172 -161
package/esm/midy-GM2.js
CHANGED
|
@@ -66,31 +66,37 @@ class Note {
|
|
|
66
66
|
writable: true,
|
|
67
67
|
value: void 0
|
|
68
68
|
});
|
|
69
|
+
Object.defineProperty(this, "filterDepth", {
|
|
70
|
+
enumerable: true,
|
|
71
|
+
configurable: true,
|
|
72
|
+
writable: true,
|
|
73
|
+
value: void 0
|
|
74
|
+
});
|
|
69
75
|
Object.defineProperty(this, "volumeEnvelopeNode", {
|
|
70
76
|
enumerable: true,
|
|
71
77
|
configurable: true,
|
|
72
78
|
writable: true,
|
|
73
79
|
value: void 0
|
|
74
80
|
});
|
|
75
|
-
Object.defineProperty(this, "
|
|
81
|
+
Object.defineProperty(this, "volumeDepth", {
|
|
76
82
|
enumerable: true,
|
|
77
83
|
configurable: true,
|
|
78
84
|
writable: true,
|
|
79
85
|
value: void 0
|
|
80
86
|
});
|
|
81
|
-
Object.defineProperty(this, "
|
|
87
|
+
Object.defineProperty(this, "volumeNode", {
|
|
82
88
|
enumerable: true,
|
|
83
89
|
configurable: true,
|
|
84
90
|
writable: true,
|
|
85
91
|
value: void 0
|
|
86
92
|
});
|
|
87
|
-
Object.defineProperty(this, "
|
|
93
|
+
Object.defineProperty(this, "gainL", {
|
|
88
94
|
enumerable: true,
|
|
89
95
|
configurable: true,
|
|
90
96
|
writable: true,
|
|
91
97
|
value: void 0
|
|
92
98
|
});
|
|
93
|
-
Object.defineProperty(this, "
|
|
99
|
+
Object.defineProperty(this, "gainR", {
|
|
94
100
|
enumerable: true,
|
|
95
101
|
configurable: true,
|
|
96
102
|
writable: true,
|
|
@@ -492,6 +498,9 @@ export class MidyGM2 {
|
|
|
492
498
|
...this.setChannelAudioNodes(audioContext),
|
|
493
499
|
scheduledNotes: new SparseMap(128),
|
|
494
500
|
sostenutoNotes: new SparseMap(128),
|
|
501
|
+
scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
|
|
502
|
+
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
503
|
+
keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
|
|
495
504
|
};
|
|
496
505
|
});
|
|
497
506
|
return channels;
|
|
@@ -558,10 +567,11 @@ export class MidyGM2 {
|
|
|
558
567
|
const event = this.timeline[queueIndex];
|
|
559
568
|
if (event.startTime > t + this.lookAhead)
|
|
560
569
|
break;
|
|
570
|
+
const startTime = event.startTime + this.startDelay - offset;
|
|
561
571
|
switch (event.type) {
|
|
562
572
|
case "noteOn":
|
|
563
573
|
if (event.velocity !== 0) {
|
|
564
|
-
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity,
|
|
574
|
+
await this.scheduleNoteOn(event.channel, event.noteNumber, event.velocity, startTime, event.portamento);
|
|
565
575
|
break;
|
|
566
576
|
}
|
|
567
577
|
/* falls through */
|
|
@@ -569,26 +579,26 @@ export class MidyGM2 {
|
|
|
569
579
|
const portamentoTarget = this.findPortamentoTarget(queueIndex);
|
|
570
580
|
if (portamentoTarget)
|
|
571
581
|
portamentoTarget.portamento = true;
|
|
572
|
-
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity,
|
|
582
|
+
const notePromise = this.scheduleNoteRelease(this.omni ? 0 : event.channel, event.noteNumber, event.velocity, startTime, portamentoTarget?.noteNumber, false);
|
|
573
583
|
if (notePromise) {
|
|
574
584
|
this.notePromises.push(notePromise);
|
|
575
585
|
}
|
|
576
586
|
break;
|
|
577
587
|
}
|
|
578
588
|
case "controller":
|
|
579
|
-
this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value);
|
|
589
|
+
this.handleControlChange(this.omni ? 0 : event.channel, event.controllerType, event.value, startTime);
|
|
580
590
|
break;
|
|
581
591
|
case "programChange":
|
|
582
|
-
this.handleProgramChange(event.channel, event.programNumber);
|
|
592
|
+
this.handleProgramChange(event.channel, event.programNumber, startTime);
|
|
583
593
|
break;
|
|
584
594
|
case "channelAftertouch":
|
|
585
|
-
this.handleChannelPressure(event.channel, event.amount);
|
|
595
|
+
this.handleChannelPressure(event.channel, event.amount, startTime);
|
|
586
596
|
break;
|
|
587
597
|
case "pitchBend":
|
|
588
|
-
this.setPitchBend(event.channel, event.value + 8192);
|
|
598
|
+
this.setPitchBend(event.channel, event.value + 8192, startTime);
|
|
589
599
|
break;
|
|
590
600
|
case "sysEx":
|
|
591
|
-
this.handleSysEx(event.data);
|
|
601
|
+
this.handleSysEx(event.data, startTime);
|
|
592
602
|
}
|
|
593
603
|
queueIndex++;
|
|
594
604
|
}
|
|
@@ -835,6 +845,18 @@ export class MidyGM2 {
|
|
|
835
845
|
const now = this.audioContext.currentTime;
|
|
836
846
|
return this.resumeTime + now - this.startTime - this.startDelay;
|
|
837
847
|
}
|
|
848
|
+
processScheduledNotes(channel, scheduleTime, callback) {
|
|
849
|
+
channel.scheduledNotes.forEach((noteList) => {
|
|
850
|
+
for (let i = 0; i < noteList.length; i++) {
|
|
851
|
+
const note = noteList[i];
|
|
852
|
+
if (!note)
|
|
853
|
+
continue;
|
|
854
|
+
if (scheduleTime < note.startTime)
|
|
855
|
+
continue;
|
|
856
|
+
callback(note);
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
}
|
|
838
860
|
getActiveNotes(channel, time) {
|
|
839
861
|
const activeNotes = new SparseMap(128);
|
|
840
862
|
channel.scheduledNotes.forEach((noteList) => {
|
|
@@ -1016,14 +1038,14 @@ export class MidyGM2 {
|
|
|
1016
1038
|
const note = noteList[i];
|
|
1017
1039
|
if (!note)
|
|
1018
1040
|
continue;
|
|
1019
|
-
this.updateDetune(channel, note
|
|
1041
|
+
this.updateDetune(channel, note);
|
|
1020
1042
|
}
|
|
1021
1043
|
});
|
|
1022
1044
|
}
|
|
1023
|
-
updateDetune(channel, note
|
|
1045
|
+
updateDetune(channel, note) {
|
|
1024
1046
|
const now = this.audioContext.currentTime;
|
|
1025
1047
|
const noteDetune = this.calcNoteDetune(channel, note);
|
|
1026
|
-
const detune = channel.detune + noteDetune
|
|
1048
|
+
const detune = channel.detune + noteDetune;
|
|
1027
1049
|
note.bufferSource.detune
|
|
1028
1050
|
.cancelScheduledValues(now)
|
|
1029
1051
|
.setValueAtTime(detune, now);
|
|
@@ -1045,11 +1067,11 @@ export class MidyGM2 {
|
|
|
1045
1067
|
.setValueAtTime(0, volDelay)
|
|
1046
1068
|
.linearRampToValueAtTime(sustainVolume, portamentoTime);
|
|
1047
1069
|
}
|
|
1048
|
-
setVolumeEnvelope(
|
|
1070
|
+
setVolumeEnvelope(channel, note) {
|
|
1049
1071
|
const now = this.audioContext.currentTime;
|
|
1050
1072
|
const { voiceParams, startTime } = note;
|
|
1051
1073
|
const attackVolume = this.cbToRatio(-voiceParams.initialAttenuation) *
|
|
1052
|
-
(1 +
|
|
1074
|
+
(1 + this.getAmplitudeControl(channel));
|
|
1053
1075
|
const sustainVolume = attackVolume * (1 - voiceParams.volSustain);
|
|
1054
1076
|
const volDelay = startTime + voiceParams.volDelay;
|
|
1055
1077
|
const volAttack = volDelay + voiceParams.volAttack;
|
|
@@ -1063,20 +1085,20 @@ export class MidyGM2 {
|
|
|
1063
1085
|
.setValueAtTime(attackVolume, volHold)
|
|
1064
1086
|
.linearRampToValueAtTime(sustainVolume, volDecay);
|
|
1065
1087
|
}
|
|
1066
|
-
setPitchEnvelope(note) {
|
|
1067
|
-
|
|
1088
|
+
setPitchEnvelope(note, scheduleTime) {
|
|
1089
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1068
1090
|
const { voiceParams } = note;
|
|
1069
1091
|
const baseRate = voiceParams.playbackRate;
|
|
1070
1092
|
note.bufferSource.playbackRate
|
|
1071
|
-
.cancelScheduledValues(
|
|
1072
|
-
.setValueAtTime(baseRate,
|
|
1093
|
+
.cancelScheduledValues(scheduleTime)
|
|
1094
|
+
.setValueAtTime(baseRate, scheduleTime);
|
|
1073
1095
|
const modEnvToPitch = voiceParams.modEnvToPitch;
|
|
1074
1096
|
if (modEnvToPitch === 0)
|
|
1075
1097
|
return;
|
|
1076
1098
|
const basePitch = this.rateToCent(baseRate);
|
|
1077
1099
|
const peekPitch = basePitch + modEnvToPitch;
|
|
1078
1100
|
const peekRate = this.centToRate(peekPitch);
|
|
1079
|
-
const modDelay = startTime + voiceParams.modDelay;
|
|
1101
|
+
const modDelay = note.startTime + voiceParams.modDelay;
|
|
1080
1102
|
const modAttack = modDelay + voiceParams.modAttack;
|
|
1081
1103
|
const modHold = modAttack + voiceParams.modHold;
|
|
1082
1104
|
const modDecay = modHold + voiceParams.modDecay;
|
|
@@ -1112,13 +1134,14 @@ export class MidyGM2 {
|
|
|
1112
1134
|
.setValueAtTime(adjustedBaseFreq, modDelay)
|
|
1113
1135
|
.linearRampToValueAtTime(adjustedSustainFreq, portamentoTime);
|
|
1114
1136
|
}
|
|
1115
|
-
setFilterEnvelope(channel, note
|
|
1137
|
+
setFilterEnvelope(channel, note) {
|
|
1116
1138
|
const now = this.audioContext.currentTime;
|
|
1117
1139
|
const state = channel.state;
|
|
1118
1140
|
const { voiceParams, noteNumber, startTime } = note;
|
|
1119
1141
|
const softPedalFactor = 1 -
|
|
1120
1142
|
(0.1 + (noteNumber / 127) * 0.2) * state.softPedal;
|
|
1121
|
-
const baseCent = voiceParams.initialFilterFc +
|
|
1143
|
+
const baseCent = voiceParams.initialFilterFc +
|
|
1144
|
+
this.getFilterCutoffControl(channel);
|
|
1122
1145
|
const baseFreq = this.centToHz(baseCent) * softPedalFactor;
|
|
1123
1146
|
const peekFreq = this.centToHz(baseCent + voiceParams.modEnvToFilterFc) *
|
|
1124
1147
|
softPedalFactor;
|
|
@@ -1148,9 +1171,9 @@ export class MidyGM2 {
|
|
|
1148
1171
|
gain: voiceParams.modLfoToFilterFc,
|
|
1149
1172
|
});
|
|
1150
1173
|
note.modulationDepth = new GainNode(this.audioContext);
|
|
1151
|
-
this.setModLfoToPitch(channel, note
|
|
1174
|
+
this.setModLfoToPitch(channel, note);
|
|
1152
1175
|
note.volumeDepth = new GainNode(this.audioContext);
|
|
1153
|
-
this.setModLfoToVolume(
|
|
1176
|
+
this.setModLfoToVolume(channel, note);
|
|
1154
1177
|
note.modulationLFO.start(startTime + voiceParams.delayModLFO);
|
|
1155
1178
|
note.modulationLFO.connect(note.filterDepth);
|
|
1156
1179
|
note.filterDepth.connect(note.filterNode.frequency);
|
|
@@ -1211,8 +1234,8 @@ export class MidyGM2 {
|
|
|
1211
1234
|
}
|
|
1212
1235
|
else {
|
|
1213
1236
|
note.portamento = false;
|
|
1214
|
-
this.setVolumeEnvelope(
|
|
1215
|
-
this.setFilterEnvelope(channel, note
|
|
1237
|
+
this.setVolumeEnvelope(channel, note);
|
|
1238
|
+
this.setFilterEnvelope(channel, note);
|
|
1216
1239
|
}
|
|
1217
1240
|
if (0 < state.vibratoDepth) {
|
|
1218
1241
|
this.startVibrato(channel, note, startTime);
|
|
@@ -1426,8 +1449,9 @@ export class MidyGM2 {
|
|
|
1426
1449
|
channel.bank = channel.bankMSB * 128 + channel.bankLSB;
|
|
1427
1450
|
channel.program = program;
|
|
1428
1451
|
}
|
|
1429
|
-
handleChannelPressure(channelNumber, value) {
|
|
1430
|
-
|
|
1452
|
+
handleChannelPressure(channelNumber, value, startTime) {
|
|
1453
|
+
if (!startTime)
|
|
1454
|
+
startTime = this.audioContext.currentTime;
|
|
1431
1455
|
const channel = this.channels[channelNumber];
|
|
1432
1456
|
const prev = channel.state.channelPressure;
|
|
1433
1457
|
const next = value / 127;
|
|
@@ -1437,8 +1461,8 @@ export class MidyGM2 {
|
|
|
1437
1461
|
channel.detune += pressureDepth * (next - prev);
|
|
1438
1462
|
}
|
|
1439
1463
|
const table = channel.channelPressureTable;
|
|
1440
|
-
this.getActiveNotes(channel,
|
|
1441
|
-
this.
|
|
1464
|
+
this.getActiveNotes(channel, startTime).forEach((note) => {
|
|
1465
|
+
this.setControllerParameters(channel, note, table);
|
|
1442
1466
|
});
|
|
1443
1467
|
// this.applyVoiceParams(channel, 13);
|
|
1444
1468
|
}
|
|
@@ -1456,9 +1480,10 @@ export class MidyGM2 {
|
|
|
1456
1480
|
this.updateChannelDetune(channel);
|
|
1457
1481
|
this.applyVoiceParams(channel, 14);
|
|
1458
1482
|
}
|
|
1459
|
-
setModLfoToPitch(channel, note
|
|
1483
|
+
setModLfoToPitch(channel, note) {
|
|
1460
1484
|
const now = this.audioContext.currentTime;
|
|
1461
|
-
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1485
|
+
const modLfoToPitch = note.voiceParams.modLfoToPitch +
|
|
1486
|
+
this.getLFOPitchDepth(channel);
|
|
1462
1487
|
const baseDepth = Math.abs(modLfoToPitch) + channel.state.modulationDepth;
|
|
1463
1488
|
const modulationDepth = baseDepth * Math.sign(modLfoToPitch);
|
|
1464
1489
|
note.modulationDepth.gain
|
|
@@ -1475,18 +1500,20 @@ export class MidyGM2 {
|
|
|
1475
1500
|
.cancelScheduledValues(now)
|
|
1476
1501
|
.setValueAtTime(vibratoDepth * vibratoDepthSign, now);
|
|
1477
1502
|
}
|
|
1478
|
-
setModLfoToFilterFc(
|
|
1503
|
+
setModLfoToFilterFc(channel, note) {
|
|
1479
1504
|
const now = this.audioContext.currentTime;
|
|
1480
|
-
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc +
|
|
1505
|
+
const modLfoToFilterFc = note.voiceParams.modLfoToFilterFc +
|
|
1506
|
+
this.getLFOFilterDepth(channel);
|
|
1481
1507
|
note.filterDepth.gain
|
|
1482
1508
|
.cancelScheduledValues(now)
|
|
1483
1509
|
.setValueAtTime(modLfoToFilterFc, now);
|
|
1484
1510
|
}
|
|
1485
|
-
setModLfoToVolume(
|
|
1511
|
+
setModLfoToVolume(channel, note) {
|
|
1486
1512
|
const now = this.audioContext.currentTime;
|
|
1487
1513
|
const modLfoToVolume = note.voiceParams.modLfoToVolume;
|
|
1488
1514
|
const baseDepth = this.cbToRatio(Math.abs(modLfoToVolume)) - 1;
|
|
1489
|
-
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) *
|
|
1515
|
+
const volumeDepth = baseDepth * Math.sign(modLfoToVolume) *
|
|
1516
|
+
(1 + this.getLFOAmplitudeDepth(channel));
|
|
1490
1517
|
note.volumeDepth.gain
|
|
1491
1518
|
.cancelScheduledValues(now)
|
|
1492
1519
|
.setValueAtTime(volumeDepth, now);
|
|
@@ -1570,7 +1597,7 @@ export class MidyGM2 {
|
|
|
1570
1597
|
return {
|
|
1571
1598
|
modLfoToPitch: (channel, note, _prevValue) => {
|
|
1572
1599
|
if (0 < channel.state.modulationDepth) {
|
|
1573
|
-
this.setModLfoToPitch(channel, note
|
|
1600
|
+
this.setModLfoToPitch(channel, note);
|
|
1574
1601
|
}
|
|
1575
1602
|
},
|
|
1576
1603
|
vibLfoToPitch: (channel, note, _prevValue) => {
|
|
@@ -1580,12 +1607,12 @@ export class MidyGM2 {
|
|
|
1580
1607
|
},
|
|
1581
1608
|
modLfoToFilterFc: (channel, note, _prevValue) => {
|
|
1582
1609
|
if (0 < channel.state.modulationDepth) {
|
|
1583
|
-
this.setModLfoToFilterFc(
|
|
1610
|
+
this.setModLfoToFilterFc(channel, note);
|
|
1584
1611
|
}
|
|
1585
1612
|
},
|
|
1586
1613
|
modLfoToVolume: (channel, note, _prevValue) => {
|
|
1587
1614
|
if (0 < channel.state.modulationDepth) {
|
|
1588
|
-
this.setModLfoToVolume(
|
|
1615
|
+
this.setModLfoToVolume(channel, note);
|
|
1589
1616
|
}
|
|
1590
1617
|
},
|
|
1591
1618
|
chorusEffectsSend: (channel, note, prevValue) => {
|
|
@@ -1655,7 +1682,7 @@ export class MidyGM2 {
|
|
|
1655
1682
|
this.setPortamentoStartFilterEnvelope(channel, note);
|
|
1656
1683
|
}
|
|
1657
1684
|
else {
|
|
1658
|
-
this.setFilterEnvelope(channel, note
|
|
1685
|
+
this.setFilterEnvelope(channel, note);
|
|
1659
1686
|
}
|
|
1660
1687
|
this.setPitchEnvelope(note);
|
|
1661
1688
|
}
|
|
@@ -1669,7 +1696,7 @@ export class MidyGM2 {
|
|
|
1669
1696
|
if (key in voiceParams)
|
|
1670
1697
|
noteVoiceParams[key] = voiceParams[key];
|
|
1671
1698
|
}
|
|
1672
|
-
this.setVolumeEnvelope(
|
|
1699
|
+
this.setVolumeEnvelope(channel, note);
|
|
1673
1700
|
}
|
|
1674
1701
|
}
|
|
1675
1702
|
}
|
|
@@ -1703,10 +1730,10 @@ export class MidyGM2 {
|
|
|
1703
1730
|
127: this.polyOn,
|
|
1704
1731
|
};
|
|
1705
1732
|
}
|
|
1706
|
-
handleControlChange(channelNumber, controllerType, value) {
|
|
1733
|
+
handleControlChange(channelNumber, controllerType, value, startTime) {
|
|
1707
1734
|
const handler = this.controlChangeHandlers[controllerType];
|
|
1708
1735
|
if (handler) {
|
|
1709
|
-
handler.call(this, channelNumber, value);
|
|
1736
|
+
handler.call(this, channelNumber, value, startTime);
|
|
1710
1737
|
const channel = this.channels[channelNumber];
|
|
1711
1738
|
this.applyVoiceParams(channel, controllerType + 128);
|
|
1712
1739
|
this.applyControlTable(channel, controllerType);
|
|
@@ -1718,55 +1745,45 @@ export class MidyGM2 {
|
|
|
1718
1745
|
setBankMSB(channelNumber, msb) {
|
|
1719
1746
|
this.channels[channelNumber].bankMSB = msb;
|
|
1720
1747
|
}
|
|
1721
|
-
updateModulation(channel) {
|
|
1722
|
-
|
|
1748
|
+
updateModulation(channel, scheduleTime) {
|
|
1749
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1723
1750
|
const depth = channel.state.modulationDepth * channel.modulationDepthRange;
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
}
|
|
1732
|
-
else {
|
|
1733
|
-
this.setPitchEnvelope(note);
|
|
1734
|
-
this.startModulation(channel, note, now);
|
|
1735
|
-
}
|
|
1751
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1752
|
+
if (note.modulationDepth) {
|
|
1753
|
+
note.modulationDepth.gain.setValueAtTime(depth, scheduleTime);
|
|
1754
|
+
}
|
|
1755
|
+
else {
|
|
1756
|
+
this.setPitchEnvelope(note, scheduleTime);
|
|
1757
|
+
this.startModulation(channel, note, scheduleTime);
|
|
1736
1758
|
}
|
|
1737
1759
|
});
|
|
1738
1760
|
}
|
|
1739
|
-
setModulationDepth(channelNumber, modulation) {
|
|
1761
|
+
setModulationDepth(channelNumber, modulation, scheduleTime) {
|
|
1740
1762
|
const channel = this.channels[channelNumber];
|
|
1741
1763
|
channel.state.modulationDepth = modulation / 127;
|
|
1742
|
-
this.updateModulation(channel);
|
|
1764
|
+
this.updateModulation(channel, scheduleTime);
|
|
1743
1765
|
}
|
|
1744
1766
|
setPortamentoTime(channelNumber, portamentoTime) {
|
|
1745
1767
|
const channel = this.channels[channelNumber];
|
|
1746
1768
|
const factor = 5 * Math.log(10) / 127;
|
|
1747
1769
|
channel.state.portamentoTime = Math.exp(factor * portamentoTime);
|
|
1748
1770
|
}
|
|
1749
|
-
setKeyBasedVolume(channel) {
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
if (!note)
|
|
1755
|
-
continue;
|
|
1756
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
|
|
1757
|
-
if (keyBasedValue === 0)
|
|
1758
|
-
continue;
|
|
1771
|
+
setKeyBasedVolume(channel, scheduleTime) {
|
|
1772
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1773
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1774
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 7);
|
|
1775
|
+
if (keyBasedValue !== 0) {
|
|
1759
1776
|
note.volumeNode.gain
|
|
1760
|
-
.cancelScheduledValues(
|
|
1761
|
-
.setValueAtTime(1 + keyBasedValue,
|
|
1777
|
+
.cancelScheduledValues(scheduleTime)
|
|
1778
|
+
.setValueAtTime(1 + keyBasedValue, scheduleTime);
|
|
1762
1779
|
}
|
|
1763
1780
|
});
|
|
1764
1781
|
}
|
|
1765
|
-
setVolume(channelNumber, volume) {
|
|
1782
|
+
setVolume(channelNumber, volume, scheduleTime) {
|
|
1766
1783
|
const channel = this.channels[channelNumber];
|
|
1767
1784
|
channel.state.volume = volume / 127;
|
|
1768
|
-
this.updateChannelVolume(channel);
|
|
1769
|
-
this.setKeyBasedVolume(channel);
|
|
1785
|
+
this.updateChannelVolume(channel, scheduleTime);
|
|
1786
|
+
this.setKeyBasedVolume(channel, scheduleTime);
|
|
1770
1787
|
}
|
|
1771
1788
|
panToGain(pan) {
|
|
1772
1789
|
const theta = Math.PI / 2 * Math.max(0, pan * 127 - 1) / 126;
|
|
@@ -1775,36 +1792,31 @@ export class MidyGM2 {
|
|
|
1775
1792
|
gainRight: Math.sin(theta),
|
|
1776
1793
|
};
|
|
1777
1794
|
}
|
|
1778
|
-
setKeyBasedPan(channel) {
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
if (!note)
|
|
1784
|
-
continue;
|
|
1785
|
-
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
|
|
1786
|
-
if (keyBasedValue === 0)
|
|
1787
|
-
continue;
|
|
1795
|
+
setKeyBasedPan(channel, scheduleTime) {
|
|
1796
|
+
scheduleTime ??= this.audioContext.currentTime;
|
|
1797
|
+
this.processScheduledNotes(channel, scheduleTime, (note) => {
|
|
1798
|
+
const keyBasedValue = this.getKeyBasedInstrumentControlValue(channel, note.noteNumber, 10);
|
|
1799
|
+
if (keyBasedValue !== 0) {
|
|
1788
1800
|
const { gainLeft, gainRight } = this.panToGain((keyBasedValue + 1) / 2);
|
|
1789
1801
|
note.gainL.gain
|
|
1790
|
-
.cancelScheduledValues(
|
|
1791
|
-
.setValueAtTime(gainLeft,
|
|
1802
|
+
.cancelScheduledValues(scheduleTime)
|
|
1803
|
+
.setValueAtTime(gainLeft, scheduleTime);
|
|
1792
1804
|
note.gainR.gain
|
|
1793
|
-
.cancelScheduledValues(
|
|
1794
|
-
.setValueAtTime(gainRight,
|
|
1805
|
+
.cancelScheduledValues(scheduleTime)
|
|
1806
|
+
.setValueAtTime(gainRight, scheduleTime);
|
|
1795
1807
|
}
|
|
1796
1808
|
});
|
|
1797
1809
|
}
|
|
1798
|
-
setPan(channelNumber, pan) {
|
|
1810
|
+
setPan(channelNumber, pan, scheduleTime) {
|
|
1799
1811
|
const channel = this.channels[channelNumber];
|
|
1800
1812
|
channel.state.pan = pan / 127;
|
|
1801
|
-
this.updateChannelVolume(channel);
|
|
1802
|
-
this.setKeyBasedPan(channel);
|
|
1813
|
+
this.updateChannelVolume(channel, scheduleTime);
|
|
1814
|
+
this.setKeyBasedPan(channel, scheduleTime);
|
|
1803
1815
|
}
|
|
1804
|
-
setExpression(channelNumber, expression) {
|
|
1816
|
+
setExpression(channelNumber, expression, scheduleTime) {
|
|
1805
1817
|
const channel = this.channels[channelNumber];
|
|
1806
1818
|
channel.state.expression = expression / 127;
|
|
1807
|
-
this.updateChannelVolume(channel);
|
|
1819
|
+
this.updateChannelVolume(channel, scheduleTime);
|
|
1808
1820
|
}
|
|
1809
1821
|
setBankLSB(channelNumber, lsb) {
|
|
1810
1822
|
this.channels[channelNumber].bankLSB = lsb;
|
|
@@ -2432,42 +2444,46 @@ export class MidyGM2 {
|
|
|
2432
2444
|
this.updateChannelDetune(channel);
|
|
2433
2445
|
}
|
|
2434
2446
|
}
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2447
|
+
getFilterCutoffControl(channel) {
|
|
2448
|
+
const channelPressure = (channel.channelPressureTable[1] - 64) *
|
|
2449
|
+
channel.state.channelPressure;
|
|
2450
|
+
return channelPressure * 15;
|
|
2451
|
+
}
|
|
2452
|
+
getAmplitudeControl(channel) {
|
|
2453
|
+
const channelPressure = channel.channelPressureTable[2] *
|
|
2454
|
+
channel.state.channelPressure;
|
|
2455
|
+
return channelPressure / 64;
|
|
2456
|
+
}
|
|
2457
|
+
getLFOPitchDepth(channel) {
|
|
2458
|
+
const channelPressure = channel.channelPressureTable[3] *
|
|
2459
|
+
channel.state.channelPressure;
|
|
2460
|
+
return channelPressure / 127 * 600;
|
|
2461
|
+
}
|
|
2462
|
+
getLFOFilterDepth(channel) {
|
|
2463
|
+
const channelPressure = channel.channelPressureTable[4] *
|
|
2464
|
+
channel.state.channelPressure;
|
|
2465
|
+
return channelPressure / 127 * 2400;
|
|
2466
|
+
}
|
|
2467
|
+
getLFOAmplitudeDepth(channel) {
|
|
2468
|
+
const channelPressure = channel.channelPressureTable[5] *
|
|
2469
|
+
channel.state.channelPressure;
|
|
2470
|
+
return channelPressure / 127;
|
|
2471
|
+
}
|
|
2472
|
+
setControllerParameters(channel, note, table) {
|
|
2473
|
+
if (table[0] !== 64)
|
|
2474
|
+
this.updateDetune(channel, note);
|
|
2439
2475
|
if (!note.portamento) {
|
|
2440
|
-
if (table[1] !== 64)
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
}
|
|
2452
|
-
}
|
|
2453
|
-
if (table[3] !== 0) {
|
|
2454
|
-
const channelPressure = channel.channelPressureTable[3] *
|
|
2455
|
-
channel.state.channelPressure;
|
|
2456
|
-
const pressure = channelPressure / 127 * 600;
|
|
2457
|
-
this.setModLfoToPitch(channel, note, pressure);
|
|
2458
|
-
}
|
|
2459
|
-
if (table[4] !== 0) {
|
|
2460
|
-
const channelPressure = channel.channelPressureTable[4] *
|
|
2461
|
-
channel.state.channelPressure;
|
|
2462
|
-
const pressure = channelPressure / 127 * 2400;
|
|
2463
|
-
this.setModLfoToFilterFc(note, pressure);
|
|
2464
|
-
}
|
|
2465
|
-
if (table[5] !== 0) {
|
|
2466
|
-
const channelPressure = channel.channelPressureTable[5] *
|
|
2467
|
-
channel.state.channelPressure;
|
|
2468
|
-
const pressure = channelPressure / 127;
|
|
2469
|
-
this.setModLfoToVolume(note, pressure);
|
|
2470
|
-
}
|
|
2476
|
+
if (table[1] !== 64)
|
|
2477
|
+
this.setFilterEnvelope(channel, note);
|
|
2478
|
+
if (table[2] !== 64)
|
|
2479
|
+
this.setVolumeEnvelope(channel, note);
|
|
2480
|
+
}
|
|
2481
|
+
if (table[3] !== 0)
|
|
2482
|
+
this.setModLfoToPitch(channel, note);
|
|
2483
|
+
if (table[4] !== 0)
|
|
2484
|
+
this.setModLfoToFilterFc(channel, note);
|
|
2485
|
+
if (table[5] !== 0)
|
|
2486
|
+
this.setModLfoToVolume(channel, note);
|
|
2471
2487
|
}
|
|
2472
2488
|
handleChannelPressureSysEx(data, tableName) {
|
|
2473
2489
|
const channelNumber = data[4];
|
|
@@ -2498,7 +2514,7 @@ export class MidyGM2 {
|
|
|
2498
2514
|
const note = noteList[i];
|
|
2499
2515
|
if (!note)
|
|
2500
2516
|
continue;
|
|
2501
|
-
this.
|
|
2517
|
+
this.setControllerParameters(channel, note, table);
|
|
2502
2518
|
}
|
|
2503
2519
|
});
|
|
2504
2520
|
}
|
|
@@ -2561,9 +2577,6 @@ Object.defineProperty(MidyGM2, "channelSettings", {
|
|
|
2561
2577
|
value: {
|
|
2562
2578
|
currentBufferSource: null,
|
|
2563
2579
|
detune: 0,
|
|
2564
|
-
scaleOctaveTuningTable: new Int8Array(12), // [-64, 63] cent
|
|
2565
|
-
channelPressureTable: new Uint8Array([64, 64, 64, 0, 0, 0]),
|
|
2566
|
-
keyBasedInstrumentControlTable: new Int8Array(128 * 128), // [-64, 63]
|
|
2567
2580
|
program: 0,
|
|
2568
2581
|
bank: 121 * 128,
|
|
2569
2582
|
bankMSB: 121,
|
package/esm/midy-GMLite.d.ts
CHANGED
|
@@ -45,11 +45,11 @@ export class MidyGMLite {
|
|
|
45
45
|
freqVibLFO: (_channel: any, _note: any, _prevValue: any) => void;
|
|
46
46
|
};
|
|
47
47
|
controlChangeHandlers: {
|
|
48
|
-
1: (channelNumber: any, modulation: any) => void;
|
|
48
|
+
1: (channelNumber: any, modulation: any, scheduleTime: any) => void;
|
|
49
49
|
6: (channelNumber: any, value: any) => void;
|
|
50
|
-
7: (channelNumber: any, volume: any) => void;
|
|
51
|
-
10: (channelNumber: any, pan: any) => void;
|
|
52
|
-
11: (channelNumber: any, expression: any) => void;
|
|
50
|
+
7: (channelNumber: any, volume: any, scheduleTime: any) => void;
|
|
51
|
+
10: (channelNumber: any, pan: any, scheduleTime: any) => void;
|
|
52
|
+
11: (channelNumber: any, expression: any, scheduleTime: any) => void;
|
|
53
53
|
38: (channelNumber: any, value: any) => void;
|
|
54
54
|
64: (channelNumber: any, value: any) => void;
|
|
55
55
|
100: (channelNumber: any, value: any) => void;
|
|
@@ -90,6 +90,7 @@ export class MidyGMLite {
|
|
|
90
90
|
seekTo(second: any): void;
|
|
91
91
|
calcTotalTime(): number;
|
|
92
92
|
currentTime(): number;
|
|
93
|
+
processScheduledNotes(channel: any, scheduleTime: any, callback: any): void;
|
|
93
94
|
getActiveNotes(channel: any, time: any): SparseMap;
|
|
94
95
|
getActiveNote(noteList: any, time: any): any;
|
|
95
96
|
cbToRatio(cb: any): number;
|
|
@@ -100,7 +101,7 @@ export class MidyGMLite {
|
|
|
100
101
|
updateChannelDetune(channel: any): void;
|
|
101
102
|
updateDetune(channel: any, note: any): void;
|
|
102
103
|
setVolumeEnvelope(note: any): void;
|
|
103
|
-
setPitchEnvelope(note: any): void;
|
|
104
|
+
setPitchEnvelope(note: any, scheduleTime: any): void;
|
|
104
105
|
clampCutoffFrequency(frequency: any): number;
|
|
105
106
|
setFilterEnvelope(note: any): void;
|
|
106
107
|
startModulation(channel: any, note: any, startTime: any): void;
|
|
@@ -136,11 +137,11 @@ export class MidyGMLite {
|
|
|
136
137
|
getControllerState(channel: any, noteNumber: any, velocity: any): Float32Array<any>;
|
|
137
138
|
applyVoiceParams(channel: any, controllerType: any): void;
|
|
138
139
|
createControlChangeHandlers(): {
|
|
139
|
-
1: (channelNumber: any, modulation: any) => void;
|
|
140
|
+
1: (channelNumber: any, modulation: any, scheduleTime: any) => void;
|
|
140
141
|
6: (channelNumber: any, value: any) => void;
|
|
141
|
-
7: (channelNumber: any, volume: any) => void;
|
|
142
|
-
10: (channelNumber: any, pan: any) => void;
|
|
143
|
-
11: (channelNumber: any, expression: any) => void;
|
|
142
|
+
7: (channelNumber: any, volume: any, scheduleTime: any) => void;
|
|
143
|
+
10: (channelNumber: any, pan: any, scheduleTime: any) => void;
|
|
144
|
+
11: (channelNumber: any, expression: any, scheduleTime: any) => void;
|
|
144
145
|
38: (channelNumber: any, value: any) => void;
|
|
145
146
|
64: (channelNumber: any, value: any) => void;
|
|
146
147
|
100: (channelNumber: any, value: any) => void;
|
|
@@ -149,18 +150,18 @@ export class MidyGMLite {
|
|
|
149
150
|
121: (channelNumber: any) => void;
|
|
150
151
|
123: (channelNumber: any) => Promise<void>;
|
|
151
152
|
};
|
|
152
|
-
handleControlChange(channelNumber: any, controllerType: any, value: any): void;
|
|
153
|
-
updateModulation(channel: any): void;
|
|
154
|
-
setModulationDepth(channelNumber: any, modulation: any): void;
|
|
155
|
-
setVolume(channelNumber: any, volume: any): void;
|
|
153
|
+
handleControlChange(channelNumber: any, controllerType: any, value: any, startTime: any): void;
|
|
154
|
+
updateModulation(channel: any, scheduleTime: any): void;
|
|
155
|
+
setModulationDepth(channelNumber: any, modulation: any, scheduleTime: any): void;
|
|
156
|
+
setVolume(channelNumber: any, volume: any, scheduleTime: any): void;
|
|
156
157
|
panToGain(pan: any): {
|
|
157
158
|
gainLeft: number;
|
|
158
159
|
gainRight: number;
|
|
159
160
|
};
|
|
160
|
-
setPan(channelNumber: any, pan: any): void;
|
|
161
|
-
setExpression(channelNumber: any, expression: any): void;
|
|
161
|
+
setPan(channelNumber: any, pan: any, scheduleTime: any): void;
|
|
162
|
+
setExpression(channelNumber: any, expression: any, scheduleTime: any): void;
|
|
162
163
|
dataEntryLSB(channelNumber: any, value: any): void;
|
|
163
|
-
updateChannelVolume(channel: any): void;
|
|
164
|
+
updateChannelVolume(channel: any, scheduleTime: any): void;
|
|
164
165
|
setSustainPedal(channelNumber: any, value: any): void;
|
|
165
166
|
limitData(channel: any, minMSB: any, maxMSB: any, minLSB: any, maxLSB: any): void;
|
|
166
167
|
handleRPN(channelNumber: any): void;
|
|
@@ -198,6 +199,7 @@ declare class Note {
|
|
|
198
199
|
constructor(noteNumber: any, velocity: any, startTime: any, voice: any, voiceParams: any);
|
|
199
200
|
bufferSource: any;
|
|
200
201
|
filterNode: any;
|
|
202
|
+
filterDepth: any;
|
|
201
203
|
volumeEnvelopeNode: any;
|
|
202
204
|
volumeDepth: any;
|
|
203
205
|
modulationLFO: any;
|
package/esm/midy-GMLite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"midy-GMLite.d.ts","sourceRoot":"","sources":["../src/midy-GMLite.js"],"names":[],"mappings":"AAiJA;IAsBE;;;;;;;;;MASE;IAEF,+BAQC;IAxCD,qBAAmB;IACnB,kBAAc;IACd,0BAAwB;IACxB,kBAAc;IACd,mBAAiB;IACjB,kBAAc;IACd,mBAAe;IACf,kBAAgB;IAChB,sBAA2C;IAC3C,kCAA+B;IAC/B,gCAA6B;IAC7B,mBAAkB;IAClB,mBAAkB;IAClB,kBAAiB;IACjB,oBAAmB;IACnB,mBAAkB;IAClB,gBAAc;IACd,mBAAiB;IACjB,oBAAkB;IAClB,6BAAuC;IAcrC,kBAAgC;IAChC,kBAA8C;IAC9C;;;;;;;;;;;MAA2D;IAC3D;;;;;;;;;;;;;MAA+D;IAC/D,gBAAiD;IAKnD,4BAMC;IAED,mCAWC;IAED,gDAMC;IAED,sCASC;IAED;;;;MAeC;IAED,yCAUC;IAED,6DA2BC;IAED,8DASC;IAED,2EAqDC;IAED,mCAOC;IAED,0BAoDC;IAED,uDAEC;IAED,wDAEC;IAED,6EAEC;IAED;;;MA4EC;IAED,+EAmBC;IAED,qDAKC;IAED,uBAKC;IAED,aAGC;IAED,cAKC;IAED,wBAIC;IAED,0BAKC;IAED,wBAOC;IAED,sBAGC;IAED,4EASC;IAED,mDASC;IAED,6CAQC;IAED,2BAEC;IAED,8BAEC;IAED,8BAEC;IAED,4BAEC;IAED,wCAIC;IAED,wCAQC;IAED,4CAKC;IAED,mCAgBC;IAED,qDAqBC;IAED,6CAIC;IAED,mCAuBC;IAED,+DAoBC;IAED,yGAgBC;IAED,gHAuCC;IAED,kGAgDC;IAED,0EAGC;IAED,qFAwBC;IAED,6HAuBC;IAED,0FAGC;IAED,kEAeC;IAED,gFAiBC;IAED,4DAGC;IAED,qEAGC;IAED,mDASC;IAED,gDASC;IAED,qCAMC;IAED,mCAQC;IAED,gCAOC;IAED,+BAMC;IAED;;;;;;;;;;;MAqBC;IAED,oFAMC;IAED,0DA6CC;IAED;;;;;;;;;;;;;MAeC;IAED,+FAWC;IAED,wDAWC;IAED,iFAIC;IAED,oEAIC;IAED;;;MAMC;IAED,8DAIC;IAED,4EAIC;IAED,mDAGC;IAED,2DAWC;IAED,sDAKC;IAED,kFAeC;IAED,oCAYC;IAED,gDAEC;IAED,gDAEC;IAED,mDAGC;IAED,kDAKC;IAED,wDASC;IAED,+CAEC;IAED,8CAqBC;IAED,+CAEC;IAED,4DAgBC;IAED,oBAMC;IAED,yDAaC;IAED,yCAGC;IAED,mCAQC;IAED,wCAEC;IAED,6BASC;IAED,0DAUC;CACF;AAt1CD;IACE,uBAGC;IAFC,YAA2B;IAC3B,qBAAuB;IAGzB,gCAKC;IAED,mBAEC;IAED,0BAUC;IAED,uBAEC;IAED,mBAEC;IAED,cAMC;IASD,6BAKC;IAZD,qDAKC;CAQF;AAED;IASE,0FAMC;IAdD,kBAAa;IACb,gBAAW;IACX,iBAAY;IACZ,wBAAmB;IACnB,iBAAY;IACZ,mBAAc;IACd,qBAAgB;IAGd,gBAA4B;IAC5B,cAAwB;IACxB,eAA0B;IAC1B,WAAkB;IAClB,iBAA8B;CAEjC"}
|