spessasynth_core 4.3.6 → 4.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/dist/index.d.ts +621 -453
- package/dist/index.js +765 -345
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1124,7 +1124,11 @@ const NonRegisteredLSB = {
|
|
|
1124
1124
|
};
|
|
1125
1125
|
//#endregion
|
|
1126
1126
|
//#region src/midi/midi_message.ts
|
|
1127
|
-
|
|
1127
|
+
/**
|
|
1128
|
+
* Midi_message.ts
|
|
1129
|
+
* purpose: contains enums for midi events and controllers and functions to parse them
|
|
1130
|
+
*/
|
|
1131
|
+
var MIDIMessage = class MIDIMessage {
|
|
1128
1132
|
/**
|
|
1129
1133
|
* Absolute number of MIDI ticks from the start of the track.
|
|
1130
1134
|
*/
|
|
@@ -1148,6 +1152,68 @@ var MIDIMessage = class {
|
|
|
1148
1152
|
this.statusByte = byte;
|
|
1149
1153
|
this.data = data;
|
|
1150
1154
|
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Returns a new MIDI Pitch Wheel message.
|
|
1157
|
+
* @param ticks time of this message in absolute MIDI ticks.
|
|
1158
|
+
* @param channel the channel number of this message.
|
|
1159
|
+
* @param value the new value, between 0 and 16383, where 8192 is the center (no pitch change).
|
|
1160
|
+
*/
|
|
1161
|
+
static pitchWheel(ticks, channel, value) {
|
|
1162
|
+
return new MIDIMessage(ticks, MIDIMessageTypes.pitchWheel | channel % 16, new Uint8Array([value & 127, value >> 7 & 127]));
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Returns a new MIDI Channel Pressure message.
|
|
1166
|
+
* @param ticks time of this message in absolute MIDI ticks.
|
|
1167
|
+
* @param channel the channel number of this message.
|
|
1168
|
+
* @param value the new value, between 0 and 127.
|
|
1169
|
+
*/
|
|
1170
|
+
static channelPressure(ticks, channel, value) {
|
|
1171
|
+
return new MIDIMessage(ticks, MIDIMessageTypes.channelPressure | channel % 16, new Uint8Array([value]));
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Returns a new MIDI Program Change message.
|
|
1175
|
+
* @param ticks time of this message in absolute MIDI ticks.
|
|
1176
|
+
* @param channel the channel number of this message.
|
|
1177
|
+
* @param program the new MIDI program number, between 0 and 127.
|
|
1178
|
+
*/
|
|
1179
|
+
static programChange(ticks, channel, program) {
|
|
1180
|
+
return new MIDIMessage(ticks, MIDIMessageTypes.programChange | channel % 16, new Uint8Array([program]));
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Returns a new MIDI Controller Change message.
|
|
1184
|
+
* @param ticks time of this message in absolute MIDI ticks.
|
|
1185
|
+
* @param channel the channel number of this message.
|
|
1186
|
+
* @param controller the MIDI controller.
|
|
1187
|
+
* @param value the new value.
|
|
1188
|
+
*/
|
|
1189
|
+
static controllerChange(ticks, channel, controller, value) {
|
|
1190
|
+
return new MIDIMessage(ticks, MIDIMessageTypes.controllerChange | channel % 16, new Uint8Array([controller, value]));
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Returns a new MIDI System Exclusive message.
|
|
1194
|
+
* @param ticks time of this message in absolute MIDI ticks.
|
|
1195
|
+
* @param data the data of the system exclusive message,
|
|
1196
|
+
* excluding the starting 0xF0 byte.
|
|
1197
|
+
*/
|
|
1198
|
+
static systemExclusive(ticks, data) {
|
|
1199
|
+
return new MIDIMessage(ticks, MIDIMessageTypes.systemExclusive, new Uint8Array(data));
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Returns a new MIDI Registered Parameter message. Sends both data MSB and LSB.
|
|
1203
|
+
* @param ticks time of this message in absolute MIDI ticks.
|
|
1204
|
+
* @param channel the channel number of this message.
|
|
1205
|
+
* @param parameter the 14-bit MIDI registered parameter number.
|
|
1206
|
+
* @param value the 14-bit new value.
|
|
1207
|
+
*/
|
|
1208
|
+
static registeredParameter(ticks, channel, parameter, value) {
|
|
1209
|
+
if (parameter > 16383 || parameter < 0 || value > 16383 || value < 0) throw new Error("Parameter and value must be between 0 and 16383.");
|
|
1210
|
+
return [
|
|
1211
|
+
MIDIMessage.controllerChange(ticks, channel, MIDIControllers.registeredParameterMSB, parameter >> 7),
|
|
1212
|
+
MIDIMessage.controllerChange(ticks, channel, MIDIControllers.registeredParameterLSB, parameter & 127),
|
|
1213
|
+
MIDIMessage.controllerChange(ticks, channel, MIDIControllers.dataEntryMSB, value >> 7),
|
|
1214
|
+
MIDIMessage.controllerChange(ticks, channel, MIDIControllers.dataEntryLSB, value & 127)
|
|
1215
|
+
];
|
|
1216
|
+
}
|
|
1151
1217
|
};
|
|
1152
1218
|
//#endregion
|
|
1153
1219
|
//#region src/midi/write/midi.ts
|
|
@@ -1494,16 +1560,30 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1494
1560
|
static analyzeRPN(channel, rpn, value) {
|
|
1495
1561
|
switch (rpn) {
|
|
1496
1562
|
default: return OTHER;
|
|
1563
|
+
case RegisteredParameterTypes.pitchWheelRange: return {
|
|
1564
|
+
type: "Channel MIDI Param",
|
|
1565
|
+
channel,
|
|
1566
|
+
parameter: "pitchWheelRange",
|
|
1567
|
+
value: value / 128
|
|
1568
|
+
};
|
|
1497
1569
|
case RegisteredParameterTypes.fineTuning: return {
|
|
1498
|
-
type: "
|
|
1570
|
+
type: "Channel MIDI Param",
|
|
1499
1571
|
channel,
|
|
1572
|
+
parameter: "fineTune",
|
|
1500
1573
|
value: (value - 8192) / 81.92
|
|
1501
1574
|
};
|
|
1502
1575
|
case RegisteredParameterTypes.coarseTuning: return {
|
|
1503
|
-
type: "
|
|
1576
|
+
type: "Channel MIDI Param",
|
|
1504
1577
|
channel,
|
|
1578
|
+
parameter: "keyShift",
|
|
1505
1579
|
value: (value >> 7) - 64
|
|
1506
1580
|
};
|
|
1581
|
+
case RegisteredParameterTypes.modulationDepth: return {
|
|
1582
|
+
type: "Channel MIDI Param",
|
|
1583
|
+
channel,
|
|
1584
|
+
parameter: "modulationDepth",
|
|
1585
|
+
value: value / 1.28
|
|
1586
|
+
};
|
|
1507
1587
|
}
|
|
1508
1588
|
}
|
|
1509
1589
|
/**
|
|
@@ -1579,6 +1659,104 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1579
1659
|
}
|
|
1580
1660
|
}
|
|
1581
1661
|
/**
|
|
1662
|
+
* Returns a list of MIDI events needed to set the given parameter.
|
|
1663
|
+
* @param ticks The ticks for all events.
|
|
1664
|
+
* @param system If the message has multiple ways of setting it,
|
|
1665
|
+
* this selects the preferred way. Otherwise, it prefers Universal (GM).
|
|
1666
|
+
* @param parameter The parameter to set.
|
|
1667
|
+
* @param value The value to set it to.
|
|
1668
|
+
*/
|
|
1669
|
+
static setGlobalMIDIParameter(ticks, system, parameter, value) {
|
|
1670
|
+
switch (parameter) {
|
|
1671
|
+
case "system": return [MIDIUtils.reset(ticks, value)];
|
|
1672
|
+
case "keyShift": switch (system) {
|
|
1673
|
+
default: return [MIDIUtils.deviceControlMessage(ticks, 4, [0, value + 64])];
|
|
1674
|
+
case "xg": return [MIDIUtils.xgMessage(ticks, 0, 0, 6, [value + 64])];
|
|
1675
|
+
case "gs": return [MIDIUtils.gsMessage(ticks, 64, 0, 5, [value + 64])];
|
|
1676
|
+
}
|
|
1677
|
+
case "fineTune": switch (system) {
|
|
1678
|
+
default: {
|
|
1679
|
+
const tuneValue = Math.floor(value * 81.92 + 8192);
|
|
1680
|
+
return [MIDIUtils.deviceControlMessage(ticks, 3, [tuneValue & 127, tuneValue >> 7 & 127])];
|
|
1681
|
+
}
|
|
1682
|
+
case "xg": {
|
|
1683
|
+
const tuneValue = Math.floor(value * 10 + 1024);
|
|
1684
|
+
return [MIDIUtils.xgMessage(ticks, 0, 0, 0, [
|
|
1685
|
+
tuneValue >> 12 & 15,
|
|
1686
|
+
tuneValue >> 8 & 15,
|
|
1687
|
+
tuneValue >> 4 & 15,
|
|
1688
|
+
tuneValue & 15
|
|
1689
|
+
])];
|
|
1690
|
+
}
|
|
1691
|
+
case "gs": {
|
|
1692
|
+
const tuneValue = Math.floor(value * 10 + 1024);
|
|
1693
|
+
return [MIDIUtils.gsMessage(ticks, 64, 0, 0, [
|
|
1694
|
+
tuneValue >> 12 & 15,
|
|
1695
|
+
tuneValue >> 8 & 15,
|
|
1696
|
+
tuneValue >> 4 & 15,
|
|
1697
|
+
tuneValue & 15
|
|
1698
|
+
])];
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
case "gain": switch (system) {
|
|
1702
|
+
default: {
|
|
1703
|
+
const gainValue = Math.floor(Math.sqrt(value) * 16383);
|
|
1704
|
+
return [MIDIUtils.deviceControlMessage(ticks, 1, [gainValue & 127, gainValue >> 7 & 127])];
|
|
1705
|
+
}
|
|
1706
|
+
case "xg": {
|
|
1707
|
+
const gainValue = Math.floor(value * 127);
|
|
1708
|
+
return [MIDIUtils.xgMessage(ticks, 0, 0, 4, [gainValue])];
|
|
1709
|
+
}
|
|
1710
|
+
case "gs": {
|
|
1711
|
+
const gainValue = Math.floor(value * 127);
|
|
1712
|
+
return [MIDIUtils.gsMessage(ticks, 64, 0, 4, [gainValue])];
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
case "pan": switch (system) {
|
|
1716
|
+
default: {
|
|
1717
|
+
const balance = Math.floor(value * 8192) + 8192;
|
|
1718
|
+
return [MIDIUtils.deviceControlMessage(ticks, 2, [balance & 127, balance >> 7 & 127])];
|
|
1719
|
+
}
|
|
1720
|
+
case "gs": {
|
|
1721
|
+
const balance = Math.floor(value * 63) + 64;
|
|
1722
|
+
return [MIDIUtils.gsMessage(ticks, 64, 0, 6, [balance])];
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Returns a list of MIDI events needed to set the given parameter.
|
|
1729
|
+
* @param ticks The ticks for all events.
|
|
1730
|
+
* @param channel The channel number.
|
|
1731
|
+
* @param system If the message has multiple ways of setting it,
|
|
1732
|
+
* this selects the preferred way. Otherwise, it prefers Universal (GM).
|
|
1733
|
+
* @param parameter The parameter to set.
|
|
1734
|
+
* @param value The value to set it to.
|
|
1735
|
+
* @returns The list of `MIDIMessage`s that set the parameter.
|
|
1736
|
+
*/
|
|
1737
|
+
static setChannelMIDIParameter(ticks, channel, system, parameter, value) {
|
|
1738
|
+
channel %= 16;
|
|
1739
|
+
const gsChannel = MIDIUtils.channelToSyx(channel);
|
|
1740
|
+
switch (parameter) {
|
|
1741
|
+
case "pressure": return [MIDIMessage.channelPressure(ticks, channel, value)];
|
|
1742
|
+
case "pitchWheel": return [MIDIMessage.pitchWheel(ticks, channel, value)];
|
|
1743
|
+
case "pitchWheelRange": return MIDIMessage.registeredParameter(ticks, channel, RegisteredParameterTypes.pitchWheelRange, Math.floor(value * 128));
|
|
1744
|
+
case "modulationDepth": return MIDIMessage.registeredParameter(ticks, channel, RegisteredParameterTypes.modulationDepth, Math.floor(value * 1.28));
|
|
1745
|
+
case "rxChannel": return system === "xg" ? [MIDIUtils.xgMessage(ticks, 8, channel, 4, [value])] : [MIDIUtils.gsMessage(ticks, 64, 16 | gsChannel, 2, [value])];
|
|
1746
|
+
case "polyMode": return value ? [MIDIMessage.controllerChange(ticks, channel, MIDIControllers.polyModeOn, 0)] : [MIDIMessage.controllerChange(ticks, channel, MIDIControllers.monoModeOn, 0)];
|
|
1747
|
+
case "keyShift": return MIDIMessage.registeredParameter(ticks, channel, RegisteredParameterTypes.coarseTuning, value + 64 << 7);
|
|
1748
|
+
case "fineTune": return MIDIMessage.registeredParameter(ticks, channel, RegisteredParameterTypes.fineTuning, Math.floor(value * 81.92 + 8192));
|
|
1749
|
+
case "randomPan": return system === "xg" ? [MIDIUtils.xgMessage(ticks, 8, channel, 14, [0])] : [MIDIUtils.gsMessage(ticks, 64, 16 | gsChannel, 28, [0])];
|
|
1750
|
+
case "assignMode": return [MIDIUtils.gsMessage(ticks, 64, 16 | gsChannel, 20, [value])];
|
|
1751
|
+
case "efxAssign": return [MIDIUtils.gsMessage(ticks, 64, 16 | gsChannel, 34, [value])];
|
|
1752
|
+
case "cc1": return [MIDIUtils.gsMessage(ticks, 64, 16 | gsChannel, 31, [value])];
|
|
1753
|
+
case "cc2": return [MIDIUtils.gsMessage(ticks, 64, 16 | gsChannel, 32, [value])];
|
|
1754
|
+
case "drumMap": return [MIDIUtils.gsMessage(ticks, 64, 16 | gsChannel, 21, [value])];
|
|
1755
|
+
case "velocitySenseDepth": return system === "xg" ? [MIDIUtils.xgMessage(ticks, 8, channel, 12, [value])] : [MIDIUtils.gsMessage(ticks, 64, 16 | gsChannel, 26, [value])];
|
|
1756
|
+
case "velocitySenseOffset": return system === "xg" ? [MIDIUtils.xgMessage(ticks, 8, channel, 13, [value])] : [MIDIUtils.gsMessage(ticks, 64, 16 | gsChannel, 27, [value])];
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
/**
|
|
1582
1760
|
* Converts GS/XG "part number" to MIDI channel number.
|
|
1583
1761
|
* @param part The part number.
|
|
1584
1762
|
*/
|
|
@@ -1633,7 +1811,7 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1633
1811
|
* @param a3 Address 3
|
|
1634
1812
|
* @param data Data, can be multiple bytes.
|
|
1635
1813
|
*/
|
|
1636
|
-
static
|
|
1814
|
+
static gs(a1, a2, a3, data) {
|
|
1637
1815
|
const checksum = 128 - (a1 + a2 + a3 + data.reduce((sum, cur) => sum + cur, 0)) % 128 & 127;
|
|
1638
1816
|
return [
|
|
1639
1817
|
65,
|
|
@@ -1657,36 +1835,112 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1657
1835
|
* @param data Data, can be multiple bytes.
|
|
1658
1836
|
*/
|
|
1659
1837
|
static gsMessage(ticks, a1, a2, a3, data) {
|
|
1660
|
-
return
|
|
1838
|
+
return MIDIMessage.systemExclusive(ticks, this.gs(a1, a2, a3, data));
|
|
1661
1839
|
}
|
|
1662
1840
|
/**
|
|
1663
|
-
* Gets
|
|
1841
|
+
* Gets raw XG System Exclusive message bytes, without the 0xF0 status byte.
|
|
1842
|
+
* @param a1 Address 1
|
|
1843
|
+
* @param a2 Address 2
|
|
1844
|
+
* @param a3 Address 3
|
|
1845
|
+
* @param data Data, can be multiple bytes.
|
|
1846
|
+
*/
|
|
1847
|
+
static xg(a1, a2, a3, data) {
|
|
1848
|
+
return [
|
|
1849
|
+
67,
|
|
1850
|
+
16,
|
|
1851
|
+
76,
|
|
1852
|
+
a1,
|
|
1853
|
+
a2,
|
|
1854
|
+
a3,
|
|
1855
|
+
...data,
|
|
1856
|
+
247
|
|
1857
|
+
];
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Gets a XG System Exclusive MIDI message.
|
|
1664
1861
|
* @param ticks The tick time of the message.
|
|
1665
|
-
* @param
|
|
1666
|
-
* @param
|
|
1667
|
-
*
|
|
1862
|
+
* @param a1 Address 1
|
|
1863
|
+
* @param a2 Address 2
|
|
1864
|
+
* @param a3 Address 3
|
|
1865
|
+
* @param data Data, can be multiple bytes.
|
|
1866
|
+
*/
|
|
1867
|
+
static xgMessage(ticks, a1, a2, a3, data) {
|
|
1868
|
+
return MIDIMessage.systemExclusive(ticks, this.xg(a1, a2, a3, data));
|
|
1869
|
+
}
|
|
1870
|
+
/**
|
|
1871
|
+
* Gets a raw Device Control System Exclusive message bytes, without the 0xF0 status byte.
|
|
1872
|
+
* @param subID The sub ID.
|
|
1873
|
+
* @param data Data, can be multiple bytes.
|
|
1668
1874
|
*/
|
|
1669
|
-
static
|
|
1670
|
-
|
|
1671
|
-
|
|
1875
|
+
static deviceControl(subID, data) {
|
|
1876
|
+
return [
|
|
1877
|
+
127,
|
|
1878
|
+
127,
|
|
1879
|
+
4,
|
|
1880
|
+
subID,
|
|
1881
|
+
...data,
|
|
1882
|
+
247
|
|
1883
|
+
];
|
|
1672
1884
|
}
|
|
1673
1885
|
/**
|
|
1674
|
-
* Gets a
|
|
1886
|
+
* Gets a Device Control System Exclusive MIDI message.
|
|
1675
1887
|
* @param ticks The tick time of the message.
|
|
1888
|
+
* @param subID The sub ID.
|
|
1889
|
+
* @param data Data, can be multiple bytes.
|
|
1676
1890
|
*/
|
|
1677
|
-
static
|
|
1678
|
-
return
|
|
1891
|
+
static deviceControlMessage(ticks, subID, data) {
|
|
1892
|
+
return MIDIMessage.systemExclusive(ticks, this.deviceControl(subID, data));
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Gets a selected reset System Exclusive MIDI message.
|
|
1896
|
+
* @param ticks The tick time of the message.
|
|
1897
|
+
* @param system The system to reset into.
|
|
1898
|
+
*/
|
|
1899
|
+
static reset(ticks, system) {
|
|
1900
|
+
switch (system) {
|
|
1901
|
+
case "gs": return this.gsMessage(ticks, 64, 0, 127, [0]);
|
|
1902
|
+
case "xg": return this.xgMessage(ticks, 0, 0, 126, [0]);
|
|
1903
|
+
case "gm": return MIDIMessage.systemExclusive(ticks, [
|
|
1904
|
+
126,
|
|
1905
|
+
127,
|
|
1906
|
+
9,
|
|
1907
|
+
1,
|
|
1908
|
+
127
|
|
1909
|
+
]);
|
|
1910
|
+
case "gm2": return MIDIMessage.systemExclusive(ticks, [
|
|
1911
|
+
126,
|
|
1912
|
+
127,
|
|
1913
|
+
9,
|
|
1914
|
+
3,
|
|
1915
|
+
127
|
|
1916
|
+
]);
|
|
1917
|
+
}
|
|
1679
1918
|
}
|
|
1680
1919
|
static analyzeGM(syx) {
|
|
1681
1920
|
if (syx.length < 4) return OTHER;
|
|
1682
1921
|
if (syx[2] === 4) switch (syx[3]) {
|
|
1683
1922
|
default: return OTHER;
|
|
1923
|
+
case 1: {
|
|
1924
|
+
const value = (syx[5] << 7 | syx[4]) / 16383;
|
|
1925
|
+
return {
|
|
1926
|
+
type: "Global MIDI Param",
|
|
1927
|
+
parameter: "gain",
|
|
1928
|
+
value: Math.pow(value, 2)
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
case 2: return {
|
|
1932
|
+
type: "Global MIDI Param",
|
|
1933
|
+
parameter: "pan",
|
|
1934
|
+
value: ((syx[5] << 7 | syx[4]) - 8192) / 8192
|
|
1935
|
+
};
|
|
1684
1936
|
case 3: return {
|
|
1685
|
-
type: "
|
|
1686
|
-
|
|
1937
|
+
type: "Global MIDI Param",
|
|
1938
|
+
parameter: "fineTune",
|
|
1939
|
+
value: ((syx[5] << 7 | syx[4]) - 8192) / 81.92
|
|
1687
1940
|
};
|
|
1688
1941
|
case 4: return {
|
|
1689
|
-
type: "
|
|
1942
|
+
type: "Global MIDI Param",
|
|
1943
|
+
parameter: "keyShift",
|
|
1690
1944
|
value: syx[5] - 64
|
|
1691
1945
|
};
|
|
1692
1946
|
case 5:
|
|
@@ -1710,9 +1964,21 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1710
1964
|
if (syx[2] !== 9) return OTHER;
|
|
1711
1965
|
switch (syx[3]) {
|
|
1712
1966
|
default: return OTHER;
|
|
1713
|
-
case 1: return {
|
|
1714
|
-
|
|
1715
|
-
|
|
1967
|
+
case 1: return {
|
|
1968
|
+
type: "Global MIDI Param",
|
|
1969
|
+
parameter: "system",
|
|
1970
|
+
value: "gm"
|
|
1971
|
+
};
|
|
1972
|
+
case 2: return {
|
|
1973
|
+
type: "Global MIDI Param",
|
|
1974
|
+
parameter: "system",
|
|
1975
|
+
value: "gm"
|
|
1976
|
+
};
|
|
1977
|
+
case 3: return {
|
|
1978
|
+
type: "Global MIDI Param",
|
|
1979
|
+
parameter: "system",
|
|
1980
|
+
value: "gm2"
|
|
1981
|
+
};
|
|
1716
1982
|
}
|
|
1717
1983
|
}
|
|
1718
1984
|
static analyzeXG(syx) {
|
|
@@ -1721,18 +1987,25 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1721
1987
|
const a2 = syx[4];
|
|
1722
1988
|
const a3 = syx[5];
|
|
1723
1989
|
const data = syx[6];
|
|
1990
|
+
if (a1 === 6 || a1 === 7) return { type: "Display Data" };
|
|
1724
1991
|
if (a1 === 0 && a2 === 0) switch (a3) {
|
|
1725
1992
|
default: return OTHER;
|
|
1726
1993
|
case 0: return {
|
|
1727
|
-
type: "
|
|
1994
|
+
type: "Global MIDI Param",
|
|
1995
|
+
parameter: "fineTune",
|
|
1728
1996
|
value: (((syx[6] & 15) << 12 | (syx[7] & 15) << 8 | (syx[8] & 15) << 4 | syx[9] & 15) - 1024) / 10
|
|
1729
1997
|
};
|
|
1730
1998
|
case 6: return {
|
|
1731
|
-
type: "
|
|
1999
|
+
type: "Global MIDI Param",
|
|
2000
|
+
parameter: "keyShift",
|
|
1732
2001
|
value: data - 64
|
|
1733
2002
|
};
|
|
1734
2003
|
case 126:
|
|
1735
|
-
case 127: return {
|
|
2004
|
+
case 127: return {
|
|
2005
|
+
type: "Global MIDI Param",
|
|
2006
|
+
parameter: "system",
|
|
2007
|
+
value: "xg"
|
|
2008
|
+
};
|
|
1736
2009
|
}
|
|
1737
2010
|
if (a1 === 2 && a2 === 1) {
|
|
1738
2011
|
if (a3 <= 21) return { type: "Reverb Param" };
|
|
@@ -1774,8 +2047,9 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1774
2047
|
isDrum: data > 0
|
|
1775
2048
|
};
|
|
1776
2049
|
case 8: return {
|
|
1777
|
-
type: "
|
|
2050
|
+
type: "Channel MIDI Param",
|
|
1778
2051
|
channel,
|
|
2052
|
+
parameter: "keyShift",
|
|
1779
2053
|
value: data - 64
|
|
1780
2054
|
};
|
|
1781
2055
|
case 11: return {
|
|
@@ -1856,27 +2130,54 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1856
2130
|
return OTHER;
|
|
1857
2131
|
}
|
|
1858
2132
|
static analyzeGS(syx) {
|
|
1859
|
-
if (syx.length < 10 || syx[
|
|
2133
|
+
if (syx.length < 10 || syx[3] !== 18) return OTHER;
|
|
2134
|
+
if (syx[2] === 69) return { type: "Display Data" };
|
|
2135
|
+
if (syx[2] !== 66) return OTHER;
|
|
1860
2136
|
const a1 = syx[4];
|
|
1861
2137
|
const a2 = syx[5];
|
|
1862
2138
|
const a3 = syx[6];
|
|
1863
2139
|
const data = syx[7];
|
|
1864
2140
|
if ((a1 === 0 || a1 === 64) && a2 === 0) switch (a3) {
|
|
2141
|
+
case 0: return {
|
|
2142
|
+
type: "Global MIDI Param",
|
|
2143
|
+
parameter: "fineTune",
|
|
2144
|
+
value: ((data << 12 | syx[8] << 8 | syx[9] << 4 | syx[10]) - 1024) / 10
|
|
2145
|
+
};
|
|
2146
|
+
case 4: return {
|
|
2147
|
+
type: "Global MIDI Param",
|
|
2148
|
+
parameter: "gain",
|
|
2149
|
+
value: data / 127
|
|
2150
|
+
};
|
|
2151
|
+
case 5: return {
|
|
2152
|
+
type: "Global MIDI Param",
|
|
2153
|
+
parameter: "keyShift",
|
|
2154
|
+
value: data - 64
|
|
2155
|
+
};
|
|
2156
|
+
case 6: return {
|
|
2157
|
+
type: "Global MIDI Param",
|
|
2158
|
+
parameter: "pan",
|
|
2159
|
+
value: (data - 64) / 63
|
|
2160
|
+
};
|
|
1865
2161
|
case 127:
|
|
1866
2162
|
switch (data) {
|
|
1867
|
-
case 0: return {
|
|
1868
|
-
|
|
2163
|
+
case 0: return {
|
|
2164
|
+
type: "Global MIDI Param",
|
|
2165
|
+
parameter: "system",
|
|
2166
|
+
value: "gs"
|
|
2167
|
+
};
|
|
2168
|
+
case 127: return {
|
|
2169
|
+
type: "Global MIDI Param",
|
|
2170
|
+
parameter: "system",
|
|
2171
|
+
value: "gm"
|
|
2172
|
+
};
|
|
1869
2173
|
}
|
|
1870
2174
|
return OTHER;
|
|
1871
|
-
case 0: return {
|
|
1872
|
-
type: "Master Fine Tune",
|
|
1873
|
-
value: ((data << 12 | syx[8] << 8 | syx[9] << 4 | syx[10]) - 1024) / 10
|
|
1874
|
-
};
|
|
1875
2175
|
}
|
|
1876
2176
|
if (a1 === 65) return { type: "Drum Setup" };
|
|
1877
2177
|
if (a1 !== 64) return OTHER;
|
|
1878
2178
|
if (a2 === 0 && a3 === 5) return {
|
|
1879
|
-
type: "
|
|
2179
|
+
type: "Global MIDI Param",
|
|
2180
|
+
parameter: "keyShift",
|
|
1880
2181
|
value: data - 64
|
|
1881
2182
|
};
|
|
1882
2183
|
if (a2 === 1) {
|
|
@@ -1895,10 +2196,16 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1895
2196
|
value: data
|
|
1896
2197
|
};
|
|
1897
2198
|
case 19: return {
|
|
1898
|
-
type: "
|
|
2199
|
+
type: "Channel MIDI Param",
|
|
1899
2200
|
channel,
|
|
1900
|
-
|
|
1901
|
-
value:
|
|
2201
|
+
parameter: "polyMode",
|
|
2202
|
+
value: data === 1
|
|
2203
|
+
};
|
|
2204
|
+
case 20: return {
|
|
2205
|
+
type: "Channel MIDI Param",
|
|
2206
|
+
channel,
|
|
2207
|
+
parameter: "assignMode",
|
|
2208
|
+
value: data
|
|
1902
2209
|
};
|
|
1903
2210
|
case 21: return {
|
|
1904
2211
|
type: "Drums On",
|
|
@@ -1906,8 +2213,9 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1906
2213
|
isDrum: data > 0
|
|
1907
2214
|
};
|
|
1908
2215
|
case 22: return {
|
|
1909
|
-
type: "
|
|
2216
|
+
type: "Channel MIDI Param",
|
|
1910
2217
|
channel,
|
|
2218
|
+
parameter: "keyShift",
|
|
1911
2219
|
value: data - 64
|
|
1912
2220
|
};
|
|
1913
2221
|
case 25: return {
|
|
@@ -1916,12 +2224,36 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1916
2224
|
controller: MIDIControllers.mainVolume,
|
|
1917
2225
|
value: data
|
|
1918
2226
|
};
|
|
2227
|
+
case 26: return {
|
|
2228
|
+
type: "Channel MIDI Param",
|
|
2229
|
+
channel,
|
|
2230
|
+
parameter: "velocitySenseDepth",
|
|
2231
|
+
value: data
|
|
2232
|
+
};
|
|
2233
|
+
case 27: return {
|
|
2234
|
+
type: "Channel MIDI Param",
|
|
2235
|
+
channel,
|
|
2236
|
+
parameter: "velocitySenseOffset",
|
|
2237
|
+
value: data
|
|
2238
|
+
};
|
|
1919
2239
|
case 28: return {
|
|
1920
2240
|
type: "Controller Change",
|
|
1921
2241
|
channel,
|
|
1922
2242
|
controller: MIDIControllers.pan,
|
|
1923
2243
|
value: data
|
|
1924
2244
|
};
|
|
2245
|
+
case 31: return {
|
|
2246
|
+
type: "Channel MIDI Param",
|
|
2247
|
+
channel,
|
|
2248
|
+
parameter: "cc1",
|
|
2249
|
+
value: data
|
|
2250
|
+
};
|
|
2251
|
+
case 32: return {
|
|
2252
|
+
type: "Channel MIDI Param",
|
|
2253
|
+
channel,
|
|
2254
|
+
parameter: "cc2",
|
|
2255
|
+
value: data
|
|
2256
|
+
};
|
|
1925
2257
|
case 33: return {
|
|
1926
2258
|
type: "Controller Change",
|
|
1927
2259
|
channel,
|
|
@@ -1935,8 +2267,9 @@ var MIDIUtils = class MIDIUtils {
|
|
|
1935
2267
|
value: data
|
|
1936
2268
|
};
|
|
1937
2269
|
case 42: return {
|
|
1938
|
-
type: "
|
|
2270
|
+
type: "Channel MIDI Param",
|
|
1939
2271
|
channel,
|
|
2272
|
+
parameter: "fineTune",
|
|
1940
2273
|
value: ((data << 7 | syx[8]) - 8192) / 81.92
|
|
1941
2274
|
};
|
|
1942
2275
|
case 44: return {
|
|
@@ -2006,7 +2339,12 @@ var MIDIUtils = class MIDIUtils {
|
|
|
2006
2339
|
controller: MIDIControllers.bankSelectLSB,
|
|
2007
2340
|
value: data
|
|
2008
2341
|
};
|
|
2009
|
-
case 34: return {
|
|
2342
|
+
case 34: return {
|
|
2343
|
+
type: "Channel MIDI Param",
|
|
2344
|
+
channel,
|
|
2345
|
+
parameter: "efxAssign",
|
|
2346
|
+
value: data === 1
|
|
2347
|
+
};
|
|
2010
2348
|
}
|
|
2011
2349
|
}
|
|
2012
2350
|
return OTHER;
|
|
@@ -2054,26 +2392,18 @@ function correctBankOffsetInternal(mid, bankOffset, soundBank) {
|
|
|
2054
2392
|
channels[sysexChannel].isDrum = syx.isDrum;
|
|
2055
2393
|
return;
|
|
2056
2394
|
}
|
|
2057
|
-
case "
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
unwantedSystems.push({
|
|
2067
|
-
tNum: trackNum,
|
|
2068
|
-
e
|
|
2069
|
-
});
|
|
2070
|
-
return;
|
|
2071
|
-
case "GM2 On":
|
|
2072
|
-
system = "gm2";
|
|
2073
|
-
return;
|
|
2395
|
+
case "Global MIDI Param":
|
|
2396
|
+
if (syx.parameter === "system") {
|
|
2397
|
+
system = syx.value;
|
|
2398
|
+
if (syx.value === "gm") unwantedSystems.push({
|
|
2399
|
+
tNum: trackNum,
|
|
2400
|
+
e
|
|
2401
|
+
});
|
|
2402
|
+
}
|
|
2403
|
+
break;
|
|
2074
2404
|
case "Controller Change": {
|
|
2075
2405
|
const t = mid.tracks[trackNum];
|
|
2076
|
-
const newEvent =
|
|
2406
|
+
const newEvent = MIDIMessage.controllerChange(e.ticks, syx.channel, syx.controller, syx.value);
|
|
2077
2407
|
t.events[eventIndexes[trackNum]] = newEvent;
|
|
2078
2408
|
e = newEvent;
|
|
2079
2409
|
SpessaLog.info("%cReplaced a system exclusive with controller change!", ConsoleColors.info);
|
|
@@ -2081,7 +2411,7 @@ function correctBankOffsetInternal(mid, bankOffset, soundBank) {
|
|
|
2081
2411
|
}
|
|
2082
2412
|
case "Program Change": {
|
|
2083
2413
|
const t = mid.tracks[trackNum];
|
|
2084
|
-
const newEvent =
|
|
2414
|
+
const newEvent = MIDIMessage.programChange(e.ticks, syx.channel, syx.value);
|
|
2085
2415
|
t.events[eventIndexes[trackNum]] = newEvent;
|
|
2086
2416
|
e = newEvent;
|
|
2087
2417
|
SpessaLog.info("%cReplaced a system exclusive with program change!", ConsoleColors.info);
|
|
@@ -2134,7 +2464,7 @@ function correctBankOffsetInternal(mid, bankOffset, soundBank) {
|
|
|
2134
2464
|
program: 0,
|
|
2135
2465
|
isGMGSDrum: false
|
|
2136
2466
|
}, system).program;
|
|
2137
|
-
track.addEvents(programIndex,
|
|
2467
|
+
track.addEvents(programIndex, MIDIMessage.programChange(programTicks, midiChannel, targetProgram));
|
|
2138
2468
|
indexToAdd = programIndex;
|
|
2139
2469
|
}
|
|
2140
2470
|
SpessaLog.info(`%cAdding bank select for %c${chNum}`, ConsoleColors.info, ConsoleColors.recognized);
|
|
@@ -2146,7 +2476,7 @@ function correctBankOffsetInternal(mid, bankOffset, soundBank) {
|
|
|
2146
2476
|
isGMGSDrum: ch.isDrum
|
|
2147
2477
|
}, system);
|
|
2148
2478
|
const targetBank = BankSelectHacks.addBankOffset(targetPreset.bankMSB, bankOffset, system === "xg");
|
|
2149
|
-
track.addEvents(indexToAdd,
|
|
2479
|
+
track.addEvents(indexToAdd, MIDIMessage.controllerChange(ticks, midiChannel, MIDIControllers.bankSelect, targetBank));
|
|
2150
2480
|
}
|
|
2151
2481
|
if (system === "gm" && !BankSelectHacks.isSystemXG(system)) {
|
|
2152
2482
|
for (const m of unwantedSystems) {
|
|
@@ -2155,7 +2485,7 @@ function correctBankOffsetInternal(mid, bankOffset, soundBank) {
|
|
|
2155
2485
|
}
|
|
2156
2486
|
let index = 0;
|
|
2157
2487
|
if (mid.tracks[0].events[0].statusByte === MIDIMessageTypes.trackName) index++;
|
|
2158
|
-
mid.tracks[0].addEvents(index, MIDIUtils.
|
|
2488
|
+
mid.tracks[0].addEvents(index, MIDIUtils.reset(0, "gs"));
|
|
2159
2489
|
}
|
|
2160
2490
|
mid.flush();
|
|
2161
2491
|
}
|
|
@@ -2461,7 +2791,7 @@ function getUsedProgramsAndKeys(mid, soundBank) {
|
|
|
2461
2791
|
case MIDIControllers.dataEntryMSB:
|
|
2462
2792
|
case MIDIControllers.dataEntryLSB: {
|
|
2463
2793
|
const analyzed = ch.param.controllerChange(cc, value, trackNum, event);
|
|
2464
|
-
if (analyzed?.type === "
|
|
2794
|
+
if (analyzed?.type === "Channel MIDI Param" && analyzed.parameter === "keyShift") ch.keyShift = ch.isDrum ? 0 : analyzed.value;
|
|
2465
2795
|
break;
|
|
2466
2796
|
}
|
|
2467
2797
|
case MIDIControllers.resetAllControllers:
|
|
@@ -2498,31 +2828,19 @@ function getUsedProgramsAndKeys(mid, soundBank) {
|
|
|
2498
2828
|
const syx = MIDIUtils.analyzeSysEx(e.data);
|
|
2499
2829
|
switch (syx.type) {
|
|
2500
2830
|
default: break;
|
|
2501
|
-
case "
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
SpessaLog.info("%cGM2 on detected!", ConsoleColors.recognized);
|
|
2508
|
-
break;
|
|
2509
|
-
case "GM On":
|
|
2510
|
-
reset("gm");
|
|
2511
|
-
SpessaLog.info("%cGM on detected!", ConsoleColors.recognized);
|
|
2512
|
-
break;
|
|
2513
|
-
case "GM Off":
|
|
2514
|
-
case "GS Reset":
|
|
2515
|
-
reset("gs");
|
|
2516
|
-
SpessaLog.info("%cGS on detected!", ConsoleColors.recognized);
|
|
2517
|
-
break;
|
|
2518
|
-
case "Master Key Shift":
|
|
2519
|
-
masterKeyShift = syx.value;
|
|
2831
|
+
case "Global MIDI Param":
|
|
2832
|
+
if (syx.parameter === "keyShift") masterKeyShift = syx.value;
|
|
2833
|
+
else if (syx.parameter === "system") {
|
|
2834
|
+
reset(syx.value);
|
|
2835
|
+
SpessaLog.info(`%c${syx.value.toUpperCase()} on detected!`, ConsoleColors.recognized);
|
|
2836
|
+
}
|
|
2520
2837
|
break;
|
|
2521
|
-
case "
|
|
2522
|
-
|
|
2523
|
-
|
|
2838
|
+
case "Channel MIDI Param":
|
|
2839
|
+
if (syx.parameter === "keyShift") {
|
|
2840
|
+
const ch = channels[syx.channel];
|
|
2841
|
+
ch.keyShift = ch.isDrum ? 0 : syx.value;
|
|
2842
|
+
}
|
|
2524
2843
|
break;
|
|
2525
|
-
}
|
|
2526
2844
|
case "Drums On": {
|
|
2527
2845
|
const sysexChannel = syx.channel + channelOffset;
|
|
2528
2846
|
channels[sysexChannel].isDrum = syx.isDrum;
|
|
@@ -2662,9 +2980,6 @@ const delayAddressMap = {
|
|
|
2662
2980
|
feedback: 89,
|
|
2663
2981
|
sendLevelToReverb: 90
|
|
2664
2982
|
};
|
|
2665
|
-
function getControllerChange(channel, cc, value, ticks) {
|
|
2666
|
-
return new MIDIMessage(ticks, MIDIMessageTypes.controllerChange | channel % 16, new IndexedByteArray([cc, value]));
|
|
2667
|
-
}
|
|
2668
2983
|
/**
|
|
2669
2984
|
* Allows easy editing of the file by removing channels, changing programs,
|
|
2670
2985
|
* changing controllers and transposing channels. Note that this modifies the MIDI in-place.
|
|
@@ -2683,8 +2998,8 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2683
2998
|
const channelChanges = /* @__PURE__ */ new Map();
|
|
2684
2999
|
if (channels) for (const [channel, ch] of channels) if (ch === "clear") clearedChannels.add(channel);
|
|
2685
3000
|
else channelChanges.set(channel, ch);
|
|
2686
|
-
let system = "gs";
|
|
2687
|
-
let
|
|
3001
|
+
let system = (opts.midiParams?.system === "clear" ? void 0 : opts.midiParams?.system) ?? "gs";
|
|
3002
|
+
let addedReset = false;
|
|
2688
3003
|
let resetTrack = 0;
|
|
2689
3004
|
let resetIndex = 0;
|
|
2690
3005
|
/**
|
|
@@ -2720,7 +3035,9 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2720
3035
|
data: true
|
|
2721
3036
|
},
|
|
2722
3037
|
keyShift: channelChanges.get(i)?.keyShift ?? 0,
|
|
2723
|
-
fineTune: channelChanges.get(i)?.fineTune ?? 0
|
|
3038
|
+
fineTune: channelChanges.get(i)?.fineTune ?? 0,
|
|
3039
|
+
currentFineTune: 0,
|
|
3040
|
+
currentKeyShift: 0
|
|
2724
3041
|
});
|
|
2725
3042
|
midi.iterate((e, trackNum, eventIndexes) => {
|
|
2726
3043
|
const track = midi.tracks[trackNum];
|
|
@@ -2753,10 +3070,18 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2753
3070
|
ch.clearedParams.pLSB = true;
|
|
2754
3071
|
}
|
|
2755
3072
|
};
|
|
2756
|
-
const addEventBefore = (e
|
|
2757
|
-
track.addEvents(index
|
|
3073
|
+
const addEventBefore = (e) => {
|
|
3074
|
+
track.addEvents(index, e);
|
|
2758
3075
|
eventIndexes[trackNum]++;
|
|
2759
3076
|
};
|
|
3077
|
+
/**
|
|
3078
|
+
* This function adds the events IN ORDER they are in the array,
|
|
3079
|
+
* So the first event in the array will end up as the first one before the current event.
|
|
3080
|
+
* @param events
|
|
3081
|
+
*/
|
|
3082
|
+
const addEventsBefore = (...events) => {
|
|
3083
|
+
for (let i = events.length - 1; i >= 0; i--) addEventBefore(events[i]);
|
|
3084
|
+
};
|
|
2760
3085
|
const portOffset = midiPortChannelOffsets[midiPorts[trackNum]] || 0;
|
|
2761
3086
|
if (e.statusByte === MIDIMessageTypes.midiPort) {
|
|
2762
3087
|
assignMIDIPort(trackNum, e.data[0]);
|
|
@@ -2779,17 +3104,17 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2779
3104
|
channelStatus.isFirstNoteOn = false;
|
|
2780
3105
|
if (channelChange.controllers) for (const [cc, value] of channelChange.controllers) {
|
|
2781
3106
|
if (value === "clear") continue;
|
|
2782
|
-
addEventBefore(
|
|
3107
|
+
addEventBefore(MIDIMessage.controllerChange(e.ticks, midiChannel, cc, value));
|
|
2783
3108
|
}
|
|
2784
|
-
if (
|
|
2785
|
-
const
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
3109
|
+
if (channelChange.midiParams?.fineTune !== void 0 && channelChange.midiParams.fineTune !== "clear") {
|
|
3110
|
+
const newTune = channelStatus.fineTune + channelChange.midiParams.fineTune;
|
|
3111
|
+
channelStatus.currentKeyShift = Math.trunc(newTune / 100);
|
|
3112
|
+
channelChange.midiParams.fineTune = newTune % 100;
|
|
3113
|
+
} else {
|
|
3114
|
+
const newTune = channelStatus.fineTune + channelStatus.currentFineTune;
|
|
3115
|
+
channelStatus.currentKeyShift = Math.trunc(newTune / 100);
|
|
3116
|
+
channelChange.midiParams ??= {};
|
|
3117
|
+
channelChange.midiParams.fineTune = newTune % 100;
|
|
2793
3118
|
}
|
|
2794
3119
|
const patch = channelChange.patch;
|
|
2795
3120
|
if (patch && patch !== "clear") {
|
|
@@ -2797,9 +3122,9 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2797
3122
|
let desiredBankMSB = patch.bankMSB;
|
|
2798
3123
|
let desiredBankLSB = patch.bankLSB;
|
|
2799
3124
|
const desiredProgram = patch.program;
|
|
2800
|
-
addEventBefore(
|
|
3125
|
+
addEventBefore(MIDIMessage.programChange(e.ticks, midiChannel, desiredProgram));
|
|
2801
3126
|
const addBank = (isLSB, v) => {
|
|
2802
|
-
addEventBefore(
|
|
3127
|
+
addEventBefore(MIDIMessage.controllerChange(e.ticks, midiChannel, isLSB ? MIDIControllers.bankSelectLSB : MIDIControllers.bankSelect, v));
|
|
2803
3128
|
};
|
|
2804
3129
|
if (BankSelectHacks.isSystemXG(system) && patch.isGMGSDrum) {
|
|
2805
3130
|
SpessaLog.info(`%cAdding XG Drum change on track %c${trackNum}`, ConsoleColors.recognized, ConsoleColors.value);
|
|
@@ -2810,15 +3135,20 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2810
3135
|
addBank(true, desiredBankLSB);
|
|
2811
3136
|
if (patch.isGMGSDrum && !BankSelectHacks.isSystemXG(system) && midiChannel !== 9) {
|
|
2812
3137
|
SpessaLog.info(`%cAdding GS Drum change on track %c${trackNum}`, ConsoleColors.recognized, ConsoleColors.value);
|
|
2813
|
-
|
|
3138
|
+
const chanAddress = 16 | MIDIUtils.channelToSyx(midiChannel);
|
|
3139
|
+
addEventBefore(MIDIUtils.gsMessage(e.ticks, 40, chanAddress, 21, [1]));
|
|
2814
3140
|
}
|
|
2815
3141
|
}
|
|
3142
|
+
if (channelChange.midiParams) for (const [param, value] of Object.entries(channelChange.midiParams)) {
|
|
3143
|
+
if (value === "clear") continue;
|
|
3144
|
+
addEventsBefore(...MIDIUtils.setChannelMIDIParameter(e.ticks, midiChannel, system, param, value));
|
|
3145
|
+
}
|
|
2816
3146
|
}
|
|
2817
|
-
e.data[0] += channelStatus.keyShift;
|
|
3147
|
+
e.data[0] += channelStatus.keyShift + channelStatus.currentKeyShift;
|
|
2818
3148
|
break;
|
|
2819
3149
|
case MIDIMessageTypes.noteOff:
|
|
2820
3150
|
if (!channelChange) break;
|
|
2821
|
-
e.data[0] += channelStatus.keyShift;
|
|
3151
|
+
e.data[0] += channelStatus.keyShift + channelStatus.currentKeyShift;
|
|
2822
3152
|
break;
|
|
2823
3153
|
case MIDIMessageTypes.programChange:
|
|
2824
3154
|
if (channelChange?.patch) {
|
|
@@ -2826,6 +3156,12 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2826
3156
|
return;
|
|
2827
3157
|
}
|
|
2828
3158
|
break;
|
|
3159
|
+
case MIDIMessageTypes.pitchWheel:
|
|
3160
|
+
if (channelChange?.midiParams?.pitchWheel) deleteThisEvent();
|
|
3161
|
+
break;
|
|
3162
|
+
case MIDIMessageTypes.channelPressure:
|
|
3163
|
+
if (channelChange?.midiParams?.pressure) deleteThisEvent();
|
|
3164
|
+
break;
|
|
2829
3165
|
case MIDIMessageTypes.controllerChange: {
|
|
2830
3166
|
const ccNum = e.data[0];
|
|
2831
3167
|
const value = e.data[1];
|
|
@@ -2865,17 +3201,17 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2865
3201
|
if ((ccNum === MIDIControllers.bankSelect || ccNum === MIDIControllers.bankSelectLSB) && channelChange?.patch) deleteParameter(channel);
|
|
2866
3202
|
break;
|
|
2867
3203
|
}
|
|
2868
|
-
case "
|
|
2869
|
-
if (channelStatus.fineTune) {
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
}
|
|
2878
|
-
|
|
3204
|
+
case "Channel MIDI Param":
|
|
3205
|
+
if (data.parameter === "fineTune" && channelStatus.fineTune) {
|
|
3206
|
+
channelStatus.currentFineTune = data.value;
|
|
3207
|
+
const newTune = channelStatus.fineTune + data.value;
|
|
3208
|
+
channelStatus.currentKeyShift = Math.trunc(newTune / 100);
|
|
3209
|
+
const targetTune = newTune % 100;
|
|
3210
|
+
SpessaLog.info(`%cFine tuning already present on ${channel}%c (${data.value})%c, new relative tune: %c${newTune}%c cents. Key shift: %c${channelStatus.currentKeyShift}%c semitones. Actual RPN value to set: %c${targetTune} cents.`, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info, ConsoleColors.value, ConsoleColors.info, ConsoleColors.value, ConsoleColors.info, ConsoleColors.value);
|
|
3211
|
+
const updatedData = Math.floor(targetTune * 81.92) + 8192;
|
|
3212
|
+
e.data[1] = ccNum === MIDIControllers.dataEntryMSB ? updatedData >> 7 : updatedData & 127;
|
|
3213
|
+
} else if (channelChange?.midiParams?.[data.parameter]) deleteParameter(channel);
|
|
3214
|
+
break;
|
|
2879
3215
|
}
|
|
2880
3216
|
channelStatus.clearedParams.pLSB = true;
|
|
2881
3217
|
channelStatus.clearedParams.pMSB = true;
|
|
@@ -2888,56 +3224,6 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2888
3224
|
const syx = MIDIUtils.analyzeSysEx(e.data);
|
|
2889
3225
|
switch (syx.type) {
|
|
2890
3226
|
default: return;
|
|
2891
|
-
case "XG Reset":
|
|
2892
|
-
SpessaLog.info("%cXG system on detected", ConsoleColors.info);
|
|
2893
|
-
system = "xg";
|
|
2894
|
-
addedGs = true;
|
|
2895
|
-
resetTrack = trackNum;
|
|
2896
|
-
resetIndex = index;
|
|
2897
|
-
for (const ch of channelStatuses) {
|
|
2898
|
-
ch.param.reset();
|
|
2899
|
-
ch.clearedParams = {
|
|
2900
|
-
pLSB: true,
|
|
2901
|
-
pMSB: true,
|
|
2902
|
-
data: true
|
|
2903
|
-
};
|
|
2904
|
-
}
|
|
2905
|
-
return;
|
|
2906
|
-
case "GM2 On":
|
|
2907
|
-
SpessaLog.info("%cGM2 system on detected", ConsoleColors.info);
|
|
2908
|
-
system = "gm2";
|
|
2909
|
-
addedGs = true;
|
|
2910
|
-
resetTrack = trackNum;
|
|
2911
|
-
resetIndex = index;
|
|
2912
|
-
for (const ch of channelStatuses) {
|
|
2913
|
-
ch.param.reset();
|
|
2914
|
-
ch.clearedParams = {
|
|
2915
|
-
pLSB: true,
|
|
2916
|
-
pMSB: true,
|
|
2917
|
-
data: true
|
|
2918
|
-
};
|
|
2919
|
-
}
|
|
2920
|
-
return;
|
|
2921
|
-
case "GS Reset":
|
|
2922
|
-
SpessaLog.info("%cGS on detected!", ConsoleColors.recognized);
|
|
2923
|
-
addedGs = true;
|
|
2924
|
-
resetTrack = trackNum;
|
|
2925
|
-
resetIndex = index;
|
|
2926
|
-
for (const ch of channelStatuses) {
|
|
2927
|
-
ch.param.reset();
|
|
2928
|
-
ch.clearedParams = {
|
|
2929
|
-
pLSB: true,
|
|
2930
|
-
pMSB: true,
|
|
2931
|
-
data: true
|
|
2932
|
-
};
|
|
2933
|
-
}
|
|
2934
|
-
return;
|
|
2935
|
-
case "GM Off":
|
|
2936
|
-
case "GM On":
|
|
2937
|
-
SpessaLog.info("%cGM on detected, removing!", ConsoleColors.info);
|
|
2938
|
-
deleteThisEvent();
|
|
2939
|
-
addedGs = false;
|
|
2940
|
-
return;
|
|
2941
3227
|
case "Drum Setup":
|
|
2942
3228
|
if (clearDrumParams) deleteThisEvent();
|
|
2943
3229
|
return;
|
|
@@ -2956,15 +3242,79 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2956
3242
|
case "Program Change":
|
|
2957
3243
|
if (channelChanges.get(syx.channel + portOffset)?.patch) deleteThisEvent();
|
|
2958
3244
|
return;
|
|
2959
|
-
case "
|
|
3245
|
+
case "Global MIDI Param":
|
|
3246
|
+
if (opts.midiParams?.[syx.parameter]) {
|
|
3247
|
+
deleteThisEvent();
|
|
3248
|
+
return;
|
|
3249
|
+
}
|
|
3250
|
+
if (syx.parameter === "system") switch (syx.value) {
|
|
3251
|
+
case "xg":
|
|
3252
|
+
SpessaLog.info("%cXG system on detected", ConsoleColors.info);
|
|
3253
|
+
system = "xg";
|
|
3254
|
+
addedReset = true;
|
|
3255
|
+
resetTrack = trackNum;
|
|
3256
|
+
resetIndex = index;
|
|
3257
|
+
for (const ch of channelStatuses) {
|
|
3258
|
+
ch.param.reset();
|
|
3259
|
+
ch.clearedParams = {
|
|
3260
|
+
pLSB: true,
|
|
3261
|
+
pMSB: true,
|
|
3262
|
+
data: true
|
|
3263
|
+
};
|
|
3264
|
+
}
|
|
3265
|
+
return;
|
|
3266
|
+
case "gm2":
|
|
3267
|
+
SpessaLog.info("%cGM2 system on detected", ConsoleColors.info);
|
|
3268
|
+
system = "gm2";
|
|
3269
|
+
addedReset = true;
|
|
3270
|
+
resetTrack = trackNum;
|
|
3271
|
+
resetIndex = index;
|
|
3272
|
+
for (const ch of channelStatuses) {
|
|
3273
|
+
ch.param.reset();
|
|
3274
|
+
ch.clearedParams = {
|
|
3275
|
+
pLSB: true,
|
|
3276
|
+
pMSB: true,
|
|
3277
|
+
data: true
|
|
3278
|
+
};
|
|
3279
|
+
}
|
|
3280
|
+
return;
|
|
3281
|
+
case "gs":
|
|
3282
|
+
SpessaLog.info("%cGS on detected!", ConsoleColors.recognized);
|
|
3283
|
+
addedReset = true;
|
|
3284
|
+
resetTrack = trackNum;
|
|
3285
|
+
resetIndex = index;
|
|
3286
|
+
for (const ch of channelStatuses) {
|
|
3287
|
+
ch.param.reset();
|
|
3288
|
+
ch.clearedParams = {
|
|
3289
|
+
pLSB: true,
|
|
3290
|
+
pMSB: true,
|
|
3291
|
+
data: true
|
|
3292
|
+
};
|
|
3293
|
+
}
|
|
3294
|
+
return;
|
|
3295
|
+
case "gm":
|
|
3296
|
+
SpessaLog.info("%cGM on detected, removing!", ConsoleColors.info);
|
|
3297
|
+
deleteThisEvent();
|
|
3298
|
+
addedReset = false;
|
|
3299
|
+
return;
|
|
3300
|
+
}
|
|
3301
|
+
break;
|
|
3302
|
+
case "Channel MIDI Param": {
|
|
2960
3303
|
const syxChannel = channelChanges.get(syx.channel + portOffset);
|
|
2961
|
-
|
|
2962
|
-
if (syxStatus.isFirstNoteOn && syxChannel) {
|
|
2963
|
-
const newTune = syxStatus.fineTune + syx.value;
|
|
2964
|
-
syxStatus.keyShift += Math.trunc(newTune / 100);
|
|
2965
|
-
syxStatus.fineTune = newTune % 100;
|
|
2966
|
-
SpessaLog.info(`%cFine tuning already present on ${syx.channel + portOffset}, new relative tune: %c${newTune} cents`, ConsoleColors.info, ConsoleColors.recognized);
|
|
3304
|
+
if (syxChannel?.midiParams?.[syx.parameter]) {
|
|
2967
3305
|
deleteThisEvent();
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3308
|
+
if (syx.parameter === "fineTune") {
|
|
3309
|
+
const syxStatus = channelStatuses[syx.channel + portOffset];
|
|
3310
|
+
if (syxStatus.isFirstNoteOn && syxChannel) {
|
|
3311
|
+
const newTune = syxStatus.fineTune + syx.value;
|
|
3312
|
+
syxStatus.currentKeyShift = Math.trunc(newTune / 100);
|
|
3313
|
+
syxStatus.fineTune = newTune % 100;
|
|
3314
|
+
SpessaLog.info(`%cFine tuning already present on ${syx.channel + portOffset}, new relative tune: %c${newTune} cents`, ConsoleColors.info, ConsoleColors.recognized);
|
|
3315
|
+
deleteThisEvent();
|
|
3316
|
+
}
|
|
3317
|
+
break;
|
|
2968
3318
|
}
|
|
2969
3319
|
break;
|
|
2970
3320
|
}
|
|
@@ -2982,17 +3332,25 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
2982
3332
|
}
|
|
2983
3333
|
}
|
|
2984
3334
|
});
|
|
2985
|
-
if (!
|
|
3335
|
+
if (!addedReset && [...channelChanges.values()].some((c) => c.patch && c.patch !== "clear")) {
|
|
2986
3336
|
let index = 0;
|
|
2987
3337
|
if (midi.tracks[0].events[0].statusByte === MIDIMessageTypes.trackName) index++;
|
|
2988
|
-
|
|
3338
|
+
const targetSystem = (opts.midiParams?.system === "clear" ? void 0 : opts.midiParams?.system) ?? "gs";
|
|
3339
|
+
midi.tracks[0].addEvents(index, MIDIUtils.reset(0, targetSystem));
|
|
2989
3340
|
resetTrack = 0;
|
|
2990
3341
|
resetIndex = index;
|
|
2991
|
-
|
|
3342
|
+
system = targetSystem;
|
|
3343
|
+
SpessaLog.info(`%c${targetSystem} reset on not detected. Adding it.`, ConsoleColors.info);
|
|
2992
3344
|
}
|
|
2993
3345
|
const targetTicks = Math.max(0, midi.firstNoteOn);
|
|
2994
3346
|
const targetTrack = midi.tracks[resetTrack];
|
|
2995
3347
|
const targetIndex = resetIndex + 1;
|
|
3348
|
+
for (const param of Object.keys(opts.midiParams ?? {})) {
|
|
3349
|
+
if (param === "system") continue;
|
|
3350
|
+
const value = opts.midiParams?.[param];
|
|
3351
|
+
if (!value || value === "clear") continue;
|
|
3352
|
+
targetTrack.addEvents(targetIndex, ...MIDIUtils.setGlobalMIDIParameter(targetTicks, system, param, value));
|
|
3353
|
+
}
|
|
2996
3354
|
if (reverbParams && reverbParams !== "clear") {
|
|
2997
3355
|
const m = reverbAddressMap;
|
|
2998
3356
|
const p = reverbParams;
|
|
@@ -3010,7 +3368,6 @@ function modifyMIDIInternal(midi, opts) {
|
|
|
3010
3368
|
}
|
|
3011
3369
|
if (insertionParams && insertionParams !== "clear") {
|
|
3012
3370
|
const p = insertionParams;
|
|
3013
|
-
for (let channel = 0; channel < p.channels.length; channel++) if (p.channels[channel]) targetTrack.addEvents(targetTicks, MIDIUtils.gsMessage(targetTicks, 64, 64 | MIDIUtils.channelToSyx(channel), 34, [1]));
|
|
3014
3371
|
for (let param = 0; param < p.params.length; param++) {
|
|
3015
3372
|
const value = p.params[param];
|
|
3016
3373
|
if (value === 255) continue;
|
|
@@ -3614,8 +3971,10 @@ function loadXMF(midi, binaryData, fileName) {
|
|
|
3614
3971
|
//#endregion
|
|
3615
3972
|
//#region src/midi/midi_tools/apply_snapshot.ts
|
|
3616
3973
|
/**
|
|
3617
|
-
* Modifies the sequence according to the locked presets and controllers in the given snapshot.
|
|
3618
|
-
*
|
|
3974
|
+
* Modifies the sequence *in-place* according to the locked presets and controllers in the given snapshot.
|
|
3975
|
+
*
|
|
3976
|
+
* Note that System Parameters `fineTune` and `keyShift` are passed to the relative tuning parameters of the channels.
|
|
3977
|
+
* Only locked MIDI parameters and controllers are applied.
|
|
3619
3978
|
*/
|
|
3620
3979
|
function applySnapshotInternal(midi, snapshot) {
|
|
3621
3980
|
const channels = /* @__PURE__ */ new Map();
|
|
@@ -3637,20 +3996,26 @@ function applySnapshotInternal(midi, snapshot) {
|
|
|
3637
3996
|
const targetValue = channelSnapshot.midiControllers[ccNumber] >> 7;
|
|
3638
3997
|
controllers.set(ccNumber, targetValue);
|
|
3639
3998
|
}
|
|
3999
|
+
const midiParams = {};
|
|
4000
|
+
for (const [parameter, value] of Object.entries(channelSnapshot.midiParameters)) if (channelSnapshot.lockedMIDIParameters[parameter]) midiParams[parameter] = value;
|
|
3640
4001
|
channels.set(channelNumber, {
|
|
3641
4002
|
keyShift,
|
|
3642
4003
|
fineTune,
|
|
3643
4004
|
patch,
|
|
3644
|
-
controllers
|
|
4005
|
+
controllers,
|
|
4006
|
+
midiParams
|
|
3645
4007
|
});
|
|
3646
4008
|
}
|
|
4009
|
+
const midiParams = {};
|
|
4010
|
+
for (const [parameter, value] of Object.entries(snapshot.midiParameters)) if (snapshot.lockedMIDIParameters[parameter]) midiParams[parameter] = value;
|
|
3647
4011
|
midi.modify({
|
|
3648
4012
|
channels,
|
|
3649
4013
|
drumSetupParams: snapshot.systemParameters.drumLock ? "clear" : void 0,
|
|
3650
4014
|
reverbParams: snapshot.systemParameters.reverbLock ? snapshot.reverbProcessor : void 0,
|
|
3651
4015
|
chorusParams: snapshot.systemParameters.chorusLock ? snapshot.chorusProcessor : void 0,
|
|
3652
4016
|
delayParams: snapshot.systemParameters.delayLock ? snapshot.delayProcessor : void 0,
|
|
3653
|
-
insertionParams: snapshot.systemParameters.insertionEffectLock ? snapshot.insertionProcessor : void 0
|
|
4017
|
+
insertionParams: snapshot.systemParameters.insertionEffectLock ? snapshot.insertionProcessor : void 0,
|
|
4018
|
+
midiParams
|
|
3654
4019
|
});
|
|
3655
4020
|
}
|
|
3656
4021
|
//#endregion
|
|
@@ -3787,7 +4152,7 @@ var BasicMIDI = class BasicMIDI {
|
|
|
3787
4152
|
* It supports Standard MIDI Files (SMF), RIFF MIDI (RMIDI), and Extensible Music Format (XMF).
|
|
3788
4153
|
* It also handles embedded soundbanks in RMIDI files.
|
|
3789
4154
|
* If the file is an RMIDI file, it will extract the embedded soundbank and store
|
|
3790
|
-
* it in the `
|
|
4155
|
+
* it in the `embeddedSoundBank` property of the BasicMIDI instance.
|
|
3791
4156
|
* If the file is an XMF file, it will parse the XMF structure and extract the MIDI data.
|
|
3792
4157
|
*/
|
|
3793
4158
|
static fromArrayBuffer(arrayBuffer, fileName = "") {
|
|
@@ -3947,7 +4312,9 @@ var BasicMIDI = class BasicMIDI {
|
|
|
3947
4312
|
}
|
|
3948
4313
|
/**
|
|
3949
4314
|
* Modifies the sequence *in-place* according to the locked presets and controllers in the given snapshot.
|
|
3950
|
-
*
|
|
4315
|
+
*
|
|
4316
|
+
* Note that System Parameters `fineTune` and `keyShift` are passed to the relative tuning parameters of the channels.
|
|
4317
|
+
* Only locked MIDI parameters and controllers are applied.
|
|
3951
4318
|
* @param snapshot the snapshot to apply.
|
|
3952
4319
|
*/
|
|
3953
4320
|
applySnapshot(snapshot) {
|
|
@@ -4132,15 +4499,15 @@ var BasicMIDI = class BasicMIDI {
|
|
|
4132
4499
|
switch (e.statusByte & 240) {
|
|
4133
4500
|
case MIDIMessageTypes.controllerChange:
|
|
4134
4501
|
switch (e.data[0]) {
|
|
4135
|
-
case
|
|
4136
|
-
case
|
|
4502
|
+
case MIDIControllers.breathController:
|
|
4503
|
+
case MIDIControllers.undefinedCC111LSB:
|
|
4137
4504
|
if (e.data[1] === 0) loopStart = e.ticks;
|
|
4138
4505
|
break;
|
|
4139
|
-
case
|
|
4506
|
+
case MIDIControllers.undefinedCC116LSB:
|
|
4140
4507
|
loopStart = e.ticks;
|
|
4141
4508
|
break;
|
|
4142
|
-
case
|
|
4143
|
-
case
|
|
4509
|
+
case MIDIControllers.footController:
|
|
4510
|
+
case MIDIControllers.undefinedCC117LSB:
|
|
4144
4511
|
if (loopEnd === null && (e.data[0] !== 4 || e.data[0] === 4 && e.data[1] === 0)) {
|
|
4145
4512
|
loopType = "soft";
|
|
4146
4513
|
loopEnd = e.ticks;
|
|
@@ -4149,7 +4516,7 @@ var BasicMIDI = class BasicMIDI {
|
|
|
4149
4516
|
loopType = "hard";
|
|
4150
4517
|
}
|
|
4151
4518
|
break;
|
|
4152
|
-
case
|
|
4519
|
+
case MIDIControllers.bankSelect: if (this.isDLSRMIDI && e.data[1] !== 0 && e.data[1] !== 127) {
|
|
4153
4520
|
SpessaLog.info("%cDLS RMIDI with offset 1 detected!", ConsoleColors.recognized);
|
|
4154
4521
|
this.bankOffset = 1;
|
|
4155
4522
|
}
|
|
@@ -4666,9 +5033,6 @@ function loadNewSequenceInternal(parsedMIDI) {
|
|
|
4666
5033
|
}
|
|
4667
5034
|
//#endregion
|
|
4668
5035
|
//#region src/synthesizer/audio_engine/channel/reset.ts
|
|
4669
|
-
function resetPortamento() {
|
|
4670
|
-
this.lastPortamentoNote = this.channelSystem === "xg" ? 60 : -1;
|
|
4671
|
-
}
|
|
4672
5036
|
/**
|
|
4673
5037
|
* An array with the default MIDI controller values.
|
|
4674
5038
|
* Note that these are 14-bit, requiring a 7-bit shift to the right for 7-bit values!
|
|
@@ -4714,22 +5078,24 @@ function resetChannelInternal(sendCCEvents = true) {
|
|
|
4714
5078
|
const resetValue = DEFAULT_MIDI_CONTROLLERS[cc];
|
|
4715
5079
|
if (this._midiControllers[cc] !== resetValue && cc !== MIDIControllers.portamentoControl && cc !== MIDIControllers.dataEntryMSB && cc !== MIDIControllers.registeredParameterMSB && cc !== MIDIControllers.registeredParameterLSB && cc !== MIDIControllers.nonRegisteredParameterMSB && cc !== MIDIControllers.nonRegisteredParameterLSB) this.controllerChange(cc, resetValue >> 7, sendCCEvents);
|
|
4716
5080
|
}
|
|
4717
|
-
|
|
5081
|
+
this.setMIDIParameter("pressure", 0);
|
|
5082
|
+
this.setMIDIParameter("pitchWheelRange", 2);
|
|
5083
|
+
this.setMIDIParameter("modulationDepth", 50);
|
|
4718
5084
|
this.setMIDIParameter("rxChannel", this.channel);
|
|
5085
|
+
this.setMIDIParameter("efxAssign", false);
|
|
5086
|
+
this.setMIDIParameter("polyMode", true);
|
|
5087
|
+
this.setMIDIParameter("keyShift", 0);
|
|
5088
|
+
this.setMIDIParameter("fineTune", 0);
|
|
4719
5089
|
this.setMIDIParameter("assignMode", 2);
|
|
4720
5090
|
this.setMIDIParameter("randomPan", false);
|
|
4721
5091
|
this.setMIDIParameter("cc1", 16);
|
|
4722
5092
|
this.setMIDIParameter("cc2", 17);
|
|
4723
5093
|
this.setMIDIParameter("drumMap", this.channel % 16 === 9 ? 1 : 0);
|
|
5094
|
+
this.setMIDIParameter("velocitySenseOffset", 64);
|
|
5095
|
+
this.setMIDIParameter("velocitySenseDepth", 64);
|
|
4724
5096
|
this.pitchWheel(8192);
|
|
4725
|
-
this.pitchWheelRange(2, false);
|
|
4726
|
-
this.keyShift(0, false);
|
|
4727
|
-
this.fineTune(0, false);
|
|
4728
|
-
this.setMIDIParameter("pressure", 0);
|
|
4729
|
-
this.modulationDepth(50, false);
|
|
4730
|
-
if (!this.lockedControllers[MIDIControllers.monoModeOn] && !this.lockedControllers[MIDIControllers.polyModeOn]) this.setMIDIParameter("polyMode", true);
|
|
4731
5097
|
this.octaveTuning.fill(0);
|
|
4732
|
-
|
|
5098
|
+
this.lastPortamentoNote = this.channelSystem === "xg" ? 60 : -1;
|
|
4733
5099
|
this.resetDrumParams();
|
|
4734
5100
|
this.resetGeneratorOverrides();
|
|
4735
5101
|
this.resetGeneratorOffsets();
|
|
@@ -5268,7 +5634,7 @@ var SpessaSynthSequencer = class {
|
|
|
5268
5634
|
this.synth.reset();
|
|
5269
5635
|
return;
|
|
5270
5636
|
}
|
|
5271
|
-
this.sendMIDISysEx(MIDIUtils.
|
|
5637
|
+
this.sendMIDISysEx(MIDIUtils.gs(64, 0, 127, [0]));
|
|
5272
5638
|
}
|
|
5273
5639
|
loadCurrentSong() {
|
|
5274
5640
|
let index = this._songIndex;
|
|
@@ -6017,15 +6383,16 @@ function applySnapshot$1(snapshot) {
|
|
|
6017
6383
|
for (const [key, value] of Object.entries(this.chorusProcessor)) this.chorusProcessor[key] = value;
|
|
6018
6384
|
for (const [key, value] of Object.entries(this.delayProcessor)) this.delayProcessor[key] = value;
|
|
6019
6385
|
const is = snapshot.insertionProcessor;
|
|
6020
|
-
this.systemExclusive(MIDIUtils.
|
|
6021
|
-
for (let i = 0; i < is.params.length; i++) if (is.params[i] !== 255) this.systemExclusive(MIDIUtils.
|
|
6022
|
-
for (let channel = 0; channel < is.channels.length; channel++) this.systemExclusive(MIDIUtils.gsData(64, 64 | MIDIUtils.channelToSyx(channel), 34, [is.channels[channel] ? 1 : 0]));
|
|
6386
|
+
this.systemExclusive(MIDIUtils.gs(64, 3, 0, [is.type >> 8, is.type & 127]));
|
|
6387
|
+
for (let i = 0; i < is.params.length; i++) if (is.params[i] !== 255) this.systemExclusive(MIDIUtils.gs(64, 3, 3 + i, [is.params[i]]));
|
|
6023
6388
|
for (const [parameter, value] of Object.entries(snapshot.midiParameters)) this.setMIDIParameter(parameter, value);
|
|
6389
|
+
for (const [parameter, isLocked] of Object.entries(snapshot.lockedMIDIParameters)) this.lockMIDIParameter(parameter, isLocked);
|
|
6024
6390
|
for (const [parameter, value] of Object.entries(snapshot.systemParameters)) this.setSystemParameter(parameter, value);
|
|
6025
6391
|
}
|
|
6026
6392
|
function getSynthesizerSnapshot() {
|
|
6027
6393
|
return {
|
|
6028
6394
|
midiParameters: { ...this.midiParameters },
|
|
6395
|
+
lockedMIDIParameters: { ...this.lockedMIDIParameters },
|
|
6029
6396
|
systemParameters: { ...this.systemParameters },
|
|
6030
6397
|
midiChannels: this.midiChannels.map((c) => c.getSnapshot()),
|
|
6031
6398
|
keyMappings: this.keyModifierManager.getMappings(),
|
|
@@ -6232,7 +6599,7 @@ var LowpassFilter = class LowpassFilter {
|
|
|
6232
6599
|
LowpassFilter.smoothingConstant = FILTER_SMOOTHING_FACTOR * (44100 / sampleRate);
|
|
6233
6600
|
const dummy = new LowpassFilter(sampleRate);
|
|
6234
6601
|
dummy.resonanceCb = 0;
|
|
6235
|
-
for (let i =
|
|
6602
|
+
for (let i = 8e3; i < 13500; i++) {
|
|
6236
6603
|
dummy.currentInitialFc = i;
|
|
6237
6604
|
dummy.calculateCoefficients(i);
|
|
6238
6605
|
}
|
|
@@ -7557,7 +7924,7 @@ var ModulatorSource = class ModulatorSource {
|
|
|
7557
7924
|
* If this field is set to false, the controller should be mapped with a minimum value of 0 and a maximum value of 1. This is also
|
|
7558
7925
|
* called Unipolar. Thus, it behaves similar to the Modulation Wheel controller of the MIDI specification.
|
|
7559
7926
|
*
|
|
7560
|
-
* If this field is set to true, the controller
|
|
7927
|
+
* If this field is set to true, the controller should be mapped with a minimum value of -1 and a maximum value of 1. This is also
|
|
7561
7928
|
* called Bipolar. Thus, it behaves similar to the Pitch Wheel controller of the MIDI specification.
|
|
7562
7929
|
*/
|
|
7563
7930
|
isBipolar;
|
|
@@ -8888,7 +9255,8 @@ var SoundFontSample = class extends BasicSample {
|
|
|
8888
9255
|
}
|
|
8889
9256
|
const audioData = new Float32Array(byteLength / 2);
|
|
8890
9257
|
const convertedSigned16 = new Int16Array(this.s16leData.buffer);
|
|
8891
|
-
|
|
9258
|
+
const l = convertedSigned16.length;
|
|
9259
|
+
for (let i = 0; i < l; i++) audioData[i] = convertedSigned16[i] / 32768;
|
|
8892
9260
|
this.audioData = audioData;
|
|
8893
9261
|
return audioData;
|
|
8894
9262
|
}
|
|
@@ -9430,7 +9798,8 @@ function readPCM(data, bytesPerSample) {
|
|
|
9430
9798
|
const sampleData = new Float32Array(sampleLength);
|
|
9431
9799
|
if (bytesPerSample === 2) {
|
|
9432
9800
|
const s16 = new Int16Array(data.buffer);
|
|
9433
|
-
|
|
9801
|
+
const s16l = s16.length;
|
|
9802
|
+
for (let i = 0; i < s16l; i++) sampleData[i] = s16[i] / 32768;
|
|
9434
9803
|
} else for (let i = 0; i < sampleData.length; i++) {
|
|
9435
9804
|
let sample = readLittleEndianIndexed(data, bytesPerSample);
|
|
9436
9805
|
if (isUnsigned) sampleData[i] = sample / normalizationFactor - .5;
|
|
@@ -10903,7 +11272,7 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
|
|
|
10903
11272
|
*/
|
|
10904
11273
|
toSF() {
|
|
10905
11274
|
SpessaLog.group("%cConverting DLS to SF2...", ConsoleColors.info);
|
|
10906
|
-
const soundBank = new BasicSoundBank();
|
|
11275
|
+
const soundBank = new BasicSoundBank("dls");
|
|
10907
11276
|
soundBank.soundBankInfo.version.minor = 4;
|
|
10908
11277
|
soundBank.soundBankInfo.version.major = 2;
|
|
10909
11278
|
soundBank.soundBankInfo = { ...this.soundBankInfo };
|
|
@@ -10926,6 +11295,14 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
10926
11295
|
*/
|
|
10927
11296
|
static isSF3DecoderReady = stb.isInitialized;
|
|
10928
11297
|
/**
|
|
11298
|
+
* The type of the sound bank that was loaded.
|
|
11299
|
+
* Either `sf2` for SoundFont2/SoundFont3 or `dls` for DownLoadable Sounds.
|
|
11300
|
+
*
|
|
11301
|
+
* Please note that SF3 or SFOGG files are parsed as `sf2` files, but with compressed samples.
|
|
11302
|
+
* The type is still `sf2`.
|
|
11303
|
+
*/
|
|
11304
|
+
type;
|
|
11305
|
+
/**
|
|
10929
11306
|
* Sound bank's info.
|
|
10930
11307
|
*/
|
|
10931
11308
|
soundBankInfo = {
|
|
@@ -10958,6 +11335,9 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
10958
11335
|
* If the sound bank has custom default modulators (DMOD).
|
|
10959
11336
|
*/
|
|
10960
11337
|
customDefaultModulators = false;
|
|
11338
|
+
constructor(type = "sf2") {
|
|
11339
|
+
this.type = type;
|
|
11340
|
+
}
|
|
10961
11341
|
_isXGBank = false;
|
|
10962
11342
|
/**
|
|
10963
11343
|
* Checks for XG drum sets and considers if this sound bank is XG.
|
|
@@ -11547,7 +11927,7 @@ var SoundFont2 = class extends BasicSoundBank {
|
|
|
11547
11927
|
* Initializes a new SoundFont2 Parser and parses the given data array
|
|
11548
11928
|
*/
|
|
11549
11929
|
constructor(arrayBuffer, warnDeprecated = true) {
|
|
11550
|
-
super();
|
|
11930
|
+
super("sf2");
|
|
11551
11931
|
if (warnDeprecated) throw new Error("Using the constructor directly is deprecated. Use SoundBankLoader.fromArrayBuffer() instead.");
|
|
11552
11932
|
const mainFileArray = new IndexedByteArray(arrayBuffer);
|
|
11553
11933
|
SpessaLog.group("%cParsing a SoundFont2 file...", ConsoleColors.info);
|
|
@@ -12338,22 +12718,30 @@ function dataEntry() {
|
|
|
12338
12718
|
default:
|
|
12339
12719
|
SpessaLog.info(`%cUnrecognized RPN for %c${this.channel}%c: %c(0x${rpnValue.toString(16)})%c data value: %c${dataValue}`, ConsoleColors.warn, ConsoleColors.recognized, ConsoleColors.warn, ConsoleColors.unrecognized, ConsoleColors.warn, ConsoleColors.value);
|
|
12340
12720
|
break;
|
|
12341
|
-
case RegisteredParameterTypes.pitchWheelRange:
|
|
12342
|
-
|
|
12721
|
+
case RegisteredParameterTypes.pitchWheelRange: {
|
|
12722
|
+
const range = dataValue / 128;
|
|
12723
|
+
this.setMIDIParameter("pitchWheelRange", range);
|
|
12724
|
+
SpessaLog.coolInfo(`Pitch Wheel Range for ${this.channel}`, range, "semitones");
|
|
12343
12725
|
break;
|
|
12726
|
+
}
|
|
12344
12727
|
case RegisteredParameterTypes.coarseTuning: {
|
|
12345
12728
|
const semitones = (dataValue >> 7) - 64;
|
|
12346
|
-
this.keyShift
|
|
12729
|
+
this.setMIDIParameter("keyShift", semitones);
|
|
12730
|
+
SpessaLog.coolInfo(`Key shift for ${this.channel}`, semitones);
|
|
12347
12731
|
break;
|
|
12348
12732
|
}
|
|
12349
12733
|
case RegisteredParameterTypes.fineTuning: {
|
|
12350
|
-
const
|
|
12351
|
-
this.fineTune
|
|
12734
|
+
const cents = (dataValue - 8192) / 81.92;
|
|
12735
|
+
this.setMIDIParameter("fineTune", cents);
|
|
12736
|
+
SpessaLog.coolInfo(`Fine tuning for ${this.channel}`, Math.round(cents), "cents");
|
|
12352
12737
|
break;
|
|
12353
12738
|
}
|
|
12354
|
-
case RegisteredParameterTypes.modulationDepth:
|
|
12355
|
-
|
|
12739
|
+
case RegisteredParameterTypes.modulationDepth: {
|
|
12740
|
+
const cents = dataValue / 1.28;
|
|
12741
|
+
this.setMIDIParameter("modulationDepth", cents);
|
|
12742
|
+
SpessaLog.coolInfo(`Modulation depth for ${this.channel}`, Math.round(cents), "cents");
|
|
12356
12743
|
break;
|
|
12744
|
+
}
|
|
12357
12745
|
case RegisteredParameterTypes.resetParameters: break;
|
|
12358
12746
|
}
|
|
12359
12747
|
return;
|
|
@@ -12599,9 +12987,9 @@ function noteOn(midiNote, velocity, emit = true) {
|
|
|
12599
12987
|
this.noteOff(midiNote);
|
|
12600
12988
|
return;
|
|
12601
12989
|
}
|
|
12602
|
-
velocity = clamp(velocity, 0, 127);
|
|
12603
12990
|
const black = this.synthCore.systemParameters.blackMIDIMode;
|
|
12604
12991
|
if (black && this.synthCore.voiceCount > 200 && velocity < 40 || black && velocity < 10 || this._systemParameters.isMuted || !this.preset) return;
|
|
12992
|
+
let realVelocity = clamp(velocity * (this._midiParameters.velocitySenseDepth / 64) + (this._midiParameters.velocitySenseOffset - 64) * 2, 0, 127);
|
|
12605
12993
|
let soundBankNote = midiNote + this.currentKeyShift;
|
|
12606
12994
|
if (midiNote > 127 || midiNote < 0) return;
|
|
12607
12995
|
const program = this.preset.program;
|
|
@@ -12609,7 +12997,7 @@ function noteOn(midiNote, velocity, emit = true) {
|
|
|
12609
12997
|
if (tune >= 0) soundBankNote = Math.trunc(tune);
|
|
12610
12998
|
if ((this._systemParameters.monophonicRetrigger ?? this.synthCore.systemParameters.monophonicRetrigger) || this._midiParameters.assignMode === 0) this.killNote(midiNote);
|
|
12611
12999
|
const keyVel = this.synthCore.keyModifierManager.getVelocity(this.channel, midiNote);
|
|
12612
|
-
if (keyVel > -1)
|
|
13000
|
+
if (keyVel > -1) realVelocity = keyVel;
|
|
12613
13001
|
let voiceGain = this.synthCore.keyModifierManager.getGain(this.channel, midiNote);
|
|
12614
13002
|
const previousNote = this.lastPortamentoNote;
|
|
12615
13003
|
const portamentoEnabled = this.portamentoForce || this._midiControllers[MIDIControllers.portamentoOnOff] >= 8192;
|
|
@@ -12630,7 +13018,7 @@ function noteOn(midiNote, velocity, emit = true) {
|
|
|
12630
13018
|
this.lastMonoNote = midiNote;
|
|
12631
13019
|
this.lastMonoVelocity = velocity;
|
|
12632
13020
|
}
|
|
12633
|
-
const voices = this.synthCore.getVoices(this.channel, soundBankNote,
|
|
13021
|
+
const voices = this.synthCore.getVoices(this.channel, soundBankNote, realVelocity);
|
|
12634
13022
|
let panOverride = 0;
|
|
12635
13023
|
let exclusiveOverride = 0;
|
|
12636
13024
|
let pitchOffset = 0;
|
|
@@ -12941,7 +13329,7 @@ function computeModulator(voice, pitchWheel, modulatorIndex) {
|
|
|
12941
13329
|
let computedValue = sourceValue * secondSrcValue * transformAmount;
|
|
12942
13330
|
if (modulator.transformType === 2) computedValue = Math.abs(computedValue);
|
|
12943
13331
|
if (modulator.isDefaultResonantModulator) voice.resonanceOffset = Math.max(0, computedValue / 2);
|
|
12944
|
-
if (modulator.isModWheelModulator) computedValue *= this._midiParameters.modulationDepth;
|
|
13332
|
+
if (modulator.isModWheelModulator) computedValue *= this._midiParameters.modulationDepth / 50;
|
|
12945
13333
|
voice.modulatorValues[modulatorIndex] = computedValue;
|
|
12946
13334
|
return computedValue;
|
|
12947
13335
|
}
|
|
@@ -13064,12 +13452,13 @@ function getChannelSnapshot() {
|
|
|
13064
13452
|
overrides: this.generators.overrides.slice()
|
|
13065
13453
|
},
|
|
13066
13454
|
midiParameters: { ...this._midiParameters },
|
|
13455
|
+
lockedMIDIParameters: { ...this.lockedMIDIParameters },
|
|
13067
13456
|
systemParameters: { ...this._systemParameters },
|
|
13068
13457
|
octaveTuning: this.octaveTuning.slice(),
|
|
13069
13458
|
perNotePitch: this.perNotePitch,
|
|
13070
13459
|
drumParams: this.drumParams.map((d) => ({ ...d })),
|
|
13071
13460
|
drumChannel: this._drumChannel,
|
|
13072
|
-
|
|
13461
|
+
channel: this.channel
|
|
13073
13462
|
};
|
|
13074
13463
|
}
|
|
13075
13464
|
function applySnapshot(snapshot) {
|
|
@@ -13088,6 +13477,7 @@ function applySnapshot(snapshot) {
|
|
|
13088
13477
|
if (snapshot.patch) this.setPatch(snapshot.patch);
|
|
13089
13478
|
this.lockedSystem = snapshot.lockedSystem;
|
|
13090
13479
|
for (const [parameter, value] of Object.entries(snapshot.midiParameters)) this.setMIDIParameter(parameter, value);
|
|
13480
|
+
for (const [parameter, isLocked] of Object.entries(snapshot.lockedMIDIParameters)) this.lockMIDIParameter(parameter, isLocked);
|
|
13091
13481
|
for (const [parameter, value] of Object.entries(snapshot.systemParameters)) this.setSystemParameter(parameter, value);
|
|
13092
13482
|
}
|
|
13093
13483
|
//#endregion
|
|
@@ -13096,18 +13486,53 @@ const DEFAULT_CHANNEL_MIDI_PARAMETERS = {
|
|
|
13096
13486
|
pitchWheel: 8192,
|
|
13097
13487
|
pitchWheelRange: 2,
|
|
13098
13488
|
pressure: 0,
|
|
13099
|
-
modulationDepth:
|
|
13489
|
+
modulationDepth: 50,
|
|
13100
13490
|
rxChannel: 0,
|
|
13101
13491
|
polyMode: true,
|
|
13102
13492
|
keyShift: 0,
|
|
13493
|
+
fineTune: 0,
|
|
13103
13494
|
randomPan: false,
|
|
13104
13495
|
assignMode: 2,
|
|
13105
13496
|
efxAssign: false,
|
|
13106
13497
|
cc1: 16,
|
|
13107
13498
|
cc2: 17,
|
|
13108
13499
|
drumMap: 0,
|
|
13109
|
-
|
|
13500
|
+
velocitySenseDepth: 64,
|
|
13501
|
+
velocitySenseOffset: 64
|
|
13110
13502
|
};
|
|
13503
|
+
/**
|
|
13504
|
+
* Sets a channel MIDI parameter of the synthesizer.
|
|
13505
|
+
* @param parameter The type of the channel MIDI parameter to set.
|
|
13506
|
+
* @param value The value to set for the channel MIDI parameter.
|
|
13507
|
+
* @internal
|
|
13508
|
+
*/
|
|
13509
|
+
function setMIDIParameterInternal$1(parameter, value) {
|
|
13510
|
+
if (this.lockedMIDIParameters[parameter]) return;
|
|
13511
|
+
this._midiParameters[parameter] = value;
|
|
13512
|
+
switch (parameter) {
|
|
13513
|
+
case "pitchWheel":
|
|
13514
|
+
this.computeModulatorsAll(0, ModulatorControllerSources.pitchWheel);
|
|
13515
|
+
break;
|
|
13516
|
+
case "pressure":
|
|
13517
|
+
this.computeModulatorsAll(0, ModulatorControllerSources.channelPressure);
|
|
13518
|
+
break;
|
|
13519
|
+
}
|
|
13520
|
+
this.updateInternalParams();
|
|
13521
|
+
this.synthCore.callEvent("channelParamChange", {
|
|
13522
|
+
channel: this.channel,
|
|
13523
|
+
parameter,
|
|
13524
|
+
value
|
|
13525
|
+
});
|
|
13526
|
+
}
|
|
13527
|
+
/**
|
|
13528
|
+
* Locks or unlocks a given Channel MIDI Parameter.
|
|
13529
|
+
* This prevents any changes to it until it's unlocked.
|
|
13530
|
+
* @param parameter The Channel MIDI Parameter to lock.
|
|
13531
|
+
* @param isLocked If the parameter should be locked.
|
|
13532
|
+
*/
|
|
13533
|
+
function lockMIDIParameterInternal$1(parameter, isLocked) {
|
|
13534
|
+
this.lockedMIDIParameters[parameter] = isLocked;
|
|
13535
|
+
}
|
|
13111
13536
|
//#endregion
|
|
13112
13537
|
//#region src/synthesizer/audio_engine/channel/parameters/system.ts
|
|
13113
13538
|
const DEFAULT_CHANNEL_SYSTEM_PARAMETERS = {
|
|
@@ -13154,13 +13579,6 @@ var MIDIChannel = class {
|
|
|
13154
13579
|
*/
|
|
13155
13580
|
pitchWheels = new Int16Array(128).fill(8192);
|
|
13156
13581
|
/**
|
|
13157
|
-
* An array indicating if a controller, at the equivalent index in the midiControllers array, is locked
|
|
13158
|
-
* (i.e., not allowed changing).
|
|
13159
|
-
* A locked controller cannot be modified.
|
|
13160
|
-
* @internal
|
|
13161
|
-
*/
|
|
13162
|
-
lockedControllers = new Array(128).fill(false);
|
|
13163
|
-
/**
|
|
13164
13582
|
* Parameters for each drum instrument.
|
|
13165
13583
|
* @internal
|
|
13166
13584
|
*/
|
|
@@ -13211,6 +13629,13 @@ var MIDIChannel = class {
|
|
|
13211
13629
|
*/
|
|
13212
13630
|
setSystemParameter = setSystemParameterInternal.bind(this);
|
|
13213
13631
|
/**
|
|
13632
|
+
* Locks or unlocks a given Channel MIDI Parameter.
|
|
13633
|
+
* This prevents any changes to it until it's unlocked.
|
|
13634
|
+
* @param parameter The Channel MIDI Parameter to lock.
|
|
13635
|
+
* @param isLocked If the parameter should be locked.
|
|
13636
|
+
*/
|
|
13637
|
+
lockMIDIParameter = lockMIDIParameterInternal$1.bind(this);
|
|
13638
|
+
/**
|
|
13214
13639
|
* Sends a "MIDI Note on" message and starts a note.
|
|
13215
13640
|
* @param midiNote The MIDI note number (0-127).
|
|
13216
13641
|
* @param velocity The velocity of the note (0-127). If less than 1, it will send a note off instead.
|
|
@@ -13263,6 +13688,20 @@ var MIDIChannel = class {
|
|
|
13263
13688
|
*/
|
|
13264
13689
|
renderVoice = renderVoice.bind(this);
|
|
13265
13690
|
/**
|
|
13691
|
+
* Sets a channel MIDI parameter of the synthesizer.
|
|
13692
|
+
* @param parameter The type of the channel MIDI parameter to set.
|
|
13693
|
+
* @param value The value to set for the channel MIDI parameter.
|
|
13694
|
+
* @internal
|
|
13695
|
+
*/
|
|
13696
|
+
setMIDIParameter = setMIDIParameterInternal$1.bind(this);
|
|
13697
|
+
/**
|
|
13698
|
+
* An array indicating if a controller, at the equivalent index in the midiControllers array, is locked
|
|
13699
|
+
* (i.e., not allowed changing).
|
|
13700
|
+
* A locked controller cannot be modified.
|
|
13701
|
+
* @internal
|
|
13702
|
+
*/
|
|
13703
|
+
lockedControllers = new Array(128).fill(false);
|
|
13704
|
+
/**
|
|
13266
13705
|
* An array of MIDI controllers for the channel.
|
|
13267
13706
|
* This array is used to store the state of various MIDI controllers
|
|
13268
13707
|
* such as volume, pan, modulation, etc.
|
|
@@ -13291,6 +13730,13 @@ var MIDIChannel = class {
|
|
|
13291
13730
|
* @internal
|
|
13292
13731
|
*/
|
|
13293
13732
|
dataEntry = dataEntry.bind(this);
|
|
13733
|
+
/**
|
|
13734
|
+
* An object indicating if a Channel MIDI parameter, at the equivalent key, is locked
|
|
13735
|
+
* (i.e., not allowed changing).
|
|
13736
|
+
* A locked parameter cannot be modified.
|
|
13737
|
+
* @internal
|
|
13738
|
+
*/
|
|
13739
|
+
lockedMIDIParameters = Object.fromEntries(Object.keys(DEFAULT_CHANNEL_MIDI_PARAMETERS).map((key) => [key, false]));
|
|
13294
13740
|
_midiParameters = { ...DEFAULT_CHANNEL_MIDI_PARAMETERS };
|
|
13295
13741
|
/**
|
|
13296
13742
|
* All system parameters of this channel.
|
|
@@ -13518,29 +13964,6 @@ var MIDIChannel = class {
|
|
|
13518
13964
|
});
|
|
13519
13965
|
}
|
|
13520
13966
|
/**
|
|
13521
|
-
* Sets a channel MIDI parameter of the synthesizer.
|
|
13522
|
-
* @param parameter The type of the channel MIDI parameter to set.
|
|
13523
|
-
* @param value The value to set for the channel MIDI parameter.
|
|
13524
|
-
* @internal
|
|
13525
|
-
*/
|
|
13526
|
-
setMIDIParameter(parameter, value) {
|
|
13527
|
-
this._midiParameters[parameter] = value;
|
|
13528
|
-
switch (parameter) {
|
|
13529
|
-
case "pitchWheel":
|
|
13530
|
-
this.computeModulatorsAll(0, ModulatorControllerSources.pitchWheel);
|
|
13531
|
-
break;
|
|
13532
|
-
case "pressure":
|
|
13533
|
-
this.computeModulatorsAll(0, ModulatorControllerSources.channelPressure);
|
|
13534
|
-
break;
|
|
13535
|
-
}
|
|
13536
|
-
this.updateInternalParams();
|
|
13537
|
-
this.synthCore.callEvent("channelParamChange", {
|
|
13538
|
-
channel: this.channel,
|
|
13539
|
-
parameter,
|
|
13540
|
-
value
|
|
13541
|
-
});
|
|
13542
|
-
}
|
|
13543
|
-
/**
|
|
13544
13967
|
* @internal
|
|
13545
13968
|
*/
|
|
13546
13969
|
clearVoiceCount() {
|
|
@@ -13558,49 +13981,6 @@ var MIDIChannel = class {
|
|
|
13558
13981
|
for (let i = 0; i < 128; i++) this.octaveTuning[i] = tuning[i % 12];
|
|
13559
13982
|
}
|
|
13560
13983
|
/**
|
|
13561
|
-
* Sets the modulation depth for the channel.
|
|
13562
|
-
* @param cents The modulation depth in cents to set.
|
|
13563
|
-
* @param log If true, logs the change to the console.
|
|
13564
|
-
* @remarks
|
|
13565
|
-
* This method sets the modulation depth for the channel by converting the given cents value into a
|
|
13566
|
-
* multiplier. The MIDI specification assumes the default modulation depth is 50 cents,
|
|
13567
|
-
* but it may vary for different sound banks.
|
|
13568
|
-
* For example, if you want a modulation depth of 100 cents,
|
|
13569
|
-
* the multiplier will be 2,
|
|
13570
|
-
* which, for a preset with a depth of 50,
|
|
13571
|
-
* will create a total modulation depth of 100 cents.
|
|
13572
|
-
* @internal
|
|
13573
|
-
*/
|
|
13574
|
-
modulationDepth(cents, log = true) {
|
|
13575
|
-
this.setMIDIParameter("modulationDepth", cents / 50);
|
|
13576
|
-
if (!log) return;
|
|
13577
|
-
SpessaLog.info(`%cChannel ${this.channel} modulation depth. Cents: %c${Math.round(cents)}`, ConsoleColors.info, ConsoleColors.value);
|
|
13578
|
-
}
|
|
13579
|
-
/**
|
|
13580
|
-
* Sets the channel's key shift (MIDI).
|
|
13581
|
-
* @param shift the key shift.
|
|
13582
|
-
* @param log If true, logs the change to the console.
|
|
13583
|
-
* @internal
|
|
13584
|
-
*/
|
|
13585
|
-
keyShift(shift, log = true) {
|
|
13586
|
-
if (this._drumChannel) shift = 0;
|
|
13587
|
-
if (this._midiParameters.keyShift === shift) return;
|
|
13588
|
-
this.setMIDIParameter("keyShift", shift);
|
|
13589
|
-
if (!log) return;
|
|
13590
|
-
SpessaLog.info(`%cKey shift for %c${this.channel}%c is now set to %c${shift}.`, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info, ConsoleColors.value);
|
|
13591
|
-
}
|
|
13592
|
-
/**
|
|
13593
|
-
* Sets the channel's tuning.
|
|
13594
|
-
* @param cents The tuning in cents to set.
|
|
13595
|
-
* @param log If true, logs the change to the console.
|
|
13596
|
-
* @internal
|
|
13597
|
-
*/
|
|
13598
|
-
fineTune(cents, log = true) {
|
|
13599
|
-
this.setMIDIParameter("fineTune", cents);
|
|
13600
|
-
if (!log) return;
|
|
13601
|
-
SpessaLog.info(`%cFine tuning for %c${this.channel}%c is now set to %c${Math.round(cents)}%c cents.`, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info, ConsoleColors.value, ConsoleColors.info);
|
|
13602
|
-
}
|
|
13603
|
-
/**
|
|
13604
13984
|
* Sets the pitch of the given channel.
|
|
13605
13985
|
* @param pitch The pitch (0 - 16383)
|
|
13606
13986
|
* @param midiNote The MIDI note number, pass -1 for the regular pitch wheel
|
|
@@ -13640,17 +14020,6 @@ var MIDIChannel = class {
|
|
|
13640
14020
|
});
|
|
13641
14021
|
}
|
|
13642
14022
|
/**
|
|
13643
|
-
* Sets the pitch wheel range for this channel.
|
|
13644
|
-
* @param range range in semitones.
|
|
13645
|
-
* @param log If true, logs the change to the console.
|
|
13646
|
-
* @internal
|
|
13647
|
-
*/
|
|
13648
|
-
pitchWheelRange(range, log = true) {
|
|
13649
|
-
this.setMIDIParameter("pitchWheelRange", range);
|
|
13650
|
-
if (!log) return;
|
|
13651
|
-
SpessaLog.coolInfo(`Pitch Wheel Range for ${this.channel}`, range, "semitones");
|
|
13652
|
-
}
|
|
13653
|
-
/**
|
|
13654
14023
|
* @internal
|
|
13655
14024
|
*/
|
|
13656
14025
|
updateInternalParams() {
|
|
@@ -13811,7 +14180,7 @@ var MIDIChannel = class {
|
|
|
13811
14180
|
setDrumFlag(isDrum) {
|
|
13812
14181
|
if (this._systemParameters.presetLock || !this.preset || this._drumChannel === isDrum) return;
|
|
13813
14182
|
this._drumChannel = isDrum;
|
|
13814
|
-
this.
|
|
14183
|
+
this.updateInternalParams();
|
|
13815
14184
|
}
|
|
13816
14185
|
};
|
|
13817
14186
|
//#endregion
|
|
@@ -13972,7 +14341,8 @@ function universalSystemExclusive(syx, channelOffset = 0) {
|
|
|
13972
14341
|
break;
|
|
13973
14342
|
case 1: {
|
|
13974
14343
|
const vol = syx[5] << 7 | syx[4];
|
|
13975
|
-
|
|
14344
|
+
const gain = Math.pow(vol / 16383, 2);
|
|
14345
|
+
this.setMIDIParameter("gain", gain);
|
|
13976
14346
|
SpessaLog.gmInfo("Master Volume", vol);
|
|
13977
14347
|
break;
|
|
13978
14348
|
}
|
|
@@ -13983,7 +14353,7 @@ function universalSystemExclusive(syx, channelOffset = 0) {
|
|
|
13983
14353
|
break;
|
|
13984
14354
|
}
|
|
13985
14355
|
case 3: {
|
|
13986
|
-
const cents = ((syx[5] << 7 | syx[
|
|
14356
|
+
const cents = ((syx[5] << 7 | syx[4]) - 8192) / 81.92;
|
|
13987
14357
|
this.setMIDIParameter("fineTune", cents);
|
|
13988
14358
|
SpessaLog.gmInfo("Master Fine Tuning", cents, "cents");
|
|
13989
14359
|
break;
|
|
@@ -14154,6 +14524,7 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14154
14524
|
}
|
|
14155
14525
|
case 4:
|
|
14156
14526
|
SpessaLog.gsInfo("Master Volume", data);
|
|
14527
|
+
this.setMIDIParameter("gain", data / 127);
|
|
14157
14528
|
break;
|
|
14158
14529
|
case 5: {
|
|
14159
14530
|
const transpose = data - 64;
|
|
@@ -14161,10 +14532,12 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14161
14532
|
this.setMIDIParameter("keyShift", transpose);
|
|
14162
14533
|
break;
|
|
14163
14534
|
}
|
|
14164
|
-
case 6:
|
|
14165
|
-
|
|
14166
|
-
|
|
14535
|
+
case 6: {
|
|
14536
|
+
const pan = (data - 64) / 63;
|
|
14537
|
+
SpessaLog.gsInfo("Master Pan", pan);
|
|
14538
|
+
this.setMIDIParameter("pan", pan);
|
|
14167
14539
|
break;
|
|
14540
|
+
}
|
|
14168
14541
|
case 127:
|
|
14169
14542
|
if (data === 0) {
|
|
14170
14543
|
SpessaLog.coolInfo("MIDI System", "Roland GS");
|
|
@@ -14195,6 +14568,7 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14195
14568
|
case 0: {
|
|
14196
14569
|
const patchName = readBinaryString(syx, 16, 7);
|
|
14197
14570
|
SpessaLog.gsInfo("Patch name", patchName);
|
|
14571
|
+
this.callEvent("displayMessage", [...syx]);
|
|
14198
14572
|
break;
|
|
14199
14573
|
}
|
|
14200
14574
|
case 48:
|
|
@@ -14530,12 +14904,21 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14530
14904
|
}
|
|
14531
14905
|
case 22: {
|
|
14532
14906
|
const keyShift = data - 64;
|
|
14533
|
-
ch.
|
|
14907
|
+
ch.setMIDIParameter("keyShift", keyShift);
|
|
14908
|
+
SpessaLog.gsInfo(`Key Shift for ${channel}`, keyShift);
|
|
14534
14909
|
return;
|
|
14535
14910
|
}
|
|
14536
14911
|
case 25:
|
|
14537
14912
|
ch.controllerChange(MIDIControllers.mainVolume, data);
|
|
14538
14913
|
return;
|
|
14914
|
+
case 26:
|
|
14915
|
+
ch.setMIDIParameter("velocitySenseDepth", data);
|
|
14916
|
+
SpessaLog.gsInfo(`Velocity Sense Depth for ${channel}`, data);
|
|
14917
|
+
return;
|
|
14918
|
+
case 27:
|
|
14919
|
+
ch.setMIDIParameter("velocitySenseOffset", data);
|
|
14920
|
+
SpessaLog.gsInfo(`Velocity Sense Offset for ${channel}`, data);
|
|
14921
|
+
return;
|
|
14539
14922
|
case 28: {
|
|
14540
14923
|
const panPosition = data;
|
|
14541
14924
|
const randomPan = panPosition === 0;
|
|
@@ -14546,11 +14929,11 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14546
14929
|
}
|
|
14547
14930
|
case 31:
|
|
14548
14931
|
ch.setMIDIParameter("cc1", data);
|
|
14549
|
-
SpessaLog.gsInfo(
|
|
14932
|
+
SpessaLog.gsInfo(`CC1 Controller Number for ${channel}`, data);
|
|
14550
14933
|
break;
|
|
14551
14934
|
case 32:
|
|
14552
14935
|
ch.setMIDIParameter("cc2", data);
|
|
14553
|
-
SpessaLog.gsInfo(
|
|
14936
|
+
SpessaLog.gsInfo(`CC2 Controller Number for ${channel}`, data);
|
|
14554
14937
|
break;
|
|
14555
14938
|
case 33:
|
|
14556
14939
|
ch.controllerChange(MIDIControllers.chorusDepth, data);
|
|
@@ -14559,8 +14942,9 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14559
14942
|
ch.controllerChange(MIDIControllers.reverbDepth, data);
|
|
14560
14943
|
break;
|
|
14561
14944
|
case 42: {
|
|
14562
|
-
const
|
|
14563
|
-
ch.fineTune
|
|
14945
|
+
const cents = ((data << 7 | syx[8]) - 8192) / 81.92;
|
|
14946
|
+
ch.setMIDIParameter("fineTune", cents);
|
|
14947
|
+
SpessaLog.gsInfo(`Fine tuning for ${channel}`, Math.round(cents), "cents");
|
|
14564
14948
|
break;
|
|
14565
14949
|
}
|
|
14566
14950
|
case 44:
|
|
@@ -14595,7 +14979,7 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14595
14979
|
const newTuning = new Int8Array(12);
|
|
14596
14980
|
for (let i = 0; i < tuningBytes; i++) newTuning[i] = syx[i + 7] - 64;
|
|
14597
14981
|
ch.setOctaveTuning(newTuning);
|
|
14598
|
-
SpessaLog.gsInfo(`Octave Scale Tuning
|
|
14982
|
+
SpessaLog.gsInfo(`Octave Scale Tuning for ${channel}`, newTuning.join(", "));
|
|
14599
14983
|
break;
|
|
14600
14984
|
}
|
|
14601
14985
|
}
|
|
@@ -14611,7 +14995,8 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14611
14995
|
case 0:
|
|
14612
14996
|
if ((a3 & 15) === 4) {
|
|
14613
14997
|
const cents = data / 127 * 600;
|
|
14614
|
-
ch.modulationDepth
|
|
14998
|
+
ch.setMIDIParameter("modulationDepth", cents);
|
|
14999
|
+
SpessaLog.gsInfo(`Modulation depth for ${channel}`, Math.round(cents), "cents");
|
|
14615
15000
|
break;
|
|
14616
15001
|
}
|
|
14617
15002
|
ch.dynamicModulators.setupReceiver(a3, data, MIDIControllers.modulationWheel, true, "mod wheel");
|
|
@@ -14619,7 +15004,8 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14619
15004
|
case 16:
|
|
14620
15005
|
if ((a3 & 15) === 0) {
|
|
14621
15006
|
const centeredValue = data - 64;
|
|
14622
|
-
ch.pitchWheelRange
|
|
15007
|
+
ch.setMIDIParameter("pitchWheelRange", centeredValue);
|
|
15008
|
+
SpessaLog.gsInfo(`Pitch Wheel Range for ${channel}`, centeredValue, "semitones");
|
|
14623
15009
|
break;
|
|
14624
15010
|
}
|
|
14625
15011
|
ch.dynamicModulators.setupReceiver(a3, data, ModulatorControllerSources.pitchWheel, false, "pitch wheel", true);
|
|
@@ -14649,7 +15035,6 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
14649
15035
|
ch.controllerChange(MIDIControllers.bankSelectLSB, data);
|
|
14650
15036
|
break;
|
|
14651
15037
|
case 34: {
|
|
14652
|
-
if (this.systemParameters.insertionEffectLock) return;
|
|
14653
15038
|
const efx = data === 1;
|
|
14654
15039
|
ch.setMIDIParameter("efxAssign", efx);
|
|
14655
15040
|
this.insertionActive ||= efx;
|
|
@@ -14859,12 +15244,21 @@ function yamahaSystemExclusive(syx, channelOffset = 0) {
|
|
|
14859
15244
|
}
|
|
14860
15245
|
case 8: {
|
|
14861
15246
|
const keyShift = data - 64;
|
|
14862
|
-
ch.
|
|
15247
|
+
ch.setMIDIParameter("keyShift", keyShift);
|
|
15248
|
+
SpessaLog.xgInfo(`Key Shift on ${channel}`, keyShift);
|
|
14863
15249
|
break;
|
|
14864
15250
|
}
|
|
14865
15251
|
case 11:
|
|
14866
15252
|
ch.controllerChange(MIDIControllers.mainVolume, data);
|
|
14867
15253
|
break;
|
|
15254
|
+
case 12:
|
|
15255
|
+
ch.setMIDIParameter("velocitySenseDepth", data);
|
|
15256
|
+
SpessaLog.xgInfo(`Velocity Sense Depth on ${channel}`, data);
|
|
15257
|
+
return;
|
|
15258
|
+
case 13:
|
|
15259
|
+
ch.setMIDIParameter("velocitySenseOffset", data);
|
|
15260
|
+
SpessaLog.xgInfo(`Velocity Sense Offset on ${channel}`, data);
|
|
15261
|
+
return;
|
|
14868
15262
|
case 14: {
|
|
14869
15263
|
const pan = data;
|
|
14870
15264
|
const randomPan = pan === 0;
|
|
@@ -14905,7 +15299,8 @@ function yamahaSystemExclusive(syx, channelOffset = 0) {
|
|
|
14905
15299
|
break;
|
|
14906
15300
|
case 35: {
|
|
14907
15301
|
const centeredValue = data - 64;
|
|
14908
|
-
ch.pitchWheelRange
|
|
15302
|
+
ch.setMIDIParameter("pitchWheelRange", centeredValue);
|
|
15303
|
+
SpessaLog.xgInfo(`Pitch Wheel Range for ${channel}`, centeredValue, "semitones");
|
|
14909
15304
|
}
|
|
14910
15305
|
}
|
|
14911
15306
|
return;
|
|
@@ -19368,6 +19763,7 @@ const DEFAULT_GLOBAL_MIDI_PARAMETERS = {
|
|
|
19368
19763
|
* @param value The value to set for the global MIDI parameter.
|
|
19369
19764
|
*/
|
|
19370
19765
|
function setMIDIParameterInternal(parameter, value) {
|
|
19766
|
+
if (this.lockedMIDIParameters[parameter]) return;
|
|
19371
19767
|
this.midiParameters[parameter] = value;
|
|
19372
19768
|
for (const ch of this.midiChannels) ch.updateInternalParams();
|
|
19373
19769
|
this.callEvent("globalParamChange", {
|
|
@@ -19376,15 +19772,13 @@ function setMIDIParameterInternal(parameter, value) {
|
|
|
19376
19772
|
});
|
|
19377
19773
|
}
|
|
19378
19774
|
/**
|
|
19379
|
-
*
|
|
19380
|
-
*
|
|
19775
|
+
* Locks or unlocks a given Global MIDI Parameter.
|
|
19776
|
+
* This prevents any changes to it until it's unlocked.
|
|
19777
|
+
* @param parameter The Global MIDI Parameter to lock.
|
|
19778
|
+
* @param isLocked If the parameter should be locked.
|
|
19381
19779
|
*/
|
|
19382
|
-
function
|
|
19383
|
-
this.
|
|
19384
|
-
this.setMIDIParameter("pan", 0);
|
|
19385
|
-
this.setMIDIParameter("keyShift", 0);
|
|
19386
|
-
this.setMIDIParameter("fineTune", 0);
|
|
19387
|
-
this.setMIDIParameter("system", system);
|
|
19780
|
+
function lockMIDIParameterInternal(parameter, isLocked) {
|
|
19781
|
+
this.lockedMIDIParameters[parameter] = isLocked;
|
|
19388
19782
|
}
|
|
19389
19783
|
//#endregion
|
|
19390
19784
|
//#region src/synthesizer/audio_engine/synthesizer_core.ts
|
|
@@ -19450,6 +19844,13 @@ var SynthesizerCore = class {
|
|
|
19450
19844
|
*/
|
|
19451
19845
|
tunings = new Float32Array(16384).fill(-1);
|
|
19452
19846
|
/**
|
|
19847
|
+
* An object indicating if a Global MIDI parameter, at the equivalent key, is locked
|
|
19848
|
+
* (i.e., not allowed changing).
|
|
19849
|
+
* A locked parameter cannot be modified.
|
|
19850
|
+
* @internal
|
|
19851
|
+
*/
|
|
19852
|
+
lockedMIDIParameters = Object.fromEntries(Object.keys(DEFAULT_GLOBAL_MIDI_PARAMETERS).map((key) => [key, false]));
|
|
19853
|
+
/**
|
|
19453
19854
|
* The global MIDI parameters of the synthesizer.
|
|
19454
19855
|
*/
|
|
19455
19856
|
midiParameters = { ...DEFAULT_GLOBAL_MIDI_PARAMETERS };
|
|
@@ -19490,6 +19891,13 @@ var SynthesizerCore = class {
|
|
|
19490
19891
|
*/
|
|
19491
19892
|
cachedVoices = /* @__PURE__ */ new Map();
|
|
19492
19893
|
/**
|
|
19894
|
+
* Locks or unlocks a given Global MIDI Parameter.
|
|
19895
|
+
* This prevents any changes to it until it's unlocked.
|
|
19896
|
+
* @param parameter The Global MIDI Parameter to lock.
|
|
19897
|
+
* @param isLocked If the parameter should be locked.
|
|
19898
|
+
*/
|
|
19899
|
+
lockMIDIParameter = lockMIDIParameterInternal.bind(this);
|
|
19900
|
+
/**
|
|
19493
19901
|
* Sets a system parameter of the synthesizer.
|
|
19494
19902
|
* @param type The type of the system parameter to set.
|
|
19495
19903
|
* @param value The value to set for the system parameter.
|
|
@@ -19519,7 +19927,6 @@ var SynthesizerCore = class {
|
|
|
19519
19927
|
* @param value The value to set for the global MIDI parameter.
|
|
19520
19928
|
*/
|
|
19521
19929
|
setMIDIParameter = setMIDIParameterInternal.bind(this);
|
|
19522
|
-
resetMIDIParameters = resetMIDIParametersInternal.bind(this);
|
|
19523
19930
|
/**
|
|
19524
19931
|
* The fallback processor when the requested insertion is not available.
|
|
19525
19932
|
*/
|
|
@@ -19737,10 +20144,15 @@ var SynthesizerCore = class {
|
|
|
19737
20144
|
* Executes a full system reset of the synthesizer.
|
|
19738
20145
|
* This will reset all controllers to their default values,
|
|
19739
20146
|
* except for the locked controllers.
|
|
20147
|
+
* @param system The MIDI system to reset the synthesizer to. Defaults to `gs`.
|
|
19740
20148
|
*/
|
|
19741
20149
|
reset(system = "gs") {
|
|
19742
20150
|
this.callEvent("reset", system);
|
|
19743
|
-
this.
|
|
20151
|
+
this.setMIDIParameter("system", system);
|
|
20152
|
+
this.setMIDIParameter("gain", 1);
|
|
20153
|
+
this.setMIDIParameter("pan", 0);
|
|
20154
|
+
this.setMIDIParameter("keyShift", 0);
|
|
20155
|
+
this.setMIDIParameter("fineTune", 0);
|
|
19744
20156
|
this.tunings.fill(-1);
|
|
19745
20157
|
this.portSelectChannelOffset = 0;
|
|
19746
20158
|
this.customChannelNumbers = false;
|
|
@@ -19756,7 +20168,7 @@ var SynthesizerCore = class {
|
|
|
19756
20168
|
this.processSplit([[left, right]], left, right, startIndex, sampleCount);
|
|
19757
20169
|
}
|
|
19758
20170
|
/**
|
|
19759
|
-
* The main rendering pipeline, renders all voices
|
|
20171
|
+
* The main rendering pipeline, renders all voices and processes the effects:
|
|
19760
20172
|
* ```
|
|
19761
20173
|
* ┌────────────────────────────────┐
|
|
19762
20174
|
* │ Voice Processor │
|
|
@@ -19874,8 +20286,7 @@ var SynthesizerCore = class {
|
|
|
19874
20286
|
getInsertionSnapshot() {
|
|
19875
20287
|
return {
|
|
19876
20288
|
type: this.insertionProcessor.type,
|
|
19877
|
-
params: this.insertionParams.slice()
|
|
19878
|
-
channels: this.midiChannels.map((c) => c.midiParameters.efxAssign)
|
|
20289
|
+
params: this.insertionParams.slice()
|
|
19879
20290
|
};
|
|
19880
20291
|
}
|
|
19881
20292
|
resetInsertionParams() {
|
|
@@ -20407,7 +20818,7 @@ var SpessaSynthProcessor = class {
|
|
|
20407
20818
|
*/
|
|
20408
20819
|
synthCore;
|
|
20409
20820
|
/**
|
|
20410
|
-
*
|
|
20821
|
+
* For applying the snapshot after an override sound bank too.
|
|
20411
20822
|
*/
|
|
20412
20823
|
savedSnapshot;
|
|
20413
20824
|
/**
|
|
@@ -20510,12 +20921,21 @@ var SpessaSynthProcessor = class {
|
|
|
20510
20921
|
SpessaLog.warn(`No preset found for ${MIDIPatchTools.toMIDIString(patch)}! Did you forget to add a sound bank?`);
|
|
20511
20922
|
};
|
|
20512
20923
|
/**
|
|
20924
|
+
* Locks or unlocks a given Global MIDI Parameter.
|
|
20925
|
+
* This prevents any changes to it until it's unlocked.
|
|
20926
|
+
* @param parameter The Global MIDI Parameter to lock.
|
|
20927
|
+
* @param isLocked If the parameter should be locked.
|
|
20928
|
+
*/
|
|
20929
|
+
lockMIDIParameter(parameter, isLocked) {
|
|
20930
|
+
this.synthCore.lockMIDIParameter(parameter, isLocked);
|
|
20931
|
+
}
|
|
20932
|
+
/**
|
|
20513
20933
|
* Sets a system parameter of the synthesizer.
|
|
20514
|
-
* @param
|
|
20934
|
+
* @param parameter The type of the system parameter to set.
|
|
20515
20935
|
* @param value The value to set for the system parameter.
|
|
20516
20936
|
*/
|
|
20517
|
-
setSystemParameter(
|
|
20518
|
-
this.synthCore.setSystemParameter(
|
|
20937
|
+
setSystemParameter(parameter, value) {
|
|
20938
|
+
this.synthCore.setSystemParameter(parameter, value);
|
|
20519
20939
|
}
|
|
20520
20940
|
/**
|
|
20521
20941
|
* Executes a full synthesizer reset.
|